Compare commits

..

201 Commits

Author SHA1 Message Date
Marek Kotewicz
70389b2888 Merge pull request #922 from ethcore/beta-staging
Master to beta v1.0.2
2016-04-11 16:42:09 +02:00
debris
8f63f6d32a fixed transaction deadlock 2016-04-11 14:39:01 +02:00
debris
f02f99cd3f fixed eth_getLogs 2016-04-11 14:38:18 +02:00
Gav Wood
bbbd6f01fc --unlock is comma-delimited. 2016-04-11 14:37:10 +02:00
debris
ddbce9f014 removed empty lines 2016-04-11 14:36:27 +02:00
debris
423c5e87da fixed eth_getLogs 2016-04-11 14:36:16 +02:00
NikVolf
a15d2882d0 removing lower-level defaults 2016-04-11 14:36:02 +02:00
NikVolf
6d5809757f create provided custom dir for keys if none 2016-04-11 14:35:55 +02:00
Gav Wood
e179b0e0c2 Merge pull request #908 from ethcore/beta-staging
Master to beta 1.0.2
2016-04-09 10:18:41 -07:00
arkpar
37eb0fb04e Use new json RPC server 2016-04-09 13:31:34 +02:00
Tomasz Drwięga
32b0a80a18 Removing Option from setup_rpc_server method return type 2016-04-09 13:29:13 +02:00
Tomasz Drwięga
9df9937922 Fixing compilation with rpc feature disabled 2016-04-09 13:29:06 +02:00
debris
eae66568a9 fixed #895 2016-04-09 13:28:55 +02:00
Tomasz Drwięga
ece348d173 Avoid signalling readiness when app is about to be closed 2016-04-09 13:28:43 +02:00
arkpar
cc0c54a45c Propagate transaction queue 2016-04-09 13:28:37 +02:00
Tomasz Drwięga
3f139116b2 Reverting order of shutdown event 2016-04-09 13:26:14 +02:00
Tomasz Drwięga
771e3998a9 Updating rpc comments 2016-04-09 13:26:08 +02:00
Tomasz Drwięga
03fbde9723 More descriptive message when closing 2016-04-09 13:26:00 +02:00
Tomasz Drwięga
3303aa73ff Tracing shutdown and changed order of IoManager shutdown process 2016-04-09 13:25:54 +02:00
Tomasz Drwięga
8367be6992 Rewriting messages 2016-04-09 13:25:46 +02:00
Tomasz Drwięga
e6237ed05e Removing additional thread from JSON-RPC 2016-04-09 13:25:39 +02:00
Tomasz Drwięga
0abdcedc03 Additional logging and error messages 2016-04-09 13:25:28 +02:00
Tomasz Drwięga
59a735e2c4 Removing match on constant 2016-04-09 13:25:20 +02:00
arkpar
bf24355480 Fixed eth_call nonce and gas handling 2016-04-09 13:25:16 +02:00
NikVolf
4ed15931af replacing /home/nikky also 2016-04-09 13:21:18 +02:00
NikVolf
c5553c1f21 passing key path to all invocations 2016-04-09 13:19:09 +02:00
debris
33d633eed1 fixed #875 and added tests for eth_sendTransaction 2016-04-09 13:16:40 +02:00
Nikolay Volf
a3a0de096d replace add with or 2016-04-09 13:10:20 +02:00
Tomasz Drwięga
aed708ffd8 More descriptive expectations to transaction queue consistency. 2016-04-09 13:09:55 +02:00
Tomasz Drwięga
66d3d7ec11 Fixing typo in bigint 2016-04-09 13:09:45 +02:00
Nikolay Volf
8503aeb2df [ci skip] update misleading cli help msg for author 2016-04-09 13:09:06 +02:00
arkpar
a7ebc1dd25 Handle geth keys with lowercase crypto key 2016-04-09 13:08:52 +02:00
Robert Habermeier
584d89f964 Find geth data store cross-platform.
Fixes #869
2016-04-09 13:08:40 +02:00
arkpar
4aeaab3a2f Fixed bootnode URL and error message 2016-04-09 13:08:03 +02:00
NikVolf
9750a31336 replace popcnt with mov 2016-04-09 13:07:48 +02:00
debris
404005cc93 fixed #855 2016-04-09 13:06:51 +02:00
Gav Wood
9b5bd37dc5 Merge pull request #905 from ethcore/sync-beta
Imporved sync error handling
2016-04-08 17:43:42 -07:00
arkpar
f96e5bc0d5 v1.0.2 2016-04-06 16:15:36 +02:00
arkpar
444c9c4375 Track import errors and restart sync 2016-04-06 16:15:31 +02:00
Gav Wood
2a2c24855c Fix build. 2016-03-28 21:19:28 +02:00
Gav Wood
712302391c Use sensible gas price. 2016-03-28 20:42:23 +02:00
Gav Wood
3023d76a46 Publish locally-made transactions to peers. 2016-03-28 19:43:49 +02:00
Gav Wood
f60d74b231 Merge pull request #844 from ethcore/sync-beta
Full sync restart on bad block
2016-03-28 15:49:39 +02:00
arkpar
3e192ab486 Full restart on bad block 2016-03-28 13:05:59 +02:00
Gav Wood
fe313677b3 Merge pull request #845 from ethcore/beta-staging
Merge fixes from master to beta
2016-03-28 10:00:50 +02:00
arkpar
20f3f356e5 v1.0.1 2016-03-28 02:25:12 +02:00
NikVolf
b9141b830b test for pending 2016-03-28 02:19:29 +02:00
NikVolf
a938385692 checking tx queue 2016-03-28 02:19:19 +02:00
debris
c9ac0392d8 tests for deserialization of issue #835 2016-03-28 02:18:50 +02:00
NikVolf
02dfc8632e fix test 2016-03-28 02:17:32 +02:00
NikVolf
ac2a62707a in cli call 2016-03-28 02:17:28 +02:00
NikVolf
6f78523bf0 unlimited unlock 2016-03-28 02:15:12 +02:00
Gav Wood
5dbbf9db13 Allow passwords on multiple lines in --password files. 2016-03-28 02:13:47 +02:00
Gav Wood
0d6ffa2a97 Minor refactor. 2016-03-28 02:13:47 +02:00
Gav Wood
3afb6370f9 Unlock accounts on CLI. 2016-03-28 02:13:47 +02:00
Gav Wood
dad4bfecdf Merge pull request #828 from ethcore/optionalblocknumberinrpc
Make BlockNumber optional, fix eth_call
2016-03-26 23:29:45 +01:00
Gav Wood
902b44c94f Remove info!s. 2016-03-26 13:44:42 +01:00
Gav Wood
54bf237e4f Fix backport. 2016-03-26 13:31:04 +01:00
Gav Wood
f835ce8253 Fix eth_call so it doesn't need the secret of the sender. 2016-03-26 13:30:50 +01:00
Gav Wood
4d04668c48 Make BlockNumber optional. 2016-03-26 12:35:15 +01:00
Arkadiy Paronyan
b4d95089d4 Merge pull request #826 from ethcore/web3sha3-beta
Web3sha3 beta
2016-03-26 12:08:30 +01:00
Gav Wood
cca30f964e Update web3.rs
[ci-skip]
2016-03-26 11:22:29 +01:00
NikVolf
f11cc80561 wiki test 2016-03-26 11:22:23 +01:00
NikVolf
4f71fd5d4d avoid copy 2016-03-26 11:22:13 +01:00
NikVolf
0340466f5f web3_sha3 2016-03-26 11:22:06 +01:00
Nikolay Volf
8847819b31 Merge pull request #821 from ethcore/netidfix-beta
Use network id for the web3_net_version return.
2016-03-26 04:01:53 +04:00
Gav Wood
ca91fd5e76 Use network id for the web3_net_version return. 2016-03-26 00:22:09 +01:00
Arkadiy Paronyan
df2619a37d Merge pull request #806 from ethcore/tracegetwork-beta
Fix mining from spinning
2016-03-24 17:34:07 +01:00
arkpar
4840371635 Fixed test 2016-03-24 17:07:54 +01:00
Gav Wood
68e4156143 Don't care if engaged in sync since it's typically doing so. 2016-03-24 14:29:54 +00:00
Gav Wood
83f812b176 Fix logic. agaib. 2016-03-24 14:24:58 +00:00
Gav Wood
36c5525e3f Fix logic error. 2016-03-24 14:15:55 +00:00
Gav Wood
78b5c12cf9 Fix mining. 2016-03-24 14:13:43 +00:00
Gav Wood
0564e7c168 Fix compile. 2016-03-24 14:00:15 +00:00
Gav Wood
7a4e91d26d Add tracing. 2016-03-24 14:00:09 +00:00
Tomasz Drwięga
ec26827f58 Fixing future-current transactions clash 2016-03-23 14:45:02 +01:00
arkpar
01b71bf9ba Merge with master 2016-03-22 22:09:44 +01:00
Gav Wood
42040438ed Fix author reporting. num_cpus for JSONRPC threads. 2016-03-22 21:58:28 +01:00
Gav Wood
799828b51e Increase threads to 4. 2016-03-22 21:58:28 +01:00
debris
f78e8c3a38 fixed eth_call, eth_sendTransaction and eth_estimateGas 2016-03-22 21:58:28 +01:00
debris
4f242996d1 uncle 2016-03-22 21:58:28 +01:00
debris
daecf5db3e removed outdated comment 2016-03-22 21:58:28 +01:00
debris
9a09f52679 implemented eth_sendRawTransaction 2016-03-22 21:58:28 +01:00
debris
841a996fa6 test for eth_getTransactionReceipt 2016-03-22 21:58:28 +01:00
Alexander Orlov
ff99ccaf82 complete getting started steps for OS X
You get 
`multirust: no default toolchain configured` 
when 
`multirust default stable`
is not executed.
2016-03-22 21:58:28 +01:00
arkpar
60f2aadb8e Prettier version wo git dir; Use rustc compile time version 2016-03-22 21:53:29 +01:00
Tomasz Drwięga
c143b1b783 Validating senders balance before importing transaction to queue 2016-03-22 21:52:28 +01:00
Tomasz Drwięga
2377d46c59 Speeding up build 2016-03-22 21:44:58 +01:00
Arkadiy Paronyan
10d361aec5 Merge pull request #796 from ethcore/beta-staging
Merge master to beta
2016-03-22 20:55:57 +01:00
arkpar
bf4e4df4c8 Fixed sync tests 2016-03-21 18:51:22 +01:00
arkpar
3b766f01f2 Fixed rpc tests 2016-03-21 18:07:07 +01:00
arkpar
2aa3864d67 Merge with master 2016-03-21 15:55:46 +01:00
debris
04082d1514 client implementation of transaction receipt 2016-03-21 00:05:45 +01:00
debris
85fa07728c LocalizedReceipt 2016-03-21 00:05:45 +01:00
Tomasz Drwięga
a36ac996a0 Avoiding possible overflow when block number gets smaller. 2016-03-21 00:05:45 +01:00
Tomasz Drwięga
6e666ecac7 Fixing ethminer doctest 2016-03-21 00:05:45 +01:00
Tomasz Drwięga
08dc83205c Flipping sealing_enabled flag after no requests for sealing_block for some time 2016-03-21 00:05:43 +01:00
Gav Wood
bff142828d JSONRPC interface defaults to local.
Please enter the commit message for your changes. Lines starting
2016-03-21 00:04:55 +01:00
Gav Wood
6d1ae888ca Listen on all interfaces for JSONRPC by default. 2016-03-21 00:04:55 +01:00
debris
d5cc2c9e67 implemented rpc eth_estimateGas method, added tests for rpc eth_call and eth_estimateGas 2016-03-21 00:04:55 +01:00
debris
048dba7ef3 fixed indentation 2016-03-21 00:04:55 +01:00
debris
280c5e8a99 implementation of eth_estimateGas 2016-03-21 00:04:55 +01:00
debris
1257ad2993 fixes after merge 2016-03-21 00:04:55 +01:00
debris
5c72e60b14 implemented eth_call 2016-03-21 00:04:55 +01:00
debris
aa6b6e477c added output to execution result 2016-03-21 00:04:55 +01:00
Gav Wood
245ef854dd Allow 0x prefix for --author. 2016-03-21 00:04:55 +01:00
debris
c2cffd1e06 improvements to build, updated dependencies, moved rpctest to its own submodule 2016-03-21 00:04:53 +01:00
debris
ddab958e61 fixed missing import 2016-03-20 23:56:57 +01:00
debris
a10160bd56 use ethjson module to load chain json tests 2016-03-20 23:56:57 +01:00
debris
49be60e984 test ethjson module on travis 2016-03-20 23:56:57 +01:00
debris
2b8a151b82 fixed failing eth rpc tests 2016-03-20 23:56:57 +01:00
debris
9bcc6c9e69 next batch of rpc fixes, 103 still failing 2016-03-20 23:56:57 +01:00
debris
0fc605face rpc tests, now 421 passing / 116 failing 2016-03-20 23:56:57 +01:00
debris
8f1618687c fixed parsing blockchain file, added default account to rpc tests 2016-03-20 23:56:57 +01:00
debris
cb246c26c0 fixed invalid json files 2016-03-20 23:56:56 +01:00
debris
086e45671b removed warnings by adding missing documentation 2016-03-20 23:56:56 +01:00
debris
f3ad643b69 pricing { linear: {} } 2016-03-20 23:56:56 +01:00
debris
c5a4993e39 fixed failing builin test 2016-03-20 23:56:56 +01:00
debris
32cc0bddf6 fixed od builting parsing 2016-03-20 23:56:56 +01:00
debris
d87286c5c3 updated rpc helpers docs 2016-03-20 23:56:56 +01:00
debris
ac3acdef71 linear -> pricing 2016-03-20 23:56:56 +01:00
debris
dffa9bcef6 fixed compilation with --release flag 2016-03-20 23:56:56 +01:00
debris
9ce75a173e fixed name of rpctest executable in rpctest --help 2016-03-20 23:56:56 +01:00
debris
755faf37e3 running rpc tests 2016-03-20 23:56:56 +01:00
debris
de21fef868 new way of loading PodState 2016-03-20 23:56:56 +01:00
debris
39aa02ed26 moved ethcores spec to its own module, added genesis 2016-03-20 23:56:54 +01:00
debris
7a0dfec115 added genesis method to ethjson blockchain 2016-03-20 23:54:53 +01:00
debris
1fa019d92a ethjson spec submodule 2016-03-20 23:54:53 +01:00
debris
7105ed3f61 rpctest executable 2016-03-20 23:54:53 +01:00
Tomasz Drwięga
2f411c93fd Removing update_seal when new transactions arrives 2016-03-20 23:54:53 +01:00
Tomasz Drwięga
808f959a94 Common error handling 2016-03-20 23:54:51 +01:00
Tomasz Drwięga
2aae862330 Updating sealing when new transactions are received 2016-03-20 23:54:24 +01:00
Tomasz Drwięga
b18a7bde9a Updating gas_limit in test_client generated blocks 2016-03-20 23:53:23 +01:00
Tomasz Drwięga
a0cdf5c420 Ignoring transactions slightly above gas_limit 2016-03-20 23:53:21 +01:00
Tomasz Drwięga
b3dd72ab0a BlockGasLimit taken from push_transaction result 2016-03-20 23:52:58 +01:00
Tomasz Drwięga
4d984ead4f Fixing compilation 2016-03-20 23:52:58 +01:00
Tomasz Drwięga
fbc85e31c2 Refactoring removing invalid transactions from queue 2016-03-20 23:52:58 +01:00
Tomasz Drwięga
177d26a25e Stop adding transactions right after we know that no other will make it to block. 2016-03-20 23:52:58 +01:00
Tomasz Drwięga
1f363b22fc Removing invalid transactions from queue 2016-03-20 23:52:58 +01:00
Tomasz Drwięga
5aed421fa3 Removing allow dead_code 2016-03-20 23:52:58 +01:00
Tomasz Drwięga
f4df9c8dfd Fixing warnings 2016-03-20 23:52:58 +01:00
Tomasz Drwięga
144385e67d Bumping clippy 2016-03-20 23:52:56 +01:00
Tomasz Drwięga
9b6abb5861 Attempting to add all transactions to mined block 2016-03-20 23:52:31 +01:00
arkpar
6c81f1fb74 Prettier version wo git dir; Use rustc compile time version 2016-03-20 23:52:29 +01:00
Nikolay Volf
7371beb26c get rid of the function 2016-03-20 23:52:00 +01:00
Nikolay Volf
7acdc80b27 propagation is out 2016-03-20 23:51:59 +01:00
Nikolay Volf
3847f8a9fc no flag also 2016-03-20 23:51:58 +01:00
Tomasz Drwięga
56e977b425 Fixing doctest 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
2f8e6be2ea Adding missing commas 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
180debb260 Reverting check if block queue is empty 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
b79c7f05c0 Increasing balance in tests 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
d17c629b71 Full transaction cost 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
4be4a1c151 Fixing sync test 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
b0d7d48391 Adding test 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
164cd268ad Fixing build 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
3a16ef19c1 Removing unused import 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
74ea9cc74c Validating senders balance before importing transaction to queue 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
2e88183df4 Removing printlns 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
fca8eb5810 Importing transactions from hashset. Notifying about every block 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
df4b326cb3 Adding missing space 2016-03-20 23:51:22 +01:00
Tomasz Drwięga
4db397ecb1 Renaming status fields to something more descriptive. 2016-03-20 23:51:20 +01:00
Tomasz Drwięga
e767fd88f6 Returning number of transactions pending in block not queue 2016-03-20 23:50:59 +01:00
Tomasz Drwięga
fae7039fda Disabling benches and beta tests 2016-03-20 23:50:59 +01:00
Tomasz Drwięga
69d6f3ed3e Enabling fast finish 2016-03-20 23:50:59 +01:00
Tomasz Drwięga
4292e75dca More cleaning 2016-03-20 23:50:59 +01:00
Tomasz Drwięga
2b4640c94c Cleaning 2016-03-20 23:50:59 +01:00
Tomasz Drwięga
42a6954ec9 Speeding up build 2016-03-20 23:50:57 +01:00
Nikolay Volf
114bab66c4 overhaul to flag 2016-03-20 23:50:31 +01:00
Nikolay Volf
13d0d6d006 adding helper can_sync 2016-03-20 23:50:31 +01:00
debris
c15e78cd95 tests for loading test json files 2016-03-20 23:50:31 +01:00
debris
24993b8d4e finished blockchain test deserialization 2016-03-20 23:50:31 +01:00
debris
69d89583ca proper blockchain json parsing 2016-03-20 23:50:29 +01:00
Arkadiy Paronyan
3c8fb60cbd Update install-parity.sh 2016-03-20 23:49:34 +01:00
arkpar
9f8a131fa4 restart sync on getting old unknown header 2016-03-20 23:49:34 +01:00
Nikolay Volf
31f4a214a9 adding message from client to sync and disabling sending transactions to the queue while syncing 2016-03-20 23:49:34 +01:00
Gav Wood
81f8f939b9 Update sync_provider.rs 2016-03-20 23:49:34 +01:00
Nikolay Volf
28e4847b59 tests 2016-03-20 23:49:34 +01:00
Nikolay Volf
30ecff3959 checking queue also 2016-03-20 23:49:34 +01:00
Nikolay Volf
8add773b12 [ci skip] grammar fix 2016-03-20 23:49:34 +01:00
Nikolay Volf
c20e761051 adding check for a sync 2016-03-20 23:49:34 +01:00
arkpar
35be0e9943 Indent 2016-03-17 19:40:08 +01:00
arkpar
3bf0a00ba4 Prettier version wo git dir; Use rustc compile time version 2016-03-17 19:39:54 +01:00
arkpar
570d2c21d3 Merge branch 'sync' into beta 2016-03-15 19:44:15 +01:00
arkpar
b9fc5bdda0 Merge remote-tracking branch 'origin/transaction_queue_race' into beta 2016-03-15 12:56:30 +01:00
arkpar
1964ed7eb3 Merge remote-tracking branch 'origin/sync' into beta 2016-03-15 12:56:17 +01:00
arkpar
60bcab3e48 Merge remote-tracking branch 'origin/docker' into beta 2016-03-14 19:13:07 +01:00
arkpar
ebb7699da2 Merge remote-tracking branch 'origin/pv63-receipts' into beta 2016-03-14 18:58:23 +01:00
arkpar
78821bb935 Merge remote-tracking branch 'origin/unclesminingfix' into beta 2016-03-14 18:56:27 +01:00
arkpar
e8a21116f7 missing mut 2016-03-14 15:50:45 +01:00
Gav Wood
9f81b07224 Lock reports to avoid out of order badness. 2016-03-14 15:50:32 +01:00
arkpar
8eeda2b1a3 Fixed handshake leak 2016-03-14 15:50:03 +01:00
Arkadiy Paronyan
0087568c16 Merge pull request #712 from ethcore/beta-version
Version 1.0 in beta
2016-03-14 14:56:44 +01:00
arkpar
75a11ba9be Merge remote-tracking branch 'origin/targetgaslimit' into beta 2016-03-14 14:05:38 +01:00
Nikolay Volf
4ac9807bc1 fixing tests for beta 2016-03-14 11:12:15 +01:00
arkpar
1c26ffa208 Merge branch 'network' into beta 2016-03-14 11:11:36 +01:00
arkpar
f8b18d5d7b Merge remote-tracking branch 'origin/rpc-personal-tests' into beta 2016-03-14 10:30:22 +01:00
arkpar
b327e79b42 Merge remote-tracking branch 'origin/fixing_warnings' into beta 2016-03-14 10:28:38 +01:00
arkpar
8e2f35856a Merge remote-tracking branch 'origin/docoptclean' into beta 2016-03-14 10:26:59 +01:00
arkpar
a029551b0b Merge branch 'build' into beta 2016-03-14 09:56:44 +01:00
arkpar
320581a3e4 Merge branch 'fix-discovery' into beta 2016-03-14 01:33:41 +01:00
arkpar
f77b4119da Version 1.0 2016-03-14 01:01:15 +01:00
2886 changed files with 36384 additions and 349082 deletions

View File

@@ -1,22 +0,0 @@
# Generated by Cargo
# will have compiled files and executables
target
*.swp
*.swo
*.swn
*.DS_Store
# Visual Studio Code stuff
.vscode
# GitEye stuff
.project
# idea ide
.idea
# git stuff
.git
ethcore/res/ethereum/tests

View File

@@ -9,8 +9,3 @@ trim_trailing_whitespace=true
max_line_length=120
insert_final_newline=true
[.travis.yml]
indent_style=space
indent_size=2
tab_width=8
end_of_line=lf

8
.gitignore vendored
View File

@@ -19,10 +19,6 @@
# mac stuff
.DS_Store
# npm stuff
npm-debug.log
node_modules
# gdb files
.gdb_history
@@ -34,7 +30,3 @@ node_modules
# Build artifacts
out/
.vscode
/parity.*

View File

@@ -1,700 +0,0 @@
stages:
- test
- js-build
- push-release
- build
variables:
GIT_DEPTH: "3"
SIMPLECOV: "true"
RUST_BACKTRACE: "1"
RUSTFLAGS: ""
CARGOFLAGS: ""
CI_SERVER_NAME: "GitLab CI"
cache:
key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME"
untracked: true
linux-stable:
stage: build
image: parity/rust:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- rustup default stable
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
- cargo build -j $(nproc) --release -p evmbin
- cargo build -j $(nproc) --release -p ethstore-cli
- cargo build -j $(nproc) --release -p ethkey-cli
- strip target/release/parity
- strip target/release/parity-evm
- strip target/release/ethstore
- strip target/release/ethkey
- export SHA3=$(target/release/parity tools hash target/release/parity)
- md5sum target/release/parity > parity.md5
- sh scripts/deb-build.sh amd64
- cp target/release/parity deb/usr/bin/parity
- cp target/release/parity-evm deb/usr/bin/parity-evm
- cp target/release/ethstore deb/usr/bin/ethstore
- cp target/release/ethkey deb/usr/bin/ethkey
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable|nightly)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
tags:
- rust
- rust-stable
artifacts:
paths:
- target/release/parity
- target/release/parity-evm
- target/release/ethstore
- target/release/ethkey
name: "stable-x86_64-unknown-linux-gnu_parity"
linux-snap:
stage: build
image: parity/snapcraft:gitlab-ci
only:
- snap
- beta
- tags
- triggers
script:
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- cd snap
- rm -rf *snap
- sed -i 's/master/'"$VER"'/g' snapcraft.yaml
- echo "Version:"$VER
- snapcraft
- ls
#- 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
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.snap" --body "parity_"$VER"_amd64.snap"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.snap.md5" --body "parity_"$VER"_amd64.snap.md5"
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
tags:
- rust
- rust-stable
artifacts:
paths:
- scripts/parity_*_amd64.snap
name: "stable-x86_64-unknown-snap-gnu_parity"
allow_failure: true
linux-stable-debian:
stage: build
image: parity/rust-debian:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
- cargo build -j $(nproc) --release -p evmbin
- cargo build -j $(nproc) --release -p ethstore-cli
- cargo build -j $(nproc) --release -p ethkey-cli
- strip target/release/parity
- strip target/release/parity-evm
- strip target/release/ethstore
- strip target/release/ethkey
- export SHA3=$(target/release/parity tools hash target/release/parity)
- md5sum target/release/parity > parity.md5
- sh scripts/deb-build.sh amd64
- cp target/release/parity deb/usr/bin/parity
- cp target/release/parity-evm deb/usr/bin/parity-evm
- cp target/release/ethstore deb/usr/bin/ethstore
- cp target/release/ethkey deb/usr/bin/ethkey
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_amd64.deb"
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable|nightly)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/parity --body target/release/parity
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/x86_64-unknown-debian-gnu
tags:
- rust
- rust-debian
artifacts:
paths:
- target/release/parity
name: "stable-x86_64-unknown-debian-gnu_parity"
linux-beta:
stage: build
image: parity/rust:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- rustup default beta
- cargo build -j $(nproc) --release $CARGOFLAGS
- strip target/release/parity
tags:
- rust
- rust-beta
artifacts:
paths:
- target/release/parity
name: "beta-x86_64-unknown-linux-gnu_parity"
allow_failure: true
linux-nightly:
stage: build
image: parity/rust:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- rustup default nightly
- cargo build -j $(nproc) --release $CARGOFLAGS
- strip target/release/parity
tags:
- rust
- rust-nightly
artifacts:
paths:
- target/release/parity
name: "nigthly-x86_64-unknown-linux-gnu_parity"
allow_failure: true
linux-centos:
stage: build
image: parity/rust-centos:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- export CXX="g++"
- export CC="gcc"
- export PLATFORM=x86_64-unknown-centos-gnu
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
- cargo build -j $(nproc) --release -p evmbin
- cargo build -j $(nproc) --release -p ethstore-cli
- cargo build -j $(nproc) --release -p ethkey-cli
- strip target/release/parity
- strip target/release/parity-evm
- strip target/release/ethstore
- strip target/release/ethkey
- md5sum target/release/parity > parity.md5
- md5sum target/release/parity-evm > parity-evm.md5
- md5sum target/release/ethstore > ethstore.md5
- md5sum target/release/ethkey > ethkey.md5
- export SHA3=$(target/release/parity tools hash target/release/parity)
- 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
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity-evm --body target/release/parity-evm
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity-evm.md5 --body parity-evm.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/ethstore --body target/release/ethstore
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/ethstore.md5 --body ethstore.md5
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/ethkey --body target/release/ethkey
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/ethkey.md5 --body ethkey.md5
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
- rust-centos
artifacts:
paths:
- target/release/parity
name: "x86_64-unknown-centos-gnu_parity"
linux-i686:
stage: build
image: parity/rust-i686:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- export HOST_CC=gcc
- export HOST_CXX=g++
- export COMMIT=$(git rev-parse HEAD)
- export PLATFORM=i686-unknown-linux-gnu
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
- cargo build -j $(nproc) --target $PLATFORM --release -p evmbin
- cargo build -j $(nproc) --target $PLATFORM --release -p ethstore-cli
- cargo build -j $(nproc) --target $PLATFORM --release -p ethkey-cli
- strip target/$PLATFORM/release/parity
- strip target/$PLATFORM/release/parity-evm
- strip target/$PLATFORM/release/ethstore
- strip target/$PLATFORM/release/ethkey
- strip target/$PLATFORM/release/parity
- md5sum target/$PLATFORM/release/parity > parity.md5
- export SHA3=$(target/$PLATFORM/release/parity tools hash target/$PLATFORM/release/parity)
- sh scripts/deb-build.sh i386
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
- cp target/$PLATFORM/release/parity-evm deb/usr/bin/parity-evm
- cp target/$PLATFORM/release/ethstore deb/usr/bin/ethstore
- cp target/$PLATFORM/release/ethkey deb/usr/bin/ethkey
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_i386.deb"
- md5sum "parity_"$VER"_i386.deb" > "parity_"$VER"_i386.deb.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable|nightly)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5"
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
- rust-i686
artifacts:
paths:
- target/i686-unknown-linux-gnu/release/parity
name: "i686-unknown-linux-gnu"
allow_failure: true
linux-armv7:
stage: build
image: parity/rust-armv7:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- export CC=arm-linux-gnueabihf-gcc
- export CXX=arm-linux-gnueabihf-g++
- export HOST_CC=gcc
- export HOST_CXX=g++
- export PLATFORM=armv7-unknown-linux-gnueabihf
- rm -rf .cargo
- mkdir -p .cargo
- echo "[target.$PLATFORM]" >> .cargo/config
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
- cat .cargo/config
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
- cargo build -j $(nproc) --target $PLATFORM --release -p evmbin
- cargo build -j $(nproc) --target $PLATFORM --release -p ethstore-cli
- cargo build -j $(nproc) --target $PLATFORM --release -p ethkey-cli
- md5sum target/$PLATFORM/release/parity > parity.md5
- export SHA3=$(target/$PLATFORM/release/parity tools hash target/$PLATFORM/release/parity)
- sh scripts/deb-build.sh i386
- arm-linux-gnueabihf-strip target/$PLATFORM/release/parity
- arm-linux-gnueabihf-strip target/$PLATFORM/release/parity-evm
- arm-linux-gnueabihf-strip target/$PLATFORM/release/ethstore
- arm-linux-gnueabihf-strip target/$PLATFORM/release/ethkey
- export SHA3=$(rhash --sha3-256 target/$PLATFORM/release/parity -p %h)
- md5sum target/$PLATFORM/release/parity > parity.md5
- sh scripts/deb-build.sh armhf
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
- cp target/$PLATFORM/release/parity-evm deb/usr/bin/parity-evm
- cp target/$PLATFORM/release/ethstore deb/usr/bin/ethstore
- cp target/$PLATFORM/release/ethkey deb/usr/bin/ethkey
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable|nightly)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
- rust-arm
artifacts:
paths:
- target/armv7-unknown-linux-gnueabihf/release/parity
name: "armv7_unknown_linux_gnueabihf_parity"
allow_failure: true
linux-arm:
stage: build
image: parity/rust-arm:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- export CC=arm-linux-gnueabihf-gcc
- export CXX=arm-linux-gnueabihf-g++
- export HOST_CC=gcc
- export HOST_CXX=g++
- export PLATFORM=arm-unknown-linux-gnueabihf
- rm -rf .cargo
- mkdir -p .cargo
- echo "[target.$PLATFORM]" >> .cargo/config
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
- cat .cargo/config
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
- cargo build -j $(nproc) --target $PLATFORM --release -p evmbin
- cargo build -j $(nproc) --target $PLATFORM --release -p ethstore-cli
- cargo build -j $(nproc) --target $PLATFORM --release -p ethkey-cli
- arm-linux-gnueabihf-strip target/$PLATFORM/release/parity
- arm-linux-gnueabihf-strip target/$PLATFORM/release/parity-evm
- arm-linux-gnueabihf-strip target/$PLATFORM/release/ethstore
- arm-linux-gnueabihf-strip target/$PLATFORM/release/ethkey
- export SHA3=$(rhash --sha3-256 target/$PLATFORM/release/parity -p %h)
- md5sum target/$PLATFORM/release/parity > parity.md5
- sh scripts/deb-build.sh armhf
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
- cp target/$PLATFORM/release/parity-evm deb/usr/bin/parity-evm
- cp target/$PLATFORM/release/ethstore deb/usr/bin/ethstore
- cp target/$PLATFORM/release/ethkey deb/usr/bin/ethkey
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable|nightly)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
- rust-arm
artifacts:
paths:
- target/arm-unknown-linux-gnueabihf/release/parity
name: "arm-unknown-linux-gnueabihf_parity"
allow_failure: true
linux-aarch64:
stage: build
image: parity/rust-arm64:gitlab-ci
only:
- beta
- tags
- stable
- triggers
script:
- export CC=aarch64-linux-gnu-gcc
- export CXX=aarch64-linux-gnu-g++
- export HOST_CC=gcc
- export HOST_CXX=g++
- export PLATFORM=aarch64-unknown-linux-gnu
- rm -rf .cargo
- mkdir -p .cargo
- echo "[target.$PLATFORM]" >> .cargo/config
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
- cat .cargo/config
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
- cargo build -j $(nproc) --target $PLATFORM --release -p evmbin
- cargo build -j $(nproc) --target $PLATFORM --release -p ethstore-cli
- cargo build -j $(nproc) --target $PLATFORM --release -p ethkey-cli
- aarch64-linux-gnu-strip target/$PLATFORM/release/parity
- aarch64-linux-gnu-strip target/$PLATFORM/release/parity-evm
- aarch64-linux-gnu-strip target/$PLATFORM/release/ethstore
- aarch64-linux-gnu-strip target/$PLATFORM/release/ethkey
- export SHA3=$(rhash --sha3-256 target/$PLATFORM/release/parity -p %h)
- md5sum target/$PLATFORM/release/parity > parity.md5
- sh scripts/deb-build.sh arm64
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
- cp target/$PLATFORM/release/parity-evm deb/usr/bin/parity-evm
- cp target/$PLATFORM/release/ethstore deb/usr/bin/ethstore
- cp target/$PLATFORM/release/ethkey deb/usr/bin/ethkey
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- dpkg-deb -b deb "parity_"$VER"_arm64.deb"
- md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable|nightly)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- rust
- rust-arm
artifacts:
paths:
- target/aarch64-unknown-linux-gnu/release/parity
name: "aarch64-unknown-linux-gnu_parity"
allow_failure: true
darwin:
stage: build
only:
- beta
- tags
- stable
- triggers
script: |
export COMMIT=$(git rev-parse HEAD)
export PLATFORM=x86_64-apple-darwin
rustup default stable
cargo clean
cargo build -j 8 --features final --release #$CARGOFLAGS
cargo build -j 8 --release -p ethstore-cli #$CARGOFLAGS
cargo build -j 8 --release -p ethkey-cli #$CARGOFLAGS
cargo build -j 8 --release -p evmbin #$CARGOFLAGS
rm -rf parity.md5
md5sum target/release/parity > parity.md5
export SHA3=$(target/release/parity tools hash target/release/parity)
cd mac
xcodebuild -configuration Release
cd ..
packagesbuild -v mac/Parity.pkgproj
productsign --sign 'Developer ID Installer: PARITY TECHNOLOGIES LIMITED (P2PX3JU8FT)' target/release/Parity\ Ethereum.pkg target/release/Parity\ Ethereum-signed.pkg
export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
mv target/release/Parity\ Ethereum-signed.pkg "parity-"$VER"-macos-installer.pkg"
md5sum "parity-"$VER"-macos-installer.pkg" >> "parity-"$VER"-macos-installer.pkg.md5"
aws configure set aws_access_key_id $s3_key
aws configure set aws_secret_access_key $s3_secret
if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable|nightly)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/release/parity
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-macos-installer.pkg" --body "parity-"$VER"-macos-installer.pkg"
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-macos-installer.pkg.md5" --body "parity-"$VER"-macos-installer.pkg.md5"
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
tags:
- osx
artifacts:
paths:
- target/release/parity
name: "x86_64-apple-darwin_parity"
windows:
cache:
key: "%CI_BUILD_STAGE%/%CI_BUILD_REF_NAME%"
untracked: true
stage: build
only:
- beta
- tags
- stable
- triggers
script:
- set PLATFORM=x86_64-pc-windows-msvc
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
- set RUST_BACKTRACE=1
- set RUSTFLAGS=%RUSTFLAGS%
- rustup default stable-x86_64-pc-windows-msvc
- cargo clean
- cargo build --features final --release #%CARGOFLAGS%
- cargo build --release -p ethstore-cli #%CARGOFLAGS%
- cargo build --release -p ethkey-cli #%CARGOFLAGS%
- cargo build --release -p evmbin #%CARGOFLAGS%
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
- target\release\parity.exe tools hash target\release\parity.exe > parity.sha3
- set /P SHA3=<parity.sha3
- curl -sL --url "https://github.com/paritytech/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
- curl -sL --url "https://github.com/paritytech/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
- msbuild windows\ptray\ptray.vcxproj /p:Platform=x64 /p:Configuration=Release
- signtool sign /f %keyfile% /p %certpass% windows\ptray\x64\release\ptray.exe
- cd nsis
- makensis.exe installer.nsi
- copy installer.exe InstallParity.exe
- signtool sign /f %keyfile% /p %certpass% InstallParity.exe
- md5sums InstallParity.exe > InstallParity.exe.md5
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
- md5sums win-installer.zip > win-installer.zip.md5
- cd ..\target\release\
- md5sums parity.exe > parity.exe.md5
- zip parity.zip parity.exe parity.md5
- md5sums parity.zip > parity.zip.md5
- cd ..\..
- aws configure set aws_access_key_id %s3_key%
- aws configure set aws_secret_access_key %s3_secret%
- echo %CI_BUILD_REF_NAME%
- echo %CI_BUILD_REF_NAME% | findstr /R "master" >nul 2>&1 && set S3_BUCKET=builds-parity-published|| set S3_BUCKET=builds-parity
- echo %CI_BUILD_REF_NAME% | findstr /R "beta" >nul 2>&1 && set S3_BUCKET=builds-parity-published|| set S3_BUCKET=builds-parity
- echo %CI_BUILD_REF_NAME% | findstr /R "stable" >nul 2>&1 && set S3_BUCKET=builds-parity-published|| set S3_BUCKET=builds-parity
- echo %CI_BUILD_REF_NAME% | findstr /R "nightly" >nul 2>&1 && set S3_BUCKET=builds-parity-published|| set S3_BUCKET=builds-parity
- echo %S3_BUCKET%
- aws s3 rm --recursive s3://%S3_BUCKET%/%CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe --body target\release\parity.exe
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe.md5 --body target\release\parity.exe.md5
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip --body target\release\parity.zip
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip.md5 --body target\release\parity.zip.md5
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe --body nsis\InstallParity.exe
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
- curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1337/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
- curl --data "commit=%CI_BUILD_REF%&sha3=%SHA3%&filename=parity.exe&secret=%RELEASES_SECRET%" http://update.parity.io:1338/push-build/%CI_BUILD_REF_NAME%/%PLATFORM%
tags:
- rust-windows
artifacts:
paths:
- target/release/parity.exe
- target/release/parity.pdb
- nsis/InstallParity.exe
name: "x86_64-pc-windows-msvc_parity"
docker-build:
stage: build
only:
- tags
- triggers
before_script:
- docker info
script:
- if [ "$CI_BUILD_REF_NAME" == "beta-release" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
- echo "Tag:" $DOCKER_TAG
- docker login -u $Docker_Hub_User_Parity -p $Docker_Hub_Pass_Parity
- sh scripts/docker-build.sh $DOCKER_TAG
- docker logout
tags:
- docker
test-coverage:
stage: test
only:
- master
script:
- git submodule update --init --recursive
- rm -rf target/*
- rm -rf js/.coverage
- scripts/cov.sh
# - COVERAGE=$(grep -Po 'covered":.*?[^\\]"' target/cov/index.json | grep "[0-9]*\.[0-9]" -o)
# - echo "Coverage:" $COVERAGE
tags:
- kcov
allow_failure: true
test-darwin:
stage: test
only:
- triggers
before_script:
- git submodule update --init --recursive
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e "^js/" -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
script:
- export RUST_BACKTRACE=1
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
tags:
- osx
allow_failure: true
test-windows:
stage: test
only:
- triggers
before_script:
- git submodule update --init --recursive
script:
- set RUST_BACKTRACE=1
- echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p parity-dapps -p parity-rpc -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity-rpc-client -p parity %CARGOFLAGS% --verbose --release
tags:
- rust-windows
allow_failure: true
test-rust-stable:
stage: test
image: parity/rust:gitlab-ci
before_script:
- git submodule update --init --recursive
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
script:
- rustup show
- export RUST_BACKTRACE=1
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
tags:
- rust
- rust-stable
js-test:
stage: test
image: parity/rust:gitlab-ci
before_script:
- git submodule update --init --recursive
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi
script:
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi
tags:
- rust
- rust-stable
test-rust-beta:
stage: test
only:
- triggers
- master
image: parity/rust:gitlab-ci
before_script:
- git submodule update --init --recursive
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
script:
- rustup default beta
- export RUST_BACKTRACE=1
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
tags:
- rust
- rust-beta
allow_failure: true
test-rust-nightly:
stage: test
only:
- triggers
- master
image: parity/rust:gitlab-ci
before_script:
- git submodule update --init --recursive
- export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l)
script:
- rustup default nightly
- export RUST_BACKTRACE=1
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
tags:
- rust
- rust-nightly
allow_failure: true
js-release:
stage: js-build
only:
- master
- beta
- stable
- tags
- triggers
image: parity/rust:gitlab-ci
before_script:
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
- echo $JS_FILES_MODIFIED
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi
script:
- rustup default stable
- echo $JS_FILES_MODIFIED
- if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi
tags:
- javascript
push-release:
stage: push-release
only:
- tags
- triggers
image: parity/rust:gitlab-ci
script:
- rustup default stable
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1337/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1338/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
tags:
- curl

3
.gitmodules vendored
View File

@@ -2,6 +2,3 @@
path = ethcore/res/ethereum/tests
url = https://github.com/ethereum/tests.git
branch = develop
[submodule "ethcore/res/wasm-tests"]
path = ethcore/res/wasm-tests
url = https://github.com/paritytech/wasm-tests

103
.travis.yml Normal file
View File

@@ -0,0 +1,103 @@
sudo: required
dist: trusty
language: rust
branches:
only:
- master
- /^beta-.*$/
- /^stable-.*$/
- /^beta$/
- /^stable$/
git:
depth: 3
matrix:
fast_finish: true
allow_failures:
- rust: nightly
include:
- rust: stable
env: FEATURES="--features travis-beta" RUN_TESTS="true"
# - rust: beta
# env: FEATURES="--features travis-beta" RUN_TESTS="true"
- rust: stable
env: FEATURES="--features travis-beta" RUN_BUILD="true"
- rust: beta
env: FEATURES="--features travis-beta" RUN_BUILD="true"
- rust: stable
env: FEATURES="--features travis-beta" RUN_COVERAGE="true"
# - rust: nightly
# env: FEATURES="--features travis-nightly" RUN_BENCHES="true"
- rust: nightly
env: FEATURES="--features travis-nightly" RUN_TESTS="true"
env:
global:
# GH_TOKEN
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
- CVER=5
- NUM_JOBS=1
# - TARGETS="-p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer"
- TARGETS="-p ethcore-util"
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
- KCOV_FEATURES=""
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests target/kcov"
- RUN_TESTS="false"
- RUN_COVERAGE="false"
- RUN_BUILD="false"
- RUN_BENCHES="false"
cache:
apt: true
directories:
- $TRAVIS_BUILD_DIR/target
- $HOME/.cargo
addons:
apt:
packages:
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
script:
- echo "$TRAVIS_BUILD_DIR/target"
- ls "$TRAVIS_BUILD_DIR/target"
- if [ "$RUN_TESTS" = "true" ]; then cargo test --release --verbose ${FEATURES} ${TARGETS}; fi
- if [ "$RUN_BENCHES" = "true" ]; then cargo bench --no-run ${FEATURES} ${TARGETS}; fi
- if [ "$RUN_BUILD" = "true" ]; then cargo build --release --verbose ${FEATURES}; fi
- if [ "$RUN_BUILD" = "true" ]; then tar cvzf parity${ARCHIVE_SUFFIX}.tar.gz -C target/release parity; fi
after_success: |
[ "$RUN_COVERAGE" = "true" ] &&
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && make install DESTDIR=../tmp && cd ../.. &&
cargo test --no-run ${KCOV_FEATURES} ${TARGETS} &&
$KCOV_CMD target/debug/deps/ethcore_util-* &&
$KCOV_CMD target/debug/deps/ethash-* &&
$KCOV_CMD target/debug/deps/ethcore-* &&
$KCOV_CMD target/debug/deps/ethsync-* &&
$KCOV_CMD target/debug/deps/ethcore_rpc-* &&
$KCOV_CMD target/debug/deps/ethminer-* &&
$KCOV_CMD target/debug/parity-* &&
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
[ $TRAVIS_RUST_VERSION = stable ] &&
cargo doc --no-deps --verbose ${KCOV_FEATURES} ${TARGETS} &&
echo '<meta http-equiv=refresh content=0;url=ethcore/index.html>' > target/doc/index.html &&
pip install --user ghp-import &&
/home/travis/.local/bin/ghp-import -n target/doc &&
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
deploy:
provider: releases
api_key:
secure: "t+oGT/4lsy7IScw5s86Dpntl5Nyck4qG6nhHwMScc6FYzwLldgwgJaafL8Ej+HG+b7nFLriN+Snoa4YQ5o74X5ZlSWubVREOYQlL/fq7vcPB0DwAZ0Jufq1QW2R1M+3SwwF1eAwTv2W3G7A2K7dxjCVvENcy/gdxnZ36NeUPsqaCC9UcI2Yc7+4jyQwvx6ZfBvQeu+HbKENA0eUNs2ZQOID/1IPy0LJBvSyxAQYsysXdjTzGdNu4+Iba20E8uWYe4fAbgz+gwGarXg1L6D6gKyMlWkViqWjvXWBuDJJqMQZ3rw41AwZOoh3mKd2Lc0l6l4oZcEqPuob0yKTNjz1tuJy9xKTC2F2bDzsvUgk1IRfMK5ukXXXS09ZCZWuA9/GtnsqJ1xGTiwX+DhQzpVBHaBiseSNlYE1YN/3jNyGY+iSts1qut+1BwE7swmcTLsAPoAy8Ue+f7ErNoCg1lm71vq7VO2DLn7x2NqHyHUEuJ+7olDHSdE84G7d9otDRu/+TfMOw7GXwTaha6yJRInuNsnj4CFMLNVvYACzCC2idB7f7nUZoSFi9jf18S9fCMPVmazMrFj4g95HWrVHkjpV5zRTeUdTWw6DJl6pC9HFqORHdCvLv4Rc4dm5r3CmOcAQ0ZuiccV2oKzw4/Wic96daae8M5f5KSQ/WTr+h0wXZKp0="
skip_cleanup: true
file: parity${ARCHIVE_SUFFIX}.tar.gz
on:
tags: true
notifications:
webhooks:
urls:
- https://hooks.slack.com/services/${SLACK_WEBHOOK}
on_success: always
on_failure: always
on_start: never

File diff suppressed because it is too large Load Diff

3035
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,109 +1,45 @@
[package]
description = "Parity Ethereum client"
description = "Ethcore client."
name = "parity"
version = "1.7.0"
version = "1.0.2"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
authors = ["Ethcore <admin@ethcore.io>"]
build = "build.rs"
[build-dependencies]
rustc_version = "0.1"
[dependencies]
log = "0.3"
env_logger = "0.4"
rustc-hex = "1.0"
docopt = "0.8"
env_logger = "0.3"
rustc-serialize = "0.3"
docopt = "0.6"
time = "0.1"
num_cpus = "1.2"
ctrlc = { git = "https://github.com/tomusdrw/rust-ctrlc.git" }
fdlimit = { path = "util/fdlimit" }
daemonize = "0.2"
num_cpus = "0.2"
number_prefix = "0.2"
rpassword = "0.2.1"
semver = "0.6"
ansi_term = "0.9"
regex = "0.2"
isatty = "0.1"
toml = "0.4"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
app_dirs = "1.1.1"
futures = "0.1"
futures-cpupool = "0.1"
fdlimit = "0.1"
ws2_32-sys = "0.2"
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
ethsync = { path = "sync" }
rpassword = "0.1"
clippy = { version = "0.0.54", optional = true }
ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
ethcore-io = { path = "util/io" }
ethsync = { path = "sync" }
ethminer = { path = "miner" }
ethcore-devtools = { path = "devtools" }
ethcore-ipc = { path = "ipc/rpc" }
ethcore-ipc-nano = { path = "ipc/nano" }
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
ethcore-light = { path = "ethcore/light" }
ethcore-logger = { path = "logger" }
ethcore-stratum = { path = "stratum" }
ethkey = { path = "ethkey" }
rlp = { path = "util/rlp" }
rpc-cli = { path = "rpc_cli" }
parity-hash-fetch = { path = "hash-fetch" }
parity-ipfs-api = { path = "ipfs" }
parity-local-store = { path = "local-store" }
parity-reactor = { path = "util/reactor" }
parity-rpc = { path = "rpc" }
parity-rpc-client = { path = "rpc_client" }
parity-updater = { path = "updater" }
path = { path = "util/path" }
parity-dapps = { path = "dapps", optional = true }
clippy = { version = "0.0.103", optional = true}
ethcore-secretstore = { path = "secret_store", optional = true }
[build-dependencies]
rustc_version = "0.2"
[dev-dependencies]
ethcore-ipc-tests = { path = "ipc/tests" }
pretty_assertions = "0.1"
[target.'cfg(windows)'.dependencies]
winapi = "0.2"
[target.'cfg(not(windows))'.dependencies]
daemonize = "0.2"
ethcore-rpc = { path = "rpc", optional = true }
[features]
default = ["ui-precompiled"]
ui = [
"ui-enabled",
"parity-dapps/ui",
]
ui-precompiled = [
"ui-enabled",
"parity-dapps/ui-precompiled",
]
ui-enabled = ["dapps"]
dapps = ["parity-dapps"]
ipc = ["ethcore/ipc", "ethsync/ipc"]
jit = ["ethcore/jit"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev"]
json-tests = ["ethcore/json-tests"]
test-heavy = ["ethcore/test-heavy"]
evm-debug = ["ethcore/evm-debug"]
evm-debug-tests = ["ethcore/evm-debug-tests"]
slow-blocks = ["ethcore/slow-blocks"]
final = ["ethcore-util/final"]
secretstore = ["ethcore-secretstore"]
default = ["rpc"]
rpc = ["ethcore-rpc"]
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethminer/dev"]
travis-beta = ["ethcore/json-tests"]
travis-nightly = ["ethcore/json-tests", "dev"]
[[bin]]
path = "parity/main.rs"
name = "parity"
[profile.dev]
panic = "abort"
[profile.release]
debug = false
lto = false
panic = "abort"
[workspace]
members = ["ethstore/cli", "ethkey/cli", "evmbin"]

138
README.md
View File

@@ -1,138 +1,40 @@
# [Parity](https://parity.io/parity.html)
### Fast, light, and robust Ethereum implementation
# ethcore
### [Download latest release](https://github.com/paritytech/parity/releases)
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/trogdoro/xiki][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
[![build status](https://gitlab.parity.io/parity/parity/badges/master/build.svg)](https://gitlab.parity.io/parity/parity/commits/master) [![Coverage Status][coveralls-image]][coveralls-url] [![GPLv3][license-image]][license-url]
### Join the chat!
Parity [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] and
parity.js [![Join the chat at https://gitter.im/ethcore/parity.js](https://badges.gitter.im/ethcore/parity.js.svg)](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
[travis-image]: https://travis-ci.org/ethcore/parity.svg?branch=master
[travis-url]: https://travis-ci.org/ethcore/parity
[coveralls-image]: https://coveralls.io/repos/github/ethcore/parity/badge.svg?branch=master
[coveralls-url]: https://coveralls.io/github/ethcore/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
[license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html
[Documentation](http://ethcore.github.io/parity/ethcore/index.html)
----
### Building from source
## 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 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.
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!
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.
----
## Build dependencies
**Parity requires Rust version 1.18.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:
First (if you don't already have it) get multirust:
- Linux:
```bash
$ curl https://sh.rustup.rs -sSf | sh
```
Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl`, `libudev-dev` and `pkg-config` packages to be installed.
- OSX:
```bash
$ curl https://sh.rustup.rs -sSf | sh
```
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
- Windows
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from
https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the msvc toolchain:
```
$ rustup default stable-x86_64-pc-windows-msvc
```
Once you have rustup, install parity or download and build from source
----
## Install from the snap store
In any of the [supported Linux distros](https://snapcraft.io/docs/core/install):
```bash
sudo snap install parity --edge
curl -sf https://raw.githubusercontent.com/brson/multirust/master/quick-install.sh | sudo sh -s -- --yes
```
(Note that this is an experimental and unstable release, at the moment)
- OSX with Homebrew:
```bash
brew update && brew install multirust
multirust default stable
```
----
## Build from source
Then, download and build Parity:
```bash
# download Parity code
$ git clone https://github.com/paritytech/parity
$ cd parity
git clone https://github.com/ethcore/parity
cd parity
# build in release mode
$ cargo build --release
cargo build --release
```
This will produce an executable in the `./target/release` subdirectory.
Note: if cargo fails to parse manifest try:
```bash
$ ~/.cargo/bin/cargo build --release
```
----
## Simple one-line installer for Mac and Ubuntu
```bash
bash <(curl https://get.parity.io -Lk)
```
## Start Parity
### Manually
To start Parity manually, just run
```bash
$ ./target/release/parity
```
and Parity will begin syncing the Ethereum blockchain.
### Using systemd service file
To start Parity as a regular user using systemd init:
1. Copy `parity/scripts/parity.service` to your
systemd user directory (usually `~/.config/systemd/user`).
2. To pass any argument to Parity, write a `~/.parity/parity.conf` file this way:
`ARGS="ARG1 ARG2 ARG3"`.
Example: `ARGS="ui --identity MyMachine"`.

View File

@@ -1,4 +1,4 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -16,20 +16,10 @@
extern crate rustc_version;
const MIN_RUSTC_VERSION: &'static str = "1.15.1";
use rustc_version::{version_meta, Channel};
fn main() {
let is = rustc_version::version().unwrap();
let required = MIN_RUSTC_VERSION.parse().unwrap();
assert!(is >= required, format!("
It looks like you are compiling Parity with an old rustc compiler {}.
Parity requires version {}. Please update your compiler.
If you use rustup, try this:
rustup update stable
and try building Parity again.
", is, required));
if let Channel::Nightly = version_meta().channel {
println!("cargo:rustc-cfg=nightly");
}
}

37
cov.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/bin/sh
# Installing KCOV under ubuntu
# https://users.rust-lang.org/t/tutorial-how-to-collect-test-coverages-for-rust-project/650#
### Install deps
# $ sudo apt-get install libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc binutils-dev libiberty-dev
#
### Compile kcov
# $ wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && tar xf master.tar.gz
# $ cd kcov-master && mkdir build && cd build
# $ cmake .. && make && sudo make install
### Running coverage
if ! type kcov > /dev/null; then
echo "Install kcov first (details inside this file). Aborting."
exit 1
fi
cargo test \
-p ethash \
-p ethcore-util \
-p ethcore \
-p ethsync \
-p ethcore-rpc \
-p parity \
-p ethminer \
--no-run || exit $?
rm -rf target/coverage
mkdir -p target/coverage
EXCLUDE="~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests"
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethash-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethsync-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethminer-*
xdg-open target/coverage/index.html

View File

@@ -1,47 +0,0 @@
[package]
description = "Parity Dapps crate"
name = "parity-dapps"
version = "1.7.0"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
[lib]
[dependencies]
base32 = "0.3"
env_logger = "0.4"
futures = "0.1"
futures-cpupool = "0.1"
linked-hash-map = "0.3"
log = "0.3"
parity-dapps-glue = "1.7"
mime = "0.2"
mime_guess = "1.6.1"
ntp = "0.2.0"
rand = "0.3"
rustc-hex = "1.0"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
time = "0.1.35"
unicase = "1.3"
url = "1.0"
zip = { version = "0.1", default-features = false }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
ethcore-devtools = { path = "../devtools" }
ethcore-util = { path = "../util" }
fetch = { path = "../util/fetch" }
parity-hash-fetch = { path = "../hash-fetch" }
parity-reactor = { path = "../util/reactor" }
parity-ui = { path = "./ui" }
clippy = { version = "0.0.103", optional = true}
[features]
dev = ["clippy", "ethcore-util/dev"]
ui = ["parity-ui/no-precompiled-js"]
ui-precompiled = ["parity-ui/use-precompiled-js"]

View File

@@ -1,31 +0,0 @@
[package]
description = "Base Package for all Parity built-in dapps"
name = "parity-dapps-glue"
version = "1.7.0"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"
[build-dependencies]
quasi_codegen = { version = "0.32", optional = true }
syntex = { version = "0.58", optional = true }
[dependencies]
glob = { version = "0.2.11" }
mime_guess = { version = "1.6.1" }
aster = { version = "0.41", default-features = false }
quasi = { version = "0.32", default-features = false }
quasi_macros = { version = "0.32", optional = true }
syntex = { version = "0.58", optional = true }
syntex_syntax = { version = "0.58", optional = true }
clippy = { version = "0.0.90", optional = true }
[features]
dev = ["clippy"]
default = ["with-syntex"]
nightly = ["quasi_macros"]
nightly-testing = ["clippy"]
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
use-precompiled-js = []

View File

@@ -1,65 +0,0 @@
# Parity Dapps (JS-glue)
Code generator to simplify creating a built-in Parity Dapp
# How to create new builtin Dapp.
1. Clone this repository.
```bash
$ git clone https://github.com/paritytech/parity.git
```
1. Create a new directory for your Dapp. (`./myapp`)
```bash
$ mkdir -p ./parity/dapps/myapp/src/web
```
1. Copy your frontend files to `./dapps/myapp/src/web` (bundled ones)
```bash
$ cp -r ./myapp-src/* ./parity/dapps/myapp/src/web
```
1. Instead of creating `web3` in your app. Load (as the first script tag in `head`):
```html
<script src="/parity-utils/inject.js"></script>
```
The `inject.js` script will create global `web3` instance with proper provider that should be used by your dapp.
1. Create `./parity/dapps/myapp/Cargo.toml` with you apps details. See example here: [parity-status Cargo.toml](https://github.com/paritytech/parity-ui/blob/master/status/Cargo.toml).
```bash
$ git clone https://github.com/paritytech/parity-ui.git
$ cd ./parity-ui/
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
$ cp ./home/src/lib.rs ../parity/dapps/myapp/src/lib.rs
$ cp ./home/src/lib.rs.in ../parity/dapps/myapp/src/lib.rs.in
# And edit the details of your app
$ vim ../parity/dapps/myapp/Cargo.toml # Edit the details
$ vim ./parity/dapps/myapp/src/lib.rs.in # Edit the details
```
# How to include your Dapp into `Parity`?
1. Edit `dapps/Cargo.toml` and add dependency to your application (it can be optional)
```toml
# Use git repo and version
parity-dapps-myapp = { path="./myapp" }
```
1. Edit `dapps/src/apps.rs` and add your application to `all_pages` (if it's optional you need to specify two functions - see `parity-dapps-wallet` example)
1. Compile parity.
```bash
$ cargo build --release # While inside `parity`
```
1. Commit the results.
```bash
$ git add myapp && git commit -am "My first Parity Dapp".
```

View File

@@ -1,65 +0,0 @@
#[cfg(feature = "with-syntex")]
pub mod inner {
use syntex;
use codegen;
use syntax::{ast, fold};
use std::env;
use std::path::Path;
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
/// Helper folder that strips the serde attributes after the extensions have been expanded.
struct StripAttributeFolder;
impl fold::Folder for StripAttributeFolder {
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
if &*attr.value.name.as_str() == "webapp" {
return None;
}
Some(attr)
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self)
}
}
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
}
pub fn register(reg: &mut syntex::Registry) {
reg.add_attr("feature(custom_derive)");
reg.add_attr("feature(custom_attribute)");
reg.add_decorator("derive_WebAppFiles", codegen::expand_webapp_implementation);
reg.add_post_expansion_pass(strip_attributes);
}
pub fn generate() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let mut registry = syntex::Registry::new();
register(&mut registry);
let src = Path::new("src/lib.rs.in");
let dst = Path::new(&out_dir).join("lib.rs");
registry.expand("", &src, &dst).unwrap();
}
}
#[cfg(not(feature = "with-syntex"))]
pub mod inner {
use codegen;
pub fn register(reg: &mut rustc_plugin::Registry) {
reg.register_syntax_extension(
syntax::parse::token::intern("derive_WebAppFiles"),
syntax::ext::base::MultiDecorator(
Box::new(codegen::expand_webapp_implementation)));
reg.register_attribute("webapp".to_owned(), AttributeType::Normal);
}
pub fn generate() {}
}

View File

@@ -1,193 +0,0 @@
// 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/>.
extern crate aster;
extern crate glob;
extern crate mime_guess;
use self::mime_guess::guess_mime_type;
use std::path::{self, Path, PathBuf};
use std::ops::Deref;
use syntax::attr;
use syntax::ast::{self, MetaItem, Item};
use syntax::codemap::Span;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::print::pprust::lit_to_string;
use syntax::symbol::InternedString;
pub fn expand_webapp_implementation(
cx: &mut ExtCtxt,
span: Span,
meta_item: &MetaItem,
annotatable: &Annotatable,
push: &mut FnMut(Annotatable)
) {
let item = match *annotatable {
Annotatable::Item(ref item) => item,
_ => {
cx.span_err(meta_item.span, "`#[derive(WebAppFiles)]` may only be applied to struct implementations");
return;
},
};
let builder = aster::AstBuilder::new().span(span);
implement_webapp(cx, &builder, item, push);
}
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
let static_files_dir = extract_path(cx, item);
let src = Path::new("src");
let static_files = {
let mut buf = src.to_path_buf();
buf.push(static_files_dir.deref());
buf
};
let search_location = {
let mut buf = static_files.to_path_buf();
buf.push("**");
buf.push("*");
buf
};
let files = glob::glob(search_location.to_str().expect("Valid UTF8 path"))
.expect("The sources directory is missing.")
.collect::<Result<Vec<PathBuf>, glob::GlobError>>()
.expect("There should be no error when reading a list of files.");
let statements = files
.iter()
.filter(|path_buf| path_buf.is_file())
.map(|path_buf| {
let path = path_buf.as_path();
let filename = path.file_name().and_then(|s| s.to_str()).expect("Only UTF8 paths.");
let mime_type = guess_mime_type(filename).to_string();
let file_path = as_uri(path.strip_prefix(&static_files).ok().expect("Prefix is always there, cause it's absolute path;qed"));
let file_path_in_source = path.to_str().expect("Only UTF8 paths.");
let path_lit = builder.expr().str(file_path.as_str());
let mime_lit = builder.expr().str(mime_type.as_str());
let web_path_lit = builder.expr().str(file_path_in_source);
let separator_lit = builder.expr().str(path::MAIN_SEPARATOR.to_string().as_str());
let concat_id = builder.id("concat!");
let env_id = builder.id("env!");
let macro_id = builder.id("include_bytes!");
let content = quote_expr!(
cx,
$macro_id($concat_id($env_id("CARGO_MANIFEST_DIR"), $separator_lit, $web_path_lit))
);
quote_stmt!(
cx,
files.insert($path_lit, File { path: $path_lit, content_type: $mime_lit, content: $content });
).expect("The statement is always ok, because it just uses literals.")
}).collect::<Vec<ast::Stmt>>();
let type_name = item.ident;
let files_impl = quote_item!(cx,
impl $type_name {
fn files() -> ::std::collections::HashMap<&'static str, File> {
let mut files = ::std::collections::HashMap::new();
$statements
files
}
}
).unwrap();
push(Annotatable::Item(files_impl));
}
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
for meta_items in item.attrs.iter().filter_map(webapp_meta_items) {
for meta_item in meta_items {
let is_path = &*meta_item.name.as_str() == "path";
match meta_item.node {
ast::MetaItemKind::NameValue(ref lit) if is_path => {
if let Some(s) = get_str_from_lit(cx, lit) {
return s.deref().to_owned();
}
},
_ => {},
}
}
}
// default
"web".to_owned()
}
fn webapp_meta_items(attr: &ast::Attribute) -> Option<Vec<ast::MetaItem>> {
let is_webapp = &*attr.value.name.as_str() == "webapp";
match attr.value.node {
ast::MetaItemKind::List(ref items) if is_webapp => {
attr::mark_used(&attr);
Some(
items.iter()
.map(|item| item.node.clone())
.filter_map(|item| match item {
ast::NestedMetaItemKind::MetaItem(item) => Some(item),
_ => None,
})
.collect()
)
}
_ => None
}
}
fn get_str_from_lit(cx: &ExtCtxt, lit: &ast::Lit) -> Option<InternedString> {
match lit.node {
ast::LitKind::Str(ref s, _) => Some(s.clone().as_str()),
_ => {
cx.span_err(
lit.span,
&format!("webapp annotation path must be a string, not `{}`",
lit_to_string(lit)
)
);
return None;
}
}
}
fn as_uri(path: &Path) -> String {
let mut s = String::new();
for component in path.iter() {
s.push_str(component.to_str().expect("Only UTF-8 filenames are supported."));
s.push('/');
}
s[0..s.len()-1].into()
}
#[test]
fn should_convert_path_separators_on_all_platforms() {
// given
let p = {
let mut p = PathBuf::new();
p.push("web");
p.push("src");
p.push("index.html");
p
};
// when
let path = as_uri(&p);
// then
assert_eq!(path, "web/src/index.html".to_owned());
}

View File

@@ -1,89 +0,0 @@
// 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/>.
#![cfg_attr(feature="use-precompiled-js", allow(dead_code))]
#![cfg_attr(feature="use-precompiled-js", allow(unused_imports))]
use std::fmt;
use std::process::Command;
#[cfg(not(windows))]
mod platform {
use std::process::Command;
pub static NPM_CMD: &'static str = "npm";
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
cmd
}
}
#[cfg(windows)]
mod platform {
use std::process::{Command, Stdio};
pub static NPM_CMD: &'static str = "npm.cmd";
// NOTE [ToDr] For some reason on windows
// We cannot have any file descriptors open when running a child process
// during build phase.
pub fn handle_fd(cmd: &mut Command) -> &mut Command {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
}
}
fn die<T : fmt::Debug>(s: &'static str, e: T) -> ! {
panic!("Error: {}: {:?}", s, e);
}
#[cfg(feature = "use-precompiled-js")]
pub fn test(_path: &str) {
}
#[cfg(feature = "use-precompiled-js")]
pub fn build(_path: &str, _dest: &str) {
}
#[cfg(not(feature = "use-precompiled-js"))]
pub fn build(path: &str, dest: &str) {
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
.arg("install")
.arg("--no-progress")
.current_dir(path)
.status()
.unwrap_or_else(|e| die("Installing node.js dependencies with npm", e));
assert!(child.success(), "There was an error installing dependencies.");
let child = platform::handle_fd(&mut Command::new(platform::NPM_CMD))
.arg("run")
.arg("build")
.env("NODE_ENV", "production")
.env("BUILD_DEST", dest)
.current_dir(path)
.status()
.unwrap_or_else(|e| die("Building JS code", e));
assert!(child.success(), "There was an error build JS code.");
}
#[cfg(not(feature = "use-precompiled-js"))]
pub fn test(path: &str) {
let child = Command::new(platform::NPM_CMD)
.arg("run")
.arg("test")
.current_dir(path)
.status()
.unwrap_or_else(|e| die("Running test command", e));
assert!(child.success(), "There was an error while running JS tests.");
}

View File

@@ -1,38 +0,0 @@
// 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/>.
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
#[cfg(feature = "with-syntex")]
extern crate syntex;
#[cfg(feature = "with-syntex")]
extern crate syntex_syntax as syntax;
#[cfg(feature = "with-syntex")]
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
#[cfg(not(feature = "with-syntex"))]
#[macro_use]
extern crate syntax;
#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;
#[cfg(not(feature = "with-syntex"))]
include!("lib.rs.in");

View File

@@ -1,46 +0,0 @@
// 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/>.
extern crate quasi;
mod codegen;
mod build;
pub mod js;
pub use build::inner::generate;
use std::default::Default;
#[derive(Clone)]
pub struct File {
pub path: &'static str,
pub content: &'static [u8],
// TODO: use strongly-typed MIME.
pub content_type: &'static str,
}
#[derive(Clone, Debug)]
pub struct Info {
pub name: &'static str,
pub version: &'static str,
pub author: &'static str,
pub description: &'static str,
pub icon_url: &'static str,
}
pub trait WebApp : Default + Send + Sync {
fn file(&self, path: &str) -> Option<&File>;
fn info(&self) -> Info;
}

Binary file not shown.

View File

@@ -1,215 +0,0 @@
// 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/>.
use std::sync::Arc;
use hyper::{server, net, Decoder, Encoder, Next, Control};
use hyper::method::Method;
use hyper::status::StatusCode;
use api::{response, types};
use api::time::TimeChecker;
use apps::fetcher::Fetcher;
use handlers::{self, extract_url};
use endpoint::{Endpoint, Handler, EndpointPath};
use parity_reactor::Remote;
use {SyncStatus};
#[derive(Clone)]
pub struct RestApi {
fetcher: Arc<Fetcher>,
sync_status: Arc<SyncStatus>,
time: TimeChecker,
remote: Remote,
}
impl RestApi {
pub fn new(
fetcher: Arc<Fetcher>,
sync_status: Arc<SyncStatus>,
time: TimeChecker,
remote: Remote,
) -> Box<Endpoint> {
Box::new(RestApi {
fetcher,
sync_status,
time,
remote,
})
}
}
impl Endpoint for RestApi {
fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<Handler> {
Box::new(RestApiRouter::new((*self).clone(), path, control))
}
}
struct RestApiRouter {
api: RestApi,
path: Option<EndpointPath>,
control: Option<Control>,
handler: Box<Handler>,
}
impl RestApiRouter {
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
RestApiRouter {
path: Some(path),
control: Some(control),
api: api,
handler: Box::new(response::as_json_error(StatusCode::NotFound, &types::ApiError {
code: "404".into(),
title: "Not Found".into(),
detail: "Resource you requested has not been found.".into(),
})),
}
}
fn resolve_content(&self, hash: Option<&str>, path: EndpointPath, control: Control) -> Option<Box<Handler>> {
trace!(target: "dapps", "Resolving content: {:?} from path: {:?}", hash, path);
match hash {
Some(hash) if self.api.fetcher.contains(hash) => {
Some(self.api.fetcher.to_async_handler(path, control))
},
_ => None
}
}
fn health(&self, control: Control) -> Box<Handler> {
use self::types::{HealthInfo, HealthStatus, Health};
trace!(target: "dapps", "Checking node health.");
// Check timediff
let sync_status = self.api.sync_status.clone();
let map = move |time| {
// Check peers
let peers = {
let (connected, max) = sync_status.peers();
let (status, message) = match connected {
0 => {
(HealthStatus::Bad, "You are not connected to any peers. There is most likely some network issue. Fix connectivity.".into())
},
1 => (HealthStatus::NeedsAttention, "You are connected to only one peer. Your node might not be reliable. Check your network connection.".into()),
_ => (HealthStatus::Ok, "".into()),
};
HealthInfo { status, message, details: (connected, max) }
};
// Check sync
let sync = {
let is_syncing = sync_status.is_major_importing();
let (status, message) = if is_syncing {
(HealthStatus::NeedsAttention, "Your node is still syncing, the values you see might be outdated. Wait until it's fully synced.".into())
} else {
(HealthStatus::Ok, "".into())
};
HealthInfo { status, message, details: is_syncing }
};
// 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)
},
Ok(Ok(diff)) => {
(HealthStatus::Bad, format!(
"Your clock is not in sync. Detected difference is too big for the protocol to work: {}ms. Synchronize your clock.",
diff,
), diff)
},
Ok(Err(err)) => {
(HealthStatus::NeedsAttention, format!(
"Unable to reach time API: {}. Make sure that your clock is synchronized.",
err,
), 0)
},
Err(_) => {
(HealthStatus::NeedsAttention, "Time API request timed out. Make sure that the clock is synchronized.".into(), 0)
},
};
HealthInfo { status, message, details, }
};
let status = if [&peers.status, &sync.status, &time.status].iter().any(|x| *x != &HealthStatus::Ok) {
StatusCode::PreconditionFailed // HTTP 412
} else {
StatusCode::Ok // HTTP 200
};
response::as_json(status, &Health { peers, sync, time })
};
let time = self.api.time.time_drift();
let remote = self.api.remote.clone();
Box::new(handlers::AsyncHandler::new(time, map, remote, control))
}
}
impl server::Handler<net::HttpStream> for RestApiRouter {
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
if let Method::Options = *request.method() {
self.handler = response::empty();
return Next::write();
}
// TODO [ToDr] Consider using `path.app_params` instead
let url = extract_url(&request);
if url.is_none() {
// Just return 404 if we can't parse URL
return Next::write();
}
let url = url.expect("Check for None early-exists above; qed");
let mut path = self.path.take().expect("on_request called only once, and path is always defined in new; qed");
let control = self.control.take().expect("on_request called only once, and control is always defined in new; qed");
let endpoint = url.path.get(1).map(|v| v.as_str());
let hash = url.path.get(2).map(|v| v.as_str());
// at this point path.app_id contains 'api', adjust it to the hash properly, otherwise
// we will try and retrieve 'api' as the hash when doing the /api/content route
if let Some(ref hash) = hash { path.app_id = hash.clone().to_owned() }
let handler = endpoint.and_then(|v| match v {
"ping" => Some(response::ping()),
"health" => Some(self.health(control)),
"content" => self.resolve_content(hash, path, control),
_ => None
});
// Overwrite default
if let Some(h) = handler {
self.handler = h;
}
self.handler.on_request(request)
}
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
self.handler.on_request_readable(decoder)
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
self.handler.on_response(res)
}
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
self.handler.on_response_writable(encoder)
}
}

View File

@@ -1,42 +0,0 @@
// 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/>.
use serde::Serialize;
use serde_json;
use hyper::status::StatusCode;
use endpoint::Handler;
use handlers::{ContentHandler, EchoHandler};
pub fn empty() -> Box<Handler> {
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
}
pub fn as_json<T: Serialize>(status: StatusCode, val: &T) -> ContentHandler {
let json = serde_json::to_string(val)
.expect("serialization to string is infallible; qed");
ContentHandler::new(status, json, mime!(Application/Json))
}
pub fn as_json_error<T: Serialize>(status: StatusCode, val: &T) -> ContentHandler {
let json = serde_json::to_string(val)
.expect("serialization to string is infallible; qed");
ContentHandler::new(status, json, mime!(Application/Json))
}
pub fn ping() -> Box<Handler> {
Box::new(EchoHandler::default())
}

View File

@@ -1,270 +0,0 @@
// 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/>.
//! Periodically checks node's time drift using [SNTP](https://tools.ietf.org/html/rfc1769).
//!
//! An NTP packet is sent to the server with a local timestamp, the server then completes the packet, yielding the
//! following timestamps:
//!
//! Timestamp Name ID When Generated
//! ------------------------------------------------------------
//! Originate Timestamp T1 time request sent by client
//! Receive Timestamp T2 time request received at server
//! Transmit Timestamp T3 time reply sent by server
//! Destination Timestamp T4 time reply received at client
//!
//! The drift is defined as:
//!
//! drift = ((T2 - T1) + (T3 - T4)) / 2.
//!
use std::io;
use std::{fmt, mem, time};
use std::collections::VecDeque;
use futures::{self, Future, BoxFuture};
use futures_cpupool::CpuPool;
use ntp;
use time::{Duration, Timespec};
use util::{Arc, RwLock};
/// Time checker error.
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
/// There was an error when trying to reach the NTP server.
Ntp(String),
/// IO error when reading NTP response.
Io(String),
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match *self {
Ntp(ref err) => write!(fmt, "NTP error: {}", err),
Io(ref err) => write!(fmt, "Connection Error: {}", err),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self { Error::Io(format!("{}", err)) }
}
impl From<ntp::errors::Error> for Error {
fn from(err: ntp::errors::Error) -> Self { Error::Ntp(format!("{}", err)) }
}
/// NTP time drift checker.
pub trait Ntp {
/// Returns the current time drift.
fn drift(&self) -> BoxFuture<Duration, Error>;
}
/// NTP client using the SNTP algorithm for calculating drift.
#[derive(Clone)]
pub struct SimpleNtp {
address: Arc<String>,
pool: CpuPool,
}
impl fmt::Debug for SimpleNtp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Ntp {{ address: {} }}", self.address)
}
}
impl SimpleNtp {
fn new(address: &str, pool: CpuPool) -> SimpleNtp {
SimpleNtp {
address: Arc::new(address.to_owned()),
pool: pool,
}
}
}
impl Ntp for SimpleNtp {
fn drift(&self) -> BoxFuture<Duration, Error> {
let address = self.address.clone();
if &*address == "none" {
return futures::future::err(Error::Ntp("NTP server is not provided.".into())).boxed();
}
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);
let drift = ((recv_time - orig_time) + (transmit_time - dest_time)) / 2;
Ok(drift)
}).boxed()
}
}
// NOTE In a positive scenario first results will be seen after:
// MAX_RESULTS * UPDATE_TIMEOUT_OK_SECS seconds.
const MAX_RESULTS: usize = 7;
const UPDATE_TIMEOUT_OK_SECS: u64 = 30;
const UPDATE_TIMEOUT_ERR_SECS: u64 = 2;
#[derive(Debug, Clone)]
/// A time checker.
pub struct TimeChecker<N: Ntp = SimpleNtp> {
ntp: N,
last_result: Arc<RwLock<(time::Instant, VecDeque<Result<i64, Error>>)>>,
}
impl TimeChecker<SimpleNtp> {
/// Creates new time checker given the NTP server address.
pub fn new(ntp_address: String, 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);
TimeChecker {
ntp,
last_result,
}
}
}
impl<N: Ntp> TimeChecker<N> {
/// Updates the time
pub fn update(&self) -> BoxFuture<i64, Error> {
let last_result = self.last_result.clone();
self.ntp.drift().then(move |res| {
let mut results = mem::replace(&mut last_result.write().1, VecDeque::new());
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
}
);
// Push the result.
results.push_back(res.map(|d| d.num_milliseconds()));
while results.len() > MAX_RESULTS {
results.pop_front();
}
// Select a response and update last result.
let res = select_result(results.iter());
*last_result.write() = (valid_till, results);
res
}).boxed()
}
/// Returns a current time drift or error if last request to NTP server failed.
pub fn time_drift(&self) -> BoxFuture<i64, Error> {
// return cached result
{
let res = self.last_result.read();
if res.0 > time::Instant::now() {
return futures::done(select_result(res.1.iter())).boxed();
}
}
// or update and return result
self.update()
}
}
fn select_result<'a, T: Iterator<Item=&'a Result<i64, Error>>>(results: T) -> Result<i64, Error> {
let mut min = None;
for res in results {
min = Some(match (min.take(), res) {
(Some(Ok(min)), &Ok(ref new)) => Ok(::std::cmp::min(min, *new)),
(Some(Ok(old)), &Err(_)) => Ok(old),
(_, ref new) => (*new).clone(),
})
}
min.unwrap_or_else(|| Err(Error::Ntp("NTP server unavailable.".into())))
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::cell::{Cell, RefCell};
use std::time::Instant;
use time::Duration;
use futures::{self, BoxFuture, Future};
use super::{Ntp, TimeChecker, Error};
use util::RwLock;
#[derive(Clone)]
struct FakeNtp(RefCell<Vec<Duration>>, Cell<u64>);
impl FakeNtp {
fn new() -> FakeNtp {
FakeNtp(
RefCell::new(vec![Duration::milliseconds(150)]),
Cell::new(0))
}
}
impl Ntp for FakeNtp {
fn drift(&self) -> BoxFuture<Duration, Error> {
self.1.set(self.1.get() + 1);
futures::future::ok(self.0.borrow_mut().pop().expect("Unexpected call to drift().")).boxed()
}
}
fn time_checker() -> TimeChecker<FakeNtp> {
let last_result = Arc::new(RwLock::new(
(Instant::now(), vec![Err(Error::Ntp("NTP server unavailable".into()))].into())
));
TimeChecker {
ntp: FakeNtp::new(),
last_result: last_result,
}
}
#[test]
fn should_fetch_time_on_start() {
// given
let time = time_checker();
// when
let diff = time.time_drift().wait().unwrap();
// then
assert_eq!(diff, 150);
assert_eq!(time.ntp.1.get(), 1);
}
#[test]
fn should_not_fetch_twice_if_timeout_has_not_passed() {
// given
let time = time_checker();
// when
let diff1 = time.time_drift().wait().unwrap();
let diff2 = time.time_drift().wait().unwrap();
// then
assert_eq!(diff1, 150);
assert_eq!(diff2, 150);
assert_eq!(time.ntp.1.get(), 1);
}
}

View File

@@ -1,67 +0,0 @@
// 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/>.
/// A structure representing any error in REST API.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ApiError {
/// Error code.
pub code: String,
/// Human-readable error summary.
pub title: String,
/// More technical error details.
pub detail: String,
}
/// Health API endpoint status.
#[derive(Debug, PartialEq, Serialize)]
pub enum HealthStatus {
/// Everything's OK.
#[serde(rename = "ok")]
Ok,
/// Node health need attention
/// (the issue is not critical, but may need investigation)
#[serde(rename = "needsAttention")]
NeedsAttention,
/// There is something bad detected with the node.
#[serde(rename = "bad")]
Bad
}
/// Represents a single check in node health.
/// Cointains the status of that check and apropriate message and details.
#[derive(Debug, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct HealthInfo<T> {
/// Check status.
pub status: HealthStatus,
/// Human-readable message.
pub message: String,
/// Technical details of the check.
pub details: T,
}
/// Node Health status.
#[derive(Debug, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Health {
/// Status of peers.
pub peers: HealthInfo<(usize, usize)>,
/// Sync status.
pub sync: HealthInfo<bool>,
/// Time diff info.
pub time: HealthInfo<i64>,
}

View File

@@ -1,55 +0,0 @@
// 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/>.
use endpoint::EndpointInfo;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct App {
pub id: String,
pub name: String,
pub description: String,
pub version: String,
pub author: String,
#[serde(rename="iconUrl")]
pub icon_url: String,
}
impl App {
/// Creates `App` instance from `EndpointInfo` and `id`.
pub fn from_info(id: &str, info: &EndpointInfo) -> Self {
App {
id: id.to_owned(),
name: info.name.to_owned(),
description: info.description.to_owned(),
version: info.version.to_owned(),
author: info.author.to_owned(),
icon_url: info.icon_url.to_owned(),
}
}
}
impl Into<EndpointInfo> for App {
fn into(self) -> EndpointInfo {
EndpointInfo {
name: self.name,
description: self.description,
version: self.version,
author: self.author,
icon_url: self.icon_url,
}
}
}

View File

@@ -1,128 +0,0 @@
// 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/>.
//! Fetchable Dapps support.
use std::fs;
use linked_hash_map::LinkedHashMap;
use page::LocalPageEndpoint;
use handlers::FetchControl;
pub enum ContentStatus {
Fetching(FetchControl),
Ready(LocalPageEndpoint),
}
#[derive(Default)]
pub struct ContentCache {
cache: LinkedHashMap<String, ContentStatus>,
}
impl ContentCache {
pub fn insert(&mut self, content_id: String, status: ContentStatus) -> Option<ContentStatus> {
self.cache.insert(content_id, status)
}
pub fn remove(&mut self, content_id: &str) -> Option<ContentStatus> {
self.cache.remove(content_id)
}
pub fn get(&mut self, content_id: &str) -> Option<&mut ContentStatus> {
self.cache.get_refresh(content_id)
}
pub fn clear_garbage(&mut self, expected_size: usize) -> Vec<(String, ContentStatus)> {
let len = self.cache.len();
if len <= expected_size {
return Vec::new();
}
let mut removed = Vec::with_capacity(len - expected_size);
while self.cache.len() > expected_size {
let entry = self.cache.pop_front().expect("expected_size bounded at 0, len is greater; qed");
match entry.1 {
ContentStatus::Fetching(ref fetch) => {
trace!(target: "dapps", "Aborting {} because of limit.", entry.0);
// Mark as aborted
fetch.abort()
},
ContentStatus::Ready(ref endpoint) => {
trace!(target: "dapps", "Removing {} because of limit.", entry.0);
// Remove path (dir or file)
let res = fs::remove_dir_all(&endpoint.path()).or_else(|_| fs::remove_file(&endpoint.path()));
if let Err(e) = res {
warn!(target: "dapps", "Unable to remove dapp/content from cache: {:?}", e);
}
}
}
removed.push(entry);
}
removed
}
#[cfg(test)]
pub fn len(&self) -> usize {
self.cache.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn only_keys(data: Vec<(String, ContentStatus)>) -> Vec<String> {
data.into_iter().map(|x| x.0).collect()
}
#[test]
fn should_remove_least_recently_used() {
// given
let mut cache = ContentCache::default();
cache.insert("a".into(), ContentStatus::Fetching(Default::default()));
cache.insert("b".into(), ContentStatus::Fetching(Default::default()));
cache.insert("c".into(), ContentStatus::Fetching(Default::default()));
// when
let res = cache.clear_garbage(2);
// then
assert_eq!(cache.len(), 2);
assert_eq!(only_keys(res), vec!["a"]);
}
#[test]
fn should_update_lru_if_accessed() {
// given
let mut cache = ContentCache::default();
cache.insert("a".into(), ContentStatus::Fetching(Default::default()));
cache.insert("b".into(), ContentStatus::Fetching(Default::default()));
cache.insert("c".into(), ContentStatus::Fetching(Default::default()));
// when
cache.get("a");
let res = cache.clear_garbage(2);
// then
assert_eq!(cache.len(), 2);
assert_eq!(only_keys(res), vec!["b"]);
}
}

View File

@@ -1,256 +0,0 @@
// 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/>.
use zip;
use std::{fs, fmt};
use std::io::{self, Read, Write};
use std::path::PathBuf;
use fetch::{self, Mime};
use util::H256;
use util::sha3::sha3;
use page::{LocalPageEndpoint, PageCache};
use handlers::{ContentValidator, ValidatorResponse};
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
use Embeddable;
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
fn write_response_and_check_hash(
id: &str,
mut content_path: PathBuf,
filename: &str,
response: fetch::Response
) -> Result<(fs::File, PathBuf), ValidationError> {
// try to parse id
let id = id.parse().map_err(|_| ValidationError::InvalidContentId)?;
// check if content exists
if content_path.exists() {
warn!(target: "dapps", "Overwriting existing content at 0x{:?}", id);
fs::remove_dir_all(&content_path)?
}
// create directory
fs::create_dir_all(&content_path)?;
// append filename
content_path.push(filename);
// Now write the response
let mut file = io::BufWriter::new(fs::File::create(&content_path)?);
let mut reader = io::BufReader::new(response);
io::copy(&mut reader, &mut file)?;
file.flush()?;
// Validate hash
// TODO [ToDr] calculate sha3 in-flight while reading the response
let mut file = io::BufReader::new(fs::File::open(&content_path)?);
let hash = sha3(&mut file)?;
if id == hash {
Ok((file.into_inner(), content_path))
} else {
Err(ValidationError::HashMismatch {
expected: id,
got: hash,
})
}
}
pub struct Content {
id: String,
mime: Mime,
content_path: PathBuf,
on_done: OnDone,
}
impl Content {
pub fn new(id: String, mime: Mime, content_path: PathBuf, on_done: OnDone) -> Self {
Content {
id: id,
mime: mime,
content_path: content_path,
on_done: on_done,
}
}
}
impl ContentValidator for Content {
type Error = ValidationError;
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
let validate = |content_path: PathBuf| {
// Create dir
let (_, content_path) = write_response_and_check_hash(self.id.as_str(), content_path.clone(), self.id.as_str(), response)?;
Ok(LocalPageEndpoint::single_file(content_path, self.mime.clone(), PageCache::Enabled))
};
// Prepare path for a file
let content_path = self.content_path.join(&self.id);
// Make sure to always call on_done (even in case of errors)!
let result = validate(content_path.clone());
// remove the file if there was an error
if result.is_err() {
// Ignore errors since the file might not exist
let _ = fs::remove_dir_all(&content_path);
}
(self.on_done)(result.as_ref().ok().cloned());
result.map(ValidatorResponse::Local)
}
}
pub struct Dapp {
id: String,
dapps_path: PathBuf,
on_done: OnDone,
embeddable_on: Embeddable,
}
impl Dapp {
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable) -> Self {
Dapp {
id,
dapps_path,
on_done,
embeddable_on,
}
}
fn find_manifest(zip: &mut zip::ZipArchive<fs::File>) -> Result<(Manifest, PathBuf), ValidationError> {
for i in 0..zip.len() {
let mut file = zip.by_index(i)?;
if !file.name().ends_with(MANIFEST_FILENAME) {
continue;
}
// try to read manifest
let mut manifest = String::new();
let manifest = file
.read_to_string(&mut manifest).ok()
.and_then(|_| deserialize_manifest(manifest).ok());
if let Some(manifest) = manifest {
let mut manifest_location = PathBuf::from(file.name());
manifest_location.pop(); // get rid of filename
return Ok((manifest, manifest_location));
}
}
Err(ValidationError::ManifestNotFound)
}
}
impl ContentValidator for Dapp {
type Error = ValidationError;
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
let validate = |dapp_path: PathBuf| {
let (file, zip_path) = write_response_and_check_hash(self.id.as_str(), dapp_path.clone(), &format!("{}.zip", self.id), response)?;
trace!(target: "dapps", "Opening dapp bundle at {:?}", zip_path);
// Unpack archive
let mut zip = zip::ZipArchive::new(file)?;
// First find manifest file
let (mut manifest, manifest_dir) = Self::find_manifest(&mut zip)?;
// Overwrite id to match hash
manifest.id = self.id.clone();
// Unpack zip
for i in 0..zip.len() {
let mut file = zip.by_index(i)?;
let is_dir = file.name().chars().rev().next() == Some('/');
let file_path = PathBuf::from(file.name());
let location_in_manifest_base = file_path.strip_prefix(&manifest_dir);
// Create files that are inside manifest directory
if let Ok(location_in_manifest_base) = location_in_manifest_base {
let p = dapp_path.join(location_in_manifest_base);
// Check if it's a directory
if is_dir {
fs::create_dir_all(p)?;
} else {
let mut target = fs::File::create(p)?;
io::copy(&mut file, &mut target)?;
}
}
}
// Remove zip
fs::remove_file(&zip_path)?;
// Write manifest
let manifest_str = serialize_manifest(&manifest).map_err(ValidationError::ManifestSerialization)?;
let manifest_path = dapp_path.join(MANIFEST_FILENAME);
let mut manifest_file = fs::File::create(manifest_path)?;
manifest_file.write_all(manifest_str.as_bytes())?;
// Create endpoint
let endpoint = LocalPageEndpoint::new(dapp_path, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone());
Ok(endpoint)
};
// Prepare directory for dapp
let target = self.dapps_path.join(&self.id);
// Validate the dapp
let result = validate(target.clone());
// remove the file if there was an error
if result.is_err() {
// Ignore errors since the file might not exist
let _ = fs::remove_dir_all(&target);
}
(self.on_done)(result.as_ref().ok().cloned());
result.map(ValidatorResponse::Local)
}
}
#[derive(Debug)]
pub enum ValidationError {
Io(io::Error),
Zip(zip::result::ZipError),
InvalidContentId,
ManifestNotFound,
ManifestSerialization(String),
HashMismatch { expected: H256, got: H256, },
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
ValidationError::Io(ref io) => write!(f, "Unexpected IO error occured: {:?}", io),
ValidationError::Zip(ref zip) => write!(f, "Unable to read ZIP archive: {:?}", zip),
ValidationError::InvalidContentId => write!(f, "ID is invalid. It should be 256 bits keccak hash of content."),
ValidationError::ManifestNotFound => write!(f, "Downloaded Dapp bundle did not contain valid manifest.json file."),
ValidationError::ManifestSerialization(ref err) => {
write!(f, "There was an error during Dapp Manifest serialization: {:?}", err)
},
ValidationError::HashMismatch { ref expected, ref got } => {
write!(f, "Hash of downloaded content did not match. Expected:{:?}, Got:{:?}.", expected, got)
},
}
}
}
impl From<io::Error> for ValidationError {
fn from(err: io::Error) -> Self {
ValidationError::Io(err)
}
}
impl From<zip::result::ZipError> for ValidationError {
fn from(err: zip::result::ZipError) -> Self {
ValidationError::Zip(err)
}
}

View File

@@ -1,313 +0,0 @@
// 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/>.
//! Fetchable Dapps support.
//! Manages downloaded (cached) Dapps and downloads them when necessary.
//! Uses `URLHint` to resolve addresses into Dapps bundle file location.
mod installers;
use std::{fs, env};
use std::path::PathBuf;
use std::sync::Arc;
use rustc_hex::FromHex;
use fetch::{Client as FetchClient, Fetch};
use hash_fetch::urlhint::{URLHintContract, URLHint, URLHintResult};
use parity_reactor::Remote;
use hyper;
use hyper::status::StatusCode;
use {Embeddable, SyncStatus, random_filename};
use util::Mutex;
use page::LocalPageEndpoint;
use handlers::{ContentHandler, ContentFetcherHandler};
use endpoint::{Endpoint, EndpointPath, Handler};
use apps::cache::{ContentCache, ContentStatus};
/// Limit of cached dapps/content
const MAX_CACHED_DAPPS: usize = 20;
pub trait Fetcher: Send + Sync + 'static {
fn contains(&self, content_id: &str) -> bool;
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler>;
}
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHintContract> {
cache_path: PathBuf,
resolver: R,
cache: Arc<Mutex<ContentCache>>,
sync: Arc<SyncStatus>,
embeddable_on: Embeddable,
remote: Remote,
fetch: F,
only_content: bool,
}
impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
fn drop(&mut self) {
// Clear cache path
let _ = fs::remove_dir_all(&self.cache_path);
}
}
impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
pub fn new(
resolver: R,
sync_status: Arc<SyncStatus>,
remote: Remote,
fetch: F,
) -> Self {
let mut cache_path = env::temp_dir();
cache_path.push(random_filename());
ContentFetcher {
cache_path: cache_path,
resolver: resolver,
sync: sync_status,
cache: Arc::new(Mutex::new(ContentCache::default())),
embeddable_on: None,
remote: remote,
fetch: fetch,
only_content: true,
}
}
pub fn allow_dapps(mut self, dapps: bool) -> Self {
self.only_content = !dapps;
self
}
pub fn embeddable_on(mut self, embeddable_on: Embeddable) -> Self {
self.embeddable_on = embeddable_on;
self
}
fn still_syncing(embeddable: Embeddable) -> Box<Handler> {
Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
"Sync In Progress",
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
embeddable,
))
}
fn dapps_disabled(address: Embeddable) -> Box<Handler> {
Box::new(ContentHandler::error(
StatusCode::ServiceUnavailable,
"Network Dapps Not Available",
"This interface doesn't support network dapps for security reasons.",
None,
address,
))
}
#[cfg(test)]
fn set_status(&self, content_id: &str, status: ContentStatus) {
self.cache.lock().insert(content_id.to_owned(), status);
}
// resolve contract call synchronously.
// TODO: port to futures-based hyper and make it all async.
fn resolve(&self, content_id: Vec<u8>) -> Option<URLHintResult> {
use futures::Future;
self.resolver.resolve(content_id)
.wait()
.unwrap_or_else(|e| { warn!("Error resolving content-id: {}", e); None })
}
}
impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
fn contains(&self, content_id: &str) -> bool {
{
let mut cache = self.cache.lock();
// Check if we already have the app
if cache.get(content_id).is_some() {
return true;
}
}
// fallback to resolver
if let Ok(content_id) = content_id.from_hex() {
// if there is content or we are syncing return true
self.sync.is_major_importing() || self.resolve(content_id).is_some()
} else {
false
}
}
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler> {
let mut cache = self.cache.lock();
let content_id = path.app_id.clone();
let (new_status, handler) = {
let status = cache.get(&content_id);
match status {
// Just serve the content
Some(&mut ContentStatus::Ready(ref endpoint)) => {
(None, endpoint.to_async_handler(path, control))
},
// Content is already being fetched
Some(&mut ContentStatus::Fetching(ref fetch_control)) if !fetch_control.is_deadline_reached() => {
trace!(target: "dapps", "Content fetching in progress. Waiting...");
(None, fetch_control.to_async_handler(path, control))
},
// We need to start fetching the content
_ => {
trace!(target: "dapps", "Content unavailable. Fetching... {:?}", content_id);
let content_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true.");
let content = self.resolve(content_hex);
let cache = self.cache.clone();
let id = content_id.clone();
let on_done = move |result: Option<LocalPageEndpoint>| {
let mut cache = cache.lock();
match result {
Some(endpoint) => cache.insert(id.clone(), ContentStatus::Ready(endpoint)),
// In case of error
None => cache.remove(&id),
};
};
match content {
// Don't serve dapps if we are still syncing (but serve content)
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
(None, Self::still_syncing(self.embeddable_on.clone()))
},
Some(URLHintResult::Dapp(_)) if self.only_content => {
(None, Self::dapps_disabled(self.embeddable_on.clone()))
},
Some(URLHintResult::Dapp(dapp)) => {
let handler = ContentFetcherHandler::new(
dapp.url(),
path,
control,
installers::Dapp::new(
content_id.clone(),
self.cache_path.clone(),
Box::new(on_done),
self.embeddable_on.clone(),
),
self.embeddable_on.clone(),
self.remote.clone(),
self.fetch.clone(),
);
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as Box<Handler>)
},
Some(URLHintResult::Content(content)) => {
let handler = ContentFetcherHandler::new(
content.url,
path,
control,
installers::Content::new(
content_id.clone(),
content.mime,
self.cache_path.clone(),
Box::new(on_done),
),
self.embeddable_on.clone(),
self.remote.clone(),
self.fetch.clone(),
);
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as Box<Handler>)
},
None if self.sync.is_major_importing() => {
(None, Self::still_syncing(self.embeddable_on.clone()))
},
None => {
// This may happen when sync status changes in between
// `contains` and `to_handler`
(None, Box::new(ContentHandler::error(
StatusCode::NotFound,
"Resource Not Found",
"Requested resource was not found.",
None,
self.embeddable_on.clone(),
)) as Box<Handler>)
},
}
},
}
};
if let Some(status) = new_status {
cache.clear_garbage(MAX_CACHED_DAPPS);
cache.insert(content_id, status);
}
handler
}
}
#[cfg(test)]
mod tests {
use std::env;
use std::sync::Arc;
use util::Bytes;
use fetch::{Fetch, Client};
use futures::{future, Future, BoxFuture};
use hash_fetch::urlhint::{URLHint, URLHintResult};
use parity_reactor::Remote;
use apps::cache::ContentStatus;
use endpoint::EndpointInfo;
use page::LocalPageEndpoint;
use super::{ContentFetcher, Fetcher};
use {SyncStatus};
#[derive(Clone)]
struct FakeResolver;
impl URLHint for FakeResolver {
fn resolve(&self, _id: Bytes) -> BoxFuture<Option<URLHintResult>, String> {
future::ok(None).boxed()
}
}
struct FakeSync(bool);
impl SyncStatus for FakeSync {
fn is_major_importing(&self) -> bool { self.0 }
fn peers(&self) -> (usize, usize) { (0, 5) }
}
#[test]
fn should_true_if_contains_the_app() {
// given
let path = env::temp_dir();
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(FakeSync(false)), Remote::new_sync(), Client::new().unwrap())
.allow_dapps(true);
let handler = LocalPageEndpoint::new(path, EndpointInfo {
name: "fake".into(),
description: "".into(),
version: "".into(),
author: "".into(),
icon_url: "".into(),
}, Default::default(), None);
// when
fetcher.set_status("test", ContentStatus::Ready(handler));
fetcher.set_status("test2", ContentStatus::Fetching(Default::default()));
// then
assert_eq!(fetcher.contains("test"), true);
assert_eq!(fetcher.contains("test2"), true);
assert_eq!(fetcher.contains("test3"), false);
}
}

View File

@@ -1,135 +0,0 @@
// 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/>.
use std::collections::BTreeMap;
use std::io;
use std::io::Read;
use std::fs;
use std::path::{Path, PathBuf};
use page::{LocalPageEndpoint, PageCache};
use endpoint::{Endpoint, EndpointInfo};
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
use Embeddable;
struct LocalDapp {
id: String,
path: PathBuf,
info: EndpointInfo,
}
/// Tries to find and read manifest file in given `path` to extract `EndpointInfo`
/// If manifest is not found sensible default `EndpointInfo` is returned based on given `name`.
fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
path.push(MANIFEST_FILENAME);
fs::File::open(path.clone())
.map_err(|e| format!("{:?}", e))
.and_then(|mut f| {
// Reat file
let mut s = String::new();
f.read_to_string(&mut s).map_err(|e| format!("{:?}", e))?;
// Try to deserialize manifest
deserialize_manifest(s)
})
.map(Into::into)
.unwrap_or_else(|e| {
warn!(target: "dapps", "Cannot read manifest file at: {:?}. Error: {:?}", path, e);
EndpointInfo {
name: name.into(),
description: name.into(),
version: "0.0.0".into(),
author: "?".into(),
icon_url: "icon.png".into(),
}
})
}
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
/// Parses the path to extract last component (for name).
/// `None` is returned when path is invalid or non-existent.
pub fn local_endpoint<P: AsRef<Path>>(path: P, embeddable: Embeddable) -> Option<(String, Box<LocalPageEndpoint>)> {
let path = path.as_ref().to_owned();
path.canonicalize().ok().and_then(|path| {
let name = path.file_name().and_then(|name| name.to_str());
name.map(|name| {
let dapp = local_dapp(name.into(), path.clone());
(dapp.id, Box::new(LocalPageEndpoint::new(
dapp.path, dapp.info, PageCache::Disabled, embeddable.clone())
))
})
})
}
fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
// try to get manifest file
let info = read_manifest(&name, path.clone());
LocalDapp {
id: name,
path: path,
info: info,
}
}
/// Returns endpoints for Local Dapps found for given filesystem path.
/// Scans the directory and collects `LocalPageEndpoints`.
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, embeddable: Embeddable) -> BTreeMap<String, Box<Endpoint>> {
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
for dapp in local_dapps(dapps_path.as_ref()) {
pages.insert(
dapp.id,
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, embeddable.clone()))
);
}
pages
}
fn local_dapps(dapps_path: &Path) -> Vec<LocalDapp> {
let files = fs::read_dir(dapps_path);
if let Err(e) = files {
warn!(target: "dapps", "Unable to load local dapps from: {}. Reason: {:?}", dapps_path.display(), e);
return vec![];
}
let files = files.expect("Check is done earlier");
files.map(|dir| {
let entry = dir?;
let file_type = entry.file_type()?;
// skip files
if file_type.is_file() {
return Err(io::Error::new(io::ErrorKind::NotFound, "Not a file"));
}
// take directory name and path
entry.file_name().into_string()
.map(|name| (name, entry.path()))
.map_err(|e| {
info!(target: "dapps", "Unable to load dapp: {:?}. Reason: {:?}", entry.path(), e);
io::Error::new(io::ErrorKind::NotFound, "Invalid name")
})
})
.filter_map(|m| {
if let Err(ref e) = m {
debug!(target: "dapps", "Ignoring local dapp: {:?}", e);
}
m.ok()
})
.map(|(name, path)| local_dapp(name, path))
.collect()
}

View File

@@ -1,29 +0,0 @@
// 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/>.
use serde_json;
pub use apps::App as Manifest;
pub const MANIFEST_FILENAME: &'static str = "manifest.json";
pub fn deserialize_manifest(manifest: String) -> Result<Manifest, String> {
serde_json::from_str::<Manifest>(&manifest).map_err(|e| format!("{:?}", e))
// TODO [todr] Manifest validation (especialy: id (used as path))
}
pub fn serialize_manifest(manifest: &Manifest) -> Result<String, String> {
serde_json::to_string_pretty(manifest).map_err(|e| format!("{:?}", e))
}

View File

@@ -1,97 +0,0 @@
// 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/>.
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::Arc;
use endpoint::{Endpoints, Endpoint};
use page::PageEndpoint;
use proxypac::ProxyPac;
use web::Web;
use fetch::Fetch;
use parity_dapps::WebApp;
use parity_reactor::Remote;
use parity_ui;
use {WebProxyTokens, ParentFrameSettings};
mod app;
mod cache;
mod fs;
mod ui;
pub mod fetcher;
pub mod manifest;
pub use self::app::App;
pub const HOME_PAGE: &'static str = "home";
pub const RPC_PATH: &'static str = "rpc";
pub const API_PATH: &'static str = "api";
pub const UTILS_PATH: &'static str = "parity-utils";
pub const WEB_PATH: &'static str = "web";
pub const URL_REFERER: &'static str = "__referer=";
pub fn utils() -> Box<Endpoint> {
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
}
pub fn ui() -> Box<Endpoint> {
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
}
pub fn ui_redirection(embeddable: Option<ParentFrameSettings>) -> Box<Endpoint> {
Box::new(ui::Redirection::new(embeddable))
}
pub fn all_endpoints<F: Fetch>(
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
dapps_domain: &str,
embeddable: Option<ParentFrameSettings>,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
fetch: F,
) -> Endpoints {
// fetch fs dapps at first to avoid overwriting builtins
let mut pages = fs::local_endpoints(dapps_path, embeddable.clone());
for path in extra_dapps {
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone()) {
pages.insert(id, endpoint);
} else {
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
}
}
// NOTE [ToDr] Dapps will be currently embeded on 8180
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(embeddable.clone()));
pages.insert("proxy".into(), ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned()));
pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
Arc::new(pages)
}
fn insert<T : WebApp + Default + 'static>(pages: &mut BTreeMap<String, Box<Endpoint>>, id: &str, embed_at: Embeddable) {
pages.insert(id.to_owned(), Box::new(match embed_at {
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
Embeddable::No => PageEndpoint::new(T::default()),
}));
}
enum Embeddable {
Yes(Option<ParentFrameSettings>),
#[allow(dead_code)]
No,
}

View File

@@ -1,55 +0,0 @@
// 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/>.
//! UI redirections
use hyper::{Control, StatusCode};
use endpoint::{Endpoint, Handler, EndpointPath};
use {handlers, Embeddable};
/// Redirection to UI server.
pub struct Redirection {
embeddable_on: Embeddable,
}
impl Redirection {
pub fn new(
embeddable_on: Embeddable,
) -> Self {
Redirection {
embeddable_on,
}
}
}
impl Endpoint for Redirection {
fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box<Handler> {
if let Some(ref frame) = self.embeddable_on {
trace!(target: "dapps", "Redirecting to signer interface.");
handlers::Redirection::boxed(&format!("http://{}:{}", &frame.host, frame.port))
} else {
trace!(target: "dapps", "Signer disabled, returning 404.");
Box::new(handlers::ContentHandler::error(
StatusCode::NotFound,
"404 Not Found",
"Your homepage is not available when Trusted Signer is disabled.",
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
None,
))
}
}
}

View File

@@ -1,55 +0,0 @@
// 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/>.
//! URL Endpoint traits
use std::sync::Arc;
use std::collections::BTreeMap;
use hyper::{self, server, net};
#[derive(Debug, PartialEq, Default, Clone)]
pub struct EndpointPath {
pub app_id: String,
pub app_params: Vec<String>,
pub host: String,
pub port: u16,
pub using_dapps_domains: bool,
}
#[derive(Debug, PartialEq, Clone)]
pub struct EndpointInfo {
pub name: String,
pub description: String,
pub version: String,
pub author: String,
pub icon_url: String,
}
pub type Endpoints = Arc<BTreeMap<String, Box<Endpoint>>>;
pub type Handler = server::Handler<net::HttpStream> + Send;
pub trait Endpoint : Send + Sync {
fn info(&self) -> Option<&EndpointInfo> { None }
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
panic!("This Endpoint is asynchronous and requires Control object.");
}
fn to_async_handler(&self, path: EndpointPath, _control: hyper::Control) -> Box<Handler> {
self.to_handler(path)
}
}

View File

@@ -1,21 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>{title}</title>
<link rel="stylesheet" href="/parity-utils/styles.css">
</head>
<body>
<div class="parity-navbar">
</div>
<div class="parity-box">
<h1>{title}</h1>
<h3>{message}</h3>
<p><code>{details}</code></p>
</div>
<div class="parity-status">
<small>{version}</small>
</div>
</body>
</html>

View File

@@ -1,112 +0,0 @@
// 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/>.
//! Async Content Handler
//! Temporary solution until we switch to future-based server.
//! Wraps a future and converts it to hyper::server::Handler;
use std::{mem, time};
use std::sync::mpsc;
use futures::Future;
use hyper::{server, Decoder, Encoder, Next, Control};
use hyper::net::HttpStream;
use handlers::ContentHandler;
use parity_reactor::Remote;
const TIMEOUT_SECS: u64 = 15;
enum State<F, T, M> {
Initial(F, M, Remote, Control),
Waiting(mpsc::Receiver<Result<T, ()>>, M),
Done(ContentHandler),
Invalid,
}
pub struct AsyncHandler<F, T, M> {
state: State<F, T, M>,
}
impl<F, T, M> AsyncHandler<F, T, M> {
pub fn new(future: F, map: M, remote: Remote, control: Control) -> Self {
AsyncHandler {
state: State::Initial(future, map, remote, control),
}
}
}
impl<F, T, E, M> server::Handler<HttpStream> for AsyncHandler<F, Result<T, E>, M> where
F: Future<Item=T, Error=E> + Send + 'static,
M: FnOnce(Result<Result<T, E>, ()>) -> ContentHandler,
T: Send + 'static,
E: Send + 'static,
{
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
if let State::Initial(future, map, remote, control) = mem::replace(&mut self.state, State::Invalid) {
let (tx, rx) = mpsc::sync_channel(1);
let control2 = control.clone();
let tx2 = tx.clone();
remote.spawn_with_timeout(move || future.then(move |result| {
// Send a result (ignore errors if the connection was dropped)
let _ = tx.send(Ok(result));
// Resume handler
let _ = control.ready(Next::read());
Ok(())
}), time::Duration::from_secs(TIMEOUT_SECS), move || {
// Notify about error
let _ = tx2.send(Err(()));
// Resume handler
let _ = control2.ready(Next::read());
});
self.state = State::Waiting(rx, map);
}
Next::wait()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
if let State::Waiting(rx, map) = mem::replace(&mut self.state, State::Invalid) {
match rx.try_recv() {
Ok(result) => {
self.state = State::Done(map(result));
},
Err(err) => {
warn!("Resuming handler in incorrect state: {:?}", err);
}
}
}
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
if let State::Done(ref mut handler) = self.state {
handler.on_response(res)
} else {
Next::end()
}
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
if let State::Done(ref mut handler) = self.state {
handler.on_response_writable(encoder)
} else {
Next::end()
}
}
}

View File

@@ -1,116 +0,0 @@
// 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/>.
//! Simple Content Handler
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::mime::Mime;
use hyper::status::StatusCode;
use util::version;
use handlers::add_security_headers;
use Embeddable;
#[derive(Clone)]
pub struct ContentHandler {
code: StatusCode,
content: String,
mimetype: Mime,
write_pos: usize,
safe_to_embed_on: Embeddable,
}
impl ContentHandler {
pub fn ok(content: String, mimetype: Mime) -> Self {
Self::new(StatusCode::Ok, content, mimetype)
}
pub fn html(code: StatusCode, content: String, embeddable_on: Embeddable) -> Self {
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
}
pub fn error(
code: StatusCode,
title: &str,
message: &str,
details: Option<&str>,
embeddable_on: Embeddable,
) -> Self {
Self::html(code, format!(
include_str!("../error_tpl.html"),
title=title,
message=message,
details=details.unwrap_or_else(|| ""),
version=version(),
), embeddable_on)
}
pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self {
Self::new_embeddable(code, content, mimetype, None)
}
pub fn new_embeddable(
code: StatusCode,
content: String,
mimetype: Mime,
safe_to_embed_on: Embeddable,
) -> Self {
ContentHandler {
code,
content,
mimetype,
write_pos: 0,
safe_to_embed_on,
}
}
}
impl server::Handler<HttpStream> for ContentHandler {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(self.code);
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
let bytes = self.content.as_bytes();
if self.write_pos == bytes.len() {
return Next::end();
}
match encoder.write(&bytes[self.write_pos..]) {
Ok(bytes) => {
self.write_pos += bytes;
Next::write()
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end()
},
}
}
}

View File

@@ -1,60 +0,0 @@
// 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/>.
//! Echo Handler
use std::io::Read;
use hyper::{server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use super::ContentHandler;
#[derive(Default)]
pub struct EchoHandler {
content: String,
handler: Option<ContentHandler>,
}
impl server::Handler<HttpStream> for EchoHandler {
fn on_request(&mut self, _: server::Request<HttpStream>) -> Next {
Next::read()
}
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
match decoder.read_to_string(&mut self.content) {
Ok(0) => {
self.handler = Some(ContentHandler::ok(self.content.clone(), mime!(Application/Json)));
Next::write()
},
Ok(_) => Next::read(),
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::read(),
_ => Next::end(),
}
}
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
self.handler.as_mut()
.expect("handler always set in on_request, which is before now; qed")
.on_response(res)
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
self.handler.as_mut()
.expect("handler always set in on_request, which is before now; qed")
.on_response_writable(encoder)
}
}

View File

@@ -1,395 +0,0 @@
// 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/>.
//! Hyper Server Handler that fetches a file during a request (proxy).
use std::fmt;
use std::sync::{mpsc, Arc};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Instant, Duration};
use fetch::{self, Fetch};
use futures::Future;
use parity_reactor::Remote;
use util::Mutex;
use hyper::{server, Decoder, Encoder, Next, Method, Control};
use hyper::net::HttpStream;
use hyper::uri::RequestUri;
use hyper::status::StatusCode;
use endpoint::EndpointPath;
use handlers::{ContentHandler, StreamingHandler};
use page::{LocalPageEndpoint, PageHandlerWaiting};
use {Embeddable};
const FETCH_TIMEOUT: u64 = 300;
pub enum ValidatorResponse {
Local(LocalPageEndpoint),
Streaming(StreamingHandler<fetch::Response>),
}
pub trait ContentValidator: Send + 'static {
type Error: fmt::Debug + fmt::Display;
fn validate_and_install(&self, fetch::Response) -> Result<ValidatorResponse, Self::Error>;
}
enum FetchState {
Waiting,
NotStarted(String),
Error(ContentHandler),
InProgress(mpsc::Receiver<FetchState>),
Streaming(StreamingHandler<fetch::Response>),
Done(LocalPageEndpoint, Box<PageHandlerWaiting>),
}
enum WaitResult {
Error(ContentHandler),
Done(LocalPageEndpoint),
NonAwaitable,
}
#[derive(Clone)]
pub struct FetchControl {
abort: Arc<AtomicBool>,
listeners: Arc<Mutex<Vec<(Control, mpsc::Sender<WaitResult>)>>>,
deadline: Instant,
}
impl Default for FetchControl {
fn default() -> Self {
FetchControl {
abort: Arc::new(AtomicBool::new(false)),
listeners: Arc::new(Mutex::new(Vec::new())),
deadline: Instant::now() + Duration::from_secs(FETCH_TIMEOUT),
}
}
}
impl FetchControl {
fn notify<F: Fn() -> WaitResult>(&self, status: F) {
let mut listeners = self.listeners.lock();
for (control, sender) in listeners.drain(..) {
trace!(target: "dapps", "Resuming request waiting for content...");
if let Err(e) = sender.send(status()) {
trace!(target: "dapps", "Waiting listener notification failed: {:?}", e);
} else {
let _ = control.ready(Next::read());
}
}
}
fn set_status(&self, status: &FetchState) {
match *status {
FetchState::Error(ref handler) => self.notify(|| WaitResult::Error(handler.clone())),
FetchState::Done(ref endpoint, _) => self.notify(|| WaitResult::Done(endpoint.clone())),
FetchState::Streaming(_) => self.notify(|| WaitResult::NonAwaitable),
FetchState::NotStarted(_) | FetchState::InProgress(_) | FetchState::Waiting => {},
}
}
pub fn is_deadline_reached(&self) -> bool {
self.deadline < Instant::now()
}
pub fn abort(&self) {
self.abort.store(true, Ordering::SeqCst);
}
pub fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<server::Handler<HttpStream> + Send> {
let (tx, rx) = mpsc::channel();
self.listeners.lock().push((control, tx));
Box::new(WaitingHandler {
receiver: rx,
state: FetchState::Waiting,
uri: RequestUri::default(),
path: path,
})
}
}
pub struct WaitingHandler {
receiver: mpsc::Receiver<WaitResult>,
state: FetchState,
uri: RequestUri,
path: EndpointPath,
}
impl server::Handler<HttpStream> for WaitingHandler {
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
self.uri = request.uri().clone();
Next::wait()
}
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
let result = self.receiver.try_recv().ok();
self.state = match result {
Some(WaitResult::Error(handler)) => FetchState::Error(handler),
Some(WaitResult::Done(endpoint)) => {
let mut page_handler = endpoint.to_page_handler(self.path.clone());
page_handler.set_uri(&self.uri);
FetchState::Done(endpoint, page_handler)
},
_ => {
warn!("A result for waiting request was not received.");
FetchState::Waiting
},
};
match self.state {
FetchState::Done(_, ref mut handler) => handler.on_request_readable(decoder),
FetchState::Streaming(ref mut handler) => handler.on_request_readable(decoder),
FetchState::Error(ref mut handler) => handler.on_request_readable(decoder),
_ => Next::write(),
}
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
match self.state {
FetchState::Done(_, ref mut handler) => handler.on_response(res),
FetchState::Streaming(ref mut handler) => handler.on_response(res),
FetchState::Error(ref mut handler) => handler.on_response(res),
_ => Next::end(),
}
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
match self.state {
FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder),
FetchState::Streaming(ref mut handler) => handler.on_response_writable(encoder),
FetchState::Error(ref mut handler) => handler.on_response_writable(encoder),
_ => Next::end(),
}
}
}
#[derive(Clone)]
struct Errors {
embeddable_on: Embeddable,
}
impl Errors {
fn download_error<E: fmt::Debug>(&self, e: E) -> ContentHandler {
ContentHandler::error(
StatusCode::BadGateway,
"Download Error",
"There was an error when fetching the content.",
Some(&format!("{:?}", e)),
self.embeddable_on.clone(),
)
}
fn invalid_content<E: fmt::Debug>(&self, e: E) -> ContentHandler {
ContentHandler::error(
StatusCode::BadGateway,
"Invalid Dapp",
"Downloaded bundle does not contain a valid content.",
Some(&format!("{:?}", e)),
self.embeddable_on.clone(),
)
}
fn timeout_error(&self) -> ContentHandler {
ContentHandler::error(
StatusCode::GatewayTimeout,
"Download Timeout",
&format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT),
None,
self.embeddable_on.clone(),
)
}
fn method_not_allowed(&self) -> ContentHandler {
ContentHandler::error(
StatusCode::MethodNotAllowed,
"Method Not Allowed",
"Only <code>GET</code> requests are allowed.",
None,
self.embeddable_on.clone(),
)
}
}
pub struct ContentFetcherHandler<H: ContentValidator, F: Fetch> {
fetch_control: FetchControl,
control: Control,
remote: Remote,
status: FetchState,
fetch: F,
installer: Option<H>,
path: EndpointPath,
errors: Errors,
}
impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
pub fn new(
url: String,
path: EndpointPath,
control: Control,
installer: H,
embeddable_on: Embeddable,
remote: Remote,
fetch: F,
) -> Self {
ContentFetcherHandler {
fetch_control: FetchControl::default(),
control,
remote,
fetch,
status: FetchState::NotStarted(url),
installer: Some(installer),
path,
errors: Errors {
embeddable_on,
},
}
}
pub fn fetch_control(&self) -> FetchControl {
self.fetch_control.clone()
}
fn fetch_content(&self, uri: RequestUri, url: &str, installer: H) -> mpsc::Receiver<FetchState> {
let (tx, rx) = mpsc::channel();
let abort = self.fetch_control.abort.clone();
let path = self.path.clone();
let tx2 = tx.clone();
let control = self.control.clone();
let errors = self.errors.clone();
let future = self.fetch.fetch_with_abort(url, abort.into()).then(move |result| {
trace!(target: "dapps", "Fetching content finished. Starting validation: {:?}", result);
let new_state = match result {
Ok(response) => match installer.validate_and_install(response) {
Ok(ValidatorResponse::Local(endpoint)) => {
trace!(target: "dapps", "Validation OK. Returning response.");
let mut handler = endpoint.to_page_handler(path);
handler.set_uri(&uri);
FetchState::Done(endpoint, handler)
},
Ok(ValidatorResponse::Streaming(handler)) => {
trace!(target: "dapps", "Validation OK. Streaming response.");
FetchState::Streaming(handler)
},
Err(e) => {
trace!(target: "dapps", "Error while validating content: {:?}", e);
FetchState::Error(errors.invalid_content(e))
},
},
Err(e) => {
warn!(target: "dapps", "Unable to fetch content: {:?}", e);
FetchState::Error(errors.download_error(e))
},
};
// Content may be resolved when the connection is already dropped.
let _ = tx2.send(new_state);
// Ignoring control errors
let _ = control.ready(Next::read());
Ok(()) as Result<(), ()>
});
// make sure to run within fetch thread pool.
let future = self.fetch.process(future);
// spawn to event loop
let control = self.control.clone();
let errors = self.errors.clone();
self.remote.spawn_with_timeout(|| future, Duration::from_secs(FETCH_TIMEOUT), move || {
// Notify about the timeout
let _ = tx.send(FetchState::Error(errors.timeout_error()));
// Ignoring control errors
let _ = control.ready(Next::read());
});
rx
}
}
impl<H: ContentValidator, F: Fetch> server::Handler<HttpStream> for ContentFetcherHandler<H, F> {
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
let status = if let FetchState::NotStarted(ref url) = self.status {
let uri = request.uri().clone();
let installer = self.installer.take().expect("Installer always set initialy; installer used only in on_request; on_request invoked only once; qed");
Some(match *request.method() {
// Start fetching content
Method::Get => {
trace!(target: "dapps", "Fetching content from: {:?}", url);
let receiver = self.fetch_content(uri, url, installer);
FetchState::InProgress(receiver)
},
// or return error
_ => FetchState::Error(self.errors.method_not_allowed()),
})
} else { None };
if let Some(status) = status {
self.fetch_control.set_status(&status);
self.status = status;
}
Next::read()
}
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
let (status, next) = match self.status {
// Request may time out
FetchState::InProgress(_) if self.fetch_control.is_deadline_reached() => {
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
(Some(FetchState::Error(self.errors.timeout_error())), Next::write())
},
FetchState::InProgress(ref receiver) => {
// Check if there is an answer
let rec = receiver.try_recv();
match rec {
// just return the new state
Ok(state) => (Some(state), Next::write()),
// wait some more
_ => (None, Next::wait())
}
},
FetchState::Error(ref mut handler) => (None, handler.on_request_readable(decoder)),
_ => (None, Next::write()),
};
if let Some(status) = status {
self.fetch_control.set_status(&status);
self.status = status;
}
next
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
match self.status {
FetchState::Done(_, ref mut handler) => handler.on_response(res),
FetchState::Streaming(ref mut handler) => handler.on_response(res),
FetchState::Error(ref mut handler) => handler.on_response(res),
_ => Next::end(),
}
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
match self.status {
FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder),
FetchState::Streaming(ref mut handler) => handler.on_response_writable(encoder),
FetchState::Error(ref mut handler) => handler.on_response_writable(encoder),
_ => Next::end(),
}
}
}

View File

@@ -1,145 +0,0 @@
// 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/>.
//! Hyper handlers implementations.
mod async;
mod content;
mod echo;
mod fetch;
mod redirect;
mod streaming;
pub use self::async::AsyncHandler;
pub use self::content::ContentHandler;
pub use self::echo::EchoHandler;
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
pub use self::redirect::Redirection;
pub use self::streaming::StreamingHandler;
use std::iter;
use util::Itertools;
use url::Url;
use hyper::{server, header, net, uri};
use {apps, address, Embeddable};
/// Adds security-related headers to the Response.
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embeddable) {
headers.set_raw("X-XSS-Protection", vec![b"1; mode=block".to_vec()]);
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
// Embedding header:
if let None = embeddable_on {
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
}
// Content Security Policy headers
headers.set_raw("Content-Security-Policy", vec![
// Allow connecting to WS servers and HTTP(S) servers.
// We could be more restrictive and allow only RPC server URL.
b"connect-src http: https: ws: wss:;".to_vec(),
// Allow framing any content from HTTP(S).
// Again we could only allow embedding from RPC server URL.
// (deprecated)
b"frame-src 'self' http: https:;".to_vec(),
// Allow framing and web workers from HTTP(S).
b"child-src 'self' http: https:;".to_vec(),
// We allow data: blob: and HTTP(s) images.
// We could get rid of wildcarding HTTP and only allow RPC server URL.
// (http required for local dapps icons)
b"img-src 'self' 'unsafe-inline' data: blob: http: https:;".to_vec(),
// Allow style from data: blob: and HTTPS.
b"style-src 'self' 'unsafe-inline' data: blob: https:;".to_vec(),
// 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(),
// Same restrictions as script-src (fallback) with additional
// blob: that is required for camera access (worker)
b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' 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)
b"sandbox allow-same-origin allow-forms allow-modals allow-popups allow-presentation allow-scripts;".to_vec(),
// Disallow subitting forms from any dapps
b"form-action 'none';".to_vec(),
// Never allow mixed content
b"block-all-mixed-content;".to_vec(),
// Specify if the site can be embedded.
match embeddable_on {
Some(ref embed) => {
let std = address(&embed.host, embed.port);
let proxy = format!("{}.{}", apps::HOME_PAGE, embed.dapps_domain);
let domain = format!("*.{}:{}", embed.dapps_domain, embed.port);
let mut ancestors = vec![std, domain, proxy]
.into_iter()
.chain(embed.extra_embed_on
.iter()
.map(|&(ref host, port)| format!("{}:{}", host, port))
);
let ancestors = if embed.host == "127.0.0.1" {
let localhost = address("localhost", embed.port);
ancestors.chain(iter::once(localhost)).join(" ")
} else {
ancestors.join(" ")
};
format!("frame-ancestors {};", ancestors)
},
None => format!("frame-ancestors 'self';"),
}.into_bytes(),
]);
}
/// Extracts URL part from the Request.
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
convert_uri_to_url(req.uri(), req.headers().get::<header::Host>())
}
/// Extracts URL given URI and Host header.
pub fn convert_uri_to_url(uri: &uri::RequestUri, host: Option<&header::Host>) -> Option<Url> {
match *uri {
uri::RequestUri::AbsoluteUri(ref url) => {
match Url::from_generic_url(url.clone()) {
Ok(url) => Some(url),
_ => None,
}
},
uri::RequestUri::AbsolutePath { ref path, ref query } => {
let query = match *query {
Some(ref query) => format!("?{}", query),
None => "".into(),
};
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
let url_string = match host {
Some(ref host) => {
format!("http://{}:{}{}{}", host.hostname, host.port.unwrap_or(80), path, query)
},
None => return None,
};
match Url::parse(&url_string) {
Ok(url) => Some(url),
_ => None,
}
},
_ => None,
}
}

View File

@@ -1,61 +0,0 @@
// 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/>.
//! HTTP Redirection hyper handler
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::status::StatusCode;
#[derive(Clone)]
pub struct Redirection {
to_url: String
}
impl Redirection {
pub fn new(url: &str) -> Self {
Redirection {
to_url: url.to_owned()
}
}
pub fn boxed(url: &str) -> Box<Self> {
Box::new(Self::new(url))
}
}
impl server::Handler<HttpStream> for Redirection {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
// Don't use `MovedPermanently` here to prevent browser from caching the redirections.
res.set_status(StatusCode::Found);
res.headers_mut().set(header::Location(self.to_url.to_owned()));
Next::write()
}
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
Next::end()
}
}

View File

@@ -1,102 +0,0 @@
// 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/>.
//! Content Stream Response
use std::io::{self, Read};
use hyper::{header, server, Decoder, Encoder, Next};
use hyper::net::HttpStream;
use hyper::mime::Mime;
use hyper::status::StatusCode;
use handlers::add_security_headers;
use Embeddable;
const BUFFER_SIZE: usize = 1024;
pub struct StreamingHandler<R: io::Read> {
buffer: [u8; BUFFER_SIZE],
buffer_leftover: usize,
status: StatusCode,
content: io::BufReader<R>,
mimetype: Mime,
safe_to_embed_on: Embeddable,
}
impl<R: io::Read> StreamingHandler<R> {
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Embeddable) -> Self {
StreamingHandler {
buffer: [0; BUFFER_SIZE],
buffer_leftover: 0,
status: status,
content: io::BufReader::new(content),
mimetype: mimetype,
safe_to_embed_on: embeddable_on,
}
}
pub fn set_initial_content(&mut self, content: &str) {
assert_eq!(self.buffer_leftover, 0);
let bytes = content.as_bytes();
self.buffer_leftover = bytes.len();
self.buffer[0..self.buffer_leftover].copy_from_slice(bytes);
}
}
impl<R: io::Read> server::Handler<HttpStream> for StreamingHandler<R> {
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
res.set_status(self.status);
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
Next::write()
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
fn handle_error(e: io::Error) -> Next {
match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end(),
}
}
let write_pos = self.buffer_leftover;
match self.content.read(&mut self.buffer[write_pos..]) {
Err(e) => handle_error(e),
Ok(read) => match encoder.write(&self.buffer[..write_pos + read]) {
Err(e) => handle_error(e),
Ok(0) => Next::end(),
Ok(wrote) => {
self.buffer_leftover = write_pos + read - wrote;
if self.buffer_leftover > 0 {
for i in self.buffer_leftover..write_pos + read {
self.buffer.swap(i, i - self.buffer_leftover);
}
}
Next::write()
},
},
}
}
}

View File

@@ -1,296 +0,0 @@
// 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/>.
//! Ethcore Webapplications for Parity
#![warn(missing_docs)]
#![cfg_attr(feature="nightly", feature(plugin))]
#![cfg_attr(feature="nightly", plugin(clippy))]
extern crate base32;
extern crate futures;
extern crate futures_cpupool;
extern crate linked_hash_map;
extern crate mime_guess;
extern crate ntp;
extern crate rand;
extern crate rustc_hex;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate unicase;
extern crate url as url_lib;
extern crate zip;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate ethcore_util as util;
extern crate fetch;
extern crate parity_dapps_glue as parity_dapps;
extern crate parity_hash_fetch as hash_fetch;
extern crate parity_reactor;
extern crate parity_ui;
#[macro_use]
extern crate log;
#[macro_use]
extern crate mime;
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
#[cfg(test)]
extern crate env_logger;
mod endpoint;
mod apps;
mod page;
mod router;
mod handlers;
mod api;
mod proxypac;
mod url;
mod web;
#[cfg(test)]
mod tests;
use std::path::PathBuf;
use std::sync::Arc;
use std::collections::HashMap;
use jsonrpc_http_server::{self as http, hyper, Origin};
use fetch::Fetch;
use futures_cpupool::CpuPool;
use parity_reactor::Remote;
pub use hash_fetch::urlhint::ContractClient;
/// Indicates sync status
pub trait SyncStatus: Send + Sync {
/// Returns true if there is a major sync happening.
fn is_major_importing(&self) -> bool;
/// Returns number of connected and ideal peers.
fn peers(&self) -> (usize, usize);
}
/// Validates Web Proxy tokens
pub trait WebProxyTokens: Send + Sync {
/// Should return a domain allowed to be accessed by this token or `None` if the token is not valid
fn domain(&self, token: &str) -> Option<Origin>;
}
impl<F> WebProxyTokens for F where F: Fn(String) -> Option<Origin> + Send + Sync {
fn domain(&self, token: &str) -> Option<Origin> { self(token.to_owned()) }
}
/// Current supported endpoints.
pub struct Endpoints {
endpoints: endpoint::Endpoints,
}
impl Endpoints {
/// Returns a current list of app endpoints.
pub fn list(&self) -> Vec<apps::App> {
self.endpoints.iter().filter_map(|(ref k, ref e)| {
e.info().map(|ref info| apps::App::from_info(k, info))
}).collect()
}
}
/// Dapps server as `jsonrpc-http-server` request middleware.
pub struct Middleware {
router: router::Router,
endpoints: endpoint::Endpoints,
}
impl Middleware {
/// Get local endpoints handle.
pub fn endpoints(&self) -> Endpoints {
Endpoints {
endpoints: self.endpoints.clone(),
}
}
/// Creates new middleware for UI server.
pub fn ui<F: Fetch>(
ntp_server: &str,
pool: CpuPool,
remote: Remote,
dapps_domain: &str,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
fetch: F,
) -> Self {
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar),
sync_status.clone(),
remote.clone(),
fetch.clone(),
).embeddable_on(None).allow_dapps(false));
let special = {
let mut special = special_endpoints(
ntp_server,
pool,
content_fetcher.clone(),
remote.clone(),
sync_status.clone(),
);
special.insert(router::SpecialEndpoint::Home, Some(apps::ui()));
special
};
let router = router::Router::new(
content_fetcher,
None,
special,
None,
dapps_domain.to_owned(),
);
Middleware {
router: router,
endpoints: Default::default(),
}
}
/// Creates new Dapps server middleware.
pub fn dapps<F: Fetch>(
ntp_server: &str,
pool: CpuPool,
remote: Remote,
ui_address: Option<(String, u16)>,
extra_embed_on: Vec<(String, u16)>,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
dapps_domain: &str,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
fetch: F,
) -> Self {
let embeddable = as_embeddable(ui_address, extra_embed_on, dapps_domain);
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar),
sync_status.clone(),
remote.clone(),
fetch.clone(),
).embeddable_on(embeddable.clone()).allow_dapps(true));
let endpoints = apps::all_endpoints(
dapps_path,
extra_dapps,
dapps_domain,
embeddable.clone(),
web_proxy_tokens,
remote.clone(),
fetch.clone(),
);
let special = {
let mut special = special_endpoints(
ntp_server,
pool,
content_fetcher.clone(),
remote.clone(),
sync_status,
);
special.insert(
router::SpecialEndpoint::Home,
Some(apps::ui_redirection(embeddable.clone())),
);
special
};
let router = router::Router::new(
content_fetcher,
Some(endpoints.clone()),
special,
embeddable,
dapps_domain.to_owned(),
);
Middleware {
router: router,
endpoints: endpoints,
}
}
}
impl http::RequestMiddleware for Middleware {
fn on_request(&self, req: &hyper::server::Request<hyper::net::HttpStream>, control: &hyper::Control) -> http::RequestMiddlewareAction {
self.router.on_request(req, control)
}
}
fn special_endpoints(
ntp_server: &str,
pool: CpuPool,
content_fetcher: Arc<apps::fetcher::Fetcher>,
remote: Remote,
sync_status: Arc<SyncStatus>,
) -> HashMap<router::SpecialEndpoint, Option<Box<endpoint::Endpoint>>> {
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, None);
special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(
content_fetcher,
sync_status,
api::TimeChecker::new(ntp_server.into(), pool),
remote,
)));
special
}
fn address(host: &str, port: u16) -> String {
format!("{}:{}", host, port)
}
fn as_embeddable(
ui_address: Option<(String, u16)>,
extra_embed_on: Vec<(String, u16)>,
dapps_domain: &str,
) -> Option<ParentFrameSettings> {
ui_address.map(|(host, port)| ParentFrameSettings {
host,
port,
extra_embed_on,
dapps_domain: dapps_domain.to_owned(),
})
}
/// Random filename
fn random_filename() -> String {
use ::rand::Rng;
let mut rng = ::rand::OsRng::new().unwrap();
rng.gen_ascii_chars().take(12).collect()
}
type Embeddable = Option<ParentFrameSettings>;
/// Parent frame host and port allowed to embed the content.
#[derive(Debug, Clone)]
pub struct ParentFrameSettings {
/// Hostname
pub host: String,
/// Port
pub port: u16,
/// Additional pages the pages can be embedded on.
pub extra_embed_on: Vec<(String, u16)>,
/// Dapps Domain (web3.site)
pub dapps_domain: String,
}

View File

@@ -1,181 +0,0 @@
// 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/>.
use page::{handler, PageCache};
use std::sync::Arc;
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
use parity_dapps::{WebApp, File, Info};
use Embeddable;
pub struct PageEndpoint<T : WebApp + 'static> {
/// Content of the files
pub app: Arc<T>,
/// Prefix to strip from the path (when `None` deducted from `app_id`)
pub prefix: Option<String>,
/// Safe to be loaded in frame by other origin. (use wisely!)
safe_to_embed_on: Embeddable,
info: EndpointInfo,
fallback_to_index_html: bool,
}
impl<T: WebApp + 'static> PageEndpoint<T> {
/// Creates new `PageEndpoint` for builtin (compile time) Dapp.
pub fn new(app: T) -> Self {
let info = app.info();
PageEndpoint {
app: Arc::new(app),
prefix: None,
safe_to_embed_on: None,
info: EndpointInfo::from(info),
fallback_to_index_html: false,
}
}
/// Creates a new `PageEndpoint` for builtin (compile time) Dapp.
/// Instead of returning 404 this endpoint will always server index.html.
pub fn with_fallback_to_index(app: T) -> Self {
let info = app.info();
PageEndpoint {
app: Arc::new(app),
prefix: None,
safe_to_embed_on: None,
info: EndpointInfo::from(info),
fallback_to_index_html: true,
}
}
/// Create new `PageEndpoint` and specify prefix that should be removed before looking for a file.
/// It's used only for special endpoints (i.e. `/parity-utils/`)
/// So `/parity-utils/inject.js` will be resolved to `/inject.js` is prefix is set.
pub fn with_prefix(app: T, prefix: String) -> Self {
let info = app.info();
PageEndpoint {
app: Arc::new(app),
prefix: Some(prefix),
safe_to_embed_on: None,
info: EndpointInfo::from(info),
fallback_to_index_html: false,
}
}
/// Creates new `PageEndpoint` which can be safely used in iframe
/// even from different origin. It might be dangerous (clickjacking).
/// Use wisely!
pub fn new_safe_to_embed(app: T, address: Embeddable) -> Self {
let info = app.info();
PageEndpoint {
app: Arc::new(app),
prefix: None,
safe_to_embed_on: address,
info: EndpointInfo::from(info),
fallback_to_index_html: false,
}
}
}
impl<T: WebApp> Endpoint for PageEndpoint<T> {
fn info(&self) -> Option<&EndpointInfo> {
Some(&self.info)
}
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
Box::new(handler::PageHandler {
app: BuiltinDapp::new(self.app.clone(), self.fallback_to_index_html),
prefix: self.prefix.clone(),
path: path,
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
cache: PageCache::Disabled,
safe_to_embed_on: self.safe_to_embed_on.clone(),
})
}
}
impl From<Info> for EndpointInfo {
fn from(info: Info) -> Self {
EndpointInfo {
name: info.name.into(),
description: info.description.into(),
author: info.author.into(),
icon_url: info.icon_url.into(),
version: info.version.into(),
}
}
}
struct BuiltinDapp<T: WebApp + 'static> {
app: Arc<T>,
fallback_to_index_html: bool,
}
impl<T: WebApp + 'static> BuiltinDapp<T> {
fn new(app: Arc<T>, fallback_to_index_html: bool) -> Self {
BuiltinDapp {
app: app,
fallback_to_index_html: fallback_to_index_html,
}
}
}
impl<T: WebApp + 'static> handler::Dapp for BuiltinDapp<T> {
type DappFile = BuiltinDappFile<T>;
fn file(&self, path: &str) -> Option<Self::DappFile> {
let file = |path| self.app.file(path).map(|_| {
BuiltinDappFile {
app: self.app.clone(),
path: path.into(),
write_pos: 0,
}
});
let res = file(path);
if self.fallback_to_index_html {
res.or_else(|| file("index.html"))
} else {
res
}
}
}
struct BuiltinDappFile<T: WebApp + 'static> {
app: Arc<T>,
path: String,
write_pos: usize,
}
impl<T: WebApp + 'static> BuiltinDappFile<T> {
fn file(&self) -> &File {
self.app.file(&self.path).expect("Check is done when structure is created.")
}
}
impl<T: WebApp + 'static> handler::DappFile for BuiltinDappFile<T> {
fn content_type(&self) -> &str {
self.file().content_type
}
fn is_drained(&self) -> bool {
self.write_pos == self.file().content.len()
}
fn next_chunk(&mut self) -> &[u8] {
&self.file().content[self.write_pos..]
}
fn bytes_written(&mut self, bytes: usize) {
self.write_pos += bytes;
}
}

View File

@@ -1,277 +0,0 @@
// 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/>.
use time::{self, Duration};
use hyper::header;
use hyper::server;
use hyper::uri::RequestUri;
use hyper::net::HttpStream;
use hyper::status::StatusCode;
use hyper::{Decoder, Encoder, Next};
use endpoint::EndpointPath;
use handlers::{ContentHandler, add_security_headers};
use {Embeddable};
/// Represents a file that can be sent to client.
/// Implementation should keep track of bytes already sent internally.
pub trait DappFile: Send {
/// Returns a content-type of this file.
fn content_type(&self) -> &str;
/// Checks if all bytes from that file were written.
fn is_drained(&self) -> bool;
/// Fetch next chunk to write to the client.
fn next_chunk(&mut self) -> &[u8];
/// How many files have been written to the client.
fn bytes_written(&mut self, bytes: usize);
}
/// Dapp as a (dynamic) set of files.
pub trait Dapp: Send + 'static {
/// File type
type DappFile: DappFile;
/// Returns file under given path.
fn file(&self, path: &str) -> Option<Self::DappFile>;
}
/// Currently served by `PageHandler` file
pub enum ServedFile<T: Dapp> {
/// File from dapp
File(T::DappFile),
/// Error (404)
Error(ContentHandler),
}
impl<T: Dapp> ServedFile<T> {
pub fn new(embeddable_on: Embeddable) -> Self {
ServedFile::Error(ContentHandler::error(
StatusCode::NotFound,
"404 Not Found",
"Requested dapp resource was not found.",
None,
embeddable_on,
))
}
}
/// Defines what cache headers should be appended to returned resources.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PageCache {
Enabled,
Disabled,
}
impl Default for PageCache {
fn default() -> Self {
PageCache::Disabled
}
}
/// A generic type for `PageHandler` allowing to set the URL.
/// Used by dapps fetching to set the URL after the content was downloaded.
pub trait PageHandlerWaiting: server::Handler<HttpStream> + Send {
fn set_uri(&mut self, uri: &RequestUri);
}
/// A handler for a single webapp.
/// Resolves correct paths and serves as a plumbing code between
/// hyper server and dapp.
pub struct PageHandler<T: Dapp> {
/// A Dapp.
pub app: T,
/// File currently being served
pub file: ServedFile<T>,
/// Optional prefix to strip from path.
pub prefix: Option<String>,
/// Requested path.
pub path: EndpointPath,
/// Flag indicating if the file can be safely embeded (put in iframe).
pub safe_to_embed_on: Embeddable,
/// Cache settings for this page.
pub cache: PageCache,
}
impl<T: Dapp> PageHandlerWaiting for PageHandler<T> {
fn set_uri(&mut self, uri: &RequestUri) {
trace!(target: "dapps", "Setting URI: {:?}", uri);
self.file = match *uri {
RequestUri::AbsolutePath { ref path, .. } => {
self.app.file(&self.extract_path(path))
},
RequestUri::AbsoluteUri(ref url) => {
self.app.file(&self.extract_path(url.path()))
},
_ => None,
}.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f));
}
}
impl<T: Dapp> PageHandler<T> {
fn extract_path(&self, path: &str) -> String {
let app_id = &self.path.app_id;
let prefix = "/".to_owned() + self.prefix.as_ref().unwrap_or(app_id);
let prefix_with_slash = prefix.clone() + "/";
let query_pos = path.find('?').unwrap_or_else(|| path.len());
// Index file support
match path == "/" || path == &prefix || path == &prefix_with_slash {
true => "index.html".to_owned(),
false => if path.starts_with(&prefix_with_slash) {
path[prefix_with_slash.len()..query_pos].to_owned()
} else if path.starts_with("/") {
path[1..query_pos].to_owned()
} else {
path[0..query_pos].to_owned()
}
}
}
}
impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
self.set_uri(req.uri());
Next::write()
}
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
Next::write()
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
match self.file {
ServedFile::File(ref f) => {
res.set_status(StatusCode::Ok);
if let PageCache::Enabled = self.cache {
let mut headers = res.headers_mut();
let validity = Duration::days(365);
headers.set(header::CacheControl(vec![
header::CacheDirective::Public,
header::CacheDirective::MaxAge(validity.num_seconds() as u32),
]));
headers.set(header::Expires(header::HttpDate(time::now() + validity)));
}
match f.content_type().parse() {
Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
Err(()) => debug!(target: "dapps", "invalid MIME type: {}", f.content_type()),
}
// Security headers:
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
Next::write()
},
ServedFile::Error(ref mut handler) => {
handler.on_response(res)
}
}
}
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
match self.file {
ServedFile::Error(ref mut handler) => handler.on_response_writable(encoder),
ServedFile::File(ref f) if f.is_drained() => Next::end(),
ServedFile::File(ref mut f) => match encoder.write(f.next_chunk()) {
Ok(bytes) => {
f.bytes_written(bytes);
Next::write()
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => Next::write(),
_ => Next::end(),
},
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
pub struct TestWebAppFile;
impl DappFile for TestWebAppFile {
fn content_type(&self) -> &str {
unimplemented!()
}
fn is_drained(&self) -> bool {
unimplemented!()
}
fn next_chunk(&mut self) -> &[u8] {
unimplemented!()
}
fn bytes_written(&mut self, _bytes: usize) {
unimplemented!()
}
}
#[derive(Default)]
pub struct TestWebapp;
impl Dapp for TestWebapp {
type DappFile = TestWebAppFile;
fn file(&self, _path: &str) -> Option<Self::DappFile> {
None
}
}
}
#[test]
fn should_extract_path_with_appid() {
// given
let path1 = "/";
let path2= "/test.css";
let path3 = "/app/myfile.txt";
let path4 = "/app/myfile.txt?query=123";
let page_handler = PageHandler {
app: test::TestWebapp,
prefix: None,
path: EndpointPath {
app_id: "app".to_owned(),
app_params: vec![],
host: "".to_owned(),
port: 8080,
using_dapps_domains: true,
},
file: ServedFile::new(None),
cache: Default::default(),
safe_to_embed_on: None,
};
// when
let res1 = page_handler.extract_path(path1);
let res2 = page_handler.extract_path(path2);
let res3 = page_handler.extract_path(path3);
let res4 = page_handler.extract_path(path4);
// then
assert_eq!(&res1, "index.html");
assert_eq!(&res2, "test.css");
assert_eq!(&res3, "myfile.txt");
assert_eq!(&res4, "myfile.txt");
}

View File

@@ -1,181 +0,0 @@
// 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/>.
use mime_guess;
use std::io::{Seek, Read, SeekFrom};
use std::fs;
use std::path::{Path, PathBuf};
use page::handler::{self, PageCache, PageHandlerWaiting};
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
use mime::Mime;
use Embeddable;
#[derive(Debug, Clone)]
pub struct LocalPageEndpoint {
path: PathBuf,
mime: Option<Mime>,
info: Option<EndpointInfo>,
cache: PageCache,
embeddable_on: Embeddable,
}
impl LocalPageEndpoint {
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self {
LocalPageEndpoint {
path: path,
mime: None,
info: Some(info),
cache: cache,
embeddable_on: embeddable_on,
}
}
pub fn single_file(path: PathBuf, mime: Mime, cache: PageCache) -> Self {
LocalPageEndpoint {
path: path,
mime: Some(mime),
info: None,
cache: cache,
embeddable_on: None,
}
}
pub fn path(&self) -> PathBuf {
self.path.clone()
}
fn page_handler_with_mime(&self, path: EndpointPath, mime: &Mime) -> handler::PageHandler<LocalSingleFile> {
handler::PageHandler {
app: LocalSingleFile { path: self.path.clone(), mime: format!("{}", mime) },
prefix: None,
path: path,
file: handler::ServedFile::new(None),
safe_to_embed_on: self.embeddable_on.clone(),
cache: self.cache,
}
}
fn page_handler(&self, path: EndpointPath) -> handler::PageHandler<LocalDapp> {
handler::PageHandler {
app: LocalDapp { path: self.path.clone() },
prefix: None,
path: path,
file: handler::ServedFile::new(None),
safe_to_embed_on: self.embeddable_on.clone(),
cache: self.cache,
}
}
pub fn to_page_handler(&self, path: EndpointPath) -> Box<PageHandlerWaiting> {
if let Some(ref mime) = self.mime {
Box::new(self.page_handler_with_mime(path, mime))
} else {
Box::new(self.page_handler(path))
}
}
}
impl Endpoint for LocalPageEndpoint {
fn info(&self) -> Option<&EndpointInfo> {
self.info.as_ref()
}
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
if let Some(ref mime) = self.mime {
Box::new(self.page_handler_with_mime(path, mime))
} else {
Box::new(self.page_handler(path))
}
}
}
struct LocalSingleFile {
path: PathBuf,
mime: String,
}
impl handler::Dapp for LocalSingleFile {
type DappFile = LocalFile;
fn file(&self, _path: &str) -> Option<Self::DappFile> {
LocalFile::from_path(&self.path, Some(&self.mime))
}
}
struct LocalDapp {
path: PathBuf,
}
impl handler::Dapp for LocalDapp {
type DappFile = LocalFile;
fn file(&self, file_path: &str) -> Option<Self::DappFile> {
let mut path = self.path.clone();
for part in file_path.split('/') {
path.push(part);
}
LocalFile::from_path(&path, None)
}
}
struct LocalFile {
content_type: String,
buffer: [u8; 4096],
file: fs::File,
len: u64,
pos: u64,
}
impl LocalFile {
fn from_path<P: AsRef<Path>>(path: P, mime: Option<&str>) -> Option<Self> {
// Check if file exists
fs::File::open(&path).ok().map(|file| {
let content_type = mime.map(|mime| mime.to_owned())
.unwrap_or_else(|| mime_guess::guess_mime_type(path).to_string());
let len = file.metadata().ok().map_or(0, |meta| meta.len());
LocalFile {
content_type: content_type,
buffer: [0; 4096],
file: file,
pos: 0,
len: len,
}
})
}
}
impl handler::DappFile for LocalFile {
fn content_type(&self) -> &str {
&self.content_type
}
fn is_drained(&self) -> bool {
self.pos == self.len
}
fn next_chunk(&mut self) -> &[u8] {
let _ = self.file.seek(SeekFrom::Start(self.pos));
if let Ok(n) = self.file.read(&mut self.buffer) {
&self.buffer[0..n]
} else {
&self.buffer[0..0]
}
}
fn bytes_written(&mut self, bytes: usize) {
self.pos += bytes as u64;
}
}

View File

@@ -1,25 +0,0 @@
// 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/>.
mod builtin;
mod local;
mod handler;
pub use self::local::LocalPageEndpoint;
pub use self::builtin::PageEndpoint;
pub use self::handler::{PageCache, PageHandlerWaiting};

View File

@@ -1,64 +0,0 @@
// 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/>.
//! Serving ProxyPac file
use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::ContentHandler;
use apps::HOME_PAGE;
use {address, Embeddable};
pub struct ProxyPac {
embeddable: Embeddable,
dapps_domain: String,
}
impl ProxyPac {
pub fn boxed(embeddable: Embeddable, dapps_domain: String) -> Box<Endpoint> {
Box::new(ProxyPac { embeddable, dapps_domain })
}
}
impl Endpoint for ProxyPac {
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
let ui = self.embeddable
.as_ref()
.map(|ref parent| address(&parent.host, parent.port))
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
let content = format!(
r#"
function FindProxyForURL(url, host) {{
if (shExpMatch(host, "{0}.{1}"))
{{
return "PROXY {4}";
}}
if (shExpMatch(host, "*.{1}"))
{{
return "PROXY {2}:{3}";
}}
return "DIRECT";
}}
"#,
HOME_PAGE, self.dapps_domain, path.host, path.port, ui);
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
}
}

View File

@@ -1,325 +0,0 @@
// 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/>.
//! Router implementation
//! Dispatch requests to proper application.
use std::cmp;
use std::sync::Arc;
use std::collections::HashMap;
use url::{Url, Host};
use hyper::{self, server, header, Control};
use hyper::net::HttpStream;
use jsonrpc_http_server as http;
use apps;
use apps::fetcher::Fetcher;
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
use handlers;
use Embeddable;
/// Special endpoints are accessible on every domain (every dapp)
#[derive(Debug, PartialEq, Hash, Eq)]
pub enum SpecialEndpoint {
Rpc,
Api,
Utils,
Home,
None,
}
pub struct Router {
endpoints: Option<Endpoints>,
fetch: Arc<Fetcher>,
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
embeddable_on: Embeddable,
dapps_domain: String,
}
impl http::RequestMiddleware for Router {
fn on_request(&self, req: &server::Request<HttpStream>, control: &Control) -> http::RequestMiddlewareAction {
// Choose proper handler depending on path / domain
let url = handlers::extract_url(req);
let endpoint = extract_endpoint(&url, &self.dapps_domain);
let referer = extract_referer_endpoint(req, &self.dapps_domain);
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
let is_origin_set = req.headers().get::<header::Origin>().is_some();
let is_get_request = *req.method() == hyper::Method::Get;
let is_head_request = *req.method() == hyper::Method::Head;
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
let control = control.clone();
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
let handler: Option<Box<Handler>> = match (endpoint.0, endpoint.1, referer) {
// Handle invalid web requests that we can recover from
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
if referer.app_id == apps::WEB_PATH
&& self.endpoints.as_ref().map(|ep| ep.contains_key(apps::WEB_PATH)).unwrap_or(false)
&& !is_web_endpoint(path)
=>
{
trace!(target: "dapps", "Redirecting to correct web request: {:?}", referer_url);
let len = cmp::min(referer_url.path.len(), 2); // /web/<encoded>/
let base = referer_url.path[..len].join("/");
let requested = url.map(|u| u.path.join("/")).unwrap_or_default();
Some(handlers::Redirection::boxed(&format!("/{}/{}", base, requested)))
},
// First check special endpoints
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
trace!(target: "dapps", "Resolving to special endpoint.");
self.special.get(endpoint)
.expect("special known to contain key; qed")
.as_ref()
.map(|special| special.to_async_handler(path.clone().unwrap_or_default(), control))
},
// Then delegate to dapp
(Some(ref path), _, _) if self.endpoints.as_ref().map(|ep| ep.contains_key(&path.app_id)).unwrap_or(false) => {
trace!(target: "dapps", "Resolving to local/builtin dapp.");
Some(self.endpoints
.as_ref()
.expect("endpoints known to be set; qed")
.get(&path.app_id)
.expect("endpoints known to contain key; qed")
.to_async_handler(path.clone(), control))
},
// Try to resolve and fetch the dapp
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
trace!(target: "dapps", "Resolving to fetchable content.");
Some(self.fetch.to_async_handler(path.clone(), control))
},
// 404 for non-existent content (only if serving endpoints and not homepage)
(Some(ref path), _, _)
if (is_get_request || is_head_request)
&& self.endpoints.is_some()
&& path.app_id != apps::HOME_PAGE
=>
{
trace!(target: "dapps", "Resolving to 404.");
Some(Box::new(handlers::ContentHandler::error(
hyper::StatusCode::NotFound,
"404 Not Found",
"Requested content was not found.",
None,
self.embeddable_on.clone(),
)))
},
// Any other GET|HEAD requests to home page.
_ if (is_get_request || is_head_request) && self.special.contains_key(&SpecialEndpoint::Home) => {
self.special.get(&SpecialEndpoint::Home)
.expect("special known to contain key; qed")
.as_ref()
.map(|special| special.to_async_handler(Default::default(), control))
},
// RPC by default
_ => {
trace!(target: "dapps", "Resolving to RPC call.");
None
}
};
match handler {
Some(handler) => http::RequestMiddlewareAction::Respond {
should_validate_hosts: !is_utils,
handler: handler,
},
None => http::RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: !is_origin_set,
},
}
}
}
impl Router {
pub fn new(
content_fetcher: Arc<Fetcher>,
endpoints: Option<Endpoints>,
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
embeddable_on: Embeddable,
dapps_domain: String,
) -> Self {
Router {
endpoints: endpoints,
fetch: content_fetcher,
special: special,
embeddable_on: embeddable_on,
dapps_domain: format!(".{}", dapps_domain),
}
}
}
fn is_web_endpoint(path: &Option<EndpointPath>) -> bool {
match *path {
Some(ref path) if path.app_id == apps::WEB_PATH => true,
_ => false,
}
}
fn extract_referer_endpoint(req: &server::Request<HttpStream>, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
let referer = req.headers().get::<header::Referer>();
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
url.and_then(|url| {
let option = Some(url);
extract_url_referer_endpoint(&option, dapps_domain).or_else(|| {
extract_endpoint(&option, dapps_domain).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
})
})
}
fn extract_url_referer_endpoint(url: &Option<Url>, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
let query = url.as_ref().and_then(|url| url.query.as_ref());
match (url, query) {
(&Some(ref url), Some(ref query)) if query.starts_with(apps::URL_REFERER) => {
let referer_url = format!("http://{}:{}/{}", url.host, url.port, &query[apps::URL_REFERER.len()..]);
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
let referer_url = Url::parse(&referer_url).ok();
extract_endpoint(&referer_url, dapps_domain).0.map(|endpoint| {
(endpoint, referer_url.expect("Endpoint returned only when url `is_some`").clone())
})
},
_ => None,
}
}
fn extract_endpoint(url: &Option<Url>, dapps_domain: &str) -> (Option<EndpointPath>, SpecialEndpoint) {
fn special_endpoint(url: &Url) -> SpecialEndpoint {
if url.path.len() <= 1 {
return SpecialEndpoint::None;
}
match url.path[0].as_ref() {
apps::RPC_PATH => SpecialEndpoint::Rpc,
apps::API_PATH => SpecialEndpoint::Api,
apps::UTILS_PATH => SpecialEndpoint::Utils,
apps::HOME_PAGE => SpecialEndpoint::Home,
_ => SpecialEndpoint::None,
}
}
match *url {
Some(ref url) => match url.host {
Host::Domain(ref domain) if domain.ends_with(dapps_domain) => {
let id = &domain[0..(domain.len() - dapps_domain.len())];
let (id, params) = if let Some(split) = id.rfind('.') {
let (params, id) = id.split_at(split);
(id[1..].to_owned(), [params.to_owned()].into_iter().chain(&url.path).cloned().collect())
} else {
(id.to_owned(), url.path.clone())
};
(Some(EndpointPath {
app_id: id,
app_params: params,
host: domain.clone(),
port: url.port,
using_dapps_domains: true,
}), special_endpoint(url))
},
_ if url.path.len() > 1 => {
let id = url.path[0].to_owned();
(Some(EndpointPath {
app_id: id,
app_params: url.path[1..].to_vec(),
host: format!("{}", url.host),
port: url.port,
using_dapps_domains: false,
}), special_endpoint(url))
},
_ => (None, special_endpoint(url)),
},
_ => (None, SpecialEndpoint::None)
}
}
#[test]
fn should_extract_endpoint() {
let dapps_domain = ".web3.site";
assert_eq!(extract_endpoint(&None, dapps_domain), (None, SpecialEndpoint::None));
// With path prefix
assert_eq!(
extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["index.html".to_owned()],
host: "localhost".to_owned(),
port: 8080,
using_dapps_domains: false,
}), SpecialEndpoint::None)
);
// With path prefix
assert_eq!(
extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "rpc".to_owned(),
app_params: vec!["".to_owned()],
host: "localhost".to_owned(),
port: 8080,
using_dapps_domains: false,
}), SpecialEndpoint::Rpc)
);
assert_eq!(
extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "parity-utils".into(), "inject.js".into()],
host: "my.status.web3.site".to_owned(),
port: 80,
using_dapps_domains: true,
}), SpecialEndpoint::Utils)
);
// By Subdomain
assert_eq!(
extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["test.html".to_owned()],
host: "status.web3.site".to_owned(),
port: 80,
using_dapps_domains: true,
}), SpecialEndpoint::None)
);
// RPC by subdomain
assert_eq!(
extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "rpc".into(), "".into()],
host: "my.status.web3.site".to_owned(),
port: 80,
using_dapps_domains: true,
}), SpecialEndpoint::Rpc)
);
// API by subdomain
assert_eq!(
extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok(), dapps_domain),
(Some(EndpointPath {
app_id: "status".to_owned(),
app_params: vec!["my".to_owned(), "api".into(), "".into()],
host: "my.status.web3.site".to_owned(),
port: 80,
using_dapps_domains: true,
}), SpecialEndpoint::Api)
);
}

View File

@@ -1,85 +0,0 @@
// 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/>.
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
#[test]
fn should_return_error() {
// given
let server = serve();
// when
let response = request(server,
"\
GET /api/empty HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 404 Not Found");
response.assert_header("Content-Type", "application/json");
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
assert_security_headers(&response.headers);
}
#[test]
fn should_handle_ping() {
// given
let server = serve();
// when
let response = request(server,
"\
POST /api/ping HTTP/1.1\r\n\
Host: home.parity\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Content-Type", "application/json");
assert_eq!(response.body, "0\n\n".to_owned());
assert_security_headers(&response.headers);
}
#[test]
fn should_try_to_resolve_dapp() {
// given
let (server, registrar) = serve_with_registrar();
// when
let response = request(server,
"\
GET /api/content/1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d HTTP/1.1\r\n\
Host: home.parity\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 404 Not Found");
assert_eq!(registrar.calls.lock().len(), 2);
assert_security_headers(&response.headers);
}

View File

@@ -1,540 +0,0 @@
// 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/>.
use devtools::http_client;
use rustc_hex::FromHex;
use tests::helpers::{
serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch,
serve_with_registrar_and_fetch, serve_with_registrar_and_fetch_and_threads,
request, assert_security_headers_for_embed,
};
#[test]
fn should_resolve_dapp() {
// given
let (server, registrar) = serve_with_registrar();
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 404 Not Found");
assert_eq!(registrar.calls.lock().len(), 2);
assert_security_headers_for_embed(&response.headers);
}
#[test]
fn should_return_503_when_syncing_but_should_make_the_calls() {
// given
let (server, registrar) = serve_with_registrar_and_sync();
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 503 Service Unavailable");
assert_eq!(registrar.calls.lock().len(), 2);
assert_security_headers_for_embed(&response.headers);
}
const GAVCOIN_DAPP: &'static str = "00000000000000000000000000000000000000000000000000000000000000609faf32e1e3845e237cc6efd27187cee13b3b99db000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e00000000000000000000000000000000000000000000000000000000000000116761766f66796f726b2f676176636f696e000000000000000000000000000000";
const GAVCOIN_ICON: &'static str = "00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d8bd350823e28ff75e74a34215faefdc8a52fd8e000000000000000000000000000000000000000000000000000000000000007768747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f657468636f72652f646170702d6173736574732f623838653938336162616131613661363334356238643934343863313562313137646462353430652f746f6b656e732f676176636f696e2d36347836342e706e67000000000000000000";
#[test]
fn should_return_502_on_hash_mismatch() {
// given
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
registrar.set_result(
"94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd".parse().unwrap(),
Ok(gavcoin.clone())
);
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 94f093625c06887d94d9fee0d5f9cc4aaa46f33d24d1c7e4b5237e7c37d547dd.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
assert_eq!(registrar.calls.lock().len(), 4);
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
fetch.assert_no_more_requests();
response.assert_status("HTTP/1.1 502 Bad Gateway");
assert!(response.body.contains("HashMismatch"), "Expected hash mismatch response, got: {:?}", response.body);
assert_security_headers_for_embed(&response.headers);
}
#[test]
fn should_return_error_for_invalid_dapp_zip() {
// given
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
registrar.set_result(
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
Ok(gavcoin.clone())
);
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
assert_eq!(registrar.calls.lock().len(), 4);
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
fetch.assert_no_more_requests();
response.assert_status("HTTP/1.1 502 Bad Gateway");
assert!(response.body.contains("InvalidArchive"), "Expected invalid zip response, got: {:?}", response.body);
assert_security_headers_for_embed(&response.headers);
}
#[test]
fn should_return_fetched_dapp_content() {
// given
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
let gavcoin = GAVCOIN_DAPP.from_hex().unwrap();
registrar.set_result(
"9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a".parse().unwrap(),
Ok(gavcoin.clone())
);
fetch.set_response(include_bytes!("../../res/gavcoin.zip"));
// when
let response1 = http_client::request(server.addr(),
"\
GET /index.html HTTP/1.1\r\n\
Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
let response2 = http_client::request(server.addr(),
"\
GET /manifest.json HTTP/1.1\r\n\
Host: 9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
assert_eq!(registrar.calls.lock().len(), 4);
fetch.assert_requested("https://codeload.github.com/gavofyork/gavcoin/zip/9faf32e1e3845e237cc6efd27187cee13b3b99db");
fetch.assert_no_more_requests();
response1.assert_status("HTTP/1.1 200 OK");
assert_security_headers_for_embed(&response1.headers);
assert_eq!(
response1.body,
r#"18
<h1>Hello Gavcoin!</h1>
"#
);
response2.assert_status("HTTP/1.1 200 OK");
assert_security_headers_for_embed(&response2.headers);
assert_eq!(
response2.body,
r#"BE
{
"id": "9c94e154dab8acf859b30ee80fc828fb1d38359d938751b65db71d460588d82a",
"name": "Gavcoin",
"description": "Gavcoin",
"version": "1.0.0",
"author": "",
"iconUrl": "icon.png"
}
0
"#
);
}
#[test]
fn should_return_fetched_content() {
// given
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
registrar.set_result(
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
Ok(gavcoin.clone())
);
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
assert_eq!(registrar.calls.lock().len(), 4);
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
fetch.assert_no_more_requests();
response.assert_status("HTTP/1.1 200 OK");
response.assert_security_headers_present(None);
}
#[test]
fn should_cache_content() {
// given
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
registrar.set_result(
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
Ok(gavcoin.clone())
);
let request_str = "\
GET / HTTP/1.1\r\n\
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
Connection: close\r\n\
\r\n\
";
let response = http_client::request(server.addr(), request_str);
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
fetch.assert_no_more_requests();
response.assert_status("HTTP/1.1 200 OK");
// when
let response = http_client::request(server.addr(), request_str);
// then
fetch.assert_no_more_requests();
response.assert_status("HTTP/1.1 200 OK");
}
#[test]
fn should_not_request_content_twice() {
use std::thread;
// given
let (server, fetch, registrar) = serve_with_registrar_and_fetch_and_threads(true);
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
registrar.set_result(
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
Ok(gavcoin.clone())
);
let request_str = "\
GET / HTTP/1.1\r\n\
Host: 2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e.web3.site\r\n\
Connection: close\r\n\
\r\n\
";
let fire_request = || {
let addr = server.addr().to_owned();
let req = request_str.to_owned();
thread::spawn(move || {
http_client::request(&addr, &req)
})
};
let control = fetch.manual();
// when
// Fire two requests at the same time
let r1 = fire_request();
let r2 = fire_request();
// wait for single request in fetch, the second one should go into waiting state.
control.wait_for_requests(1);
control.respond();
let response1 = r1.join().unwrap();
let response2 = r2.join().unwrap();
// then
fetch.assert_requested("https://raw.githubusercontent.com/ethcore/dapp-assets/b88e983abaa1a6a6345b8d9448c15b117ddb540e/tokens/gavcoin-64x64.png");
fetch.assert_no_more_requests();
response1.assert_status("HTTP/1.1 200 OK");
response2.assert_status("HTTP/1.1 200 OK");
}
#[test]
fn should_encode_and_decode_base32() {
use base32;
let encoded = base32::encode(base32::Alphabet::Crockford, "token+https://parity.io".as_bytes());
assert_eq!("EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY", &encoded);
let data = base32::decode(base32::Alphabet::Crockford, "EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY").unwrap();
assert_eq!("token+https://parity.io", &String::from_utf8(data).unwrap());
}
#[test]
fn should_stream_web_content() {
// given
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
assert_security_headers_for_embed(&response.headers);
fetch.assert_requested("https://parity.io/");
fetch.assert_no_more_requests();
}
#[test]
fn should_support_base32_encoded_web_urls() {
// given
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
// when
let response = request(server,
"\
GET /styles.css?test=123 HTTP/1.1\r\n\
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
assert_security_headers_for_embed(&response.headers);
fetch.assert_requested("https://parity.io/styles.css?test=123");
fetch.assert_no_more_requests();
}
#[test]
fn should_correctly_handle_long_label_when_splitted() {
// given
let (server, fetch) = serve_with_fetch("xolrg9fePeQyKLnL", "https://contribution.melonport.com");
// when
let response = request(server,
"\
GET /styles.css?test=123 HTTP/1.1\r\n\
Host: f1qprwk775k6am35a5wmpk3e9gnpgx3me1sk.mbsfcdqpwx3jd5h7ax39dxq2wvb5dhqpww3fe9t2wrvfdm.web.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
assert_security_headers_for_embed(&response.headers);
fetch.assert_requested("https://contribution.melonport.com/styles.css?test=123");
fetch.assert_no_more_requests();
}
#[test]
fn should_support_base32_encoded_web_urls_as_path() {
// given
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
// when
let response = request(server,
"\
GET /web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css?test=123 HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
assert_security_headers_for_embed(&response.headers);
fetch.assert_requested("https://parity.io/styles.css?test=123");
fetch.assert_no_more_requests();
}
#[test]
fn should_return_error_on_non_whitelisted_domain() {
// given
let (server, fetch) = serve_with_fetch("token", "https://ethcore.io");
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 400 Bad Request");
assert_security_headers_for_embed(&response.headers);
fetch.assert_no_more_requests();
}
#[test]
fn should_return_error_on_invalid_token() {
// given
let (server, fetch) = serve_with_fetch("test", "https://parity.io");
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 400 Bad Request");
assert_security_headers_for_embed(&response.headers);
fetch.assert_no_more_requests();
}
#[test]
fn should_return_error_on_invalid_protocol() {
// given
let (server, fetch) = serve_with_fetch("token", "ftp://parity.io");
// when
let response = request(server,
"\
GET /web/token/ftp/parity.io/ HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 400 Bad Request");
assert_security_headers_for_embed(&response.headers);
fetch.assert_no_more_requests();
}
#[test]
fn should_disallow_non_get_requests() {
// given
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
// when
let response = request(server,
"\
POST / HTTP/1.1\r\n\
Host: EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY.web.web3.site\r\n\
Content-Type: application/json\r\n\
Connection: close\r\n\
\r\n\
123\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 405 Method Not Allowed");
assert_security_headers_for_embed(&response.headers);
fetch.assert_no_more_requests();
}
#[test]
fn should_fix_absolute_requests_based_on_referer() {
// given
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
// when
let response = request(server,
"\
GET /styles.css HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Connection: close\r\n\
Referer: http://localhost:8080/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 302 Found");
response.assert_header("Location", "/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css");
fetch.assert_no_more_requests();
}
#[test]
fn should_fix_absolute_requests_based_on_referer_in_url() {
// given
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
// when
let response = request(server,
"\
GET /styles.css HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Connection: close\r\n\
Referer: http://localhost:8080/?__referer=web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 302 Found");
response.assert_header("Location", "/web/EHQPPSBE5DM78X3GECX2YBVGC5S6JX3S5SMPY/styles.css");
fetch.assert_no_more_requests();
}

View File

@@ -1,122 +0,0 @@
// 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/>.
use std::{io, thread, time};
use std::sync::{atomic, mpsc, Arc};
use util::Mutex;
use futures::{self, Future};
use fetch::{self, Fetch};
pub struct FetchControl {
sender: mpsc::Sender<()>,
fetch: FakeFetch,
}
impl FetchControl {
pub fn respond(self) {
self.sender.send(())
.expect("Fetch cannot be finished without sending a response at least once.");
}
pub fn wait_for_requests(&self, len: usize) {
const MAX_TIMEOUT_MS: u64 = 5000;
const ATTEMPTS: u64 = 10;
let mut attempts_left = ATTEMPTS;
loop {
let current = self.fetch.requested.lock().len();
if current == len {
break;
} else if attempts_left == 0 {
panic!(
"Timeout reached when waiting for pending requests. Expected: {}, current: {}",
len, current
);
} else {
attempts_left -= 1;
// Should we handle spurious timeouts better?
thread::park_timeout(time::Duration::from_millis(MAX_TIMEOUT_MS / ATTEMPTS));
}
}
}
}
#[derive(Clone, Default)]
pub struct FakeFetch {
manual: Arc<Mutex<Option<mpsc::Receiver<()>>>>,
response: Arc<Mutex<Option<&'static [u8]>>>,
asserted: Arc<atomic::AtomicUsize>,
requested: Arc<Mutex<Vec<String>>>,
}
impl FakeFetch {
pub fn set_response(&self, data: &'static [u8]) {
*self.response.lock() = Some(data);
}
pub fn manual(&self) -> FetchControl {
assert!(self.manual.lock().is_none(), "Only one manual control may be active.");
let (tx, rx) = mpsc::channel();
*self.manual.lock() = Some(rx);
FetchControl {
sender: tx,
fetch: self.clone(),
}
}
pub fn assert_requested(&self, url: &str) {
let requests = self.requested.lock();
let idx = self.asserted.fetch_add(1, atomic::Ordering::SeqCst);
assert_eq!(requests.get(idx), Some(&url.to_owned()), "Expected fetch from specific URL.");
}
pub fn assert_no_more_requests(&self) {
let requests = self.requested.lock();
let len = self.asserted.load(atomic::Ordering::SeqCst);
assert_eq!(requests.len(), len, "Didn't expect any more requests, got: {:?}", &requests[len..]);
}
}
impl Fetch for FakeFetch {
type Result = futures::BoxFuture<fetch::Response, fetch::Error>;
fn new() -> Result<Self, fetch::Error> where Self: Sized {
Ok(FakeFetch::default())
}
fn fetch_with_abort(&self, url: &str, _abort: fetch::Abort) -> Self::Result {
self.requested.lock().push(url.into());
let manual = self.manual.clone();
let response = self.response.clone();
let (tx, rx) = futures::oneshot();
thread::spawn(move || {
if let Some(rx) = manual.lock().take() {
// wait for manual resume
let _ = rx.recv();
}
let data = response.lock().take().unwrap_or(b"Some content");
let cursor = io::Cursor::new(data);
tx.send(fetch::Response::from_reader(cursor)).unwrap();
});
rx.map_err(|_| fetch::Error::Aborted).boxed()
}
}

View File

@@ -1,302 +0,0 @@
// 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/>.
use std::env;
use std::str;
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use env_logger::LogBuilder;
use jsonrpc_core::IoHandler;
use jsonrpc_http_server::{self as http, Host, DomainsValidation};
use devtools::http_client;
use hash_fetch::urlhint::ContractClient;
use fetch::{Fetch, Client as FetchClient};
use futures_cpupool::CpuPool;
use parity_reactor::Remote;
use {Middleware, SyncStatus, WebProxyTokens};
mod registrar;
mod fetch;
use self::registrar::FakeRegistrar;
use self::fetch::FakeFetch;
const SIGNER_PORT: u16 = 18180;
struct FakeSync(bool);
impl SyncStatus for FakeSync {
fn is_major_importing(&self) -> bool { self.0 }
fn peers(&self) -> (usize, usize) { (0, 5) }
}
fn init_logger() {
// Initialize logger
if let Ok(log) = env::var("RUST_LOG") {
let mut builder = LogBuilder::new();
builder.parse(&log);
let _ = builder.init(); // ignore errors since ./test.sh will call this multiple times.
}
}
pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (Server, Arc<FakeRegistrar>) where
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
B: Fetch,
{
init_logger();
let registrar = Arc::new(FakeRegistrar::new());
let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let server = process(ServerBuilder::new(
&dapps_path, registrar.clone(), remote,
))
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
(
server,
registrar,
)
}
pub fn serve_with_rpc(io: IoHandler) -> Server {
init_server(|builder| builder, io, Remote::new_sync()).0
}
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
let hosts = hosts.map(|hosts| hosts.into_iter().map(Into::into).collect());
init_server(|builder| builder.allowed_hosts(hosts.into()), Default::default(), Remote::new_sync()).0
}
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
init_server(|builder| builder, Default::default(), Remote::new_sync())
}
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
init_server(|builder| {
builder.sync_status(Arc::new(FakeSync(true)))
}, Default::default(), Remote::new_sync())
}
pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc<FakeRegistrar>) {
serve_with_registrar_and_fetch_and_threads(false)
}
pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Server, FakeFetch, Arc<FakeRegistrar>) {
let fetch = FakeFetch::default();
let f = fetch.clone();
let (server, reg) = init_server(move |builder| {
builder.fetch(f.clone())
}, Default::default(), if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() });
(server, fetch, reg)
}
pub fn serve_with_fetch(web_token: &'static str, domain: &'static str) -> (Server, FakeFetch) {
let fetch = FakeFetch::default();
let f = fetch.clone();
let (server, _) = init_server(move |builder| {
builder
.fetch(f.clone())
.web_proxy_tokens(Arc::new(move |token| {
if &token == web_token { Some(domain.into()) } else { None }
}))
}, Default::default(), Remote::new_sync());
(server, fetch)
}
pub fn serve() -> Server {
init_server(|builder| builder, Default::default(), Remote::new_sync()).0
}
pub fn request(server: Server, request: &str) -> http_client::Response {
http_client::request(server.addr(), request)
}
pub fn assert_security_headers(headers: &[String]) {
http_client::assert_security_headers_present(headers, None)
}
pub fn assert_security_headers_for_embed(headers: &[String]) {
http_client::assert_security_headers_present(headers, Some(SIGNER_PORT))
}
/// Webapps HTTP+RPC server build.
pub struct ServerBuilder<T: Fetch = FetchClient> {
dapps_path: PathBuf,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
signer_address: Option<(String, u16)>,
allowed_hosts: DomainsValidation<Host>,
remote: Remote,
fetch: Option<T>,
}
impl ServerBuilder {
/// Construct new dapps server
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>, remote: Remote) -> Self {
ServerBuilder {
dapps_path: dapps_path.as_ref().to_owned(),
registrar: registrar,
sync_status: Arc::new(FakeSync(false)),
web_proxy_tokens: Arc::new(|_| None),
signer_address: None,
allowed_hosts: DomainsValidation::Disabled,
remote: remote,
fetch: None,
}
}
}
impl<T: Fetch> ServerBuilder<T> {
/// Set a fetch client to use.
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
ServerBuilder {
dapps_path: self.dapps_path,
registrar: self.registrar,
sync_status: self.sync_status,
web_proxy_tokens: self.web_proxy_tokens,
signer_address: self.signer_address,
allowed_hosts: self.allowed_hosts,
remote: self.remote,
fetch: Some(fetch),
}
}
/// Change default sync status.
pub fn sync_status(mut self, status: Arc<SyncStatus>) -> Self {
self.sync_status = status;
self
}
/// Change default web proxy tokens validator.
pub fn web_proxy_tokens(mut self, tokens: Arc<WebProxyTokens>) -> Self {
self.web_proxy_tokens = tokens;
self
}
/// Change default signer port.
pub fn signer_address(mut self, signer_address: Option<(String, u16)>) -> Self {
self.signer_address = signer_address;
self
}
/// Change allowed hosts.
/// `None` - All hosts are allowed
/// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address)
pub fn allowed_hosts(mut self, allowed_hosts: DomainsValidation<Host>) -> Self {
self.allowed_hosts = allowed_hosts;
self
}
/// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error.
pub fn start_unsecured_http(self, addr: &SocketAddr, io: IoHandler) -> Result<Server, http::Error> {
let fetch = self.fetch_client();
Server::start_http(
addr,
io,
self.allowed_hosts,
self.signer_address,
self.dapps_path,
vec![],
self.registrar,
self.sync_status,
self.web_proxy_tokens,
self.remote,
fetch,
)
}
fn fetch_client(&self) -> T {
match self.fetch.clone() {
Some(fetch) => fetch,
None => T::new().unwrap(),
}
}
}
const DAPPS_DOMAIN: &'static str = "web3.site";
/// Webapps HTTP server.
pub struct Server {
server: Option<http::Server>,
}
impl Server {
fn start_http<F: Fetch>(
addr: &SocketAddr,
io: IoHandler,
allowed_hosts: DomainsValidation<Host>,
signer_address: Option<(String, u16)>,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
registrar: Arc<ContractClient>,
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
fetch: F,
) -> Result<Server, http::Error> {
let middleware = Middleware::dapps(
"pool.ntp.org:123",
CpuPool::new(4),
remote,
signer_address,
vec![],
dapps_path,
extra_dapps,
DAPPS_DOMAIN.into(),
registrar,
sync_status,
web_proxy_tokens,
fetch,
);
let mut allowed_hosts: Option<Vec<Host>> = allowed_hosts.into();
allowed_hosts.as_mut().map(|mut hosts| {
hosts.push(format!("http://*.{}:*", DAPPS_DOMAIN).into());
hosts.push(format!("http://*.{}", DAPPS_DOMAIN).into());
});
http::ServerBuilder::new(io)
.request_middleware(middleware)
.allowed_hosts(allowed_hosts.into())
.cors(http::DomainsValidation::Disabled)
.start_http(addr)
.map(|server| Server {
server: Some(server),
})
}
/// Returns address that this server is bound to.
pub fn addr(&self) -> &SocketAddr {
self.server.as_ref()
.expect("server is always Some at the start; it's consumed only when object is dropped; qed")
.addrs()
.first()
.expect("You cannot start the server without binding to at least one address; qed")
}
}
impl Drop for Server {
fn drop(&mut self) {
self.server.take().unwrap().close()
}
}

View File

@@ -1,73 +0,0 @@
// 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/>.
use std::str;
use std::sync::Arc;
use std::collections::HashMap;
use rustc_hex::FromHex;
use hash_fetch::urlhint::ContractClient;
use util::{Bytes, Address, Mutex, H256, ToPretty};
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
const URLHINT_RESOLVE: &'static str = "267b6922";
const DEFAULT_HASH: &'static str = "1472a9e190620cdf6b31f383373e45efcfe869a820c91f9ccd7eb9fb45e4985d";
pub struct FakeRegistrar {
pub calls: Arc<Mutex<Vec<(String, String)>>>,
pub responses: Mutex<HashMap<(String, String), Result<Bytes, String>>>,
}
impl FakeRegistrar {
pub fn new() -> Self {
FakeRegistrar {
calls: Arc::new(Mutex::new(Vec::new())),
responses: Mutex::new({
let mut map = HashMap::new();
map.insert(
(REGISTRAR.into(), "6795dbcd058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000000".into()),
Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()),
);
map.insert(
(URLHINT.into(), format!("{}{}", URLHINT_RESOLVE, DEFAULT_HASH)),
Ok(vec![])
);
map
}),
}
}
pub fn set_result(&self, hash: H256, result: Result<Bytes, String>) {
self.responses.lock().insert(
(URLHINT.into(), format!("{}{:?}", URLHINT_RESOLVE, hash)),
result
);
}
}
impl ContractClient for FakeRegistrar {
fn registrar(&self) -> Result<Address, String> {
Ok(REGISTRAR.parse().unwrap())
}
fn call(&self, address: Address, data: Bytes) -> ::futures::BoxFuture<Bytes, String> {
let call = (address.to_hex(), data.to_hex());
self.calls.lock().push(call.clone());
let res = self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call));
Box::new(::futures::future::done(res))
}
}

View File

@@ -1,207 +0,0 @@
// 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/>.
use tests::helpers::{serve, request, assert_security_headers, assert_security_headers_for_embed};
#[test]
fn should_redirect_to_home() {
// given
let server = serve();
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 302 Found");
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
}
#[test]
fn should_redirect_to_home_with_domain() {
// given
let server = serve();
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: home.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 302 Found");
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
}
#[test]
fn should_redirect_to_home_when_trailing_slash_is_missing() {
// given
let server = serve();
// when
let response = request(server,
"\
GET /app HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 302 Found");
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
}
#[test]
fn should_display_404_on_invalid_dapp() {
// given
let server = serve();
// when
let response = request(server,
"\
GET /invaliddapp/ HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 404 Not Found");
assert_security_headers_for_embed(&response.headers);
}
#[test]
fn should_display_404_on_invalid_dapp_with_domain() {
// given
let server = serve();
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: invaliddapp.web3.site\r\n\
Connection: close\r\n\
\r\n\
"
);
// then
response.assert_status("HTTP/1.1 404 Not Found");
assert_security_headers_for_embed(&response.headers);
}
#[test]
fn should_serve_rpc() {
// given
let server = serve();
// when
let response = request(server,
"\
POST / HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
Content-Type: application/json\r\n
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
}
#[test]
fn should_serve_rpc_at_slash_rpc() {
// given
let server = serve();
// when
let response = request(server,
"\
POST /rpc HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
Content-Type: application/json\r\n
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
}
#[test]
fn should_serve_proxy_pac() {
// given
let server = serve();
// when
let response = request(server,
"\
GET /proxy/proxy.pac HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, "DB\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"home.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
assert_security_headers(&response.headers);
}
#[test]
fn should_serve_utils() {
// given
let server = serve();
// when
let response = request(server,
"\
GET /parity-utils/inject.js HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body.contains("function(){"), true);
assert_security_headers(&response.headers);
}

View File

@@ -1,49 +0,0 @@
// 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/>.
use jsonrpc_core::{IoHandler, Value};
use tests::helpers::{serve_with_rpc, request};
#[test]
fn should_serve_rpc() {
// given
let mut io = IoHandler::default();
io.add_method("rpc_test", |_| {
Ok(Value::String("Hello World!".into()))
});
let server = serve_with_rpc(io);
// when
let req = r#"{"jsonrpc":"2.0","id":1,"method":"rpc_test","params":[]}"#;
let response = request(server, &format!(
"\
POST /rpc/ HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
Content-Type: application/json\r\n\
Content-Length: {}\r\n\
\r\n\
{}\r\n\
",
req.as_bytes().len(),
req,
));
// then
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
}

View File

@@ -1,99 +0,0 @@
// 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/>.
use tests::helpers::{serve_hosts, request};
#[test]
fn should_reject_invalid_host() {
// given
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
assert!(response.body.contains("Provided Host header is not whitelisted."), response.body);
}
#[test]
fn should_allow_valid_host() {
// given
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
// when
let response = request(server,
"\
GET /ui/ HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
}
#[test]
fn should_serve_dapps_domains() {
// given
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
// when
let response = request(server,
"\
GET / HTTP/1.1\r\n\
Host: ui.web3.site\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
}
#[test]
// NOTE [todr] This is required for error pages to be styled properly.
fn should_allow_parity_utils_even_on_invalid_domain() {
// given
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
// when
let response = request(server,
"\
GET /parity-utils/styles.css HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
\r\n\
{}
"
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
}

View File

@@ -1,150 +0,0 @@
// 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/>.
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
use url_lib::{self};
pub use url_lib::Host;
/// HTTP/HTTPS URL type for Iron.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Url {
/// Raw url of url
pub raw: url_lib::Url,
/// The host field of the URL, probably a domain.
pub host: Host,
/// The connection port.
pub port: u16,
/// The URL path, the resource to be accessed.
///
/// A *non-empty* vector encoding the parts of the URL path.
/// Empty entries of `""` correspond to trailing slashes.
pub path: Vec<String>,
/// The URL query.
pub query: Option<String>,
/// The URL username field, from the userinfo section of the URL.
///
/// `None` if the `@` character was not part of the input OR
/// if a blank username was provided.
/// Otherwise, a non-empty string.
pub username: Option<String>,
/// The URL password field, from the userinfo section of the URL.
///
/// `None` if the `@` character was not part of the input OR
/// if a blank password was provided.
/// Otherwise, a non-empty string.
pub password: Option<String>,
}
impl Url {
/// Create a URL from a string.
///
/// The input must be a valid URL with a special scheme for this to succeed.
///
/// HTTP and HTTPS are special schemes.
///
/// See: http://url.spec.whatwg.org/#special-scheme
pub fn parse(input: &str) -> Result<Url, String> {
// Parse the string using rust-url, then convert.
match url_lib::Url::parse(input) {
Ok(raw_url) => Url::from_generic_url(raw_url),
Err(e) => Err(format!("{}", e))
}
}
/// Create a `Url` from a `rust-url` `Url`.
pub fn from_generic_url(raw_url: url_lib::Url) -> Result<Url, String> {
// Map empty usernames to None.
let username = match raw_url.username() {
"" => None,
username => Some(username.to_owned())
};
// Map empty passwords to None.
let password = match raw_url.password() {
Some(password) if !password.is_empty() => Some(password.to_owned()),
_ => None,
};
let port = raw_url.port_or_known_default().ok_or_else(|| format!("Unknown port for scheme: `{}`", raw_url.scheme()))?;
let host = raw_url.host().ok_or_else(|| "Valid host, because only data:, mailto: protocols does not have host.".to_owned())?.to_owned();
let path = raw_url.path_segments().ok_or_else(|| "Valid path segments. In HTTP we won't get cannot-be-a-base URLs".to_owned())?
.map(|part| part.to_owned()).collect();
let query = raw_url.query().map(|x| x.to_owned());
Ok(Url {
port: port,
host: host,
path: path,
query: query,
raw: raw_url,
username: username,
password: password,
})
}
}
#[cfg(test)]
mod test {
use super::Url;
#[test]
fn test_default_port() {
assert_eq!(Url::parse("http://example.com/wow").unwrap().port, 80u16);
assert_eq!(Url::parse("https://example.com/wow").unwrap().port, 443u16);
}
#[test]
fn test_explicit_port() {
assert_eq!(Url::parse("http://localhost:3097").unwrap().port, 3097u16);
}
#[test]
fn test_empty_username() {
assert!(Url::parse("http://@example.com").unwrap().username.is_none());
assert!(Url::parse("http://:password@example.com").unwrap().username.is_none());
}
#[test]
fn test_not_empty_username() {
let user = Url::parse("http://john:pass@example.com").unwrap().username;
assert_eq!(user.unwrap(), "john");
let user = Url::parse("http://john:@example.com").unwrap().username;
assert_eq!(user.unwrap(), "john");
}
#[test]
fn test_empty_password() {
assert!(Url::parse("http://michael@example.com").unwrap().password.is_none());
assert!(Url::parse("http://:@example.com").unwrap().password.is_none());
}
#[test]
fn test_not_empty_password() {
let pass = Url::parse("http://michael:pass@example.com").unwrap().password;
assert_eq!(pass.unwrap(), "pass");
let pass = Url::parse("http://:pass@example.com").unwrap().password;
assert_eq!(pass.unwrap(), "pass");
}
}

View File

@@ -1,245 +0,0 @@
// 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/>.
//! Serving web-based content (proxying)
use std::sync::Arc;
use fetch::{self, Fetch};
use parity_reactor::Remote;
use base32;
use hyper::{self, server, net, Next, Encoder, Decoder};
use hyper::status::StatusCode;
use apps;
use endpoint::{Endpoint, Handler, EndpointPath};
use handlers::{
ContentFetcherHandler, ContentHandler, ContentValidator, ValidatorResponse,
StreamingHandler, extract_url,
};
use url::Url;
use {Embeddable, WebProxyTokens};
pub struct Web<F> {
embeddable_on: Embeddable,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
fetch: F,
}
impl<F: Fetch> Web<F> {
pub fn boxed(
embeddable_on: Embeddable,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
fetch: F,
) -> Box<Endpoint> {
Box::new(Web {
embeddable_on,
web_proxy_tokens,
remote,
fetch,
})
}
}
impl<F: Fetch> Endpoint for Web<F> {
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler> {
Box::new(WebHandler {
control: control,
state: State::Initial,
path: path,
remote: self.remote.clone(),
fetch: self.fetch.clone(),
web_proxy_tokens: self.web_proxy_tokens.clone(),
embeddable_on: self.embeddable_on.clone(),
})
}
}
struct WebInstaller {
embeddable_on: Embeddable,
referer: String,
}
impl ContentValidator for WebInstaller {
type Error = String;
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, String> {
let status = StatusCode::from_u16(response.status().to_u16());
let is_html = response.is_html();
let mime = response.content_type().unwrap_or(mime!(Text/Html));
let mut handler = StreamingHandler::new(
response,
status,
mime,
self.embeddable_on.clone(),
);
if is_html {
handler.set_initial_content(&format!(
r#"<script src="/{}/inject.js"></script><script>history.replaceState({{}}, "", "/?{}{}/{}")</script>"#,
apps::UTILS_PATH,
apps::URL_REFERER,
apps::WEB_PATH,
&self.referer,
));
}
Ok(ValidatorResponse::Streaming(handler))
}
}
enum State<F: Fetch> {
Initial,
Error(ContentHandler),
Fetching(ContentFetcherHandler<WebInstaller, F>),
}
struct WebHandler<F: Fetch> {
control: hyper::Control,
state: State<F>,
path: EndpointPath,
remote: Remote,
fetch: F,
web_proxy_tokens: Arc<WebProxyTokens>,
embeddable_on: Embeddable,
}
impl<F: Fetch> WebHandler<F> {
fn extract_target_url(&self, url: Option<Url>) -> Result<String, State<F>> {
let token_and_url = self.path.app_params.get(0)
.map(|encoded| encoded.replace('.', ""))
.and_then(|encoded| base32::decode(base32::Alphabet::Crockford, &encoded.to_uppercase()))
.and_then(|data| String::from_utf8(data).ok())
.ok_or_else(|| State::Error(ContentHandler::error(
StatusCode::BadRequest,
"Invalid parameter",
"Couldn't parse given parameter:",
self.path.app_params.get(0).map(String::as_str),
self.embeddable_on.clone()
)))?;
let mut token_it = token_and_url.split('+');
let token = token_it.next();
let target_url = token_it.next();
// Check if token supplied in URL is correct.
let domain = match token.and_then(|token| self.web_proxy_tokens.domain(token)) {
Some(domain) => domain,
_ => {
return Err(State::Error(ContentHandler::error(
StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), self.embeddable_on.clone()
)));
}
};
// Validate protocol
let mut target_url = match target_url {
Some(url) if url.starts_with("http://") || url.starts_with("https://") => url.to_owned(),
_ => {
return Err(State::Error(ContentHandler::error(
StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None, self.embeddable_on.clone()
)));
}
};
if !target_url.starts_with(&*domain) {
return Err(State::Error(ContentHandler::error(
StatusCode::BadRequest, "Invalid Domain", "Dapp attempted to access invalid domain.", Some(&target_url), self.embeddable_on.clone(),
)));
}
if !target_url.ends_with("/") {
target_url = format!("{}/", target_url);
}
// TODO [ToDr] Should just use `path.app_params`
let (path, query) = match (&url, self.path.using_dapps_domains) {
(&Some(ref url), true) => (&url.path[..], &url.query),
(&Some(ref url), false) => (&url.path[2..], &url.query),
_ => {
return Err(State::Error(ContentHandler::error(
StatusCode::BadRequest, "Invalid URL", "Couldn't parse URL", None, self.embeddable_on.clone()
)));
}
};
let query = match *query {
Some(ref query) => format!("?{}", query),
None => "".into(),
};
Ok(format!("{}{}{}", target_url, path.join("/"), query))
}
}
impl<F: Fetch> server::Handler<net::HttpStream> for WebHandler<F> {
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
let url = extract_url(&request);
// First extract the URL (reject invalid URLs)
let target_url = match self.extract_target_url(url) {
Ok(url) => url,
Err(error) => {
self.state = error;
return Next::write();
}
};
let mut handler = ContentFetcherHandler::new(
target_url,
self.path.clone(),
self.control.clone(),
WebInstaller {
embeddable_on: self.embeddable_on.clone(),
referer: self.path.app_params.get(0)
.expect("`target_url` is valid; app_params is not empty;qed")
.to_owned(),
},
self.embeddable_on.clone(),
self.remote.clone(),
self.fetch.clone(),
);
let res = handler.on_request(request);
self.state = State::Fetching(handler);
res
}
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
match self.state {
State::Initial => Next::end(),
State::Error(ref mut handler) => handler.on_request_readable(decoder),
State::Fetching(ref mut handler) => handler.on_request_readable(decoder),
}
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
match self.state {
State::Initial => Next::end(),
State::Error(ref mut handler) => handler.on_response(res),
State::Fetching(ref mut handler) => handler.on_response(res),
}
}
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
match self.state {
State::Initial => Next::end(),
State::Error(ref mut handler) => handler.on_response_writable(encoder),
State::Fetching(ref mut handler) => handler.on_response_writable(encoder),
}
}
}

View File

@@ -1,19 +0,0 @@
[package]
description = "Ethcore Parity UI"
homepage = "http://parity.io"
license = "GPL-3.0"
name = "parity-ui"
version = "1.7.0"
authors = ["Parity Technologies <admin@parity.io>"]
[build-dependencies]
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 = "beta" }
[features]
no-precompiled-js = ["parity-ui-dev"]
use-precompiled-js = ["parity-ui-precompiled"]

View File

@@ -1,10 +1,10 @@
[package]
description = "Ethcore development/test/build tools"
homepage = "http://parity.io"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethcore-devtools"
version = "1.7.0"
authors = ["Parity Technologies <admin@parity.io>"]
version = "1.0.0"
authors = ["Ethcore <admin@ethcore.io>"]
[dependencies]
rand = "0.3"

1
devtools/README.md Normal file
View File

@@ -0,0 +1 @@
# ethcore dev tools

View File

@@ -1,123 +0,0 @@
// 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/>.
use std::thread;
use std::time::Duration;
use std::io::{Read, Write};
use std::str::{self, Lines};
use std::net::{TcpStream, SocketAddr};
pub struct Response {
pub status: String,
pub headers: Vec<String>,
pub headers_raw: String,
pub body: String,
}
impl Response {
pub fn assert_header(&self, header: &str, value: &str) {
let header = format!("{}: {}", header, value);
assert!(self.headers.iter().find(|h| *h == &header).is_some(), "Couldn't find header {} in {:?}", header, &self.headers)
}
pub fn assert_status(&self, status: &str) {
assert_eq!(self.status, status.to_owned(), "Got unexpected code. Body: {:?}", self.body);
}
pub fn assert_security_headers_present(&self, port: Option<u16>) {
assert_security_headers_present(&self.headers, port)
}
}
pub fn read_block(lines: &mut Lines, all: bool) -> String {
let mut block = String::new();
loop {
let line = lines.next();
match line {
None => break,
Some("") if !all => break,
Some(v) => {
block.push_str(v);
block.push_str("\n");
},
}
}
block
}
fn connect(address: &SocketAddr) -> TcpStream {
let mut retries = 0;
let mut last_error = None;
while retries < 10 {
retries += 1;
let res = TcpStream::connect(address);
match res {
Ok(stream) => {
return stream;
},
Err(e) => {
last_error = Some(e);
thread::sleep(Duration::from_millis(retries * 10));
}
}
}
panic!("Unable to connect to the server. Last error: {:?}", last_error);
}
pub fn request(address: &SocketAddr, request: &str) -> Response {
let mut req = connect(address);
req.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
req.write_all(request.as_bytes()).unwrap();
let mut response = String::new();
let _ = req.read_to_string(&mut response);
let mut lines = response.lines();
let status = lines.next().expect("Expected a response").to_owned();
let headers_raw = read_block(&mut lines, false);
let headers = headers_raw.split('\n').map(|v| v.to_owned()).collect();
let body = read_block(&mut lines, true);
Response {
status: status,
headers: headers,
headers_raw: headers_raw,
body: body,
}
}
/// Check if all required security headers are present
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
if let None = port {
assert!(
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
);
}
assert!(
headers.iter().find(|header| header.as_str() == "X-XSS-Protection: 1; mode=block").is_some(),
"X-XSS-Protection missing: {:?}", headers
);
assert!(
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
"X-Content-Type-Options missing: {:?}", headers
);
assert!(
headers.iter().find(|header| header.starts_with("Content-Security-Policy: ")).is_some(),
"Content-Security-Policy missing: {:?}", headers
)
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -19,11 +19,6 @@
extern crate rand;
mod random_path;
mod test_socket;
mod stop_guard;
pub mod http_client;
pub mod random_path;
pub use random_path::*;
pub use test_socket::*;
pub use stop_guard::*;

View File

@@ -1,4 +1,4 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -19,20 +19,14 @@
use std::path::*;
use std::fs;
use std::env;
use std::ops::{Deref, DerefMut};
use rand::random;
pub struct RandomTempPath {
path: PathBuf,
pub panic_on_drop_failure: bool,
path: PathBuf
}
pub fn random_filename() -> String {
random_str(8)
}
pub fn random_str(len: usize) -> String {
(0..len).map(|_| ((random::<f32>() * 26.0) as u8 + 97) as char).collect()
(0..8).map(|_| ((random::<f32>() * 26.0) as u8 + 97) as char).collect()
}
impl RandomTempPath {
@@ -40,8 +34,7 @@ impl RandomTempPath {
let mut dir = env::temp_dir();
dir.push(random_filename());
RandomTempPath {
path: dir.clone(),
panic_on_drop_failure: true,
path: dir.clone()
}
}
@@ -50,8 +43,7 @@ impl RandomTempPath {
dir.push(random_filename());
fs::create_dir_all(dir.as_path()).unwrap();
RandomTempPath {
path: dir.clone(),
panic_on_drop_failure: true,
path: dir.clone()
}
}
@@ -62,67 +54,16 @@ impl RandomTempPath {
pub fn as_str(&self) -> &str {
self.path.to_str().unwrap()
}
pub fn new_in(&self, name: &str) -> String {
let mut path = self.path.clone();
path.push(name);
path.to_str().unwrap().to_owned()
}
}
impl AsRef<Path> for RandomTempPath {
fn as_ref(&self) -> &Path {
self.as_path()
}
}
impl Deref for RandomTempPath {
type Target = Path;
fn deref(&self) -> &Self::Target {
self.as_path()
}
}
impl Drop for RandomTempPath {
fn drop(&mut self) {
if let Err(_) = fs::remove_dir_all(&self) {
if let Err(e) = fs::remove_file(&self) {
if self.panic_on_drop_failure {
panic!("Failed to remove temp directory. Here's what prevented this from happening: ({})", e);
}
}
if let Err(e) = fs::remove_dir_all(self.as_path()) {
panic!("failed to remove temp directory, probably something failed to destroyed ({})", e);
}
}
}
pub struct GuardedTempResult<T> {
pub result: Option<T>,
pub _temp: RandomTempPath,
}
impl<T> GuardedTempResult<T> {
pub fn reference(&self) -> &T {
self.result.as_ref().unwrap()
}
pub fn reference_mut(&mut self) -> &mut T {
self.result.as_mut().unwrap()
}
pub fn take(&mut self) -> T {
self.result.take().unwrap()
}
}
impl<T> Deref for GuardedTempResult<T> {
type Target = T;
fn deref(&self) -> &T { self.result.as_ref().unwrap() }
}
impl<T> DerefMut for GuardedTempResult<T> {
fn deref_mut(&mut self) -> &mut T { self.result.as_mut().unwrap() }
}
#[test]
fn creates_dir() {
let temp = RandomTempPath::create_dir();

View File

@@ -1,45 +0,0 @@
// 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/>.
//! Stop guard mod
use std::sync::Arc;
use std::sync::atomic::*;
/// Stop guard that will set a stop flag on drop
pub struct StopGuard {
flag: Arc<AtomicBool>,
}
impl StopGuard {
/// Create a stop guard
pub fn new() -> StopGuard {
StopGuard {
flag: Arc::new(AtomicBool::new(false))
}
}
/// Share stop guard between the threads
pub fn share(&self) -> Arc<AtomicBool> {
self.flag.clone()
}
}
impl Drop for StopGuard {
fn drop(&mut self) {
self.flag.store(true, Ordering::Relaxed)
}
}

View File

@@ -1,95 +0,0 @@
// 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/>.
use std::io::*;
use std::cmp;
pub struct TestSocket {
pub read_buffer: Vec<u8>,
pub write_buffer: Vec<u8>,
pub cursor: usize,
pub buf_size: usize,
}
impl Default for TestSocket {
fn default() -> Self {
TestSocket::new()
}
}
impl TestSocket {
pub fn new() -> Self {
TestSocket {
read_buffer: vec![],
write_buffer: vec![],
cursor: 0,
buf_size: 0,
}
}
pub fn new_buf(buf_size: usize) -> TestSocket {
TestSocket {
read_buffer: vec![],
write_buffer: vec![],
cursor: 0,
buf_size: buf_size,
}
}
pub fn new_ready(data: Vec<u8>) -> TestSocket {
TestSocket {
read_buffer: data,
write_buffer: vec![],
cursor: 0,
buf_size: 0,
}
}
}
impl Read for TestSocket {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let end_position = cmp::min(self.read_buffer.len(), self.cursor+buf.len());
if self.cursor > end_position { return Ok(0) }
let len = cmp::max(end_position - self.cursor, 0);
match len {
0 => Ok(0),
_ => {
for i in self.cursor..end_position {
buf[i-self.cursor] = self.read_buffer[i];
}
self.cursor = end_position;
Ok(len)
}
}
}
}
impl Write for TestSocket {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
if self.buf_size == 0 || buf.len() < self.buf_size {
self.write_buffer.extend(buf.iter().cloned());
Ok(buf.len())
}
else {
self.write_buffer.extend(buf.iter().take(self.buf_size).cloned());
Ok(self.buf_size)
}
}
fn flush(&mut self) -> Result<()> {
unimplemented!();
}
}

11
doc.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
# generate documentation only for partiy and ethcore libraries
cargo doc --no-deps --verbose \
-p ethash \
-p ethcore-util \
-p ethcore \
-p ethsync \
-p ethcore-rpc \
-p parity \
-p ethminer

View File

@@ -1,3 +0,0 @@
Usage
```docker build -f docker/ubuntu/Dockerfile --tag ethcore/parity:branch_or_tag_name .```

View File

@@ -1,36 +0,0 @@
FROM centos:latest
WORKDIR /build
# install tools and dependencies
RUN yum -y update&& \
yum install -y git make gcc-c++ gcc file binutils
# install rustup
RUN curl -sSf https://static.rust-lang.org/rustup.sh -o rustup.sh &&\
ls&&\
sh rustup.sh --disable-sudo
# show backtraces
ENV RUST_BACKTRACE 1
# set compiler
ENV CXX g++
ENV CC gcc
# show tools
RUN rustc -vV && \
cargo -V && \
gcc -v &&\
g++ -v
# build parity
ADD . /build/parity
RUN cd parity&&\
cargo build --release --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity
RUN file /build/parity/target/release/parity
EXPOSE 8080 8545 8180
ENTRYPOINT ["/build/parity/target/release/parity"]

View File

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

View File

@@ -1,45 +0,0 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get -y update && \
apt-get install -y --force-yes --no-install-recommends \
curl git make g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
libc6-arm64-cross libc6-dev-arm64-cross wget file ca-certificates \
binutils-aarch64-linux-gnu \
&& \
apt-get clean
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
ENV RUST_TARGETS="aarch64-unknown-linux-gnu"
# multirust add arm--linux-gnuabhf toolchain
RUN rustup target add aarch64-unknown-linux-gnu
# show backtraces
ENV RUST_BACKTRACE 1
# show tools
RUN rustc -vV && cargo -V
# build parity
ADD . /build/parity
RUN cd parity && \
mkdir -p .cargo && \
echo '[target.aarch64-unknown-linux-gnu]\n\
linker = "aarch64-linux-gnu-gcc"\n'\
>>.cargo/config && \
cat .cargo/config && \
cargo build --target aarch64-unknown-linux-gnu --release --verbose && \
ls /build/parity/target/aarch64-unknown-linux-gnu/release/parity && \
/usr/bin/aarch64-linux-gnu-strip /build/parity/target/aarch64-unknown-linux-gnu/release/parity
RUN file /build/parity/target/aarch64-unknown-linux-gnu/release/parity
EXPOSE 8080 8545 8180
ENTRYPOINT ["/build/parity/target/aarch64-unknown-linux-gnu/release/parity"]

View File

@@ -1,45 +0,0 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get -y update && \
apt-get install -y --force-yes --no-install-recommends \
curl git make g++ gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
libc6-dev-armhf-cross wget file ca-certificates \
binutils-arm-linux-gnueabihf \
&& \
apt-get clean
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
ENV RUST_TARGETS="arm-unknown-linux-gnueabihf"
# multirust add arm--linux-gnuabhf toolchain
RUN rustup target add armv7-unknown-linux-gnueabihf
# show backtraces
ENV RUST_BACKTRACE 1
# show tools
RUN rustc -vV && cargo -V
# build parity
ADD . /build/parity
RUN cd parity && \
mkdir -p .cargo && \
echo '[target.armv7-unknown-linux-gnueabihf]\n\
linker = "arm-linux-gnueabihf-gcc"\n'\
>>.cargo/config && \
cat .cargo/config && \
cargo build --target armv7-unknown-linux-gnueabihf --release --verbose && \
ls /build/parity/target/armv7-unknown-linux-gnueabihf/release/parity && \
/usr/bin/arm-linux-gnueabihf-strip /build/parity/target/armv7-unknown-linux-gnueabihf/release/parity
RUN file /build/parity/target/armv7-unknown-linux-gnueabihf/release/parity
EXPOSE 8080 8545 8180
ENTRYPOINT ["/build/parity/target/armv7-unknown-linux-gnueabihf/release/parity"]

View File

@@ -0,0 +1,37 @@
FROM ubuntu:14.04
# install tools and dependencies
RUN apt-get update && \
apt-get install -y \
# make
build-essential \
# add-apt-repository
software-properties-common \
curl \
g++ \
wget \
git \
# evmjit dependencies
zlib1g-dev \
libedit-dev
# cmake, llvm and rocksdb ppas. then update ppas
RUN add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
apt-get update && \
apt-get install -y --force-yes cmake llvm-3.7-dev
# install evmjit
RUN git clone https://github.com/debris/evmjit && \
cd evmjit && \
mkdir build && cd build && \
cmake .. && make && make install && cd
# install multirust
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
# export rust LIBRARY_PATH
ENV LIBRARY_PATH /usr/local/lib
# show backtraces
ENV RUST_BACKTRACE 1

View File

@@ -1,58 +1,42 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get update && \
apt-get install -y \
# make
build-essential \
# add-apt-repository
software-properties-common \
curl \
wget \
git \
g++ \
binutils \
file \
# evmjit dependencies
zlib1g-dev \
libedit-dev
apt-get install -y \
# make
build-essential \
# add-apt-repository
software-properties-common \
curl \
wget \
git \
g++ \
# evmjit dependencies
zlib1g-dev \
libedit-dev
# cmake and llvm ppas. then update ppas
# cmake, llvm and rocksdb ppas. then update ppas
RUN add-apt-repository -y "ppa:george-edison55/cmake-3.x" && \
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
apt-get update && \
apt-get install -y --force-yes cmake llvm-3.7-dev
add-apt-repository "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main" && \
apt-get update && \
apt-get install -y --force-yes cmake llvm-3.7-dev
# install evmjit
RUN git clone https://github.com/debris/evmjit && \
cd evmjit && \
mkdir build && cd build && \
cmake .. && make && make install && cd
cd evmjit && \
mkdir build && cd build && \
cmake .. && make && make install && cd
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# install multirust
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
# export rust LIBRARY_PATH
ENV LIBRARY_PATH /usr/local/lib
# show backtraces
ENV RUST_BACKTRACE 1
# show tools
RUN rustc -vV && \
cargo -V && \
gcc -v &&\
g++ -v
# build parity
ADD . /build/parity
RUN cd parity && \
cargo build --release --features ethcore/jit --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity
RUN file /build/parity/target/release/parity
EXPOSE 8080 8545 8180
ENTRYPOINT ["/build/parity/target/release/parity"]
RUN git clone https://github.com/ethcore/parity && \
cd parity && \
cargo build --release --features ethcore/jit

View File

@@ -1,42 +1,23 @@
FROM ubuntu:14.04
WORKDIR /build
# install tools and dependencies
RUN apt-get update && \
apt-get install -y \
g++ \
build-essential \
curl \
git \
file \
binutils \
libssl-dev \
pkg-config \
libudev-dev
apt-get install -y \
g++ \
curl \
git \
make
# install rustup
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
# install multirust
RUN curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes
# rustup directory
ENV PATH /root/.cargo/bin:$PATH
# export rust LIBRARY_PATH
ENV LIBRARY_PATH /usr/local/lib
# show backtraces
ENV RUST_BACKTRACE 1
# show tools
RUN rustc -vV && \
cargo -V && \
gcc -v &&\
g++ -v
# build parity
ADD . /build/parity
RUN cd parity && \
cargo build --release --verbose && \
ls /build/parity/target/release/parity && \
strip /build/parity/target/release/parity
RUN file /build/parity/target/release/parity
EXPOSE 8080 8545 8180
ENTRYPOINT ["/build/parity/target/release/parity"]
RUN git clone https://github.com/ethcore/parity && \
cd parity && \
cargo build --release

View File

@@ -1,7 +1,7 @@
[package]
name = "ethash"
version = "1.7.0"
authors = ["Parity Technologies <admin@parity.io>"]
version = "1.0.0"
authors = ["arkpar <arkadiy@ethcore.io"]
[lib]
@@ -9,4 +9,3 @@ authors = ["Parity Technologies <admin@parity.io>"]
log = "0.3"
sha3 = { path = "../util/sha3" }
primal = "0.2.3"
parking_lot = "0.4"

View File

@@ -1,4 +1,4 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -20,17 +20,14 @@
// TODO: fix endianess for big endian
use primal::is_prime;
use std::cell::Cell;
use std::mem;
use std::ptr;
use sha3;
use std::slice;
use std::path::{Path, PathBuf};
use std::io::{self, Read, Write};
use std::path::PathBuf;
use std::io::{Read, Write, self};
use std::fs::{self, File};
use parking_lot::Mutex;
pub const ETHASH_EPOCH_LENGTH: u64 = 30000;
pub const ETHASH_CACHE_ROUNDS: usize = 3;
pub const ETHASH_MIX_BYTES: usize = 128;
@@ -45,14 +42,14 @@ const NODE_WORDS: usize = 64 / 4;
const NODE_BYTES: usize = 64;
const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4;
const MIX_NODES: usize = MIX_WORDS / NODE_WORDS;
const FNV_PRIME: u32 = 0x01000193;
const FNV_PRIME: u32 = 0x01000193;
/// Computation result
pub struct ProofOfWork {
/// Difficulty boundary
pub value: H256,
/// Mix
pub mix_hash: H256,
pub mix_hash: H256
}
struct Node {
@@ -86,17 +83,15 @@ impl Node {
pub type H256 = [u8; 32];
pub struct Light {
cache_dir: PathBuf,
block_number: u64,
cache: Vec<Node>,
seed_compute: Mutex<SeedHashCompute>,
}
/// Light cache structure
/// Light cache structur
impl Light {
/// Create a new light cache for a given block number
pub fn new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
light_new(cache_dir, block_number)
pub fn new(block_number: u64) -> Light {
light_new(block_number)
}
/// Calculate the light boundary data
@@ -106,108 +101,46 @@ impl Light {
light_compute(self, header_hash, nonce)
}
pub fn file_path<T: AsRef<Path>>(cache_dir: T, seed_hash: H256) -> PathBuf {
let mut cache_dir = cache_dir.as_ref().to_path_buf();
cache_dir.push(to_hex(&seed_hash));
cache_dir
pub fn file_path(block_number: u64) -> PathBuf {
let mut home = ::std::env::home_dir().unwrap();
home.push(".ethash");
home.push("light");
let seed_hash = get_seedhash(block_number);
home.push(to_hex(&seed_hash));
home
}
pub fn from_file<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> io::Result<Light> {
let seed_compute = SeedHashCompute::new();
let path = Light::file_path(&cache_dir, seed_compute.get_seedhash(block_number));
let mut file = File::open(path)?;
pub fn from_file(block_number: u64) -> io::Result<Light> {
let path = Light::file_path(block_number);
let mut file = try!(File::open(path));
let cache_size = get_cache_size(block_number);
if file.metadata()?.len() != cache_size as u64 {
if try!(file.metadata()).len() != cache_size as u64 {
return Err(io::Error::new(io::ErrorKind::Other, "Cache file size mismatch"));
}
let num_nodes = cache_size / NODE_BYTES;
let mut nodes: Vec<Node> = Vec::with_capacity(num_nodes);
let mut nodes: Vec<Node> = Vec::new();
nodes.resize(num_nodes, unsafe { mem::uninitialized() });
let buf = unsafe { slice::from_raw_parts_mut(nodes.as_mut_ptr() as *mut u8, cache_size) };
file.read_exact(buf)?;
try!(file.read_exact(buf));
Ok(Light {
block_number,
cache_dir: cache_dir.as_ref().to_path_buf(),
cache: nodes,
seed_compute: Mutex::new(seed_compute),
block_number: block_number,
})
}
pub fn to_file(&self) -> io::Result<PathBuf> {
let seed_compute = self.seed_compute.lock();
let path = Light::file_path(&self.cache_dir, seed_compute.get_seedhash(self.block_number));
if self.block_number >= ETHASH_EPOCH_LENGTH * 2 {
let deprecated = Light::file_path(
&self.cache_dir,
seed_compute.get_seedhash(self.block_number - ETHASH_EPOCH_LENGTH * 2)
);
if deprecated.exists() {
debug!(target: "ethash", "removing: {:?}", &deprecated);
fs::remove_file(deprecated)?;
}
}
fs::create_dir_all(path.parent().unwrap())?;
let mut file = File::create(&path)?;
pub fn to_file(&self) -> io::Result<()> {
let path = Light::file_path(self.block_number);
try!(fs::create_dir_all(path.parent().unwrap()));
let mut file = try!(File::create(path));
let cache_size = self.cache.len() * NODE_BYTES;
let buf = unsafe { slice::from_raw_parts(self.cache.as_ptr() as *const u8, cache_size) };
file.write(buf)?;
Ok(path)
try!(file.write(buf));
Ok(())
}
}
pub struct SeedHashCompute {
prev_epoch: Cell<u64>,
prev_seedhash: Cell<H256>,
}
impl SeedHashCompute {
#[inline]
pub fn new() -> SeedHashCompute {
SeedHashCompute {
prev_epoch: Cell::new(0),
prev_seedhash: Cell::new([0u8; 32]),
}
}
#[inline]
fn reset_cache(&self) {
self.prev_epoch.set(0);
self.prev_seedhash.set([0u8; 32]);
}
#[inline]
pub fn get_seedhash(&self, block_number: u64) -> H256 {
let epoch = block_number / ETHASH_EPOCH_LENGTH;
if epoch < self.prev_epoch.get() {
// can't build on previous hash if requesting an older block
self.reset_cache();
}
if epoch > self.prev_epoch.get() {
let seed_hash = SeedHashCompute::resume_compute_seedhash(self.prev_seedhash.get(), self.prev_epoch.get(), epoch);
self.prev_seedhash.set(seed_hash);
self.prev_epoch.set(epoch);
}
self.prev_seedhash.get()
}
#[inline]
pub fn resume_compute_seedhash(mut hash: H256, start_epoch: u64, end_epoch: u64) -> H256 {
for _ in start_epoch..end_epoch {
unsafe { sha3::sha3_256(hash[..].as_mut_ptr(), 32, hash[..].as_ptr(), 32) };
}
hash
}
}
pub fn slow_get_seedhash(block_number: u64) -> H256 {
SeedHashCompute::resume_compute_seedhash([0u8; 32], 0, block_number / ETHASH_EPOCH_LENGTH)
}
#[inline]
fn fnv_hash(x: u32, y: u32) -> u32 {
return x.wrapping_mul(FNV_PRIME) ^ y;
@@ -220,24 +153,34 @@ fn sha3_512(input: &[u8], output: &mut [u8]) {
#[inline]
fn get_cache_size(block_number: u64) -> usize {
let mut sz: u64 = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
sz = sz - NODE_BYTES as u64;
while !is_prime(sz / NODE_BYTES as u64) {
sz = sz - 2 * NODE_BYTES as u64;
}
sz as usize
let mut sz: u64 = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
sz = sz - NODE_BYTES as u64;
while !is_prime(sz / NODE_BYTES as u64) {
sz = sz - 2 * NODE_BYTES as u64;
}
sz as usize
}
#[inline]
fn get_data_size(block_number: u64) -> usize {
let mut sz: u64 = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
sz = sz - ETHASH_MIX_BYTES as u64;
while !is_prime(sz / ETHASH_MIX_BYTES as u64) {
sz = sz - 2 * ETHASH_MIX_BYTES as u64;
}
sz as usize
let mut sz: u64 = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
sz = sz - ETHASH_MIX_BYTES as u64;
while !is_prime(sz / ETHASH_MIX_BYTES as u64) {
sz = sz - 2 * ETHASH_MIX_BYTES as u64;
}
sz as usize
}
#[inline]
/// Given the `block_number`, determine the seed hash for Ethash.
pub fn get_seedhash(block_number: u64) -> H256 {
let epochs = block_number / ETHASH_EPOCH_LENGTH;
let mut ret: H256 = [0u8; 32];
for _ in 0..epochs {
unsafe { sha3::sha3_256(ret[..].as_mut_ptr(), 32, ret[..].as_ptr(), 32) };
}
ret
}
/// Difficulty quick check for POW preverification
///
@@ -268,12 +211,12 @@ pub fn light_compute(light: &Light, header_hash: &H256, nonce: u64) -> ProofOfWo
hash_compute(light, full_size, header_hash, nonce)
}
fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) -> ProofOfWork {
fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) -> ProofOfWork {
if full_size % MIX_WORDS != 0 {
panic!("Unaligned full size");
}
// pack hash and nonce together into first 40 bytes of s_mix
let mut s_mix: [Node; MIX_NODES + 1] = [Node::default(), Node::default(), Node::default()];
let mut s_mix: [Node; MIX_NODES + 1] = [ Node::default(), Node::default(), Node::default() ];
unsafe { ptr::copy_nonoverlapping(header_hash.as_ptr(), s_mix.get_unchecked_mut(0).bytes.as_mut_ptr(), 32) };
unsafe { ptr::copy_nonoverlapping(mem::transmute(&nonce), s_mix.get_unchecked_mut(0).bytes[32..].as_mut_ptr(), 8) };
@@ -287,12 +230,11 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
let page_size = 4 * MIX_WORDS;
let num_full_pages = (full_size / page_size) as u32;
let cache: &[Node] = &light.cache; // deref once for better performance
for i in 0..(ETHASH_ACCESSES as u32) {
let index = fnv_hash(f_mix.get_unchecked(0).as_words().get_unchecked(0) ^ i, *mix.get_unchecked(0).as_words().get_unchecked((i as usize) % MIX_WORDS)) % num_full_pages;
for n in 0..MIX_NODES {
let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, cache);
let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, light);
for w in 0..NODE_WORDS {
*mix.get_unchecked_mut(n).as_words_mut().get_unchecked_mut(w) = fnv_hash(*mix.get_unchecked(n).as_words().get_unchecked(w), *tmp_node.as_words().get_unchecked(w));
}
@@ -315,7 +257,7 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
ptr::copy_nonoverlapping(mix.get_unchecked_mut(0).bytes.as_ptr(), buf[64..].as_mut_ptr(), 32);
ptr::copy_nonoverlapping(mix.get_unchecked_mut(0).bytes.as_ptr(), mix_hash.as_mut_ptr(), 32);
let mut value: H256 = [0u8; 32];
sha3::sha3_256(value.as_mut_ptr(), value.len(), buf.as_ptr(), buf.len());
sha3::sha3_256(value.as_mut_ptr(), value.len(), buf.as_ptr(), buf.len());
ProofOfWork {
mix_hash: mix_hash,
value: value,
@@ -323,17 +265,18 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
}
}
fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
fn calculate_dag_item(node_index: u32, light: &Light) -> Node {
unsafe {
let num_parent_nodes = cache.len();
let init = cache.get_unchecked(node_index as usize % num_parent_nodes);
let num_parent_nodes = light.cache.len();
let cache_nodes = &light.cache;
let init = cache_nodes.get_unchecked(node_index as usize % num_parent_nodes);
let mut ret = init.clone();
*ret.as_words_mut().get_unchecked_mut(0) ^= node_index;
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
for i in 0..ETHASH_DATASET_PARENTS {
let parent_index = fnv_hash(node_index ^ i, *ret.as_words().get_unchecked(i as usize % NODE_WORDS)) % num_parent_nodes as u32;
let parent = cache.get_unchecked(parent_index as usize);
let parent = cache_nodes.get_unchecked(parent_index as usize);
for w in 0..NODE_WORDS {
*ret.as_words_mut().get_unchecked_mut(w) = fnv_hash(*ret.as_words().get_unchecked(w), *parent.as_words().get_unchecked(w));
}
@@ -343,12 +286,13 @@ fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
}
}
fn light_new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
let seed_compute = SeedHashCompute::new();
let seedhash = seed_compute.get_seedhash(block_number);
fn light_new(block_number: u64) -> Light {
let seedhash = get_seedhash(block_number);
let cache_size = get_cache_size(block_number);
assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size");
if cache_size % NODE_BYTES != 0 {
panic!("Unaligned cache size");
}
let num_nodes = cache_size / NODE_BYTES;
let mut nodes = Vec::with_capacity(num_nodes);
@@ -364,7 +308,7 @@ fn light_new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
let idx = *nodes.get_unchecked_mut(i).as_words().get_unchecked(0) as usize % num_nodes;
let mut data = nodes.get_unchecked((num_nodes - 1 + i) % num_nodes).clone();
for w in 0..NODE_WORDS {
*data.as_words_mut().get_unchecked_mut(w) ^= *nodes.get_unchecked(idx).as_words().get_unchecked(w);
*data.as_words_mut().get_unchecked_mut(w) ^= *nodes.get_unchecked(idx).as_words().get_unchecked(w) ;
}
sha3_512(&data.bytes, &mut nodes.get_unchecked_mut(i).bytes);
}
@@ -372,14 +316,12 @@ fn light_new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
}
Light {
block_number,
cache_dir: cache_dir.as_ref().to_path_buf(),
cache: nodes,
seed_compute: Mutex::new(seed_compute),
block_number: block_number,
}
}
static CHARS: &'static [u8] = b"0123456789abcdef";
static CHARS: &'static[u8] = b"0123456789abcdef";
fn to_hex(bytes: &[u8]) -> String {
let mut v = Vec::with_capacity(bytes.len() * 2);
for &byte in bytes.iter() {
@@ -387,7 +329,9 @@ fn to_hex(bytes: &[u8]) -> String {
v.push(CHARS[(byte & 0xf) as usize]);
}
unsafe { String::from_utf8_unchecked(v) }
unsafe {
String::from_utf8_unchecked(v)
}
}
#[test]
@@ -417,8 +361,8 @@ fn test_get_data_size() {
#[test]
fn test_difficulty_test() {
let hash = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72];
let mix_hash = [0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d];
let hash = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72];
let mix_hash = [0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d ];
let nonce = 0xd7b3ac70a301a249;
let boundary_good = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84];
assert_eq!(quick_get_difficulty(&hash, nonce, &mix_hash)[..], boundary_good[..]);
@@ -428,60 +372,13 @@ fn test_difficulty_test() {
#[test]
fn test_light_compute() {
let hash = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72];
let mix_hash = [0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d];
let hash = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72];
let mix_hash = [0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d ];
let boundary = [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0xe9, 0x7e, 0x53, 0x84];
let nonce = 0xd7b3ac70a301a249;
// difficulty = 0x085657254bd9u64;
let light = Light::new(&::std::env::temp_dir(), 486382);
let light = Light::new(486382);
let result = light_compute(&light, &hash, nonce);
assert_eq!(result.mix_hash[..], mix_hash[..]);
assert_eq!(result.value[..], boundary[..]);
}
#[test]
fn test_seed_compute_once() {
let seed_compute = SeedHashCompute::new();
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162];
assert_eq!(seed_compute.get_seedhash(486382), hash);
}
#[test]
fn test_seed_compute_zero() {
let seed_compute = SeedHashCompute::new();
assert_eq!(seed_compute.get_seedhash(0), [0u8; 32]);
}
#[test]
fn test_seed_compute_after_older() {
let seed_compute = SeedHashCompute::new();
// calculating an older value first shouldn't affect the result
let _ = seed_compute.get_seedhash(50000);
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162];
assert_eq!(seed_compute.get_seedhash(486382), hash);
}
#[test]
fn test_seed_compute_after_newer() {
let seed_compute = SeedHashCompute::new();
// calculating an newer value first shouldn't affect the result
let _ = seed_compute.get_seedhash(972764);
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162];
assert_eq!(seed_compute.get_seedhash(486382), hash);
}
#[test]
fn test_drop_old_data() {
let path = ::std::env::temp_dir();
let first = Light::new(&path, 0).to_file().unwrap();
let second = Light::new(&path, ETHASH_EPOCH_LENGTH).to_file().unwrap();
assert!(fs::metadata(&first).is_ok());
let _ = Light::new(&path, ETHASH_EPOCH_LENGTH * 2).to_file();
assert!(fs::metadata(&first).is_err());
assert!(fs::metadata(&second).is_ok());
let _ = Light::new(&path, ETHASH_EPOCH_LENGTH * 3).to_file();
assert!(fs::metadata(&second).is_err());
}

View File

@@ -1,4 +1,4 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
@@ -18,19 +18,15 @@
//! See https://github.com/ethereum/wiki/wiki/Ethash
extern crate primal;
extern crate sha3;
extern crate parking_lot;
#[macro_use]
extern crate log;
mod compute;
use std::mem;
use std::path::{Path, PathBuf};
use compute::Light;
pub use compute::{ETHASH_EPOCH_LENGTH, H256, ProofOfWork, SeedHashCompute, quick_get_difficulty, slow_get_seedhash};
pub use compute::{get_seedhash, quick_get_difficulty, H256, ProofOfWork, ETHASH_EPOCH_LENGTH};
use std::sync::Arc;
use parking_lot::Mutex;
use std::sync::{Arc, Mutex};
struct LightCache {
recent_epoch: Option<u64>,
@@ -42,14 +38,12 @@ struct LightCache {
/// Light/Full cache manager.
pub struct EthashManager {
cache: Mutex<LightCache>,
cache_dir: PathBuf,
}
impl EthashManager {
/// Create a new new instance of ethash manager
pub fn new<T: AsRef<Path>>(cache_dir: T) -> EthashManager {
pub fn new() -> EthashManager {
EthashManager {
cache_dir: cache_dir.as_ref().to_path_buf(),
cache: Mutex::new(LightCache {
recent_epoch: None,
recent: None,
@@ -67,35 +61,30 @@ impl EthashManager {
pub fn compute_light(&self, block_number: u64, header_hash: &H256, nonce: u64) -> ProofOfWork {
let epoch = block_number / ETHASH_EPOCH_LENGTH;
let light = {
let mut lights = self.cache.lock();
let mut lights = self.cache.lock().unwrap();
let light = match lights.recent_epoch.clone() {
Some(ref e) if *e == epoch => lights.recent.clone(),
_ => match lights.prev_epoch.clone() {
Some(e) if e == epoch => {
// don't swap if recent is newer.
if lights.recent_epoch > lights.prev_epoch {
None
} else {
// swap
let t = lights.prev_epoch;
lights.prev_epoch = lights.recent_epoch;
lights.recent_epoch = t;
let t = lights.prev.clone();
lights.prev = lights.recent.clone();
lights.recent = t;
lights.recent.clone()
}
// swap
let t = lights.prev_epoch;
lights.prev_epoch = lights.recent_epoch;
lights.recent_epoch = t;
let t = lights.prev.clone();
lights.prev = lights.recent.clone();
lights.recent = t;
lights.recent.clone()
}
_ => None,
},
}
};
match light {
None => {
let light = match Light::from_file(&self.cache_dir, block_number) {
let light = match Light::from_file(block_number) {
Ok(light) => Arc::new(light),
Err(e) => {
debug!("Light cache file not found for {}:{}", block_number, e);
let light = Light::new(&self.cache_dir, block_number);
let light = Light::new(block_number);
if let Err(e) = light.to_file() {
warn!("Light cache file write error: {}", e);
}
@@ -106,7 +95,7 @@ impl EthashManager {
lights.prev = mem::replace(&mut lights.recent, Some(light.clone()));
light
}
Some(light) => light,
Some(light) => light
}
};
light.compute(header_hash, nonce)
@@ -115,16 +104,16 @@ impl EthashManager {
#[test]
fn test_lru() {
let ethash = EthashManager::new(&::std::env::temp_dir());
let ethash = EthashManager::new();
let hash = [0u8; 32];
ethash.compute_light(1, &hash, 1);
ethash.compute_light(50000, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 1);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 1);
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 0);
ethash.compute_light(1, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 1);
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 1);
ethash.compute_light(70000, &hash, 1);
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2);
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 2);
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 0);
}

View File

@@ -1,70 +1,32 @@
[package]
description = "Ethcore library"
homepage = "http://parity.io"
homepage = "http://ethcore.io"
license = "GPL-3.0"
name = "ethcore"
version = "1.7.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"
[build-dependencies]
"ethcore-ipc-codegen" = { path = "../ipc/codegen" }
version = "1.0.2"
authors = ["Ethcore <admin@ethcore.io>"]
[dependencies]
bit-set = "0.4"
bloomchain = "0.1"
bn = { git = "https://github.com/paritytech/bn" }
byteorder = "1.0"
clippy = { version = "0.0.103", optional = true}
common-types = { path = "types" }
crossbeam = "0.2.9"
env_logger = "0.4"
ethabi = "2.0"
ethash = { path = "../ethash" }
ethcore-bloom-journal = { path = "../util/bloom" }
ethcore-devtools = { path = "../devtools" }
ethcore-io = { path = "../util/io" }
ethcore-ipc = { path = "../ipc/rpc" }
ethcore-ipc-nano = { path = "../ipc/nano" }
ethcore-logger = { path = "../logger" }
ethcore-stratum = { path = "../stratum" }
ethcore-util = { path = "../util" }
ethjson = { path = "../json" }
ethkey = { path = "../ethkey" }
ethstore = { path = "../ethstore" }
evm = { path = "evm" }
futures = "0.1"
hardware-wallet = { path = "../hw" }
hyper = { git = "https://github.com/paritytech/hyper", default-features = false }
itertools = "0.5"
lazy_static = "0.2"
linked-hash-map = "0.3.0"
log = "0.3"
lru-cache = "0.1.0"
native-contracts = { path = "native_contracts" }
num = "0.1"
num_cpus = "1.2"
rand = "0.3"
rlp = { path = "../util/rlp" }
rust-crypto = "0.2.34"
rustc-hex = "1.0"
env_logger = "0.3"
rustc-serialize = "0.3"
semver = "0.6"
stats = { path = "../util/stats" }
heapsize = "0.3"
rust-crypto = "0.2.34"
time = "0.1"
transient-hashmap = "0.4"
[dev-dependencies]
native-contracts = { path = "native_contracts", features = ["test_contracts"] }
ethcore-util = { path = "../util" }
evmjit = { path = "../evmjit", optional = true }
ethash = { path = "../ethash" }
num_cpus = "0.2"
clippy = { version = "0.0.54", optional = true }
crossbeam = "0.1.5"
lazy_static = "0.1"
ethcore-devtools = { path = "../devtools" }
ethjson = { path = "../json" }
[features]
jit = ["evm/jit"]
evm-debug = ["slow-blocks"]
evm-debug-tests = ["evm-debug"]
slow-blocks = [] # Use SLOW_TX_DURATION="50" (compile time!) to track transactions over 50ms
jit = ["evmjit"]
evm-debug = []
json-tests = []
test-heavy = []
dev = ["clippy"]
default = []
benches = []
ipc = []

View File

@@ -1,23 +0,0 @@
// 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/>.
extern crate ethcore_ipc_codegen;
fn main() {
ethcore_ipc_codegen::derive_ipc_cond("src/client/traits.rs", cfg!(feature="ipc")).unwrap();
ethcore_ipc_codegen::derive_ipc_cond("src/snapshot/snapshot_service_trait.rs", cfg!(feature="ipc")).unwrap();
ethcore_ipc_codegen::derive_ipc_cond("src/client/chain_notify.rs", cfg!(feature="ipc")).unwrap();
}

View File

@@ -1,23 +0,0 @@
[package]
name = "evm"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
bit-set = "0.4"
byteorder = "1.0"
common-types = { path = "../types" }
ethcore-util = { path = "../../util" }
evmjit = { path = "../../evmjit", optional = true }
ethjson = { path = "../../json" }
lazy_static = "0.2"
log = "0.3"
rlp = { path = "../../util/rlp" }
parity-wasm = "0.12"
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
[dev-dependencies]
rustc-hex = "1.0"
[features]
jit = ["evmjit"]

View File

@@ -1,127 +0,0 @@
// 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/>.
//! benchmarking for EVM
//! should be started with:
//! ```bash
//! multirust run nightly cargo bench
//! ```
extern crate test;
use self::test::{Bencher, black_box};
use util::*;
use evm::action_params::ActionParams;
use evm::{self, Factory, VMType};
use evm::tests::FakeExt;
#[bench]
fn simple_loop_log0_usize(b: &mut Bencher) {
simple_loop_log0(U256::from(::std::usize::MAX), b)
}
#[bench]
fn simple_loop_log0_u256(b: &mut Bencher) {
simple_loop_log0(!U256::zero(), b)
}
fn simple_loop_log0(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = black_box(
"62ffffff5b600190036000600fa0600357".from_hex().unwrap()
);
b.iter(|| {
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
#[bench]
fn mem_gas_calculation_same_usize(b: &mut Bencher) {
mem_gas_calculation_same(U256::from(::std::usize::MAX), b)
}
#[bench]
fn mem_gas_calculation_same_u256(b: &mut Bencher) {
mem_gas_calculation_same(!U256::zero(), b)
}
fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
b.iter(|| {
let code = black_box(
"6110006001556001546000555b610fff805560016000540380600055600c57".from_hex().unwrap()
);
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
#[bench]
fn mem_gas_calculation_increasing_usize(b: &mut Bencher) {
mem_gas_calculation_increasing(U256::from(::std::usize::MAX), b)
}
#[bench]
fn mem_gas_calculation_increasing_u256(b: &mut Bencher) {
mem_gas_calculation_increasing(!U256::zero(), b)
}
fn mem_gas_calculation_increasing(gas: U256, b: &mut Bencher) {
let mut vm = Factory::new(VMType::Interpreter).create(gas);
let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
b.iter(|| {
let code = black_box(
"6110006001556001546000555b610fff60005401805560016000540380600055600c57".from_hex().unwrap()
);
let mut params = ActionParams::default();
params.address = address.clone();
params.gas = gas;
params.code = Some(code.clone());
result(vm.exec(params, &mut ext))
});
}
fn result(r: evm::Result<evm::GasLeft>) -> U256 {
match r {
Ok(evm::GasLeft::Known(v)) => v,
Ok(evm::GasLeft::NeedsReturn(v, _)) => v,
_ => U256::zero(),
}
}

View File

@@ -1,70 +0,0 @@
//! EVM call types.
use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp};
/// The type of the call-like instruction.
#[derive(Debug, PartialEq, Clone)]
pub enum CallType {
/// Not a CALL.
None,
/// CALL.
Call,
/// CALLCODE.
CallCode,
/// DELEGATECALL.
DelegateCall,
/// STATICCALL
StaticCall,
}
impl Encodable for CallType {
fn rlp_append(&self, s: &mut RlpStream) {
let v = match *self {
CallType::None => 0u32,
CallType::Call => 1,
CallType::CallCode => 2,
CallType::DelegateCall => 3,
CallType::StaticCall => 4,
};
Encodable::rlp_append(&v, s);
}
}
impl Decodable for CallType {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.as_val().and_then(|v| Ok(match v {
0u32 => CallType::None,
1 => CallType::Call,
2 => CallType::CallCode,
3 => CallType::DelegateCall,
4 => CallType::StaticCall,
_ => return Err(DecoderError::Custom("Invalid value of CallType item")),
}))
}
}
#[cfg(test)]
mod tests {
use rlp::*;
use super::CallType;
#[test]
fn encode_call_type() {
let ct = CallType::Call;
let mut s = RlpStream::new_list(2);
s.append(&ct);
assert!(!s.is_finished(), "List shouldn't finished yet");
s.append(&ct);
assert!(s.is_finished(), "List should be finished now");
s.out();
}
#[test]
fn should_encode_and_decode_call_type() {
let original = CallType::Call;
let encoded = encode(&original);
let decoded = decode(&encoded);
assert_eq!(original, decoded);
}
}

View File

@@ -1,344 +0,0 @@
// 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/>.
//! Evm interface.
use std::{ops, cmp, fmt};
use util::{U128, U256, U512, trie};
use action_params::ActionParams;
use {Ext};
use super::wasm;
/// Evm errors.
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas.
/// The state should be reverted to the state from before the
/// transaction execution. But it does not mean that transaction
/// was invalid. Balance still should be transfered and nonce
/// should be increased.
OutOfGas,
/// `BadJumpDestination` is returned when execution tried to move
/// to position that wasn't marked with JUMPDEST instruction
BadJumpDestination {
/// Position the code tried to jump to.
destination: usize
},
/// `BadInstructions` is returned when given instruction is not supported
BadInstruction {
/// Unrecognized opcode
instruction: u8,
},
/// `StackUnderflow` when there is not enough stack elements to execute instruction
StackUnderflow {
/// Invoked instruction
instruction: &'static str,
/// How many stack elements was requested by instruction
wanted: usize,
/// How many elements were on stack
on_stack: usize
},
/// When execution would exceed defined Stack Limit
OutOfStack {
/// Invoked instruction
instruction: &'static str,
/// How many stack elements instruction wanted to push
wanted: usize,
/// What was the stack limit
limit: usize
},
/// Built-in contract failed on given input
BuiltIn(&'static str),
/// When execution tries to modify the state in static context
MutableCallInStaticContext,
/// Likely to cause consensus issues.
Internal(String),
/// Wasm runtime error
Wasm(String),
}
impl From<Box<trie::TrieError>> for Error {
fn from(err: Box<trie::TrieError>) -> Self {
Error::Internal(format!("Internal error: {}", err))
}
}
impl From<wasm::RuntimeError> for Error {
fn from(err: wasm::RuntimeError) -> Self {
Error::Wasm(format!("Runtime error: {:?}", err))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match *self {
OutOfGas => write!(f, "Out of gas"),
BadJumpDestination { destination } => write!(f, "Bad jump destination {:x}", destination),
BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction),
StackUnderflow { instruction, wanted, on_stack } => write!(f, "Stack underflow {} {}/{}", instruction, wanted, on_stack),
OutOfStack { instruction, wanted, limit } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
Internal(ref msg) => write!(f, "Internal error: {}", msg),
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
Wasm(ref msg) => write!(f, "Internal error: {}", msg),
}
}
}
/// A specialized version of Result over EVM errors.
pub type Result<T> = ::std::result::Result<T, Error>;
/// Return data buffer. Holds memory from a previous call and a slice into that memory.
#[derive(Debug)]
pub struct ReturnData {
mem: Vec<u8>,
offset: usize,
size: usize,
}
impl ::std::ops::Deref for ReturnData {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.mem[self.offset..self.offset + self.size]
}
}
impl ReturnData {
/// Create empty `ReturnData`.
pub fn empty() -> Self {
ReturnData {
mem: Vec::new(),
offset: 0,
size: 0,
}
}
/// Create `ReturnData` from give buffer and slice.
pub fn new(mem: Vec<u8>, offset: usize, size: usize) -> Self {
ReturnData {
mem: mem,
offset: offset,
size: size,
}
}
}
/// Gas Left: either it is a known value, or it needs to be computed by processing
/// a return instruction.
#[derive(Debug)]
pub enum GasLeft {
/// Known gas left
Known(U256),
/// Return or Revert instruction must be processed.
NeedsReturn {
/// Amount of gas left.
gas_left: U256,
/// Return data buffer.
data: ReturnData,
/// Apply or revert state changes on revert.
apply_state: bool
},
}
/// Finalization result. Gas Left: either it is a known value, or it needs to be computed by processing
/// a return instruction.
#[derive(Debug)]
pub struct FinalizationResult {
/// Final amount of gas left.
pub gas_left: U256,
/// Apply execution state changes or revert them.
pub apply_state: bool,
/// Return data buffer.
pub return_data: ReturnData,
}
/// Types that can be "finalized" using an EVM.
///
/// In practice, this is just used to define an inherent impl on
/// `Reult<GasLeft<'a>>`.
pub trait Finalize {
/// Consume the externalities, call return if necessary, and produce call result.
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult>;
}
impl Finalize for Result<GasLeft> {
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
match self {
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true, return_data: ReturnData::empty() }),
Ok(GasLeft::NeedsReturn {gas_left, data, apply_state}) => ext.ret(&gas_left, &data).map(|gas_left| FinalizationResult {
gas_left: gas_left,
apply_state: apply_state,
return_data: data,
}),
Err(err) => Err(err),
}
}
}
/// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
pub trait CostType: Sized + From<usize> + Copy
+ ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> +ops::Sub<Output=Self>
+ ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self>
+ cmp::Ord + fmt::Debug {
/// Converts this cost into `U256`
fn as_u256(&self) -> U256;
/// Tries to fit `U256` into this `Cost` type
fn from_u256(val: U256) -> Result<Self>;
/// Convert to usize (may panic)
fn as_usize(&self) -> usize;
/// Add with overflow
fn overflow_add(self, other: Self) -> (Self, bool);
/// Multiple with overflow
fn overflow_mul(self, other: Self) -> (Self, bool);
/// Single-step full multiplication and shift: `(self*other) >> shr`
/// Should not overflow on intermediate steps
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool);
}
impl CostType for U256 {
fn as_u256(&self) -> U256 {
*self
}
fn from_u256(val: U256) -> Result<Self> {
Ok(val)
}
fn as_usize(&self) -> usize {
self.as_u64() as usize
}
fn overflow_add(self, other: Self) -> (Self, bool) {
self.overflowing_add(other)
}
fn overflow_mul(self, other: Self) -> (Self, bool) {
self.overflowing_mul(other)
}
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
let x = self.full_mul(other);
let U512(parts) = x;
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
let U512(parts) = x >> shr;
(
U256([parts[0], parts[1], parts[2], parts[3]]),
overflow
)
}
}
impl CostType for usize {
fn as_u256(&self) -> U256 {
U256::from(*self)
}
fn from_u256(val: U256) -> Result<Self> {
let res = val.low_u64() as usize;
// validate if value fits into usize
if U256::from(res) != val {
return Err(Error::OutOfGas);
}
Ok(res)
}
fn as_usize(&self) -> usize {
*self
}
fn overflow_add(self, other: Self) -> (Self, bool) {
self.overflowing_add(other)
}
fn overflow_mul(self, other: Self) -> (Self, bool) {
self.overflowing_mul(other)
}
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
let (c, o) = U128::from(self).overflowing_mul(U128::from(other));
let U128(parts) = c;
let overflow = o | (parts[1] > 0);
let U128(parts) = c >> shr;
let result = parts[0] as usize;
let overflow = overflow | (parts[0] > result as u64);
(result, overflow)
}
}
/// Evm interface
pub trait Evm {
/// This function should be used to execute transaction.
///
/// It returns either an error, a known amount of gas left, or parameters to be used
/// to compute the final gas left.
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
}
#[cfg(test)]
mod tests {
use util::U256;
use super::CostType;
#[test]
fn should_calculate_overflow_mul_shr_without_overflow() {
// given
let num = 1048576;
// when
let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
let (res2, o2) = num.overflow_mul_shr(num, 20);
// then
assert_eq!(res1, U256::from(num));
assert!(!o1);
assert_eq!(res2, num);
assert!(!o2);
}
#[test]
fn should_calculate_overflow_mul_shr_with_overflow() {
// given
let max = u64::max_value();
let num1 = U256([max, max, max, max]);
let num2 = usize::max_value();
// when
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
let (res2, o2) = num2.overflow_mul_shr(num2, 64);
// then
assert_eq!(res2, num2 - 1);
assert!(o2);
assert_eq!(res1, !U256::zero() - U256::one());
assert!(o1);
}
#[test]
fn should_validate_u256_to_usize_conversion() {
// given
let v = U256::from(usize::max_value()) + U256::from(1);
// when
let res = usize::from_u256(v);
// then
assert!(res.is_err());
}
}

View File

@@ -1,592 +0,0 @@
// 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/>.
//! VM Instructions list and utility functions
pub type Instruction = u8;
/// Returns true if given instruction is `PUSHN` instruction.
pub fn is_push(i: Instruction) -> bool {
i >= PUSH1 && i <= PUSH32
}
#[test]
fn test_is_push() {
assert!(is_push(PUSH1));
assert!(is_push(PUSH32));
assert!(!is_push(DUP1));
}
/// Returns number of bytes to read for `PUSHN` instruction
/// PUSH1 -> 1
pub fn get_push_bytes(i: Instruction) -> usize {
assert!(is_push(i), "Only for PUSH instructions.");
(i - PUSH1 + 1) as usize
}
/// Returns number of bytes to read for `PUSHN` instruction or 0.
pub fn push_bytes(i: Instruction) -> usize {
if is_push(i) {
get_push_bytes(i)
} else {
0
}
}
#[test]
fn test_get_push_bytes() {
assert_eq!(get_push_bytes(PUSH1), 1);
assert_eq!(get_push_bytes(PUSH3), 3);
assert_eq!(get_push_bytes(PUSH32), 32);
}
/// Returns stack position of item to duplicate
/// DUP1 -> 0
pub fn get_dup_position(i: Instruction) -> usize {
assert!(i >= DUP1 && i <= DUP16);
(i - DUP1) as usize
}
#[test]
fn test_get_dup_position() {
assert_eq!(get_dup_position(DUP1), 0);
assert_eq!(get_dup_position(DUP5), 4);
assert_eq!(get_dup_position(DUP10), 9);
}
/// Returns stack position of item to SWAP top with
/// SWAP1 -> 1
pub fn get_swap_position(i: Instruction) -> usize {
assert!(i >= SWAP1 && i <= SWAP16);
(i - SWAP1 + 1) as usize
}
#[test]
fn test_get_swap_position() {
assert_eq!(get_swap_position(SWAP1), 1);
assert_eq!(get_swap_position(SWAP5), 5);
assert_eq!(get_swap_position(SWAP10), 10);
}
/// Returns number of topcis to take from stack
/// LOG0 -> 0
pub fn get_log_topics (i: Instruction) -> usize {
assert!(i >= LOG0 && i <= LOG4);
(i - LOG0) as usize
}
#[test]
fn test_get_log_topics() {
assert_eq!(get_log_topics(LOG0), 0);
assert_eq!(get_log_topics(LOG2), 2);
assert_eq!(get_log_topics(LOG4), 4);
}
#[derive(PartialEq, Clone, Copy)]
pub enum GasPriceTier {
/// 0 Zero
Zero,
/// 2 Quick
Base,
/// 3 Fastest
VeryLow,
/// 5 Fast
Low,
/// 8 Mid
Mid,
/// 10 Slow
High,
/// 20 Ext
Ext,
/// Multiparam or otherwise special
Special,
/// Invalid
Invalid
}
impl Default for GasPriceTier {
fn default() -> Self {
GasPriceTier::Invalid
}
}
/// Returns the index in schedule for specific `GasPriceTier`
pub fn get_tier_idx (tier: GasPriceTier) -> usize {
match tier {
GasPriceTier::Zero => 0,
GasPriceTier::Base => 1,
GasPriceTier::VeryLow => 2,
GasPriceTier::Low => 3,
GasPriceTier::Mid => 4,
GasPriceTier::High => 5,
GasPriceTier::Ext => 6,
GasPriceTier::Special => 7,
GasPriceTier::Invalid => 8
}
}
/// EVM instruction information.
#[derive(Copy, Clone, Default)]
pub struct InstructionInfo {
/// Mnemonic name.
pub name: &'static str,
/// Number of stack arguments.
pub args: usize,
/// Number of returned stack items.
pub ret: usize,
/// Gas price tier.
pub tier: GasPriceTier
}
impl InstructionInfo {
/// Create new instruction info.
pub fn new(name: &'static str, args: usize, ret: usize, tier: GasPriceTier) -> Self {
InstructionInfo {
name: name,
args: args,
ret: ret,
tier: tier
}
}
}
lazy_static! {
/// Static instruction table.
pub static ref INSTRUCTIONS: [InstructionInfo; 0x100] = {
let mut arr = [InstructionInfo::default(); 0x100];
arr[STOP as usize] = InstructionInfo::new("STOP", 0, 0, GasPriceTier::Zero);
arr[ADD as usize] = InstructionInfo::new("ADD", 2, 1, GasPriceTier::VeryLow);
arr[SUB as usize] = InstructionInfo::new("SUB", 2, 1, GasPriceTier::VeryLow);
arr[MUL as usize] = InstructionInfo::new("MUL", 2, 1, GasPriceTier::Low);
arr[DIV as usize] = InstructionInfo::new("DIV", 2, 1, GasPriceTier::Low);
arr[SDIV as usize] = InstructionInfo::new("SDIV", 2, 1, GasPriceTier::Low);
arr[MOD as usize] = InstructionInfo::new("MOD", 2, 1, GasPriceTier::Low);
arr[SMOD as usize] = InstructionInfo::new("SMOD", 2, 1, GasPriceTier::Low);
arr[EXP as usize] = InstructionInfo::new("EXP", 2, 1, GasPriceTier::Special);
arr[NOT as usize] = InstructionInfo::new("NOT", 1, 1, GasPriceTier::VeryLow);
arr[LT as usize] = InstructionInfo::new("LT", 2, 1, GasPriceTier::VeryLow);
arr[GT as usize] = InstructionInfo::new("GT", 2, 1, GasPriceTier::VeryLow);
arr[SLT as usize] = InstructionInfo::new("SLT", 2, 1, GasPriceTier::VeryLow);
arr[SGT as usize] = InstructionInfo::new("SGT", 2, 1, GasPriceTier::VeryLow);
arr[EQ as usize] = InstructionInfo::new("EQ", 2, 1, GasPriceTier::VeryLow);
arr[ISZERO as usize] = InstructionInfo::new("ISZERO", 1, 1, GasPriceTier::VeryLow);
arr[AND as usize] = InstructionInfo::new("AND", 2, 1, GasPriceTier::VeryLow);
arr[OR as usize] = InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow);
arr[XOR as usize] = InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow);
arr[BYTE as usize] = InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow);
arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid);
arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid);
arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low);
arr[RETURNDATASIZE as usize] = InstructionInfo::new("RETURNDATASIZE", 0, 1, GasPriceTier::Base);
arr[RETURNDATACOPY as usize] = InstructionInfo::new("RETURNDATACOPY", 3, 0, GasPriceTier::VeryLow);
arr[SHA3 as usize] = InstructionInfo::new("SHA3", 2, 1, GasPriceTier::Special);
arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 1, GasPriceTier::Base);
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 1, 1, GasPriceTier::Special);
arr[ORIGIN as usize] = InstructionInfo::new("ORIGIN", 0, 1, GasPriceTier::Base);
arr[CALLER as usize] = InstructionInfo::new("CALLER", 0, 1, GasPriceTier::Base);
arr[CALLVALUE as usize] = InstructionInfo::new("CALLVALUE", 0, 1, GasPriceTier::Base);
arr[CALLDATALOAD as usize] = InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow);
arr[CALLDATASIZE as usize] = InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base);
arr[CALLDATACOPY as usize] = InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow);
arr[CODESIZE as usize] = InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base);
arr[CODECOPY as usize] = InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow);
arr[GASPRICE as usize] = InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base);
arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 1, 1, GasPriceTier::Special);
arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 4, 0, GasPriceTier::Special);
arr[BLOCKHASH as usize] = InstructionInfo::new("BLOCKHASH", 1, 1, GasPriceTier::Ext);
arr[COINBASE as usize] = InstructionInfo::new("COINBASE", 0, 1, GasPriceTier::Base);
arr[TIMESTAMP as usize] = InstructionInfo::new("TIMESTAMP", 0, 1, GasPriceTier::Base);
arr[NUMBER as usize] = InstructionInfo::new("NUMBER", 0, 1, GasPriceTier::Base);
arr[DIFFICULTY as usize] = InstructionInfo::new("DIFFICULTY", 0, 1, GasPriceTier::Base);
arr[GASLIMIT as usize] = InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base);
arr[POP as usize] = InstructionInfo::new("POP", 1, 0, GasPriceTier::Base);
arr[MLOAD as usize] = InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow);
arr[MSTORE as usize] = InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow);
arr[MSTORE8 as usize] = InstructionInfo::new("MSTORE8", 2, 0, GasPriceTier::VeryLow);
arr[SLOAD as usize] = InstructionInfo::new("SLOAD", 1, 1, GasPriceTier::Special);
arr[SSTORE as usize] = InstructionInfo::new("SSTORE", 2, 0, GasPriceTier::Special);
arr[JUMP as usize] = InstructionInfo::new("JUMP", 1, 0, GasPriceTier::Mid);
arr[JUMPI as usize] = InstructionInfo::new("JUMPI", 2, 0, GasPriceTier::High);
arr[PC as usize] = InstructionInfo::new("PC", 0, 1, GasPriceTier::Base);
arr[MSIZE as usize] = InstructionInfo::new("MSIZE", 0, 1, GasPriceTier::Base);
arr[GAS as usize] = InstructionInfo::new("GAS", 0, 1, GasPriceTier::Base);
arr[JUMPDEST as usize] = InstructionInfo::new("JUMPDEST", 0, 0, GasPriceTier::Special);
arr[PUSH1 as usize] = InstructionInfo::new("PUSH1", 0, 1, GasPriceTier::VeryLow);
arr[PUSH2 as usize] = InstructionInfo::new("PUSH2", 0, 1, GasPriceTier::VeryLow);
arr[PUSH3 as usize] = InstructionInfo::new("PUSH3", 0, 1, GasPriceTier::VeryLow);
arr[PUSH4 as usize] = InstructionInfo::new("PUSH4", 0, 1, GasPriceTier::VeryLow);
arr[PUSH5 as usize] = InstructionInfo::new("PUSH5", 0, 1, GasPriceTier::VeryLow);
arr[PUSH6 as usize] = InstructionInfo::new("PUSH6", 0, 1, GasPriceTier::VeryLow);
arr[PUSH7 as usize] = InstructionInfo::new("PUSH7", 0, 1, GasPriceTier::VeryLow);
arr[PUSH8 as usize] = InstructionInfo::new("PUSH8", 0, 1, GasPriceTier::VeryLow);
arr[PUSH9 as usize] = InstructionInfo::new("PUSH9", 0, 1, GasPriceTier::VeryLow);
arr[PUSH10 as usize] = InstructionInfo::new("PUSH10", 0, 1, GasPriceTier::VeryLow);
arr[PUSH11 as usize] = InstructionInfo::new("PUSH11", 0, 1, GasPriceTier::VeryLow);
arr[PUSH12 as usize] = InstructionInfo::new("PUSH12", 0, 1, GasPriceTier::VeryLow);
arr[PUSH13 as usize] = InstructionInfo::new("PUSH13", 0, 1, GasPriceTier::VeryLow);
arr[PUSH14 as usize] = InstructionInfo::new("PUSH14", 0, 1, GasPriceTier::VeryLow);
arr[PUSH15 as usize] = InstructionInfo::new("PUSH15", 0, 1, GasPriceTier::VeryLow);
arr[PUSH16 as usize] = InstructionInfo::new("PUSH16", 0, 1, GasPriceTier::VeryLow);
arr[PUSH17 as usize] = InstructionInfo::new("PUSH17", 0, 1, GasPriceTier::VeryLow);
arr[PUSH18 as usize] = InstructionInfo::new("PUSH18", 0, 1, GasPriceTier::VeryLow);
arr[PUSH19 as usize] = InstructionInfo::new("PUSH19", 0, 1, GasPriceTier::VeryLow);
arr[PUSH20 as usize] = InstructionInfo::new("PUSH20", 0, 1, GasPriceTier::VeryLow);
arr[PUSH21 as usize] = InstructionInfo::new("PUSH21", 0, 1, GasPriceTier::VeryLow);
arr[PUSH22 as usize] = InstructionInfo::new("PUSH22", 0, 1, GasPriceTier::VeryLow);
arr[PUSH23 as usize] = InstructionInfo::new("PUSH23", 0, 1, GasPriceTier::VeryLow);
arr[PUSH24 as usize] = InstructionInfo::new("PUSH24", 0, 1, GasPriceTier::VeryLow);
arr[PUSH25 as usize] = InstructionInfo::new("PUSH25", 0, 1, GasPriceTier::VeryLow);
arr[PUSH26 as usize] = InstructionInfo::new("PUSH26", 0, 1, GasPriceTier::VeryLow);
arr[PUSH27 as usize] = InstructionInfo::new("PUSH27", 0, 1, GasPriceTier::VeryLow);
arr[PUSH28 as usize] = InstructionInfo::new("PUSH28", 0, 1, GasPriceTier::VeryLow);
arr[PUSH29 as usize] = InstructionInfo::new("PUSH29", 0, 1, GasPriceTier::VeryLow);
arr[PUSH30 as usize] = InstructionInfo::new("PUSH30", 0, 1, GasPriceTier::VeryLow);
arr[PUSH31 as usize] = InstructionInfo::new("PUSH31", 0, 1, GasPriceTier::VeryLow);
arr[PUSH32 as usize] = InstructionInfo::new("PUSH32", 0, 1, GasPriceTier::VeryLow);
arr[DUP1 as usize] = InstructionInfo::new("DUP1", 1, 2, GasPriceTier::VeryLow);
arr[DUP2 as usize] = InstructionInfo::new("DUP2", 2, 3, GasPriceTier::VeryLow);
arr[DUP3 as usize] = InstructionInfo::new("DUP3", 3, 4, GasPriceTier::VeryLow);
arr[DUP4 as usize] = InstructionInfo::new("DUP4", 4, 5, GasPriceTier::VeryLow);
arr[DUP5 as usize] = InstructionInfo::new("DUP5", 5, 6, GasPriceTier::VeryLow);
arr[DUP6 as usize] = InstructionInfo::new("DUP6", 6, 7, GasPriceTier::VeryLow);
arr[DUP7 as usize] = InstructionInfo::new("DUP7", 7, 8, GasPriceTier::VeryLow);
arr[DUP8 as usize] = InstructionInfo::new("DUP8", 8, 9, GasPriceTier::VeryLow);
arr[DUP9 as usize] = InstructionInfo::new("DUP9", 9, 10, GasPriceTier::VeryLow);
arr[DUP10 as usize] = InstructionInfo::new("DUP10", 10, 11, GasPriceTier::VeryLow);
arr[DUP11 as usize] = InstructionInfo::new("DUP11", 11, 12, GasPriceTier::VeryLow);
arr[DUP12 as usize] = InstructionInfo::new("DUP12", 12, 13, GasPriceTier::VeryLow);
arr[DUP13 as usize] = InstructionInfo::new("DUP13", 13, 14, GasPriceTier::VeryLow);
arr[DUP14 as usize] = InstructionInfo::new("DUP14", 14, 15, GasPriceTier::VeryLow);
arr[DUP15 as usize] = InstructionInfo::new("DUP15", 15, 16, GasPriceTier::VeryLow);
arr[DUP16 as usize] = InstructionInfo::new("DUP16", 16, 17, GasPriceTier::VeryLow);
arr[SWAP1 as usize] = InstructionInfo::new("SWAP1", 2, 2, GasPriceTier::VeryLow);
arr[SWAP2 as usize] = InstructionInfo::new("SWAP2", 3, 3, GasPriceTier::VeryLow);
arr[SWAP3 as usize] = InstructionInfo::new("SWAP3", 4, 4, GasPriceTier::VeryLow);
arr[SWAP4 as usize] = InstructionInfo::new("SWAP4", 5, 5, GasPriceTier::VeryLow);
arr[SWAP5 as usize] = InstructionInfo::new("SWAP5", 6, 6, GasPriceTier::VeryLow);
arr[SWAP6 as usize] = InstructionInfo::new("SWAP6", 7, 7, GasPriceTier::VeryLow);
arr[SWAP7 as usize] = InstructionInfo::new("SWAP7", 8, 8, GasPriceTier::VeryLow);
arr[SWAP8 as usize] = InstructionInfo::new("SWAP8", 9, 9, GasPriceTier::VeryLow);
arr[SWAP9 as usize] = InstructionInfo::new("SWAP9", 10, 10, GasPriceTier::VeryLow);
arr[SWAP10 as usize] = InstructionInfo::new("SWAP10", 11, 11, GasPriceTier::VeryLow);
arr[SWAP11 as usize] = InstructionInfo::new("SWAP11", 12, 12, GasPriceTier::VeryLow);
arr[SWAP12 as usize] = InstructionInfo::new("SWAP12", 13, 13, GasPriceTier::VeryLow);
arr[SWAP13 as usize] = InstructionInfo::new("SWAP13", 14, 14, GasPriceTier::VeryLow);
arr[SWAP14 as usize] = InstructionInfo::new("SWAP14", 15, 15, GasPriceTier::VeryLow);
arr[SWAP15 as usize] = InstructionInfo::new("SWAP15", 16, 16, GasPriceTier::VeryLow);
arr[SWAP16 as usize] = InstructionInfo::new("SWAP16", 17, 17, GasPriceTier::VeryLow);
arr[LOG0 as usize] = InstructionInfo::new("LOG0", 2, 0, GasPriceTier::Special);
arr[LOG1 as usize] = InstructionInfo::new("LOG1", 3, 0, GasPriceTier::Special);
arr[LOG2 as usize] = InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special);
arr[LOG3 as usize] = InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special);
arr[LOG4 as usize] = InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special);
arr[CREATE as usize] = InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special);
arr[CALL as usize] = InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special);
arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special);
arr[RETURN as usize] = InstructionInfo::new("RETURN", 2, 0, GasPriceTier::Zero);
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special);
arr[STATICCALL as usize] = InstructionInfo::new("STATICCALL", 6, 1, GasPriceTier::Special);
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special);
arr[CREATE2 as usize] = InstructionInfo::new("CREATE2", 3, 1, GasPriceTier::Special);
arr[REVERT as usize] = InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero);
arr
};
}
/// Virtual machine bytecode instruction.
/// halts execution
pub const STOP: Instruction = 0x00;
/// addition operation
pub const ADD: Instruction = 0x01;
/// mulitplication operation
pub const MUL: Instruction = 0x02;
/// subtraction operation
pub const SUB: Instruction = 0x03;
/// integer division operation
pub const DIV: Instruction = 0x04;
/// signed integer division operation
pub const SDIV: Instruction = 0x05;
/// modulo remainder operation
pub const MOD: Instruction = 0x06;
/// signed modulo remainder operation
pub const SMOD: Instruction = 0x07;
/// unsigned modular addition
pub const ADDMOD: Instruction = 0x08;
/// unsigned modular multiplication
pub const MULMOD: Instruction = 0x09;
/// exponential operation
pub const EXP: Instruction = 0x0a;
/// extend length of signed integer
pub const SIGNEXTEND: Instruction = 0x0b;
/// less-than comparision
pub const LT: Instruction = 0x10;
/// greater-than comparision
pub const GT: Instruction = 0x11;
/// signed less-than comparision
pub const SLT: Instruction = 0x12;
/// signed greater-than comparision
pub const SGT: Instruction = 0x13;
/// equality comparision
pub const EQ: Instruction = 0x14;
/// simple not operator
pub const ISZERO: Instruction = 0x15;
/// bitwise AND operation
pub const AND: Instruction = 0x16;
/// bitwise OR operation
pub const OR: Instruction = 0x17;
/// bitwise XOR operation
pub const XOR: Instruction = 0x18;
/// bitwise NOT opertation
pub const NOT: Instruction = 0x19;
/// retrieve single byte from word
pub const BYTE: Instruction = 0x1a;
/// compute SHA3-256 hash
pub const SHA3: Instruction = 0x20;
/// get address of currently executing account
pub const ADDRESS: Instruction = 0x30;
/// get balance of the given account
pub const BALANCE: Instruction = 0x31;
/// get execution origination address
pub const ORIGIN: Instruction = 0x32;
/// get caller address
pub const CALLER: Instruction = 0x33;
/// get deposited value by the instruction/transaction responsible for this execution
pub const CALLVALUE: Instruction = 0x34;
/// get input data of current environment
pub const CALLDATALOAD: Instruction = 0x35;
/// get size of input data in current environment
pub const CALLDATASIZE: Instruction = 0x36;
/// copy input data in current environment to memory
pub const CALLDATACOPY: Instruction = 0x37;
/// get size of code running in current environment
pub const CODESIZE: Instruction = 0x38;
/// copy code running in current environment to memory
pub const CODECOPY: Instruction = 0x39;
/// get price of gas in current environment
pub const GASPRICE: Instruction = 0x3a;
/// get external code size (from another contract)
pub const EXTCODESIZE: Instruction = 0x3b;
/// copy external code (from another contract)
pub const EXTCODECOPY: Instruction = 0x3c;
/// get the size of the return data buffer for the last call
pub const RETURNDATASIZE: Instruction = 0x3d;
/// copy return data buffer to memory
pub const RETURNDATACOPY: Instruction = 0x3e;
/// get hash of most recent complete block
pub const BLOCKHASH: Instruction = 0x40;
/// get the block's coinbase address
pub const COINBASE: Instruction = 0x41;
/// get the block's timestamp
pub const TIMESTAMP: Instruction = 0x42;
/// get the block's number
pub const NUMBER: Instruction = 0x43;
/// get the block's difficulty
pub const DIFFICULTY: Instruction = 0x44;
/// get the block's gas limit
pub const GASLIMIT: Instruction = 0x45;
/// remove item from stack
pub const POP: Instruction = 0x50;
/// load word from memory
pub const MLOAD: Instruction = 0x51;
/// save word to memory
pub const MSTORE: Instruction = 0x52;
/// save byte to memory
pub const MSTORE8: Instruction = 0x53;
/// load word from storage
pub const SLOAD: Instruction = 0x54;
/// save word to storage
pub const SSTORE: Instruction = 0x55;
/// alter the program counter
pub const JUMP: Instruction = 0x56;
/// conditionally alter the program counter
pub const JUMPI: Instruction = 0x57;
/// get the program counter
pub const PC: Instruction = 0x58;
/// get the size of active memory
pub const MSIZE: Instruction = 0x59;
/// get the amount of available gas
pub const GAS: Instruction = 0x5a;
/// set a potential jump destination
pub const JUMPDEST: Instruction = 0x5b;
/// place 1 byte item on stack
pub const PUSH1: Instruction = 0x60;
/// place 2 byte item on stack
pub const PUSH2: Instruction = 0x61;
/// place 3 byte item on stack
pub const PUSH3: Instruction = 0x62;
/// place 4 byte item on stack
pub const PUSH4: Instruction = 0x63;
/// place 5 byte item on stack
pub const PUSH5: Instruction = 0x64;
/// place 6 byte item on stack
pub const PUSH6: Instruction = 0x65;
/// place 7 byte item on stack
pub const PUSH7: Instruction = 0x66;
/// place 8 byte item on stack
pub const PUSH8: Instruction = 0x67;
/// place 9 byte item on stack
pub const PUSH9: Instruction = 0x68;
/// place 10 byte item on stack
pub const PUSH10: Instruction = 0x69;
/// place 11 byte item on stack
pub const PUSH11: Instruction = 0x6a;
/// place 12 byte item on stack
pub const PUSH12: Instruction = 0x6b;
/// place 13 byte item on stack
pub const PUSH13: Instruction = 0x6c;
/// place 14 byte item on stack
pub const PUSH14: Instruction = 0x6d;
/// place 15 byte item on stack
pub const PUSH15: Instruction = 0x6e;
/// place 16 byte item on stack
pub const PUSH16: Instruction = 0x6f;
/// place 17 byte item on stack
pub const PUSH17: Instruction = 0x70;
/// place 18 byte item on stack
pub const PUSH18: Instruction = 0x71;
/// place 19 byte item on stack
pub const PUSH19: Instruction = 0x72;
/// place 20 byte item on stack
pub const PUSH20: Instruction = 0x73;
/// place 21 byte item on stack
pub const PUSH21: Instruction = 0x74;
/// place 22 byte item on stack
pub const PUSH22: Instruction = 0x75;
/// place 23 byte item on stack
pub const PUSH23: Instruction = 0x76;
/// place 24 byte item on stack
pub const PUSH24: Instruction = 0x77;
/// place 25 byte item on stack
pub const PUSH25: Instruction = 0x78;
/// place 26 byte item on stack
pub const PUSH26: Instruction = 0x79;
/// place 27 byte item on stack
pub const PUSH27: Instruction = 0x7a;
/// place 28 byte item on stack
pub const PUSH28: Instruction = 0x7b;
/// place 29 byte item on stack
pub const PUSH29: Instruction = 0x7c;
/// place 30 byte item on stack
pub const PUSH30: Instruction = 0x7d;
/// place 31 byte item on stack
pub const PUSH31: Instruction = 0x7e;
/// place 32 byte item on stack
pub const PUSH32: Instruction = 0x7f;
/// copies the highest item in the stack to the top of the stack
pub const DUP1: Instruction = 0x80;
/// copies the second highest item in the stack to the top of the stack
pub const DUP2: Instruction = 0x81;
/// copies the third highest item in the stack to the top of the stack
pub const DUP3: Instruction = 0x82;
/// copies the 4th highest item in the stack to the top of the stack
pub const DUP4: Instruction = 0x83;
/// copies the 5th highest item in the stack to the top of the stack
pub const DUP5: Instruction = 0x84;
/// copies the 6th highest item in the stack to the top of the stack
pub const DUP6: Instruction = 0x85;
/// copies the 7th highest item in the stack to the top of the stack
pub const DUP7: Instruction = 0x86;
/// copies the 8th highest item in the stack to the top of the stack
pub const DUP8: Instruction = 0x87;
/// copies the 9th highest item in the stack to the top of the stack
pub const DUP9: Instruction = 0x88;
/// copies the 10th highest item in the stack to the top of the stack
pub const DUP10: Instruction = 0x89;
/// copies the 11th highest item in the stack to the top of the stack
pub const DUP11: Instruction = 0x8a;
/// copies the 12th highest item in the stack to the top of the stack
pub const DUP12: Instruction = 0x8b;
/// copies the 13th highest item in the stack to the top of the stack
pub const DUP13: Instruction = 0x8c;
/// copies the 14th highest item in the stack to the top of the stack
pub const DUP14: Instruction = 0x8d;
/// copies the 15th highest item in the stack to the top of the stack
pub const DUP15: Instruction = 0x8e;
/// copies the 16th highest item in the stack to the top of the stack
pub const DUP16: Instruction = 0x8f;
/// swaps the highest and second highest value on the stack
pub const SWAP1: Instruction = 0x90;
/// swaps the highest and third highest value on the stack
pub const SWAP2: Instruction = 0x91;
/// swaps the highest and 4th highest value on the stack
pub const SWAP3: Instruction = 0x92;
/// swaps the highest and 5th highest value on the stack
pub const SWAP4: Instruction = 0x93;
/// swaps the highest and 6th highest value on the stack
pub const SWAP5: Instruction = 0x94;
/// swaps the highest and 7th highest value on the stack
pub const SWAP6: Instruction = 0x95;
/// swaps the highest and 8th highest value on the stack
pub const SWAP7: Instruction = 0x96;
/// swaps the highest and 9th highest value on the stack
pub const SWAP8: Instruction = 0x97;
/// swaps the highest and 10th highest value on the stack
pub const SWAP9: Instruction = 0x98;
/// swaps the highest and 11th highest value on the stack
pub const SWAP10: Instruction = 0x99;
/// swaps the highest and 12th highest value on the stack
pub const SWAP11: Instruction = 0x9a;
/// swaps the highest and 13th highest value on the stack
pub const SWAP12: Instruction = 0x9b;
/// swaps the highest and 14th highest value on the stack
pub const SWAP13: Instruction = 0x9c;
/// swaps the highest and 15th highest value on the stack
pub const SWAP14: Instruction = 0x9d;
/// swaps the highest and 16th highest value on the stack
pub const SWAP15: Instruction = 0x9e;
/// swaps the highest and 17th highest value on the stack
pub const SWAP16: Instruction = 0x9f;
/// Makes a log entry; no topics.
pub const LOG0: Instruction = 0xa0;
/// Makes a log entry; 1 topic.
pub const LOG1: Instruction = 0xa1;
/// Makes a log entry; 2 topics.
pub const LOG2: Instruction = 0xa2;
/// Makes a log entry; 3 topics.
pub const LOG3: Instruction = 0xa3;
/// Makes a log entry; 4 topics.
pub const LOG4: Instruction = 0xa4;
/// Maximal number of topics for log instructions
pub const MAX_NO_OF_TOPICS : usize = 4;
/// create a new account with associated code
pub const CREATE: Instruction = 0xf0;
/// message-call into an account
pub const CALL: Instruction = 0xf1;
/// message-call with another account's code only
pub const CALLCODE: Instruction = 0xf2;
/// halt execution returning output data
pub const RETURN: Instruction = 0xf3;
/// like CALLCODE but keeps caller's value and sender
pub const DELEGATECALL: Instruction = 0xf4;
/// create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160
pub const CREATE2: Instruction = 0xfb;
/// stop execution and revert state changes. Return output data.
pub const REVERT: Instruction = 0xfd;
/// like CALL but it does not take value, nor modify the state
pub const STATICCALL: Instruction = 0xfa;
/// halt execution and register account for later deletion
pub const SUICIDE: Instruction = 0xff;

View File

@@ -1,373 +0,0 @@
// 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/>.
use util::*;
use super::u256_to_address;
use {evm, ext};
use instructions::{self, Instruction, InstructionInfo};
use interpreter::stack::Stack;
use schedule::Schedule;
macro_rules! overflowing {
($x: expr) => {{
let (v, overflow) = $x;
if overflow { return Err(evm::Error::OutOfGas); }
v
}}
}
#[cfg_attr(feature="dev", allow(enum_variant_names))]
enum Request<Cost: ::evm::CostType> {
Gas(Cost),
GasMem(Cost, Cost),
GasMemProvide(Cost, Cost, Option<U256>),
GasMemCopy(Cost, Cost, Cost)
}
pub struct InstructionRequirements<Cost> {
pub gas_cost: Cost,
pub provide_gas: Option<Cost>,
pub memory_total_gas: Cost,
pub memory_required_size: usize,
}
pub struct Gasometer<Gas> {
pub current_gas: Gas,
pub current_mem_gas: Gas,
}
impl<Gas: evm::CostType> Gasometer<Gas> {
pub fn new(current_gas: Gas) -> Self {
Gasometer {
current_gas: current_gas,
current_mem_gas: Gas::from(0),
}
}
pub fn verify_gas(&self, gas_cost: &Gas) -> evm::Result<()> {
match &self.current_gas < gas_cost {
true => Err(evm::Error::OutOfGas),
false => Ok(())
}
}
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
/// and that we `requested` some.
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<U256>) -> evm::Result<Gas> {
// Try converting requested gas to `Gas` (`U256/u64`)
// but in EIP150 even if we request more we should never fail from OOG
let requested = requested.map(Gas::from_u256);
match schedule.sub_gas_cap_divisor {
Some(cap_divisor) if self.current_gas >= needed => {
let gas_remaining = self.current_gas - needed;
let max_gas_provided = match cap_divisor {
64 => gas_remaining - (gas_remaining >> 6),
cap_divisor => gas_remaining - gas_remaining / Gas::from(cap_divisor),
};
if let Some(Ok(r)) = requested {
Ok(min(r, max_gas_provided))
} else {
Ok(max_gas_provided)
}
},
_ => {
if let Some(r) = requested {
r
} else if self.current_gas >= needed {
Ok(self.current_gas - needed)
} else {
Ok(0.into())
}
},
}
}
#[cfg_attr(feature="dev", allow(cyclomatic_complexity))]
/// Determine how much gas is used by the given instruction, given the machine's state.
///
/// We guarantee that the final element of the returned tuple (`provided`) will be `Some`
/// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case,
/// it will be the amount of gas that the current context provides to the child context.
pub fn requirements(
&mut self,
ext: &ext::Ext,
instruction: Instruction,
info: &InstructionInfo,
stack: &Stack<U256>,
current_mem_size: usize,
) -> evm::Result<InstructionRequirements<Gas>> {
let schedule = ext.schedule();
let tier = instructions::get_tier_idx(info.tier);
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
let cost = match instruction {
instructions::JUMPDEST => {
Request::Gas(Gas::from(1))
},
instructions::SSTORE => {
let address = H256::from(stack.peek(0));
let newval = stack.peek(1);
let val = U256::from(&*ext.storage_at(&address)?);
let gas = if val.is_zero() && !newval.is_zero() {
schedule.sstore_set_gas
} else {
// Refund for below case is added when actually executing sstore
// !is_zero(&val) && is_zero(newval)
schedule.sstore_reset_gas
};
Request::Gas(Gas::from(gas))
},
instructions::SLOAD => {
Request::Gas(Gas::from(schedule.sload_gas))
},
instructions::BALANCE => {
Request::Gas(Gas::from(schedule.balance_gas))
},
instructions::EXTCODESIZE => {
Request::Gas(Gas::from(schedule.extcodesize_gas))
},
instructions::SUICIDE => {
let mut gas = Gas::from(schedule.suicide_gas);
let is_value_transfer = !ext.origin_balance()?.is_zero();
let address = u256_to_address(stack.peek(0));
if (
!schedule.no_empty && !ext.exists(&address)?
) || (
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)?
) {
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
}
Request::Gas(gas)
},
instructions::MSTORE | instructions::MLOAD => {
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?)
},
instructions::MSTORE8 => {
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
},
instructions::RETURN | instructions::REVERT => {
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
},
instructions::SHA3 => {
let w = overflowing!(add_gas_usize(Gas::from_u256(*stack.peek(1))?, 31));
let words = w >> 5;
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
},
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => {
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?)
},
instructions::EXTCODECOPY => {
Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), mem_needed(stack.peek(1), stack.peek(3))?, Gas::from_u256(*stack.peek(3))?)
},
instructions::LOG0...instructions::LOG4 => {
let no_of_topics = instructions::get_log_topics(instruction);
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
let data_gas = overflowing!(Gas::from_u256(*stack.peek(1))?.overflow_mul(Gas::from(schedule.log_data_gas)));
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
},
instructions::CALL | instructions::CALLCODE => {
let mut gas = Gas::from(schedule.call_gas);
let mem = cmp::max(
mem_needed(stack.peek(5), stack.peek(6))?,
mem_needed(stack.peek(3), stack.peek(4))?
);
let address = u256_to_address(stack.peek(1));
let is_value_transfer = !stack.peek(2).is_zero();
if instruction == instructions::CALL && (
(!schedule.no_empty && !ext.exists(&address)?)
||
(schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)?)
) {
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
}
if is_value_transfer {
gas = overflowing!(gas.overflow_add(schedule.call_value_transfer_gas.into()));
}
let requested = *stack.peek(0);
Request::GasMemProvide(gas, mem, Some(requested))
},
instructions::DELEGATECALL | instructions::STATICCALL => {
let gas = Gas::from(schedule.call_gas);
let mem = cmp::max(
mem_needed(stack.peek(4), stack.peek(5))?,
mem_needed(stack.peek(2), stack.peek(3))?
);
let requested = *stack.peek(0);
Request::GasMemProvide(gas, mem, Some(requested))
},
instructions::CREATE | instructions::CREATE2 => {
let gas = Gas::from(schedule.create_gas);
let mem = mem_needed(stack.peek(1), stack.peek(2))?;
Request::GasMemProvide(gas, mem, None)
},
instructions::EXP => {
let expon = stack.peek(1);
let bytes = ((expon.bits() + 7) / 8) as usize;
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
Request::Gas(gas)
},
instructions::BLOCKHASH => {
Request::Gas(Gas::from(schedule.blockhash_gas))
},
_ => Request::Gas(default_gas),
};
Ok(match cost {
Request::Gas(gas) => {
InstructionRequirements {
gas_cost: gas,
provide_gas: None,
memory_required_size: 0,
memory_total_gas: self.current_mem_gas,
}
},
Request::GasMem(gas, mem_size) => {
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
InstructionRequirements {
gas_cost: gas,
provide_gas: None,
memory_required_size: new_mem_size,
memory_total_gas: new_mem_gas,
}
},
Request::GasMemProvide(gas, mem_size, requested) => {
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
let provided = self.gas_provided(schedule, gas, requested)?;
let total_gas = overflowing!(gas.overflow_add(provided));
InstructionRequirements {
gas_cost: total_gas,
provide_gas: Some(provided),
memory_required_size: new_mem_size,
memory_total_gas: new_mem_gas,
}
},
Request::GasMemCopy(gas, mem_size, copy) => {
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
let copy = overflowing!(add_gas_usize(copy, 31)) >> 5;
let copy_gas = Gas::from(schedule.copy_gas) * copy;
let gas = overflowing!(gas.overflow_add(copy_gas));
let gas = overflowing!(gas.overflow_add(mem_gas_cost));
InstructionRequirements {
gas_cost: gas,
provide_gas: None,
memory_required_size: new_mem_size,
memory_total_gas: new_mem_gas,
}
},
})
}
fn mem_gas_cost(&self, schedule: &Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, Gas, usize)> {
let gas_for_mem = |mem_size: Gas| {
let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
// Calculate s*s/quad_coeff_div
assert_eq!(schedule.quad_coeff_div, 512);
let b = overflowing!(s.overflow_mul_shr(s, 9));
Ok(overflowing!(a.overflow_add(b)))
};
let current_mem_size = Gas::from(current_mem_size);
let req_mem_size_rounded = (overflowing!(mem_size.overflow_add(Gas::from(31 as usize))) >> 5) << 5;
let (mem_gas_cost, new_mem_gas) = if req_mem_size_rounded > current_mem_size {
let new_mem_gas = gas_for_mem(req_mem_size_rounded)?;
(new_mem_gas - self.current_mem_gas, new_mem_gas)
} else {
(Gas::from(0), self.current_mem_gas)
};
Ok((mem_gas_cost, new_mem_gas, req_mem_size_rounded.as_usize()))
}
}
#[inline]
fn mem_needed_const<Gas: evm::CostType>(mem: &U256, add: usize) -> evm::Result<Gas> {
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
}
#[inline]
fn mem_needed<Gas: evm::CostType>(offset: &U256, size: &U256) -> evm::Result<Gas> {
if size.is_zero() {
return Ok(Gas::from(0));
}
Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
}
#[inline]
fn add_gas_usize<Gas: evm::CostType>(value: Gas, num: usize) -> (Gas, bool) {
value.overflow_add(Gas::from(num))
}
#[test]
fn test_mem_gas_cost() {
// given
let gasometer = Gasometer::<U256>::new(U256::zero());
let schedule = Schedule::default();
let current_mem_size = 5;
let mem_size = !U256::zero();
// when
let result = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size);
// then
if result.is_ok() {
assert!(false, "Should fail with OutOfGas");
}
}
#[test]
fn test_calculate_mem_cost() {
// given
let gasometer = Gasometer::<usize>::new(0);
let schedule = Schedule::default();
let current_mem_size = 0;
let mem_size = 5;
// when
let (mem_cost, new_mem_gas, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
// then
assert_eq!(mem_cost, 3);
assert_eq!(new_mem_gas, 3);
assert_eq!(mem_size, 32);
}

View File

@@ -1,164 +0,0 @@
// 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/>.
pub use self::inner::*;
#[macro_use]
#[cfg(not(feature = "evm-debug"))]
mod inner {
macro_rules! evm_debug {
($x: expr) => {}
}
pub struct EvmInformant;
impl EvmInformant {
pub fn new(_depth: usize) -> Self {
EvmInformant {}
}
pub fn done(&mut self) {}
}
}
#[macro_use]
#[cfg(feature = "evm-debug")]
mod inner {
use std::iter;
use std::collections::HashMap;
use std::time::{Instant, Duration};
use evm::interpreter::stack::Stack;
use evm::instructions::{Instruction, InstructionInfo, INSTRUCTIONS};
use evm::{CostType};
use util::U256;
macro_rules! evm_debug {
($x: expr) => {
$x
}
}
fn print(data: String) {
if cfg!(feature = "evm-debug-tests") {
println!("{}", data);
} else {
debug!(target: "evm", "{}", data);
}
}
pub struct EvmInformant {
spacing: String,
last_instruction: Instant,
stats: HashMap<Instruction, Stats>,
}
impl EvmInformant {
fn color(instruction: Instruction, name: &str) -> String {
let c = instruction as usize % 6;
let colors = [31, 34, 33, 32, 35, 36];
format!("\x1B[1;{}m{}\x1B[0m", colors[c], name)
}
fn as_micro(duration: &Duration) -> u64 {
let mut sec = duration.as_secs();
let subsec = duration.subsec_nanos() as u64;
sec = sec.saturating_mul(1_000_000u64);
sec += subsec / 1_000;
sec
}
pub fn new(depth: usize) -> Self {
EvmInformant {
spacing: iter::repeat(".").take(depth).collect(),
last_instruction: Instant::now(),
stats: HashMap::new(),
}
}
pub fn before_instruction<Cost: CostType>(&mut self, pc: usize, instruction: Instruction, info: &InstructionInfo, current_gas: &Cost, stack: &Stack<U256>) {
let time = self.last_instruction.elapsed();
self.last_instruction = Instant::now();
print(format!("{}[0x{:<3x}][{:>19}(0x{:<2x}) Gas Left: {:6?} (Previous took: {:10}μs)",
&self.spacing,
pc,
Self::color(instruction, info.name),
instruction,
current_gas,
Self::as_micro(&time),
));
if info.args > 0 {
for (idx, item) in stack.peek_top(info.args).iter().enumerate() {
print(format!("{} |{:2}: {:?}", self.spacing, idx, item));
}
}
}
pub fn after_instruction(&mut self, instruction: Instruction) {
let mut stats = self.stats.entry(instruction).or_insert_with(|| Stats::default());
let took = self.last_instruction.elapsed();
stats.note(took);
}
pub fn done(&mut self) {
// Print out stats
let infos = &*INSTRUCTIONS;
let mut stats: Vec<(_,_)> = self.stats.drain().collect();
stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg()));
print(format!("\n{}-------OPCODE STATS:", self.spacing));
for (instruction, stats) in stats.into_iter() {
let info = infos[instruction as usize];
print(format!("{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs",
self.spacing,
Self::color(instruction, info.name),
instruction,
stats.count,
stats.avg(),
));
}
}
}
struct Stats {
count: u64,
total_duration: Duration,
}
impl Default for Stats {
fn default() -> Self {
Stats {
count: 0,
total_duration: Duration::from_secs(0),
}
}
}
impl Stats {
fn note(&mut self, took: Duration) {
self.count += 1;
self.total_duration += took;
}
fn avg(&self) -> u64 {
EvmInformant::as_micro(&self.total_duration) / self.count
}
}
}

View File

@@ -1,195 +0,0 @@
// 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/>.
use util::U256;
use {ReturnData};
const MAX_RETURN_WASTE_BYTES: usize = 16384;
pub trait Memory {
/// Retrieve current size of the memory
fn size(&self) -> usize;
/// Resize (shrink or expand) the memory to specified size (fills 0)
fn resize(&mut self, new_size: usize);
/// Resize the memory only if its smaller
fn expand(&mut self, new_size: usize);
/// Write single byte to memory
fn write_byte(&mut self, offset: U256, value: U256);
/// Write a word to memory. Does not resize memory!
fn write(&mut self, offset: U256, value: U256);
/// Read a word from memory
fn read(&self, offset: U256) -> U256;
/// Write slice of bytes to memory. Does not resize memory!
fn write_slice(&mut self, offset: U256, &[u8]);
/// Retrieve part of the memory between offset and offset + size
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
/// Retrieve writeable part of memory
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
fn dump(&self);
/// Convert memory into return data.
fn into_return_data(self, offset: U256, size: U256) -> ReturnData;
}
/// Checks whether offset and size is valid memory range
fn is_valid_range(off: usize, size: usize) -> bool {
// When size is zero we haven't actually expanded the memory
let overflow = off.overflowing_add(size).1;
size > 0 && !overflow
}
impl Memory for Vec<u8> {
fn dump(&self) {
println!("MemoryDump:");
for i in self.iter() {
println!("{:02x} ", i);
}
println!("");
}
fn size(&self) -> usize {
self.len()
}
fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] {
let off = init_off_u.low_u64() as usize;
let size = init_size_u.low_u64() as usize;
if !is_valid_range(off, size) {
&self[0..0]
} else {
&self[off..off+size]
}
}
fn read(&self, offset: U256) -> U256 {
let off = offset.low_u64() as usize;
U256::from(&self[off..off+32])
}
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
let off = offset.low_u64() as usize;
let s = size.low_u64() as usize;
if !is_valid_range(off, s) {
&mut self[0..0]
} else {
&mut self[off..off+s]
}
}
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
if !slice.is_empty() {
let off = offset.low_u64() as usize;
self[off..off+slice.len()].copy_from_slice(slice);
}
}
fn write(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
value.to_big_endian(&mut self[off..off+32]);
}
fn write_byte(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize;
let val = value.low_u64() as u64;
self[off] = val as u8;
}
fn resize(&mut self, new_size: usize) {
self.resize(new_size, 0);
}
fn expand(&mut self, size: usize) {
if size > self.len() {
Memory::resize(self, size)
}
}
fn into_return_data(mut self, offset: U256, size: U256) -> ReturnData {
let mut offset = offset.low_u64() as usize;
let size = size.low_u64() as usize;
if !is_valid_range(offset, size) {
return ReturnData::empty()
}
if self.len() - size > MAX_RETURN_WASTE_BYTES {
{ let _ = self.drain(..offset); }
self.truncate(size);
self.shrink_to_fit();
offset = 0;
}
ReturnData::new(self, offset, size)
}
}
#[cfg(test)]
mod tests {
use util::U256;
use super::Memory;
#[test]
fn test_memory_read_and_write() {
// given
let mem: &mut Memory = &mut vec![];
mem.resize(0x80 + 32);
// when
mem.write(U256::from(0x80), U256::from(0xabcdef));
// then
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
}
#[test]
fn test_memory_read_and_write_byte() {
// given
let mem: &mut Memory = &mut vec![];
mem.resize(32);
// when
mem.write_byte(U256::from(0x1d), U256::from(0xab));
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
mem.write_byte(U256::from(0x1f), U256::from(0xef));
// then
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
}
#[test]
fn test_memory_read_slice_and_write_slice() {
let mem: &mut Memory = &mut vec![];
mem.resize(32);
{
let slice = "abcdefghijklmnopqrstuvwxyz012345".as_bytes();
mem.write_slice(U256::from(0), slice);
assert_eq!(mem.read_slice(U256::from(0), U256::from(32)), slice);
}
// write again
{
let slice = "67890".as_bytes();
mem.write_slice(U256::from(0x1), slice);
assert_eq!(mem.read_slice(U256::from(0), U256::from(7)), "a67890g".as_bytes());
}
// write empty slice out of bounds
{
let slice = [];
mem.write_slice(U256::from(0x1000), &slice);
assert_eq!(mem.size(), 32);
}
}
}

View File

@@ -1,858 +0,0 @@
// 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/>.
//! Rust VM implementation
#[macro_use]
mod informant;
mod gasometer;
mod stack;
mod memory;
mod shared_cache;
use self::gasometer::Gasometer;
use self::stack::{Stack, VecStack};
use self::memory::Memory;
pub use self::shared_cache::SharedCache;
use std::marker::PhantomData;
use action_params::{ActionParams, ActionValue};
use call_type::CallType;
use instructions::{self, Instruction, InstructionInfo};
use evm::{self, GasLeft, CostType, ReturnData};
use ext::{self, MessageCallResult, ContractCreateResult, CreateContractAddress};
use bit_set::BitSet;
use util::*;
type ProgramCounter = usize;
const ONE: U256 = U256([1, 0, 0, 0]);
const TWO: U256 = U256([2, 0, 0, 0]);
const TWO_POW_5: U256 = U256([0x20, 0, 0, 0]);
const TWO_POW_8: U256 = U256([0x100, 0, 0, 0]);
const TWO_POW_16: U256 = U256([0x10000, 0, 0, 0]);
const TWO_POW_24: U256 = U256([0x1000000, 0, 0, 0]);
const TWO_POW_64: U256 = U256([0, 0x1, 0, 0]); // 0x1 00000000 00000000
const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 00000000
const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000
const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000
/// Abstraction over raw vector of Bytes. Easier state management of PC.
struct CodeReader<'a> {
position: ProgramCounter,
code: &'a [u8]
}
#[cfg_attr(feature="dev", allow(len_without_is_empty))]
impl<'a> CodeReader<'a> {
/// Create new code reader - starting at position 0.
fn new(code: &'a [u8]) -> Self {
CodeReader {
position: 0,
code: code,
}
}
/// Get `no_of_bytes` from code and convert to U256. Move PC
fn read(&mut self, no_of_bytes: usize) -> U256 {
let pos = self.position;
self.position += no_of_bytes;
let max = cmp::min(pos + no_of_bytes, self.code.len());
U256::from(&self.code[pos..max])
}
fn len (&self) -> usize {
self.code.len()
}
}
enum InstructionResult<Gas> {
Ok,
UnusedGas(Gas),
JumpToPosition(U256),
StopExecutionNeedsReturn {
/// Gas left.
gas: Gas,
/// Return data offset.
init_off: U256,
/// Return data size.
init_size: U256,
/// Apply or revert state changes.
apply: bool,
},
StopExecution,
}
/// Intepreter EVM implementation
pub struct Interpreter<Cost: CostType> {
mem: Vec<u8>,
cache: Arc<SharedCache>,
return_data: ReturnData,
_type: PhantomData<Cost>,
}
impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
fn exec(&mut self, params: ActionParams, ext: &mut ext::Ext) -> evm::Result<GasLeft> {
self.mem.clear();
let mut informant = informant::EvmInformant::new(ext.depth());
let mut do_trace = true;
let code = &params.code.as_ref().expect("exec always called with code; qed");
let mut valid_jump_destinations = None;
let mut gasometer = Gasometer::<Cost>::new(Cost::from_u256(params.gas)?);
let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
let mut reader = CodeReader::new(code);
let infos = &*instructions::INSTRUCTIONS;
while reader.position < code.len() {
let instruction = code[reader.position];
reader.position += 1;
// TODO: make compile-time removable if too much of a performance hit.
do_trace = do_trace && ext.trace_next_instruction(reader.position - 1, instruction);
let info = &infos[instruction as usize];
self.verify_instruction(ext, instruction, info, &stack)?;
// Calculate gas cost
let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?;
if do_trace {
ext.trace_prepare_execute(reader.position - 1, instruction, requirements.gas_cost.as_u256());
}
gasometer.verify_gas(&requirements.gas_cost)?;
self.mem.expand(requirements.memory_required_size);
gasometer.current_mem_gas = requirements.memory_total_gas;
gasometer.current_gas = gasometer.current_gas - requirements.gas_cost;
evm_debug!({ informant.before_instruction(reader.position, instruction, info, &gasometer.current_gas, &stack) });
let (mem_written, store_written) = match do_trace {
true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
false => (None, None),
};
// Execute instruction
let result = self.exec_instruction(
gasometer.current_gas, &params, ext, instruction, &mut reader, &mut stack, requirements.provide_gas
)?;
evm_debug!({ informant.after_instruction(instruction) });
if let InstructionResult::UnusedGas(ref gas) = result {
gasometer.current_gas = gasometer.current_gas + *gas;
}
if do_trace {
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
}
// Advance
match result {
InstructionResult::JumpToPosition(position) => {
if valid_jump_destinations.is_none() {
let code_hash = params.code_hash.clone().unwrap_or_else(|| code.sha3());
valid_jump_destinations = Some(self.cache.jump_destinations(&code_hash, code));
}
let jump_destinations = valid_jump_destinations.as_ref().expect("jump_destinations are initialized on first jump; qed");
let pos = self.verify_jump(position, jump_destinations)?;
reader.position = pos;
},
InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => {
informant.done();
let mem = mem::replace(&mut self.mem, Vec::new());
return Ok(GasLeft::NeedsReturn {
gas_left: gas.as_u256(),
data: mem.into_return_data(init_off, init_size),
apply_state: apply
});
},
InstructionResult::StopExecution => break,
_ => {},
}
}
informant.done();
Ok(GasLeft::Known(gasometer.current_gas.as_u256()))
}
}
impl<Cost: CostType> Interpreter<Cost> {
/// Create a new `Interpreter` instance with shared cache.
pub fn new(cache: Arc<SharedCache>) -> Interpreter<Cost> {
Interpreter {
mem: Vec::new(),
cache: cache,
return_data: ReturnData::empty(),
_type: PhantomData::default(),
}
}
fn verify_instruction(&self, ext: &ext::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
let schedule = ext.schedule();
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 {
instruction: instruction
});
}
if info.tier == instructions::GasPriceTier::Invalid {
return Err(evm::Error::BadInstruction {
instruction: instruction
});
}
if !stack.has(info.args) {
Err(evm::Error::StackUnderflow {
instruction: info.name,
wanted: info.args,
on_stack: stack.size()
})
} else if stack.size() - info.args + info.ret > schedule.stack_limit {
Err(evm::Error::OutOfStack {
instruction: info.name,
wanted: info.ret - info.args,
limit: schedule.stack_limit
})
} else {
Ok(())
}
}
fn mem_written(
instruction: Instruction,
stack: &Stack<U256>
) -> Option<(usize, usize)> {
match instruction {
instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)),
instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)),
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)),
instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)),
instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)),
instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)),
_ => None,
}
}
fn store_written(
instruction: Instruction,
stack: &Stack<U256>
) -> Option<(U256, U256)> {
match instruction {
instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())),
_ => None,
}
}
#[cfg_attr(feature="dev", allow(too_many_arguments))]
fn exec_instruction(
&mut self,
gas: Cost,
params: &ActionParams,
ext: &mut ext::Ext,
instruction: Instruction,
code: &mut CodeReader,
stack: &mut Stack<U256>,
provided: Option<Cost>
) -> evm::Result<InstructionResult<Cost>> {
match instruction {
instructions::JUMP => {
let jump = stack.pop_back();
return Ok(InstructionResult::JumpToPosition(
jump
));
},
instructions::JUMPI => {
let jump = stack.pop_back();
let condition = stack.pop_back();
if !self.is_zero(&condition) {
return Ok(InstructionResult::JumpToPosition(
jump
));
}
},
instructions::JUMPDEST => {
// ignore
},
instructions::CREATE | instructions::CREATE2 => {
let endowment = stack.pop_back();
let init_off = stack.pop_back();
let init_size = stack.pop_back();
let address_scheme = if instruction == instructions::CREATE { CreateContractAddress::FromSenderAndNonce } else { CreateContractAddress::FromSenderAndCodeHash };
let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed");
let contract_code = self.mem.read_slice(init_off, init_size);
let can_create = ext.balance(&params.address)? >= endowment && ext.depth() < ext.schedule().max_depth;
if !can_create {
stack.push(U256::zero());
return Ok(InstructionResult::UnusedGas(create_gas));
}
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme);
return match create_result {
ContractCreateResult::Created(address, gas_left) => {
stack.push(address_to_u256(address));
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
},
ContractCreateResult::Failed => {
stack.push(U256::zero());
Ok(InstructionResult::Ok)
}
};
},
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => {
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
stack.pop_back();
let call_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is one of `CALL`/`CALLCODE`/`DELEGATECALL`; qed");
let code_address = stack.pop_back();
let code_address = u256_to_address(&code_address);
let value = if instruction == instructions::DELEGATECALL || instruction == instructions::STATICCALL {
None
} else {
Some(stack.pop_back())
};
let in_off = stack.pop_back();
let in_size = stack.pop_back();
let out_off = stack.pop_back();
let out_size = stack.pop_back();
// Add stipend (only CALL|CALLCODE when value > 0)
let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val.is_zero() {
false => Cost::from(ext.schedule().call_stipend),
true => Cost::from(0),
});
// Get sender & receive addresses, check if we have balance
let (sender_address, receive_address, has_balance, call_type) = match instruction {
instructions::CALL => {
let has_balance = ext.balance(&params.address)? >= value.expect("value set for all but delegate call; qed");
(&params.address, &code_address, has_balance, CallType::Call)
},
instructions::CALLCODE => {
let has_balance = ext.balance(&params.address)? >= value.expect("value set for all but delegate call; qed");
(&params.address, &params.address, has_balance, CallType::CallCode)
},
instructions::DELEGATECALL => (&params.sender, &params.address, true, CallType::DelegateCall),
instructions::STATICCALL => (&params.sender, &params.address, true, CallType::StaticCall),
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
};
let can_call = has_balance && ext.depth() < ext.schedule().max_depth;
if !can_call {
stack.push(U256::zero());
return Ok(InstructionResult::UnusedGas(call_gas));
}
let call_result = {
// we need to write and read from memory in the same time
// and we don't want to copy
let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) };
let output = self.mem.writeable_slice(out_off, out_size);
ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, output, call_type)
};
return match call_result {
MessageCallResult::Success(gas_left, data) => {
stack.push(U256::one());
self.return_data = data;
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one")))
},
MessageCallResult::Failed => {
stack.push(U256::zero());
Ok(InstructionResult::Ok)
}
};
},
instructions::RETURN => {
let init_off = stack.pop_back();
let init_size = stack.pop_back();
return Ok(InstructionResult::StopExecutionNeedsReturn {gas: gas, init_off: init_off, init_size: init_size, apply: true})
},
instructions::REVERT => {
let init_off = stack.pop_back();
let init_size = stack.pop_back();
return Ok(InstructionResult::StopExecutionNeedsReturn {gas: gas, init_off: init_off, init_size: init_size, apply: false})
},
instructions::STOP => {
return Ok(InstructionResult::StopExecution);
},
instructions::SUICIDE => {
let address = stack.pop_back();
ext.suicide(&u256_to_address(&address))?;
return Ok(InstructionResult::StopExecution);
},
instructions::LOG0...instructions::LOG4 => {
let no_of_topics = instructions::get_log_topics(instruction);
let offset = stack.pop_back();
let size = stack.pop_back();
let topics = stack.pop_n(no_of_topics)
.iter()
.map(H256::from)
.collect();
ext.log(topics, self.mem.read_slice(offset, size))?;
},
instructions::PUSH1...instructions::PUSH32 => {
let bytes = instructions::get_push_bytes(instruction);
let val = code.read(bytes);
stack.push(val);
},
instructions::MLOAD => {
let word = self.mem.read(stack.pop_back());
stack.push(U256::from(word));
},
instructions::MSTORE => {
let offset = stack.pop_back();
let word = stack.pop_back();
Memory::write(&mut self.mem, offset, word);
},
instructions::MSTORE8 => {
let offset = stack.pop_back();
let byte = stack.pop_back();
self.mem.write_byte(offset, byte);
},
instructions::MSIZE => {
stack.push(U256::from(self.mem.size()));
},
instructions::SHA3 => {
let offset = stack.pop_back();
let size = stack.pop_back();
let sha3 = self.mem.read_slice(offset, size).sha3();
stack.push(U256::from(&*sha3));
},
instructions::SLOAD => {
let key = H256::from(&stack.pop_back());
let word = U256::from(&*ext.storage_at(&key)?);
stack.push(word);
},
instructions::SSTORE => {
let address = H256::from(&stack.pop_back());
let val = stack.pop_back();
let current_val = U256::from(&*ext.storage_at(&address)?);
// Increase refund for clear
if !self.is_zero(&current_val) && self.is_zero(&val) {
ext.inc_sstore_clears();
}
ext.set_storage(address, H256::from(&val))?;
},
instructions::PC => {
stack.push(U256::from(code.position - 1));
},
instructions::GAS => {
stack.push(gas.as_u256());
},
instructions::ADDRESS => {
stack.push(address_to_u256(params.address.clone()));
},
instructions::ORIGIN => {
stack.push(address_to_u256(params.origin.clone()));
},
instructions::BALANCE => {
let address = u256_to_address(&stack.pop_back());
let balance = ext.balance(&address)?;
stack.push(balance);
},
instructions::CALLER => {
stack.push(address_to_u256(params.sender.clone()));
},
instructions::CALLVALUE => {
stack.push(match params.value {
ActionValue::Transfer(val) | ActionValue::Apparent(val) => val
});
},
instructions::CALLDATALOAD => {
let big_id = stack.pop_back();
let id = big_id.low_u64() as usize;
let max = id.wrapping_add(32);
if let Some(data) = params.data.as_ref() {
let bound = cmp::min(data.len(), max);
if id < bound && big_id < U256::from(data.len()) {
let mut v = [0u8; 32];
v[0..bound-id].clone_from_slice(&data[id..bound]);
stack.push(U256::from(&v[..]))
} else {
stack.push(U256::zero())
}
} else {
stack.push(U256::zero())
}
},
instructions::CALLDATASIZE => {
stack.push(U256::from(params.data.clone().map_or(0, |l| l.len())));
},
instructions::CODESIZE => {
stack.push(U256::from(code.len()));
},
instructions::RETURNDATASIZE => {
stack.push(U256::from(self.return_data.len()))
},
instructions::EXTCODESIZE => {
let address = u256_to_address(&stack.pop_back());
let len = ext.extcodesize(&address)?;
stack.push(U256::from(len));
},
instructions::CALLDATACOPY => {
Self::copy_data_to_memory(&mut self.mem, stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
},
instructions::RETURNDATACOPY => {
Self::copy_data_to_memory(&mut self.mem, stack, &*self.return_data);
},
instructions::CODECOPY => {
Self::copy_data_to_memory(&mut self.mem, stack, params.code.as_ref().map_or_else(|| &[] as &[u8], |c| &**c as &[u8]));
},
instructions::EXTCODECOPY => {
let address = u256_to_address(&stack.pop_back());
let code = ext.extcode(&address)?;
Self::copy_data_to_memory(&mut self.mem, stack, &code);
},
instructions::GASPRICE => {
stack.push(params.gas_price.clone());
},
instructions::BLOCKHASH => {
let block_number = stack.pop_back();
let block_hash = ext.blockhash(&block_number);
stack.push(U256::from(&*block_hash));
},
instructions::COINBASE => {
stack.push(address_to_u256(ext.env_info().author.clone()));
},
instructions::TIMESTAMP => {
stack.push(U256::from(ext.env_info().timestamp));
},
instructions::NUMBER => {
stack.push(U256::from(ext.env_info().number));
},
instructions::DIFFICULTY => {
stack.push(ext.env_info().difficulty.clone());
},
instructions::GASLIMIT => {
stack.push(ext.env_info().gas_limit.clone());
},
_ => {
self.exec_stack_instruction(instruction, stack)?;
}
};
Ok(InstructionResult::Ok)
}
fn copy_data_to_memory(mem: &mut Vec<u8>, stack: &mut Stack<U256>, source: &[u8]) {
let dest_offset = stack.pop_back();
let source_offset = stack.pop_back();
let size = stack.pop_back();
let source_size = U256::from(source.len());
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
true => {
let zero_slice = if source_offset > source_size {
mem.writeable_slice(dest_offset, size)
} else {
mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size)
};
for i in zero_slice.iter_mut() {
*i = 0;
}
source.len()
},
false => (size.low_u64() + source_offset.low_u64()) as usize
};
if source_offset < source_size {
let output_begin = source_offset.low_u64() as usize;
mem.write_slice(dest_offset, &source[output_begin..output_end]);
}
}
fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> evm::Result<usize> {
let jump = jump_u.low_u64() as usize;
if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u {
Ok(jump)
} else {
Err(evm::Error::BadJumpDestination {
destination: jump
})
}
}
fn is_zero(&self, val: &U256) -> bool {
val.is_zero()
}
fn bool_to_u256(&self, val: bool) -> U256 {
if val {
U256::one()
} else {
U256::zero()
}
}
fn exec_stack_instruction(&self, instruction: Instruction, stack: &mut Stack<U256>) -> evm::Result<()> {
match instruction {
instructions::DUP1...instructions::DUP16 => {
let position = instructions::get_dup_position(instruction);
let val = stack.peek(position).clone();
stack.push(val);
},
instructions::SWAP1...instructions::SWAP16 => {
let position = instructions::get_swap_position(instruction);
stack.swap_with_top(position)
},
instructions::POP => {
stack.pop_back();
},
instructions::ADD => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(a.overflowing_add(b).0);
},
instructions::MUL => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(a.overflowing_mul(b).0);
},
instructions::SUB => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(a.overflowing_sub(b).0);
},
instructions::DIV => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(if !self.is_zero(&b) {
match b {
ONE => a,
TWO => a >> 1,
TWO_POW_5 => a >> 5,
TWO_POW_8 => a >> 8,
TWO_POW_16 => a >> 16,
TWO_POW_24 => a >> 24,
TWO_POW_64 => a >> 64,
TWO_POW_96 => a >> 96,
TWO_POW_224 => a >> 224,
TWO_POW_248 => a >> 248,
_ => a.overflowing_div(b).0,
}
} else {
U256::zero()
});
},
instructions::MOD => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(if !self.is_zero(&b) {
a.overflowing_rem(b).0
} else {
U256::zero()
});
},
instructions::SDIV => {
let (a, sign_a) = get_and_reset_sign(stack.pop_back());
let (b, sign_b) = get_and_reset_sign(stack.pop_back());
// -2^255
let min = (U256::one() << 255) - U256::one();
stack.push(if self.is_zero(&b) {
U256::zero()
} else if a == min && b == !U256::zero() {
min
} else {
let c = a.overflowing_div(b).0;
set_sign(c, sign_a ^ sign_b)
});
},
instructions::SMOD => {
let ua = stack.pop_back();
let ub = stack.pop_back();
let (a, sign_a) = get_and_reset_sign(ua);
let b = get_and_reset_sign(ub).0;
stack.push(if !self.is_zero(&b) {
let c = a.overflowing_rem(b).0;
set_sign(c, sign_a)
} else {
U256::zero()
});
},
instructions::EXP => {
let base = stack.pop_back();
let expon = stack.pop_back();
let res = base.overflowing_pow(expon).0;
stack.push(res);
},
instructions::NOT => {
let a = stack.pop_back();
stack.push(!a);
},
instructions::LT => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(self.bool_to_u256(a < b));
},
instructions::SLT => {
let (a, neg_a) = get_and_reset_sign(stack.pop_back());
let (b, neg_b) = get_and_reset_sign(stack.pop_back());
let is_positive_lt = a < b && !(neg_a | neg_b);
let is_negative_lt = a > b && (neg_a & neg_b);
let has_different_signs = neg_a && !neg_b;
stack.push(self.bool_to_u256(is_positive_lt | is_negative_lt | has_different_signs));
},
instructions::GT => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(self.bool_to_u256(a > b));
},
instructions::SGT => {
let (a, neg_a) = get_and_reset_sign(stack.pop_back());
let (b, neg_b) = get_and_reset_sign(stack.pop_back());
let is_positive_gt = a > b && !(neg_a | neg_b);
let is_negative_gt = a < b && (neg_a & neg_b);
let has_different_signs = !neg_a && neg_b;
stack.push(self.bool_to_u256(is_positive_gt | is_negative_gt | has_different_signs));
},
instructions::EQ => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(self.bool_to_u256(a == b));
},
instructions::ISZERO => {
let a = stack.pop_back();
stack.push(self.bool_to_u256(self.is_zero(&a)));
},
instructions::AND => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(a & b);
},
instructions::OR => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(a | b);
},
instructions::XOR => {
let a = stack.pop_back();
let b = stack.pop_back();
stack.push(a ^ b);
},
instructions::BYTE => {
let word = stack.pop_back();
let val = stack.pop_back();
let byte = match word < U256::from(32) {
true => (val >> (8 * (31 - word.low_u64() as usize))) & U256::from(0xff),
false => U256::zero()
};
stack.push(byte);
},
instructions::ADDMOD => {
let a = stack.pop_back();
let b = stack.pop_back();
let c = stack.pop_back();
stack.push(if !self.is_zero(&c) {
// upcast to 512
let a5 = U512::from(a);
let res = a5.overflowing_add(U512::from(b)).0;
let x = res.overflowing_rem(U512::from(c)).0;
U256::from(x)
} else {
U256::zero()
});
},
instructions::MULMOD => {
let a = stack.pop_back();
let b = stack.pop_back();
let c = stack.pop_back();
stack.push(if !self.is_zero(&c) {
let a5 = U512::from(a);
let res = a5.overflowing_mul(U512::from(b)).0;
let x = res.overflowing_rem(U512::from(c)).0;
U256::from(x)
} else {
U256::zero()
});
},
instructions::SIGNEXTEND => {
let bit = stack.pop_back();
if bit < U256::from(32) {
let number = stack.pop_back();
let bit_position = (bit.low_u64() * 8 + 7) as usize;
let bit = number.bit(bit_position);
let mask = (U256::one() << bit_position) - U256::one();
stack.push(if bit {
number | !mask
} else {
number & mask
});
}
},
_ => {
return Err(evm::Error::BadInstruction {
instruction: instruction
});
}
}
Ok(())
}
}
fn get_and_reset_sign(value: U256) -> (U256, bool) {
let U256(arr) = value;
let sign = arr[3].leading_zeros() == 0;
(set_sign(value, sign), sign)
}
fn set_sign(value: U256, sign: bool) -> U256 {
if sign {
(!U256::zero() ^ value).overflowing_add(U256::one()).0
} else {
value
}
}
#[inline]
fn u256_to_address(value: &U256) -> Address {
Address::from(H256::from(value))
}
#[inline]
fn address_to_u256(value: Address) -> U256 {
U256::from(&*H256::from(value))
}

View File

@@ -1,104 +0,0 @@
// 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/>.
use std::sync::Arc;
use util::{H256, HeapSizeOf, Mutex};
use util::sha3::*;
use util::cache::MemoryLruCache;
use bit_set::BitSet;
use super::super::instructions;
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
// stub for a HeapSizeOf implementation.
struct Bits(Arc<BitSet>);
impl HeapSizeOf for Bits {
fn heap_size_of_children(&self) -> usize {
// dealing in bits here
self.0.capacity() * 8
}
}
/// Global cache for EVM interpreter
pub struct SharedCache {
jump_destinations: Mutex<MemoryLruCache<H256, Bits>>,
}
impl SharedCache {
/// Create a jump destinations cache with a maximum size in bytes
/// to cache.
pub fn new(max_size: usize) -> Self {
SharedCache {
jump_destinations: Mutex::new(MemoryLruCache::new(max_size)),
}
}
/// Get jump destinations bitmap for a contract.
pub fn jump_destinations(&self, code_hash: &H256, code: &[u8]) -> Arc<BitSet> {
if code_hash == &SHA3_EMPTY {
return Self::find_jump_destinations(code);
}
if let Some(d) = self.jump_destinations.lock().get_mut(code_hash) {
return d.0.clone();
}
let d = Self::find_jump_destinations(code);
self.jump_destinations.lock().insert(code_hash.clone(), Bits(d.clone()));
d
}
fn find_jump_destinations(code: &[u8]) -> Arc<BitSet> {
let mut jump_dests = BitSet::with_capacity(code.len());
let mut position = 0;
while position < code.len() {
let instruction = code[position];
if instruction == instructions::JUMPDEST {
jump_dests.insert(position);
} else if instructions::is_push(instruction) {
position += instructions::get_push_bytes(instruction);
}
position += 1;
}
jump_dests.shrink_to_fit();
Arc::new(jump_dests)
}
}
impl Default for SharedCache {
fn default() -> Self {
SharedCache::new(DEFAULT_CACHE_SIZE)
}
}
#[test]
fn test_find_jump_destinations() {
use rustc_hex::FromHex;
// given
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
// when
let valid_jump_destinations = SharedCache::find_jump_destinations(&code);
// then
assert!(valid_jump_destinations.contains(66));
}

View File

@@ -1,98 +0,0 @@
// 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/>.
use std::fmt;
use instructions;
/// Stack trait with VM-friendly API
pub trait Stack<T> {
/// Returns `Stack[len(Stack) - no_from_top]`
fn peek(&self, no_from_top: usize) -> &T;
/// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top]
fn swap_with_top(&mut self, no_from_top: usize);
/// Returns true if Stack has at least `no_of_elems` elements
fn has(&self, no_of_elems: usize) -> bool;
/// Get element from top and remove it from Stack. Panics if stack is empty.
fn pop_back(&mut self) -> T;
/// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty.
fn pop_n(&mut self, no_of_elems: usize) -> &[T];
/// Add element on top of the Stack
fn push(&mut self, elem: T);
/// Get number of elements on Stack
fn size(&self) -> usize;
/// Returns all data on stack.
fn peek_top(&self, no_of_elems: usize) -> &[T];
}
pub struct VecStack<S> {
stack: Vec<S>,
logs: [S; instructions::MAX_NO_OF_TOPICS]
}
impl<S : Copy> VecStack<S> {
pub fn with_capacity(capacity: usize, zero: S) -> Self {
VecStack {
stack: Vec::with_capacity(capacity),
logs: [zero; instructions::MAX_NO_OF_TOPICS]
}
}
}
impl<S : fmt::Display> Stack<S> for VecStack<S> {
fn peek(&self, no_from_top: usize) -> &S {
&self.stack[self.stack.len() - no_from_top - 1]
}
fn swap_with_top(&mut self, no_from_top: usize) {
let len = self.stack.len();
self.stack.swap(len - no_from_top - 1, len - 1);
}
fn has(&self, no_of_elems: usize) -> bool {
self.stack.len() >= no_of_elems
}
fn pop_back(&mut self) -> S {
let val = self.stack.pop();
match val {
Some(x) => x,
None => panic!("Tried to pop from empty stack.")
}
}
fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS);
for i in 0..no_of_elems {
self.logs[i] = self.pop_back();
}
&self.logs[0..no_of_elems]
}
fn push(&mut self, elem: S) {
self.stack.push(elem);
}
fn size(&self) -> usize {
self.stack.len()
}
fn peek_top(&self, no_from_top: usize) -> &[S] {
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist.");
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
}
}

View File

@@ -1,70 +0,0 @@
// 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/>.
//! Ethereum virtual machine.
extern crate byteorder;
extern crate bit_set;
extern crate common_types as types;
extern crate ethcore_util as util;
extern crate ethjson;
extern crate rlp;
extern crate parity_wasm;
extern crate wasm_utils;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[cfg(feature = "jit")]
extern crate evmjit;
#[cfg(test)]
extern crate rustc_hex;
pub mod action_params;
pub mod call_type;
pub mod env_info;
pub mod ext;
pub mod evm;
pub mod interpreter;
pub mod schedule;
pub mod wasm;
#[macro_use]
pub mod factory;
mod vmtype;
mod instructions;
#[cfg(feature = "jit" )]
mod jit;
#[cfg(test)]
mod tests;
#[cfg(all(feature="benches", test))]
mod benches;
pub use self::action_params::ActionParams;
pub use self::call_type::CallType;
pub use self::env_info::EnvInfo;
pub use self::evm::{Evm, Error, Finalize, FinalizationResult, GasLeft, Result, CostType, ReturnData};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, CreateContractAddress};
pub use self::instructions::{InstructionInfo, INSTRUCTIONS, push_bytes};
pub use self::vmtype::VMType;
pub use self::factory::Factory;
pub use self::schedule::{Schedule, CleanDustMode};

View File

@@ -1,75 +0,0 @@
// 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/>.
use std::fmt;
/// Type of EVM to use.
#[derive(Debug, PartialEq, Clone)]
pub enum VMType {
/// JIT EVM
#[cfg(feature = "jit")]
Jit,
/// RUST EVM
Interpreter
}
impl fmt::Display for VMType {
#[cfg(feature="jit")]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", match *self {
VMType::Jit => "JIT",
VMType::Interpreter => "INT"
})
}
#[cfg(not(feature="jit"))]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", match *self {
VMType::Interpreter => "INT"
})
}
}
impl Default for VMType {
fn default() -> Self {
VMType::Interpreter
}
}
impl VMType {
/// Return all possible VMs (JIT, Interpreter)
#[cfg(feature = "jit")]
pub fn all() -> Vec<VMType> {
vec![VMType::Jit, VMType::Interpreter]
}
/// Return all possible VMs (Interpreter)
#[cfg(not(feature = "jit"))]
pub fn all() -> Vec<VMType> {
vec![VMType::Interpreter]
}
/// Return new jit if it's possible
#[cfg(not(feature = "jit"))]
pub fn jit() -> Option<Self> {
None
}
/// Return new jit if it's possible
#[cfg(feature = "jit")]
pub fn jit() -> Option<Self> {
Some(VMType::Jit)
}
}

View File

@@ -1,62 +0,0 @@
// 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/>.
//! Wasm evm call arguments helper
use util::{U256, H160};
/// Input part of the wasm call descriptor
pub struct CallArgs {
/// Receiver of the transaction
pub address: [u8; 20],
/// Sender of the transaction
pub sender: [u8; 20],
/// Original transaction initiator
pub origin: [u8; 20],
/// Transfer value
pub value: [u8; 32],
/// call/create params
pub data: Vec<u8>,
}
impl CallArgs {
/// New contract call payload with known parameters
pub fn new(address: H160, sender: H160, origin: H160, value: U256, data: Vec<u8>) -> Self {
let mut descriptor = CallArgs {
address: [0u8; 20],
sender: [0u8; 20],
origin: [0u8; 20],
value: [0u8; 32],
data: data,
};
descriptor.address.copy_from_slice(&*address);
descriptor.sender.copy_from_slice(&*sender);
descriptor.origin.copy_from_slice(&*origin);
value.to_big_endian(&mut descriptor.value);
descriptor
}
/// Total call payload length in linear memory
pub fn len(&self) -> u32 {
self.data.len() as u32 + 92
}
}

View File

@@ -1,119 +0,0 @@
// 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/>.
//! Wasm env module bindings
use parity_wasm::elements::ValueType::*;
use parity_wasm::interpreter::UserFunctionDescriptor;
use parity_wasm::interpreter::UserFunctionDescriptor::*;
pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
Static(
"_storage_read",
&[I32; 2],
Some(I32),
),
Static(
"_storage_write",
&[I32; 2],
Some(I32),
),
Static(
"_malloc",
&[I32],
Some(I32),
),
Static(
"_free",
&[I32],
None,
),
Static(
"gas",
&[I32],
None,
),
Static(
"_debug",
&[I32; 2],
None,
),
Static(
"_suicide",
&[I32],
None,
),
Static(
"_create",
&[I32; 4],
Some(I32),
),
Static(
"abort",
&[I32],
None,
),
Static(
"_abort",
&[],
None,
),
Static(
"invoke_vii",
&[I32; 3],
None,
),
Static(
"invoke_vi",
&[I32; 2],
None,
),
Static(
"invoke_v",
&[I32],
None,
),
Static(
"invoke_iii",
&[I32; 3],
Some(I32),
),
Static(
"___resumeException",
&[I32],
None,
),
Static(
"_rust_begin_unwind",
&[I32; 4],
None,
),
Static(
"___cxa_find_matching_catch_2",
&[],
Some(I32),
),
Static(
"___gxx_personality_v0",
&[I32; 6],
Some(I32),
),
Static(
"_emscripten_memcpy_big",
&[I32; 3],
Some(I32),
)
];

View File

@@ -1,159 +0,0 @@
// 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/>.
//! Wasm Interpreter
mod runtime;
mod ptr;
mod call_args;
mod result;
#[cfg(test)]
mod tests;
mod env;
use std::sync::Arc;
const DEFAULT_STACK_SPACE: u32 = 5 * 1024 * 1024;
use parity_wasm::{interpreter, elements};
use parity_wasm::interpreter::ModuleInstanceInterface;
use wasm_utils;
use evm::{self, GasLeft, ReturnData};
use action_params::ActionParams;
use self::runtime::Runtime;
pub use self::runtime::Error as RuntimeError;
const DEFAULT_RESULT_BUFFER: usize = 1024;
/// Wasm interpreter instance
pub struct WasmInterpreter {
program: interpreter::ProgramInstance,
result: Vec<u8>,
}
impl WasmInterpreter {
/// New wasm interpreter instance
pub fn new() -> Result<WasmInterpreter, RuntimeError> {
Ok(WasmInterpreter {
program: interpreter::ProgramInstance::new()?,
result: Vec::with_capacity(DEFAULT_RESULT_BUFFER),
})
}
}
impl evm::Evm for WasmInterpreter {
fn exec(&mut self, params: ActionParams, ext: &mut ::ext::Ext) -> evm::Result<GasLeft> {
use parity_wasm::elements::Deserialize;
let code = params.code.expect("exec is only called on contract with code; qed");
trace!(target: "wasm", "Started wasm interpreter with code.len={:?}", code.len());
let env_instance = self.program.module("env")
// prefer explicit panic here
.expect("Wasm program to contain env module");
let env_memory = env_instance.memory(interpreter::ItemIndex::Internal(0))
// prefer explicit panic here
.expect("Linear memory to exist in wasm runtime");
if params.gas > ::std::u64::MAX.into() {
return Err(evm::Error::Wasm("Wasm interpreter cannot run contracts with gas >= 2^64".to_owned()));
}
let mut runtime = Runtime::with_params(
ext,
env_memory,
DEFAULT_STACK_SPACE,
params.gas.low_u64(),
);
let mut cursor = ::std::io::Cursor::new(&*code);
let contract_module = wasm_utils::inject_gas_counter(
elements::Module::deserialize(
&mut cursor
).map_err(|err| {
evm::Error::Wasm(format!("Error deserializing contract code ({:?})", err))
})?
);
let d_ptr = runtime.write_descriptor(
call_args::CallArgs::new(
params.address,
params.sender,
params.origin,
params.value.value(),
params.data.unwrap_or(Vec::with_capacity(0)),
)
)?;
{
let execution_params = interpreter::ExecutionParams::with_external(
"env".into(),
Arc::new(
interpreter::env_native_module(env_instance, native_bindings(&mut runtime))
.map_err(|err| {
// todo: prefer explicit panic here also?
evm::Error::Wasm(format!("Error instantiating native bindings: {:?}", err))
})?
)
).add_argument(interpreter::RuntimeValue::I32(d_ptr.as_raw() as i32));
let module_instance = self.program.add_module("contract", contract_module, Some(&execution_params.externals))
.map_err(|err| {
trace!(target: "wasm", "Error adding contract module: {:?}", err);
evm::Error::from(RuntimeError::Interpreter(err))
})?;
module_instance.execute_export("_call", execution_params)
.map_err(|err| {
trace!(target: "wasm", "Error executing contract: {:?}", err);
evm::Error::from(RuntimeError::Interpreter(err))
})?;
}
let result = result::WasmResult::new(d_ptr);
if result.peek_empty(&*runtime.memory())? {
trace!(target: "wasm", "Contract execution result is empty.");
Ok(GasLeft::Known(runtime.gas_left()?.into()))
} else {
self.result.clear();
// todo: use memory views to avoid copy
self.result.extend(result.pop(&*runtime.memory())?);
let len = self.result.len();
Ok(GasLeft::NeedsReturn {
gas_left: runtime.gas_left()?.into(),
data: ReturnData::new(
::std::mem::replace(&mut self.result, Vec::with_capacity(DEFAULT_RESULT_BUFFER)),
0,
len,
),
apply_state: true,
})
}
}
}
fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserFunctions<'a> {
interpreter::UserFunctions {
executor: runtime,
functions: ::std::borrow::Cow::from(env::SIGNATURES),
}
}

View File

@@ -1,52 +0,0 @@
// 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/>.
//! Wasm bound-checked ptr
use parity_wasm::interpreter;
/// Bound-checked wrapper for webassembly memory
pub struct WasmPtr(u32);
/// Error in bound check
#[derive(Debug)]
pub enum Error {
AccessViolation,
}
impl From<u32> for WasmPtr {
fn from(raw: u32) -> Self {
WasmPtr(raw)
}
}
impl WasmPtr {
// todo: use memory view when they are on
/// Check memory range and return data with given length starting from the current pointer value
pub fn slice(&self, len: u32, mem: &interpreter::MemoryInstance) -> Result<Vec<u8>, Error> {
mem.get(self.0, len as usize).map_err(|_| Error::AccessViolation)
}
// todo: maybe 2gb limit can be enhanced
/// Convert i32 from wasm stack to the wrapped pointer
pub fn from_i32(raw_ptr: i32) -> Result<Self, Error> {
if raw_ptr < 0 { return Err(Error::AccessViolation); }
Ok(WasmPtr(raw_ptr as u32))
}
/// Return pointer raw value
pub fn as_raw(&self) -> u32 { self.0 }
}

View File

@@ -1,51 +0,0 @@
// 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/>.
//! Wasm evm results helper
use byteorder::{LittleEndian, ByteOrder};
use parity_wasm::interpreter;
use super::ptr::WasmPtr;
use super::runtime::Error as RuntimeError;
/// Wrapper for wasm contract call result
pub struct WasmResult {
ptr: WasmPtr,
}
impl WasmResult {
/// New call result from given ptr
pub fn new(descriptor_ptr: WasmPtr) -> WasmResult {
WasmResult { ptr: descriptor_ptr }
}
/// Check if the result contains any data
pub fn peek_empty(&self, mem: &interpreter::MemoryInstance) -> Result<bool, RuntimeError> {
let result_len = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[12..16]);
Ok(result_len == 0)
}
/// Consume the result ptr and return the actual data from wasm linear memory
pub fn pop(self, mem: &interpreter::MemoryInstance) -> Result<Vec<u8>, RuntimeError> {
let result_ptr = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[8..12]);
let result_len = LittleEndian::read_u32(&self.ptr.slice(16, mem)?[12..16]);
trace!(target: "wasm", "contract result: {} bytes at @{}", result_len, result_ptr);
Ok(mem.get(result_ptr, result_len as usize)?)
}
}

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