Compare commits
942 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d097956ede | ||
|
|
949d2b7fd0 | ||
|
|
2d4f4bdd61 | ||
|
|
67bd9a33cf | ||
|
|
109168b990 | ||
|
|
e535174604 | ||
|
|
ea6d517a87 | ||
|
|
965727c5d3 | ||
|
|
083b69ffb1 | ||
|
|
9f47909edf | ||
|
|
be6fe6b005 | ||
|
|
bb311e838b | ||
|
|
d8bf5fc848 | ||
|
|
cc562d2c4f | ||
|
|
e614203170 | ||
|
|
b24bb103f7 | ||
|
|
3e60b221c8 | ||
|
|
980418898a | ||
|
|
6ef9780b81 | ||
|
|
1942d1faf4 | ||
|
|
b7324bf771 | ||
|
|
ddc7b588dc | ||
|
|
f90bdcd1e5 | ||
|
|
7bf840f80a | ||
|
|
4992064663 | ||
|
|
75eb542275 | ||
|
|
b0f89becfd | ||
|
|
fcc388703f | ||
|
|
fc226ce868 | ||
|
|
ee0df5adc0 | ||
|
|
5f2cabd6e3 | ||
|
|
65e4bad3dd | ||
|
|
a554b81f32 | ||
|
|
06be7271aa | ||
|
|
33a553726a | ||
|
|
fdc781dbfd | ||
|
|
2c95ee60b9 | ||
|
|
ac8533a56c | ||
|
|
8f75042fee | ||
|
|
3f7ac5c64b | ||
|
|
e23753eabd | ||
|
|
9f41af5077 | ||
|
|
28019a472a | ||
|
|
8116ad995d | ||
|
|
6a564f3334 | ||
|
|
2d7aecf5d9 | ||
|
|
2f066535cb | ||
|
|
e734ca4bf1 | ||
|
|
6f2ec07baa | ||
|
|
7ef4bbd5f7 | ||
|
|
96e60d9c91 | ||
|
|
ac02d12fa8 | ||
|
|
e7102d9f38 | ||
|
|
cabf626c61 | ||
|
|
91be3a4fc3 | ||
|
|
b9979137b7 | ||
|
|
4becd27029 | ||
|
|
54536dfa1b | ||
|
|
c4a58efed4 | ||
|
|
1f4d6fced3 | ||
|
|
1aaafa2d11 | ||
|
|
58307479ad | ||
|
|
22261bc2d1 | ||
|
|
40e29c92bc | ||
|
|
d365281cce | ||
|
|
24c8510932 | ||
|
|
0fca4f95d6 | ||
|
|
02f2c611d4 | ||
|
|
a30f43b36c | ||
|
|
ba4d8ceaf3 | ||
|
|
45d44bedef | ||
|
|
cd0b62338b | ||
|
|
b844c8a29a | ||
|
|
4936e99f30 | ||
|
|
7fb46bff06 | ||
|
|
dc51dde112 | ||
|
|
c05924a713 | ||
|
|
d970237658 | ||
|
|
56c275b0cb | ||
|
|
62210fb932 | ||
|
|
b49c039f41 | ||
|
|
15aebacbe7 | ||
|
|
7599e3998f | ||
|
|
204fe44b29 | ||
|
|
f4453f77b8 | ||
|
|
ed5efebec1 | ||
|
|
67c1f71b6e | ||
|
|
f0a6b5d401 | ||
|
|
a24b6ad983 | ||
|
|
b0f1f8307d | ||
|
|
ad67ea0eb5 | ||
|
|
125aa0aeb4 | ||
|
|
df0ef6618d | ||
|
|
0409e95d1e | ||
|
|
104367ce24 | ||
|
|
5f2cb5e52f | ||
|
|
8118444442 | ||
|
|
9e185030e6 | ||
|
|
f4f8b411f5 | ||
|
|
6334893561 | ||
|
|
44ae70e9ec | ||
|
|
1c2a4c116a | ||
|
|
61d8f90530 | ||
|
|
46183b1cdd | ||
|
|
a78a89b13a | ||
|
|
e91025282e | ||
|
|
a3a4a37ec7 | ||
|
|
687a542260 | ||
|
|
e7d3036ace | ||
|
|
33f1d2af1e | ||
|
|
1d386f60e9 | ||
|
|
7dcbcb5c31 | ||
|
|
981854d5ae | ||
|
|
349316f70e | ||
|
|
58a27b3334 | ||
|
|
2d69c7ecf6 | ||
|
|
2c4f55a8fb | ||
|
|
2e5f7f1632 | ||
|
|
e448b84845 | ||
|
|
972d30c288 | ||
|
|
59e87b6a51 | ||
|
|
9c911c7a28 | ||
|
|
949086baa5 | ||
|
|
286526072f | ||
|
|
a9aaacd88b | ||
|
|
2e90e02a2c | ||
|
|
cc718bb108 | ||
|
|
826a4ca0a2 | ||
|
|
7240eee5a8 | ||
|
|
9241c6a9b5 | ||
|
|
17de29e69a | ||
|
|
f22745eb0a | ||
|
|
5d9f5e3509 | ||
|
|
5eba9078fc | ||
|
|
a3e693d5c3 | ||
|
|
36c91e4ed5 | ||
|
|
83690fdb90 | ||
|
|
38c00fae88 | ||
|
|
465c7eeaa2 | ||
|
|
a4195f2780 | ||
|
|
8a3e82d99a | ||
|
|
01ce28bc9a | ||
|
|
3d8dc11442 | ||
|
|
826bf28196 | ||
|
|
d069b98b45 | ||
|
|
3637c14da5 | ||
|
|
201f34620a | ||
|
|
71c68cc000 | ||
|
|
bcc84a31b7 | ||
|
|
72094a8bee | ||
|
|
944f9d7fb5 | ||
|
|
c7a043b864 | ||
|
|
57626b60e7 | ||
|
|
6b16fe3f14 | ||
|
|
4a7bcfe4f0 | ||
|
|
59488769cf | ||
|
|
547031aa0e | ||
|
|
196c3e7e9b | ||
|
|
16caf032bd | ||
|
|
4900e90112 | ||
|
|
02edc958d7 | ||
|
|
2c254e3b32 | ||
|
|
e73569c84d | ||
|
|
cf772ef555 | ||
|
|
0f41121556 | ||
|
|
614a80bd23 | ||
|
|
796482c129 | ||
|
|
b6fd18a9ca | ||
|
|
64e6d36944 | ||
|
|
f9033facd3 | ||
|
|
53609f703e | ||
|
|
4d5280e43c | ||
|
|
dc548315b8 | ||
|
|
7489a2b36f | ||
|
|
91f1c84048 | ||
|
|
e1cd60987b | ||
|
|
df51cad7e2 | ||
|
|
a60c5527c7 | ||
|
|
d152fa3e85 | ||
|
|
a19be3cb51 | ||
|
|
e871b7a0ac | ||
|
|
c8e176b2e3 | ||
|
|
367a5c998d | ||
|
|
b824bc29b5 | ||
|
|
d168479a85 | ||
|
|
bedce59a6f | ||
|
|
b84be93bb7 | ||
|
|
a7da8eb593 | ||
|
|
575c51f5a0 | ||
|
|
c50dacff17 | ||
|
|
a99d4c3dd3 | ||
|
|
c1e5c36f7e | ||
|
|
4eff7abf3b | ||
|
|
a5531e83fd | ||
|
|
7f07f7d567 | ||
|
|
41fed96d96 | ||
|
|
ed20fa4da1 | ||
|
|
e06d1dbb0c | ||
|
|
1efa7e7b10 | ||
|
|
ce8f8f40d7 | ||
|
|
4efd673c19 | ||
|
|
ca27234a59 | ||
|
|
3111421866 | ||
|
|
42dcdde4e8 | ||
|
|
d6a0792bd9 | ||
|
|
7931d6a02a | ||
|
|
07e150a147 | ||
|
|
649de53382 | ||
|
|
ba6e41cf07 | ||
|
|
ba35d69831 | ||
|
|
8d04dffe69 | ||
|
|
8cb4bc2127 | ||
|
|
963bcba267 | ||
|
|
efc46e78f1 | ||
|
|
00b58d1206 | ||
|
|
f7fb1bfa1d | ||
|
|
8aa2ed175e | ||
|
|
fb3668f903 | ||
|
|
e4c7a7f30a | ||
|
|
57479dac27 | ||
|
|
47e1e18e7e | ||
|
|
172300158d | ||
|
|
df910277a2 | ||
|
|
9994133446 | ||
|
|
6afe0b0612 | ||
|
|
ca6e1aeb1b | ||
|
|
03de333de1 | ||
|
|
42ad8bbdb0 | ||
|
|
2501334ed7 | ||
|
|
15f19fc026 | ||
|
|
f3874ffaf2 | ||
|
|
4b74f65ed8 | ||
|
|
033a75888c | ||
|
|
6b874cedb8 | ||
|
|
5f01fb7f1a | ||
|
|
8eda5fcbbf | ||
|
|
c768c302da | ||
|
|
c7c05552f3 | ||
|
|
788dd04600 | ||
|
|
f55a69841d | ||
|
|
41df8413fc | ||
|
|
b2a42f03eb | ||
|
|
edea41d35e | ||
|
|
75ac263961 | ||
|
|
4c516e1f6f | ||
|
|
5c3ea4ec29 | ||
|
|
06eb561af5 | ||
|
|
51ef847c64 | ||
|
|
1349b4ba07 | ||
|
|
4f63377ff2 | ||
|
|
f86457ffd1 | ||
|
|
d368006526 | ||
|
|
cd3a1c1a94 | ||
|
|
dabd5b2cc8 | ||
|
|
9418fcafb6 | ||
|
|
ae8ff941f9 | ||
|
|
2e7f29cc9f | ||
|
|
c2a8415f78 | ||
|
|
0e36deec81 | ||
|
|
317df7e6a6 | ||
|
|
1fb11c23d8 | ||
|
|
52f7978d4d | ||
|
|
b9b5c84417 | ||
|
|
882f963e6b | ||
|
|
9773aa4c76 | ||
|
|
05aa960c25 | ||
|
|
19f01194b1 | ||
|
|
6dd5297f8a | ||
|
|
139ee17d39 | ||
|
|
f5d0fa2c21 | ||
|
|
a5299bdb1a | ||
|
|
99bfef2801 | ||
|
|
241de230bb | ||
|
|
ad6ea4fa49 | ||
|
|
815f427c95 | ||
|
|
754abdb861 | ||
|
|
8a364bbfaa | ||
|
|
e89d49d958 | ||
|
|
68c8948294 | ||
|
|
5ec5d2fa0b | ||
|
|
45d8cc706c | ||
|
|
d7e531c373 | ||
|
|
c5c1978584 | ||
|
|
5a20c63b70 | ||
|
|
480741894b | ||
|
|
c2c7231cad | ||
|
|
e6a31e7543 | ||
|
|
de4c9507e6 | ||
|
|
158ebef566 | ||
|
|
b14145f23b | ||
|
|
7c2c39cb11 | ||
|
|
a4aec56d18 | ||
|
|
3991178b8d | ||
|
|
a246278727 | ||
|
|
1f0f27bc65 | ||
|
|
a4a36422d4 | ||
|
|
543965411e | ||
|
|
bbbdd02a00 | ||
|
|
e1fef5c732 | ||
|
|
cbcc369a2d | ||
|
|
7499efecf6 | ||
|
|
18c3e90dbf | ||
|
|
00cdc52323 | ||
|
|
dd004aba9f | ||
|
|
2ab21acf11 | ||
|
|
6b6555852c | ||
|
|
8a54c91431 | ||
|
|
2092b805b6 | ||
|
|
53add78a28 | ||
|
|
386cdb830d | ||
|
|
aa41b48ba0 | ||
|
|
a8d99ae465 | ||
|
|
f38cc8e182 | ||
|
|
92f5aa7e10 | ||
|
|
f47cbe0be6 | ||
|
|
532801f9d6 | ||
|
|
14b715bdc7 | ||
|
|
0a62117851 | ||
|
|
86b00a9271 | ||
|
|
d35da909db | ||
|
|
73ad575306 | ||
|
|
292eb1de62 | ||
|
|
99a1636c62 | ||
|
|
c944b2e632 | ||
|
|
ae0841598f | ||
|
|
637730996a | ||
|
|
1a1d5e4a73 | ||
|
|
d10ade1963 | ||
|
|
84cab18120 | ||
|
|
3ff72794e5 | ||
|
|
e7abd3510a | ||
|
|
f96731c82b | ||
|
|
5c7c30cc4b | ||
|
|
a61b249d57 | ||
|
|
0b4eef48c5 | ||
|
|
90b8b612cc | ||
|
|
c0a8eaa3bf | ||
|
|
a0dd77ca01 | ||
|
|
f90802498f | ||
|
|
cf0b8b7681 | ||
|
|
2fd44e6ce4 | ||
|
|
23a4a22869 | ||
|
|
aa9894711b | ||
|
|
6d81b8a49a | ||
|
|
b1eab698d2 | ||
|
|
95d9706fe1 | ||
|
|
8b1f0b7cf4 | ||
|
|
2ec51fc0ad | ||
|
|
379b920d49 | ||
|
|
240704fb54 | ||
|
|
da8be072fa | ||
|
|
4c5e4ac8da | ||
|
|
5d973f8ef5 | ||
|
|
fa4426c814 | ||
|
|
c7cf43d1c1 | ||
|
|
7626ddc9c3 | ||
|
|
8642300d74 | ||
|
|
48ddd8b312 | ||
|
|
0f1a857576 | ||
|
|
945c1a9478 | ||
|
|
879195397e | ||
|
|
4df1772078 | ||
|
|
f8279bb7bb | ||
|
|
2a6f38c587 | ||
|
|
909f3d76d8 | ||
|
|
2d87f562f6 | ||
|
|
b1f6112c5c | ||
|
|
c38e33ca8b | ||
|
|
e6ecd05308 | ||
|
|
83a13ee0e6 | ||
|
|
662a8317a5 | ||
|
|
38ed39ebe0 | ||
|
|
83325b5b72 | ||
|
|
79f9e9b061 | ||
|
|
8c86bd1b31 | ||
|
|
f299190e46 | ||
|
|
feb7ac7ba8 | ||
|
|
fc3e1a4f18 | ||
|
|
28b66d1b58 | ||
|
|
2b30e0b5e0 | ||
|
|
8146cbdae7 | ||
|
|
56768ff237 | ||
|
|
710339d0a8 | ||
|
|
eff4cde738 | ||
|
|
3e86b2e666 | ||
|
|
0a16c350d1 | ||
|
|
c58c253cbf | ||
|
|
5baccafb23 | ||
|
|
076b602343 | ||
|
|
860d6c01ed | ||
|
|
a2fe46f8b3 | ||
|
|
bccf58df51 | ||
|
|
0fd3e36c23 | ||
|
|
a4b0e0c93c | ||
|
|
04eef37cad | ||
|
|
cd693dcf3e | ||
|
|
9358f81ac1 | ||
|
|
518b6e647b | ||
|
|
9947493977 | ||
|
|
e5ff36f1f1 | ||
|
|
d83d938ee3 | ||
|
|
c5116e5049 | ||
|
|
1288b4b28f | ||
|
|
df9096df80 | ||
|
|
0bd4d5bb0c | ||
|
|
1617264b69 | ||
|
|
91d6f14e3c | ||
|
|
6dd1fe0312 | ||
|
|
ed7c366b90 | ||
|
|
8b9adb4d74 | ||
|
|
0d8920347a | ||
|
|
4a2ad9fc2a | ||
|
|
25853b915e | ||
|
|
d4dc58eafe | ||
|
|
eb316fcb9c | ||
|
|
951dc757f8 | ||
|
|
f5bfb208e7 | ||
|
|
d5683d2016 | ||
|
|
de1f7ee39b | ||
|
|
74044f4821 | ||
|
|
8fdda2f5b7 | ||
|
|
15c89a8fff | ||
|
|
160183b6e8 | ||
|
|
70e87677b3 | ||
|
|
0658e42dad | ||
|
|
7fd2051d2c | ||
|
|
a3d9cce7c5 | ||
|
|
60bb0de9d6 | ||
|
|
9cfb50f90f | ||
|
|
5fd5c9e85c | ||
|
|
2bb6546ed6 | ||
|
|
09bd3a9642 | ||
|
|
b17c6ec054 | ||
|
|
a56b2f5993 | ||
|
|
b0e4c913c0 | ||
|
|
77c8d588fa | ||
|
|
02b54ff7e8 | ||
|
|
da2f684f18 | ||
|
|
c39da9643e | ||
|
|
2ecdf111b0 | ||
|
|
e9ebb2498b | ||
|
|
a1aaead6a9 | ||
|
|
e989e2f12c | ||
|
|
81c449fc99 | ||
|
|
1ed0fedfd5 | ||
|
|
28dcbc6426 | ||
|
|
43175f17e4 | ||
|
|
e439b53652 | ||
|
|
011f6102a5 | ||
|
|
2245863f04 | ||
|
|
30b3b5e2a7 | ||
|
|
3d9deb7770 | ||
|
|
012615ae46 | ||
|
|
cf904b6b2f | ||
|
|
3be3b78c90 | ||
|
|
8fc453658b | ||
|
|
3344d96329 | ||
|
|
1a262048a6 | ||
|
|
87ce264926 | ||
|
|
9d6c50ea2f | ||
|
|
35958a0965 | ||
|
|
80a58eea7b | ||
|
|
c9b2a7a980 | ||
|
|
f7d5d6c0cd | ||
|
|
6a5702f27c | ||
|
|
7ab92f0807 | ||
|
|
0768ce3600 | ||
|
|
7a1db0852b | ||
|
|
70c82c0f61 | ||
|
|
f18f81ec58 | ||
|
|
6c33b781c3 | ||
|
|
023c45f302 | ||
|
|
ba03ed4eea | ||
|
|
ee25249729 | ||
|
|
167d06d727 | ||
|
|
468a7a4a77 | ||
|
|
a33b4cc73b | ||
|
|
a16fb04280 | ||
|
|
240c111ebb | ||
|
|
6652df03df | ||
|
|
2ec3397b7d | ||
|
|
97052f3912 | ||
|
|
0d40df937b | ||
|
|
7d07c550ba | ||
|
|
37690cfde2 | ||
|
|
fc18299869 | ||
|
|
770c7e5878 | ||
|
|
4d3f137e1e | ||
|
|
23e7b53c13 | ||
|
|
e1e2674cd2 | ||
|
|
8310877f25 | ||
|
|
af868a7439 | ||
|
|
e83de5cde2 | ||
|
|
a278dd5a0a | ||
|
|
04e581db61 | ||
|
|
b50fb71dd1 | ||
|
|
6da6c755a5 | ||
|
|
0180b21dd1 | ||
|
|
f6f9816ef4 | ||
|
|
cc3adcc2c1 | ||
|
|
e1fa4ab8ec | ||
|
|
715d5daafe | ||
|
|
fd4d7c4b68 | ||
|
|
a98052fe74 | ||
|
|
ea09aa584d | ||
|
|
1df30ee83e | ||
|
|
314af4cdae | ||
|
|
1e6674f804 | ||
|
|
df5f722885 | ||
|
|
b4f3e30cd6 | ||
|
|
34a1512ff0 | ||
|
|
ec922ee5e4 | ||
|
|
0f80c57dca | ||
|
|
2f5a774325 | ||
|
|
c58fd744ee | ||
|
|
2d8a8bd3e5 | ||
|
|
13633414a3 | ||
|
|
d99f1e5f1f | ||
|
|
15ae24b541 | ||
|
|
1b36a381e8 | ||
|
|
e094043b80 | ||
|
|
a0d83537e5 | ||
|
|
7723d6281b | ||
|
|
52eae66c72 | ||
|
|
daf1495c4e | ||
|
|
e84d03f31d | ||
|
|
a254b2098f | ||
|
|
bb8adcce92 | ||
|
|
4f8e61dce9 | ||
|
|
d3b2bcdd79 | ||
|
|
bbe0eb96f4 | ||
|
|
95808f51f1 | ||
|
|
cc6cf8d7ce | ||
|
|
5b65f10828 | ||
|
|
a2bec1edb4 | ||
|
|
b11a84a347 | ||
|
|
0aaf236ad1 | ||
|
|
20d4e7139f | ||
|
|
bca0c6c172 | ||
|
|
4e498790d4 | ||
|
|
93a6047912 | ||
|
|
144d6c2379 | ||
|
|
645b8e4b0b | ||
|
|
d6bc60f968 | ||
|
|
2ce5a656e7 | ||
|
|
fa88ee148a | ||
|
|
5e33fe0aa7 | ||
|
|
9552ca7bb3 | ||
|
|
3632a98062 | ||
|
|
23c76caafa | ||
|
|
8a7ca6f0ba | ||
|
|
8ea25eeb3c | ||
|
|
e004e05037 | ||
|
|
d0e057cabd | ||
|
|
68ec7ae41e | ||
|
|
81f48c0001 | ||
|
|
50886fcb31 | ||
|
|
5793bb8fac | ||
|
|
f223ed21a5 | ||
|
|
d74e044be4 | ||
|
|
cf75a19e8d | ||
|
|
528dbf909a | ||
|
|
a45791d2c0 | ||
|
|
d19232a848 | ||
|
|
564a1b0fbb | ||
|
|
574cfae470 | ||
|
|
9bcd1245d9 | ||
|
|
89a4cb08e8 | ||
|
|
91dbf3c09d | ||
|
|
3eea77709b | ||
|
|
08d8709ef6 | ||
|
|
5ebd38f1d6 | ||
|
|
03ec27be66 | ||
|
|
455f994673 | ||
|
|
4f843ada3c | ||
|
|
35740456a4 | ||
|
|
8486e79cad | ||
|
|
6b7a286158 | ||
|
|
81db3461fe | ||
|
|
e2dfea8c12 | ||
|
|
6165eda356 | ||
|
|
0d8a2c8c44 | ||
|
|
8d0fde6f60 | ||
|
|
237bac4500 | ||
|
|
5f9dc13207 | ||
|
|
9b212dc518 | ||
|
|
94bfe116aa | ||
|
|
50e0221dd1 | ||
|
|
abec06f50c | ||
|
|
ee4f9da385 | ||
|
|
848779e465 | ||
|
|
60ce0aee1a | ||
|
|
0f65779d29 | ||
|
|
394040e22c | ||
|
|
0a90f235de | ||
|
|
2447875b26 | ||
|
|
df72c9c6cc | ||
|
|
6aa9005785 | ||
|
|
c3769b8874 | ||
|
|
c0c06fdc53 | ||
|
|
2df4532d50 | ||
|
|
6a05967bef | ||
|
|
41700a6996 | ||
|
|
c9c8f920d2 | ||
|
|
8c715e025a | ||
|
|
3eece20967 | ||
|
|
21e21f1e02 | ||
|
|
84abf5d84f | ||
|
|
83fea78d38 | ||
|
|
1987dad527 | ||
|
|
cbaa7fdee6 | ||
|
|
73dc52980e | ||
|
|
8b0f9759ec | ||
|
|
d6baadbaf2 | ||
|
|
569d63d0c9 | ||
|
|
a529427bc9 | ||
|
|
2f30b03060 | ||
|
|
d34aec29ed | ||
|
|
9271dd0cc7 | ||
|
|
3d2e9efb1e | ||
|
|
b840ab8f8b | ||
|
|
deef600caf | ||
|
|
d146ae7275 | ||
|
|
801a2339ee | ||
|
|
2f757babb9 | ||
|
|
2398311455 | ||
|
|
5d6dac495e | ||
|
|
3915943f57 | ||
|
|
e0d5eedff7 | ||
|
|
4e1ef482ad | ||
|
|
6b0b8078e9 | ||
|
|
377f20ed49 | ||
|
|
2cc111ad5d | ||
|
|
ab2c3468d5 | ||
|
|
54196a8d4f | ||
|
|
9bd3f10f41 | ||
|
|
93ee2a9b64 | ||
|
|
a4c433c749 | ||
|
|
858c974440 | ||
|
|
d2394d3ac3 | ||
|
|
d4684d6302 | ||
|
|
5fa088114c | ||
|
|
8930f510fc | ||
|
|
7134b44f5b | ||
|
|
86bc784e41 | ||
|
|
64c098627e | ||
|
|
1b0a369889 | ||
|
|
73fa0cdc31 | ||
|
|
d8893b959d | ||
|
|
f409168126 | ||
|
|
843d95966e | ||
|
|
a99721004b | ||
|
|
a6b6c312b8 | ||
|
|
e28c477075 | ||
|
|
8e91f7b701 | ||
|
|
da3dd65726 | ||
|
|
c7d99c37fb | ||
|
|
a61b1567d0 | ||
|
|
7039eaf995 | ||
|
|
d99aa3192c | ||
|
|
8a664c524c | ||
|
|
d6110d7332 | ||
|
|
1db72ad6e2 | ||
|
|
6d5bfcfe44 | ||
|
|
fb88e5d652 | ||
|
|
f5bce0cd9e | ||
|
|
62158601fb | ||
|
|
a12583f762 | ||
|
|
efe0f8449c | ||
|
|
c313857485 | ||
|
|
1ca1a4b1cc | ||
|
|
90746bd8c9 | ||
|
|
a4c5375a63 | ||
|
|
986762a0bc | ||
|
|
7707f7557a | ||
|
|
9e02271a68 | ||
|
|
eb1f76d8bd | ||
|
|
e8c64b802d | ||
|
|
23d3b79d9a | ||
|
|
387a5fb03b | ||
|
|
34fb39da5b | ||
|
|
b0a9c1e0fa | ||
|
|
b52c7bba4b | ||
|
|
56ba9a30ac | ||
|
|
4ef89b5ccb | ||
|
|
109012cae8 | ||
|
|
7e78480840 | ||
|
|
3b54b49b0b | ||
|
|
434ed1b91c | ||
|
|
ac057ebe93 | ||
|
|
a78068cbe9 | ||
|
|
3708b3be63 | ||
|
|
e0a79699ea | ||
|
|
b76860fd2b | ||
|
|
45c0a97142 | ||
|
|
5700f4ac81 | ||
|
|
9efab789aa | ||
|
|
35a43a1e82 | ||
|
|
974f89d5bf | ||
|
|
df76f010da | ||
|
|
10a470a5fa | ||
|
|
1490ba179c | ||
|
|
c75b49667e | ||
|
|
1485dd07ae | ||
|
|
54eb575000 | ||
|
|
ac7f1f6719 | ||
|
|
da837fa9d8 | ||
|
|
a1df49ef3e | ||
|
|
f5ea47a7b2 | ||
|
|
ec52a4a235 | ||
|
|
bb1bbebfd6 | ||
|
|
9fdd0e3a0a | ||
|
|
64cec5ff7d | ||
|
|
b931a225ba | ||
|
|
e5c2b25351 | ||
|
|
77f036ee21 | ||
|
|
4eb69dc0fe | ||
|
|
0d110ed47c | ||
|
|
b96eb45877 | ||
|
|
a55001ad1d | ||
|
|
23a6b19985 | ||
|
|
0abbd7ac97 | ||
|
|
83911a7290 | ||
|
|
73b2dd7a59 | ||
|
|
35d9a9815e | ||
|
|
e3d6525d83 | ||
|
|
c718b5618e | ||
|
|
41f66f33d5 | ||
|
|
5255b72f67 | ||
|
|
a9d75e2223 | ||
|
|
6b4cb35149 | ||
|
|
5df3f5d136 | ||
|
|
1879dbca8a | ||
|
|
2de67538a2 | ||
|
|
dd1a3fc60a | ||
|
|
a0619fc101 | ||
|
|
9d23101c9a | ||
|
|
044d070667 | ||
|
|
436ae1333c | ||
|
|
8a4537fc73 | ||
|
|
d1293d9fb0 | ||
|
|
3e7dc57289 | ||
|
|
219eddf33e | ||
|
|
b6f9cf4ba7 | ||
|
|
07ee7a341b | ||
|
|
67c225f971 | ||
|
|
a028e445fe | ||
|
|
b725829bfd | ||
|
|
e1f2ccd138 | ||
|
|
7e87e9e8ad | ||
|
|
d530cc86f3 | ||
|
|
5e34235a36 | ||
|
|
63f1ca9243 | ||
|
|
498d5c0660 | ||
|
|
384aeda645 | ||
|
|
7ad36ee36c | ||
|
|
e99884d0c0 | ||
|
|
9fcaea2235 | ||
|
|
dd1f8295c4 | ||
|
|
bc9c1d4824 | ||
|
|
21771aa1a6 | ||
|
|
d013a13be6 | ||
|
|
797a3e1cd9 | ||
|
|
3687a7c717 | ||
|
|
030d01102c | ||
|
|
c7e6992239 | ||
|
|
cb881108c3 | ||
|
|
1a6f23ad2b | ||
|
|
9b9cd451d1 | ||
|
|
fe52e969b6 | ||
|
|
213f61e826 | ||
|
|
37ece36685 | ||
|
|
958a8f66a0 | ||
|
|
badb3729c9 | ||
|
|
e228de18e2 | ||
|
|
05cd715c39 | ||
|
|
a555686bcd | ||
|
|
16860c3f79 | ||
|
|
1d87f24715 | ||
|
|
6fa9393a25 | ||
|
|
354ec19e3b | ||
|
|
99c247fe9f | ||
|
|
2d477946ea | ||
|
|
c009a289d5 | ||
|
|
4ebd597354 | ||
|
|
34d28189ea | ||
|
|
124ab28c9e | ||
|
|
c36f4266d2 | ||
|
|
91ab91cfb9 | ||
|
|
e756b09e5b | ||
|
|
701e116dde | ||
|
|
aa6ee10759 | ||
|
|
97e0df1363 | ||
|
|
1dddbf35d6 | ||
|
|
8be41ad946 | ||
|
|
7e9936a3cd | ||
|
|
83f56c3ad9 | ||
|
|
8c98ddb843 | ||
|
|
0c051fb78e | ||
|
|
c13f01c4f9 | ||
|
|
2ee3a7282b | ||
|
|
fa42b6acec | ||
|
|
b5527415d6 | ||
|
|
04f106aad8 | ||
|
|
cbb9314531 | ||
|
|
44052e7d8d | ||
|
|
579cff478d | ||
|
|
f84fcfee04 | ||
|
|
7846544c1b | ||
|
|
930c8b63db | ||
|
|
491eeb9878 | ||
|
|
daca82bdfc | ||
|
|
131aa14afa | ||
|
|
57d718fde1 | ||
|
|
cb79859c0e | ||
|
|
790bd87bc3 | ||
|
|
ba07c32300 | ||
|
|
8d16cdf12f | ||
|
|
a25d935a1d | ||
|
|
f8aec7571f | ||
|
|
1f7fb1591d | ||
|
|
5f103ee33d | ||
|
|
240ea8a136 | ||
|
|
e4c61a5fab | ||
|
|
b27138e93f | ||
|
|
1959f0ae12 | ||
|
|
57770ce84c | ||
|
|
2dca24cc28 | ||
|
|
7c45178076 | ||
|
|
e12bde8e5a | ||
|
|
5031221369 | ||
|
|
43d391501e | ||
|
|
7df4ff2606 | ||
|
|
b51d7e9f6a | ||
|
|
43871e393c | ||
|
|
ef3569741c | ||
|
|
84fcefba1b | ||
|
|
7525cb5ee4 | ||
|
|
e0d554e0ca | ||
|
|
599f81daa9 | ||
|
|
8bf5be0cc4 | ||
|
|
f0a587d310 | ||
|
|
29bed6ff69 | ||
|
|
8a37f9599f | ||
|
|
fddbc9e5cb | ||
|
|
b7862ac23a | ||
|
|
90a5c9e489 | ||
|
|
2924d0a20f | ||
|
|
3041c95408 | ||
|
|
8a67a0a80a | ||
|
|
2ee8f07432 | ||
|
|
18b29832ac | ||
|
|
4af49038cd | ||
|
|
3fe3353696 | ||
|
|
5d5e45ec0d | ||
|
|
79afe2d7fb | ||
|
|
429e1c7a27 | ||
|
|
e73d867dab | ||
|
|
1c37ea5860 | ||
|
|
96f4033a40 | ||
|
|
5684906d3e | ||
|
|
b0264a6af2 | ||
|
|
e2069d2e29 | ||
|
|
45414e6cfe | ||
|
|
dd529575f6 | ||
|
|
bc9464fcbd | ||
|
|
95745ef4c8 | ||
|
|
e5defeaada | ||
|
|
eebb8b87a4 | ||
|
|
e15f60b819 | ||
|
|
4e5fd92e67 | ||
|
|
d98b7aab61 | ||
|
|
8edee76f8b | ||
|
|
ca963137e9 | ||
|
|
6eefecf31e | ||
|
|
9047b4b487 | ||
|
|
0ab0cb1173 | ||
|
|
608c0dc613 | ||
|
|
aedc473f64 | ||
|
|
be87151f1c | ||
|
|
5e54c0fa91 | ||
|
|
3bb66d03ee | ||
|
|
3478c16e10 | ||
|
|
bd00256e0c | ||
|
|
64342d200c | ||
|
|
b196635816 | ||
|
|
5342a482b5 | ||
|
|
391eb4b66c | ||
|
|
ea020948fc | ||
|
|
6062aa5dcd | ||
|
|
8fb0a2d417 | ||
|
|
aea9b1d6cc | ||
|
|
d9087dd2b6 | ||
|
|
868f83e6ca | ||
|
|
a1186727af | ||
|
|
9268a1f59c | ||
|
|
ca1efc3d77 | ||
|
|
9692616958 | ||
|
|
bb39f104f4 | ||
|
|
be21671c1c | ||
|
|
ee034185a5 | ||
|
|
a84a10ff03 | ||
|
|
731f28a8c0 | ||
|
|
98be191b25 | ||
|
|
8a3b5c6332 | ||
|
|
5bbcf0482b | ||
|
|
02c51c83cd | ||
|
|
94a39619b5 | ||
|
|
f16b53d92a | ||
|
|
f0f2d00924 | ||
|
|
81b57a57c7 | ||
|
|
48e5d82186 | ||
|
|
7638b2c9e8 | ||
|
|
dbd05e6c92 | ||
|
|
a587815ddc | ||
|
|
c3c83086bc | ||
|
|
4d08e7b0ae | ||
|
|
04291fe71e | ||
|
|
973bb63dca | ||
|
|
4868f758bf | ||
|
|
ae3f85bd5b | ||
|
|
3fb8466c40 | ||
|
|
b396b56e34 | ||
|
|
87f3d53607 | ||
|
|
8e9faa416d | ||
|
|
41effadb94 | ||
|
|
bbb50caa89 | ||
|
|
af235e564e | ||
|
|
1ff0abc661 | ||
|
|
645011427a | ||
|
|
69e82e15a3 | ||
|
|
7c541117b3 | ||
|
|
4cf62e816e | ||
|
|
2b671b8476 | ||
|
|
32f906fe9f | ||
|
|
4158693470 | ||
|
|
92e5982127 | ||
|
|
ee7779df17 | ||
|
|
ddbdfafc05 | ||
|
|
f169c8dbb0 | ||
|
|
91753c53cd | ||
|
|
3655601693 |
22
.dockerignore
Normal file
22
.dockerignore
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# 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
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -19,6 +19,10 @@
|
|||||||
# mac stuff
|
# mac stuff
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# npm stuff
|
||||||
|
npm-debug.log
|
||||||
|
node_modules
|
||||||
|
|
||||||
# gdb files
|
# gdb files
|
||||||
.gdb_history
|
.gdb_history
|
||||||
|
|
||||||
|
|||||||
276
.gitlab-ci.yml
276
.gitlab-ci.yml
@@ -1,39 +1,41 @@
|
|||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
- js-build
|
- js-build
|
||||||
- build
|
|
||||||
- push-release
|
- push-release
|
||||||
|
- build
|
||||||
variables:
|
variables:
|
||||||
GIT_DEPTH: "3"
|
GIT_DEPTH: "3"
|
||||||
SIMPLECOV: "true"
|
SIMPLECOV: "true"
|
||||||
RUST_BACKTRACE: "1"
|
RUST_BACKTRACE: "1"
|
||||||
RUSTFLAGS: ""
|
RUSTFLAGS: ""
|
||||||
CARGOFLAGS: ""
|
CARGOFLAGS: ""
|
||||||
|
CI_SERVER_NAME: "GitLab CI"
|
||||||
cache:
|
cache:
|
||||||
key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME"
|
key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME"
|
||||||
untracked: true
|
untracked: true
|
||||||
linux-stable:
|
linux-stable:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust:stable
|
image: parity/rust:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
- triggers
|
- triggers
|
||||||
script:
|
script:
|
||||||
|
- rustup default stable
|
||||||
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
|
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
|
||||||
- cargo build -j $(nproc) --release -p evmbin
|
- cargo build -j $(nproc) --release -p evmbin
|
||||||
- cargo build -j $(nproc) --release -p ethstore
|
- cargo build -j $(nproc) --release -p ethstore-cli
|
||||||
- cargo build -j $(nproc) --release -p ethkey
|
- cargo build -j $(nproc) --release -p ethkey-cli
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
- strip target/release/evm
|
- strip target/release/parity-evm
|
||||||
- strip target/release/ethstore
|
- strip target/release/ethstore
|
||||||
- strip target/release/ethkey
|
- strip target/release/ethkey
|
||||||
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||||
- md5sum target/release/parity > parity.md5
|
- md5sum target/release/parity > parity.md5
|
||||||
- sh scripts/deb-build.sh amd64
|
- sh scripts/deb-build.sh amd64
|
||||||
- cp target/release/parity deb/usr/bin/parity
|
- cp target/release/parity deb/usr/bin/parity
|
||||||
- cp target/release/evm deb/usr/bin/evm
|
- cp target/release/parity-evm deb/usr/bin/parity-evm
|
||||||
- cp target/release/ethstore deb/usr/bin/ethstore
|
- cp target/release/ethstore deb/usr/bin/ethstore
|
||||||
- cp target/release/ethkey deb/usr/bin/ethkey
|
- cp target/release/ethkey deb/usr/bin/ethkey
|
||||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
@@ -41,7 +43,7 @@ linux-stable:
|
|||||||
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
- 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 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 --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.md5 --body parity.md5
|
||||||
@@ -55,13 +57,48 @@ linux-stable:
|
|||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- target/release/parity
|
- target/release/parity
|
||||||
- target/release/parity/evmbin
|
- target/release/parity-evm
|
||||||
- target/release/parity/ethstore
|
- target/release/ethstore
|
||||||
- target/release/parity/ethkey
|
- target/release/ethkey
|
||||||
name: "stable-x86_64-unknown-linux-gnu_parity"
|
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:
|
linux-stable-debian:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-debian:latest
|
image: parity/rust-debian:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
@@ -70,17 +107,17 @@ linux-stable-debian:
|
|||||||
script:
|
script:
|
||||||
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
|
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
|
||||||
- cargo build -j $(nproc) --release -p evmbin
|
- cargo build -j $(nproc) --release -p evmbin
|
||||||
- cargo build -j $(nproc) --release -p ethstore
|
- cargo build -j $(nproc) --release -p ethstore-cli
|
||||||
- cargo build -j $(nproc) --release -p ethkey
|
- cargo build -j $(nproc) --release -p ethkey-cli
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
- strip target/release/evm
|
- strip target/release/parity-evm
|
||||||
- strip target/release/ethstore
|
- strip target/release/ethstore
|
||||||
- strip target/release/ethkey
|
- strip target/release/ethkey
|
||||||
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||||
- md5sum target/release/parity > parity.md5
|
- md5sum target/release/parity > parity.md5
|
||||||
- sh scripts/deb-build.sh amd64
|
- sh scripts/deb-build.sh amd64
|
||||||
- cp target/release/parity deb/usr/bin/parity
|
- cp target/release/parity deb/usr/bin/parity
|
||||||
- cp target/release/evm deb/usr/bin/evm
|
- cp target/release/parity-evm deb/usr/bin/parity-evm
|
||||||
- cp target/release/ethstore deb/usr/bin/ethstore
|
- cp target/release/ethstore deb/usr/bin/ethstore
|
||||||
- cp target/release/ethkey deb/usr/bin/ethkey
|
- cp target/release/ethkey deb/usr/bin/ethkey
|
||||||
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
@@ -88,7 +125,7 @@ linux-stable-debian:
|
|||||||
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
- 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 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 --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.md5 --body parity.md5
|
||||||
@@ -105,13 +142,14 @@ linux-stable-debian:
|
|||||||
name: "stable-x86_64-unknown-debian-gnu_parity"
|
name: "stable-x86_64-unknown-debian-gnu_parity"
|
||||||
linux-beta:
|
linux-beta:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust:beta
|
image: parity/rust:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
- triggers
|
- triggers
|
||||||
script:
|
script:
|
||||||
|
- rustup default beta
|
||||||
- cargo build -j $(nproc) --release $CARGOFLAGS
|
- cargo build -j $(nproc) --release $CARGOFLAGS
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
tags:
|
tags:
|
||||||
@@ -124,13 +162,14 @@ linux-beta:
|
|||||||
allow_failure: true
|
allow_failure: true
|
||||||
linux-nightly:
|
linux-nightly:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust:nightly
|
image: parity/rust:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- stable
|
- stable
|
||||||
- triggers
|
- triggers
|
||||||
script:
|
script:
|
||||||
|
- rustup default nightly
|
||||||
- cargo build -j $(nproc) --release $CARGOFLAGS
|
- cargo build -j $(nproc) --release $CARGOFLAGS
|
||||||
- strip target/release/parity
|
- strip target/release/parity
|
||||||
tags:
|
tags:
|
||||||
@@ -143,7 +182,7 @@ linux-nightly:
|
|||||||
allow_failure: true
|
allow_failure: true
|
||||||
linux-centos:
|
linux-centos:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-centos:latest
|
image: parity/rust-centos:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
@@ -154,15 +193,30 @@ linux-centos:
|
|||||||
- export CC="gcc"
|
- export CC="gcc"
|
||||||
- export PLATFORM=x86_64-unknown-centos-gnu
|
- export PLATFORM=x86_64-unknown-centos-gnu
|
||||||
- cargo build -j $(nproc) --release --features final $CARGOFLAGS
|
- 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
|
||||||
|
- strip target/release/parity-evm
|
||||||
|
- strip target/release/ethstore
|
||||||
|
- strip target/release/ethkey
|
||||||
- md5sum target/release/parity > parity.md5
|
- 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)
|
- export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
- 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 s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
||||||
|
- 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: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
|
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||||
tags:
|
tags:
|
||||||
@@ -174,7 +228,7 @@ linux-centos:
|
|||||||
name: "x86_64-unknown-centos-gnu_parity"
|
name: "x86_64-unknown-centos-gnu_parity"
|
||||||
linux-i686:
|
linux-i686:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-i686:latest
|
image: parity/rust-i686:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
@@ -185,18 +239,28 @@ linux-i686:
|
|||||||
- export HOST_CXX=g++
|
- export HOST_CXX=g++
|
||||||
- export COMMIT=$(git rev-parse HEAD)
|
- export COMMIT=$(git rev-parse HEAD)
|
||||||
- export PLATFORM=i686-unknown-linux-gnu
|
- export PLATFORM=i686-unknown-linux-gnu
|
||||||
- cargo build -j $(nproc) --target i686-unknown-linux-gnu --features final --release $CARGOFLAGS
|
- 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
|
- strip target/$PLATFORM/release/parity
|
||||||
- md5sum target/$PLATFORM/release/parity > parity.md5
|
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||||
- export SHA3=$(target/$PLATFORM/release/parity tools hash target/$PLATFORM/release/parity)
|
- export SHA3=$(target/$PLATFORM/release/parity tools hash target/$PLATFORM/release/parity)
|
||||||
- sh scripts/deb-build.sh i386
|
- sh scripts/deb-build.sh i386
|
||||||
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
|
- 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")
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
- dpkg-deb -b deb "parity_"$VER"_i386.deb"
|
- dpkg-deb -b deb "parity_"$VER"_i386.deb"
|
||||||
- md5sum "parity_"$VER"_i386.deb" > "parity_"$VER"_i386.deb.md5"
|
- md5sum "parity_"$VER"_i386.deb" > "parity_"$VER"_i386.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
- 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 s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||||
@@ -214,7 +278,7 @@ linux-i686:
|
|||||||
allow_failure: true
|
allow_failure: true
|
||||||
linux-armv7:
|
linux-armv7:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-armv7:latest
|
image: parity/rust-armv7:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
@@ -232,17 +296,29 @@ linux-armv7:
|
|||||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
|
- 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
|
||||||
- export SHA3=$(rhash --sha3-256 ~/Core/parity/target/release/parity -p %h)
|
- 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
|
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||||
- sh scripts/deb-build.sh armhf
|
- sh scripts/deb-build.sh armhf
|
||||||
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
|
- 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")
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||||
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
- 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 s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||||
@@ -260,7 +336,7 @@ linux-armv7:
|
|||||||
allow_failure: true
|
allow_failure: true
|
||||||
linux-arm:
|
linux-arm:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-arm:latest
|
image: parity/rust-arm:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
@@ -278,17 +354,26 @@ linux-arm:
|
|||||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
|
- 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
|
||||||
- export SHA3=$(rhash --sha3-256 ~/Core/parity/target/release/parity -p %h)
|
- 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
|
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||||
- sh scripts/deb-build.sh armhf
|
- sh scripts/deb-build.sh armhf
|
||||||
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
|
- 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")
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
- dpkg-deb -b deb "parity_"$VER"_armhf.deb"
|
||||||
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
- 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 s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
||||||
@@ -304,48 +389,9 @@ linux-arm:
|
|||||||
- target/arm-unknown-linux-gnueabihf/release/parity
|
- target/arm-unknown-linux-gnueabihf/release/parity
|
||||||
name: "arm-unknown-linux-gnueabihf_parity"
|
name: "arm-unknown-linux-gnueabihf_parity"
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
linux-armv6:
|
|
||||||
stage: build
|
|
||||||
image: ethcore/rust-armv6:latest
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
# - tags
|
|
||||||
# - stable
|
|
||||||
# - triggers
|
|
||||||
script:
|
|
||||||
- export CC=arm-linux-gnueabi-gcc
|
|
||||||
- export CXX=arm-linux-gnueabi-g++
|
|
||||||
- export HOST_CC=gcc
|
|
||||||
- export HOST_CXX=g++
|
|
||||||
- export PLATFORM=arm-unknown-linux-gnueabi
|
|
||||||
- rm -rf .cargo
|
|
||||||
- mkdir -p .cargo
|
|
||||||
- echo "[target.$PLATFORM]" >> .cargo/config
|
|
||||||
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
|
||||||
- cat .cargo/config
|
|
||||||
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
|
|
||||||
- arm-linux-gnueabi-strip target/$PLATFORM/release/parity
|
|
||||||
- export SHA3=$(rhash --sha3-256 ~/Core/parity/target/release/parity -p %h)
|
|
||||||
- md5sum target/$PLATFORM/release/parity > parity.md5
|
|
||||||
- aws configure set aws_access_key_id $s3_key
|
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
|
||||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
|
||||||
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/$PLATFORM
|
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity --body target/$PLATFORM/release/parity
|
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/parity.md5 --body parity.md5
|
|
||||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1337/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
|
||||||
- curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
|
||||||
tags:
|
|
||||||
- rust
|
|
||||||
- rust-arm
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- target/arm-unknown-linux-gnueabi/release/parity
|
|
||||||
name: "arm-unknown-linux-gnueabi_parity"
|
|
||||||
allow_failure: true
|
|
||||||
linux-aarch64:
|
linux-aarch64:
|
||||||
stage: build
|
stage: build
|
||||||
image: ethcore/rust-aarch64:latest
|
image: parity/rust-arm64:gitlab-ci
|
||||||
only:
|
only:
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
@@ -363,17 +409,26 @@ linux-aarch64:
|
|||||||
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
||||||
- cat .cargo/config
|
- cat .cargo/config
|
||||||
- cargo build -j $(nproc) --target $PLATFORM --features final --release $CARGOFLAGS
|
- 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
|
||||||
- export SHA3=$(rhash --sha3-256 ~/Core/parity/target/release/parity -p %h)
|
- 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
|
- md5sum target/$PLATFORM/release/parity > parity.md5
|
||||||
- sh scripts/deb-build.sh arm64
|
- sh scripts/deb-build.sh arm64
|
||||||
- cp target/$PLATFORM/release/parity deb/usr/bin/parity
|
- 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")
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
- dpkg-deb -b deb "parity_"$VER"_arm64.deb"
|
- dpkg-deb -b deb "parity_"$VER"_arm64.deb"
|
||||||
- md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
|
- md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
- 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 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.md5 --body parity.md5
|
||||||
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
||||||
@@ -398,8 +453,12 @@ darwin:
|
|||||||
script: |
|
script: |
|
||||||
export COMMIT=$(git rev-parse HEAD)
|
export COMMIT=$(git rev-parse HEAD)
|
||||||
export PLATFORM=x86_64-apple-darwin
|
export PLATFORM=x86_64-apple-darwin
|
||||||
|
rustup default stable
|
||||||
|
cargo clean
|
||||||
cargo build -j 8 --features final --release #$CARGOFLAGS
|
cargo build -j 8 --features final --release #$CARGOFLAGS
|
||||||
cargo build -j 8 --features final --release -p ethstore #$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
|
rm -rf parity.md5
|
||||||
md5sum target/release/parity > parity.md5
|
md5sum target/release/parity > parity.md5
|
||||||
export SHA3=$(target/release/parity tools hash target/release/parity)
|
export SHA3=$(target/release/parity tools hash target/release/parity)
|
||||||
@@ -409,16 +468,16 @@ darwin:
|
|||||||
packagesbuild -v mac/Parity.pkgproj
|
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
|
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")
|
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"-osx-installer-EXPERIMENTAL.pkg"
|
mv target/release/Parity\ Ethereum-signed.pkg "parity-"$VER"-macos-installer.pkg"
|
||||||
md5sum "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" >> "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
|
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_access_key_id $s3_key
|
||||||
aws configure set aws_secret_access_key $s3_secret
|
aws configure set aws_secret_access_key $s3_secret
|
||||||
if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
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 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 --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.md5 --body parity.md5
|
||||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
|
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-macos-installer.pkg" --body "parity-"$VER"-macos-installer.pkg"
|
||||||
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
|
aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/$PLATFORM/"parity-"$VER"-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: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
|
curl --data "commit=$CI_BUILD_REF&sha3=$SHA3&filename=parity&secret=$RELEASES_SECRET" http://update.parity.io:1338/push-build/$CI_BUILD_REF_NAME/$PLATFORM
|
||||||
tags:
|
tags:
|
||||||
@@ -444,12 +503,16 @@ windows:
|
|||||||
- set RUST_BACKTRACE=1
|
- set RUST_BACKTRACE=1
|
||||||
- set RUSTFLAGS=%RUSTFLAGS%
|
- set RUSTFLAGS=%RUSTFLAGS%
|
||||||
- rustup default stable-x86_64-pc-windows-msvc
|
- rustup default stable-x86_64-pc-windows-msvc
|
||||||
|
- cargo clean
|
||||||
- cargo build --features final --release #%CARGOFLAGS%
|
- 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
|
- signtool sign /f %keyfile% /p %certpass% target\release\parity.exe
|
||||||
- target\release\parity.exe tools hash target\release\parity.exe > parity.sha3
|
- target\release\parity.exe tools hash target\release\parity.exe > parity.sha3
|
||||||
- set /P SHA3=<parity.sha3
|
- set /P SHA3=<parity.sha3
|
||||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
- curl -sL --url "https://github.com/paritytech/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll
|
||||||
- curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe
|
- 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
|
- msbuild windows\ptray\ptray.vcxproj /p:Platform=x64 /p:Configuration=Release
|
||||||
- signtool sign /f %keyfile% /p %certpass% windows\ptray\x64\release\ptray.exe
|
- signtool sign /f %keyfile% /p %certpass% windows\ptray\x64\release\ptray.exe
|
||||||
- cd nsis
|
- cd nsis
|
||||||
@@ -460,17 +523,17 @@ windows:
|
|||||||
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
- zip win-installer.zip InstallParity.exe InstallParity.exe.md5
|
||||||
- md5sums win-installer.zip > win-installer.zip.md5
|
- md5sums win-installer.zip > win-installer.zip.md5
|
||||||
- cd ..\target\release\
|
- cd ..\target\release\
|
||||||
- md5sums parity.exe parity.pdb > parity.md5
|
|
||||||
- md5sums parity.exe > parity.exe.md5
|
- md5sums parity.exe > parity.exe.md5
|
||||||
- zip parity.zip parity.exe parity.pdb parity.md5
|
- zip parity.zip parity.exe parity.md5
|
||||||
- md5sums parity.zip > parity.zip.md5
|
- md5sums parity.zip > parity.zip.md5
|
||||||
- cd ..\..
|
- cd ..\..
|
||||||
- aws configure set aws_access_key_id %s3_key%
|
- aws configure set aws_access_key_id %s3_key%
|
||||||
- aws configure set aws_secret_access_key %s3_secret%
|
- aws configure set aws_secret_access_key %s3_secret%
|
||||||
- echo %CI_BUILD_REF_NAME%
|
- 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 "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 "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 "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%
|
- echo %S3_BUCKET%
|
||||||
- aws s3 rm --recursive s3://%S3_BUCKET%/%CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc
|
- 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 --body target\release\parity.exe
|
||||||
@@ -500,17 +563,33 @@ docker-build:
|
|||||||
- docker info
|
- docker info
|
||||||
script:
|
script:
|
||||||
- if [ "$CI_BUILD_REF_NAME" == "beta-release" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
- if [ "$CI_BUILD_REF_NAME" == "beta-release" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
||||||
- docker login -u $Docker_Hub_User -p $Docker_Hub_Pass
|
- echo "Tag:" $DOCKER_TAG
|
||||||
|
- docker login -u $Docker_Hub_User_Parity -p $Docker_Hub_Pass_Parity
|
||||||
- sh scripts/docker-build.sh $DOCKER_TAG
|
- sh scripts/docker-build.sh $DOCKER_TAG
|
||||||
|
- docker logout
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- 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:
|
test-darwin:
|
||||||
stage: test
|
stage: test
|
||||||
only:
|
only:
|
||||||
- triggers
|
- triggers
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init --recursive
|
- 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)
|
- 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:
|
script:
|
||||||
- export RUST_BACKTRACE=1
|
- 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
|
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
|
||||||
@@ -525,25 +604,26 @@ test-windows:
|
|||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
script:
|
script:
|
||||||
- set RUST_BACKTRACE=1
|
- set RUST_BACKTRACE=1
|
||||||
- echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release
|
- echo cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p 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:
|
tags:
|
||||||
- rust-windows
|
- rust-windows
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
test-rust-stable:
|
test-rust-stable:
|
||||||
stage: test
|
stage: test
|
||||||
image: ethcore/rust:stable
|
image: parity/rust:gitlab-ci
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init --recursive
|
- 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)
|
- 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:
|
script:
|
||||||
|
- rustup show
|
||||||
- export RUST_BACKTRACE=1
|
- export RUST_BACKTRACE=1
|
||||||
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS&&./scripts/cov.sh "$KCOV_CMD"; fi
|
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-stable
|
- rust-stable
|
||||||
js-test:
|
js-test:
|
||||||
stage: test
|
stage: test
|
||||||
image: ethcore/rust:stable
|
image: parity/rust:gitlab-ci
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init --recursive
|
- 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)
|
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
|
||||||
@@ -557,11 +637,13 @@ test-rust-beta:
|
|||||||
stage: test
|
stage: test
|
||||||
only:
|
only:
|
||||||
- triggers
|
- triggers
|
||||||
image: ethcore/rust:beta
|
- master
|
||||||
|
image: parity/rust:gitlab-ci
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init --recursive
|
- 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)
|
- 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:
|
script:
|
||||||
|
- rustup default beta
|
||||||
- export RUST_BACKTRACE=1
|
- 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
|
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
|
||||||
tags:
|
tags:
|
||||||
@@ -572,11 +654,13 @@ test-rust-nightly:
|
|||||||
stage: test
|
stage: test
|
||||||
only:
|
only:
|
||||||
- triggers
|
- triggers
|
||||||
image: ethcore/rust:nightly
|
- master
|
||||||
|
image: parity/rust:gitlab-ci
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init --recursive
|
- 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)
|
- 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:
|
script:
|
||||||
|
- rustup default nightly
|
||||||
- export RUST_BACKTRACE=1
|
- 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
|
- if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi
|
||||||
tags:
|
tags:
|
||||||
@@ -590,12 +674,14 @@ js-release:
|
|||||||
- beta
|
- beta
|
||||||
- stable
|
- stable
|
||||||
- tags
|
- tags
|
||||||
image: ethcore/rust:stable
|
- triggers
|
||||||
|
image: parity/rust:gitlab-ci
|
||||||
before_script:
|
before_script:
|
||||||
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
|
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l)
|
||||||
- echo $JS_FILES_MODIFIED
|
- 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
|
- 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:
|
script:
|
||||||
|
- rustup default stable
|
||||||
- echo $JS_FILES_MODIFIED
|
- 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
|
- 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:
|
tags:
|
||||||
@@ -604,8 +690,10 @@ push-release:
|
|||||||
stage: push-release
|
stage: push-release
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
image: ethcore/rust:stable
|
- triggers
|
||||||
|
image: parity/rust:gitlab-ci
|
||||||
script:
|
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: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
|
- curl --data "secret=$RELEASES_SECRET" http://update.parity.io:1338/push-release/$CI_BUILD_REF_NAME/$CI_BUILD_REF
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -2,3 +2,6 @@
|
|||||||
path = ethcore/res/ethereum/tests
|
path = ethcore/res/ethereum/tests
|
||||||
url = https://github.com/ethereum/tests.git
|
url = https://github.com/ethereum/tests.git
|
||||||
branch = develop
|
branch = develop
|
||||||
|
[submodule "ethcore/res/wasm-tests"]
|
||||||
|
path = ethcore/res/wasm-tests
|
||||||
|
url = https://github.com/paritytech/wasm-tests
|
||||||
|
|||||||
4394
CHANGELOG.md
Normal file
4394
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
2491
Cargo.lock
generated
2491
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
75
Cargo.toml
75
Cargo.toml
@@ -1,59 +1,69 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Parity Ethereum client"
|
description = "Parity Ethereum client"
|
||||||
name = "parity"
|
name = "parity"
|
||||||
version = "1.6.0"
|
version = "1.7.3"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
env_logger = "0.3"
|
env_logger = "0.4"
|
||||||
rustc-serialize = "0.3"
|
rustc-hex = "1.0"
|
||||||
docopt = "0.6"
|
docopt = "0.8"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
num_cpus = "0.2"
|
num_cpus = "1.2"
|
||||||
number_prefix = "0.2"
|
number_prefix = "0.2"
|
||||||
rpassword = "0.2.1"
|
rpassword = "0.2.1"
|
||||||
semver = "0.5"
|
semver = "0.6"
|
||||||
ansi_term = "0.7"
|
ansi_term = "0.9"
|
||||||
regex = "0.1"
|
regex = "0.2"
|
||||||
isatty = "0.1"
|
isatty = "0.1"
|
||||||
toml = "0.2"
|
toml = "0.4"
|
||||||
serde = "0.9"
|
serde = "1.0"
|
||||||
serde_json = "0.9"
|
serde_json = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
app_dirs = "1.1.1"
|
app_dirs = "1.1.1"
|
||||||
|
futures = "0.1"
|
||||||
|
futures-cpupool = "0.1"
|
||||||
fdlimit = "0.1"
|
fdlimit = "0.1"
|
||||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
ws2_32-sys = "0.2"
|
||||||
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
|
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||||
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
|
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||||
ethsync = { path = "sync" }
|
ethsync = { path = "sync" }
|
||||||
ethcore = { path = "ethcore" }
|
ethcore = { path = "ethcore" }
|
||||||
ethcore-util = { path = "util" }
|
ethcore-util = { path = "util" }
|
||||||
ethcore-io = { path = "util/io" }
|
ethcore-io = { path = "util/io" }
|
||||||
ethcore-devtools = { path = "devtools" }
|
ethcore-devtools = { path = "devtools" }
|
||||||
ethcore-rpc = { path = "rpc" }
|
|
||||||
ethcore-signer = { path = "signer" }
|
|
||||||
ethcore-ipc = { path = "ipc/rpc" }
|
ethcore-ipc = { path = "ipc/rpc" }
|
||||||
ethcore-ipc-nano = { path = "ipc/nano" }
|
ethcore-ipc-nano = { path = "ipc/nano" }
|
||||||
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
||||||
ethcore-light = { path = "ethcore/light" }
|
ethcore-light = { path = "ethcore/light" }
|
||||||
ethcore-logger = { path = "logger" }
|
ethcore-logger = { path = "logger" }
|
||||||
ethcore-stratum = { path = "stratum" }
|
ethcore-stratum = { path = "stratum" }
|
||||||
evmbin = { path = "evmbin" }
|
ethkey = { path = "ethkey" }
|
||||||
|
node-health = { path = "dapps/node-health" }
|
||||||
rlp = { path = "util/rlp" }
|
rlp = { path = "util/rlp" }
|
||||||
rpc-cli = { path = "rpc_cli" }
|
rpc-cli = { path = "rpc_cli" }
|
||||||
parity-rpc-client = { path = "rpc_client" }
|
|
||||||
parity-hash-fetch = { path = "hash-fetch" }
|
parity-hash-fetch = { path = "hash-fetch" }
|
||||||
parity-ipfs-api = { path = "ipfs" }
|
parity-ipfs-api = { path = "ipfs" }
|
||||||
parity-updater = { path = "updater" }
|
|
||||||
parity-reactor = { path = "util/reactor" }
|
|
||||||
parity-local-store = { path = "local-store" }
|
parity-local-store = { path = "local-store" }
|
||||||
ethcore-dapps = { path = "dapps", optional = true }
|
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}
|
clippy = { version = "0.0.103", optional = true}
|
||||||
ethcore-secretstore = { path = "secret_store", optional = true }
|
ethcore-secretstore = { path = "secret_store", optional = true }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rustc_version = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ethcore-ipc-tests = { path = "ipc/tests" }
|
ethcore-ipc-tests = { path = "ipc/tests" }
|
||||||
|
pretty_assertions = "0.1"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = "0.2"
|
winapi = "0.2"
|
||||||
@@ -64,23 +74,20 @@ daemonize = "0.2"
|
|||||||
[features]
|
[features]
|
||||||
default = ["ui-precompiled"]
|
default = ["ui-precompiled"]
|
||||||
ui = [
|
ui = [
|
||||||
"dapps",
|
"ui-enabled",
|
||||||
"ethcore-dapps/ui",
|
"parity-dapps/ui",
|
||||||
"ethcore-signer/ui",
|
|
||||||
]
|
]
|
||||||
ui-precompiled = [
|
ui-precompiled = [
|
||||||
"dapps",
|
"ui-enabled",
|
||||||
"ethcore-signer/ui-precompiled",
|
"parity-dapps/ui-precompiled",
|
||||||
"ethcore-dapps/ui-precompiled",
|
|
||||||
]
|
]
|
||||||
dapps = ["ethcore-dapps"]
|
ui-enabled = ["dapps"]
|
||||||
|
dapps = ["parity-dapps"]
|
||||||
ipc = ["ethcore/ipc", "ethsync/ipc"]
|
ipc = ["ethcore/ipc", "ethsync/ipc"]
|
||||||
jit = ["ethcore/jit"]
|
jit = ["ethcore/jit"]
|
||||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "parity-rpc/dev", "parity-dapps/dev"]
|
||||||
json-tests = ["ethcore/json-tests"]
|
json-tests = ["ethcore/json-tests"]
|
||||||
test-heavy = ["ethcore/test-heavy"]
|
test-heavy = ["ethcore/test-heavy"]
|
||||||
ethkey-cli = ["ethcore/ethkey-cli"]
|
|
||||||
ethstore-cli = ["ethcore/ethstore-cli"]
|
|
||||||
evm-debug = ["ethcore/evm-debug"]
|
evm-debug = ["ethcore/evm-debug"]
|
||||||
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||||
slow-blocks = ["ethcore/slow-blocks"]
|
slow-blocks = ["ethcore/slow-blocks"]
|
||||||
@@ -91,7 +98,13 @@ secretstore = ["ethcore-secretstore"]
|
|||||||
path = "parity/main.rs"
|
path = "parity/main.rs"
|
||||||
name = "parity"
|
name = "parity"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = false
|
debug = false
|
||||||
lto = false
|
lto = false
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["ethstore/cli", "ethkey/cli", "evmbin", "dapps/node-health"]
|
||||||
|
|||||||
67
README.md
67
README.md
@@ -1,58 +1,47 @@
|
|||||||
# [Parity](https://ethcore.io/parity.html)
|
# [Parity](https://parity.io/parity.html) - fast, light, and robust Ethereum client
|
||||||
### Fast, light, and robust Ethereum implementation
|
|
||||||
|
|
||||||
[](https://gitlab.ethcore.io/parity/parity/commits/master) [![Coverage Status][coveralls-image]][coveralls-url] [![GPLv3][license-image]][license-url]
|
[](https://gitlab.parity.io/parity/parity/commits/master)
|
||||||
|
[](https://build.snapcraft.io/user/paritytech/parity)
|
||||||
|
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||||
|
|
||||||
|
- [Download the latest release here.](https://github.com/paritytech/parity/releases)
|
||||||
|
|
||||||
### Join the chat!
|
### Join the chat!
|
||||||
|
|
||||||
Parity [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] and
|
Get in touch with us on Gitter:
|
||||||
parity.js [](https://gitter.im/ethcore/parity.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/paritytech/parity)
|
||||||
|
[](https://gitter.im/paritytech/parity.js)
|
||||||
|
[](https://gitter.im/paritytech/parity/miners)
|
||||||
|
[](https://gitter.im/paritytech/parity-poa)
|
||||||
|
|
||||||
[Internal Documentation][doc-url]
|
Be sure to check out [our wiki](https://github.com/paritytech/parity/wiki) and the [internal documentation](https://paritytech.github.io/parity/ethcore/index.html) for more information.
|
||||||
|
|
||||||
|
|
||||||
Be sure to check out [our wiki][wiki-url] for more information.
|
|
||||||
|
|
||||||
[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://ethcore.github.io/parity/ethcore/index.html
|
|
||||||
[wiki-url]: https://github.com/ethcore/parity/wiki
|
|
||||||
|
|
||||||
**Parity requires Rust version 1.15.0 to build**
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
## About Parity
|
## About Parity
|
||||||
|
|
||||||
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and
|
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.
|
||||||
cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
|
|
||||||
|
Parity comes with a built-in wallet. To access [Parity Wallet](http://web3.site/) simply go to http://web3.site/ (if you don't have access to the internet, but still want to use the service, you can also use http://127.0.0.1:8180/). It includes various functionality allowing you to:
|
||||||
|
|
||||||
Parity comes with a built-in wallet. To access [Parity Wallet](http://127.0.0.1:8080/) simply go to http://127.0.0.1:8080/. It
|
|
||||||
includes various functionality allowing you to:
|
|
||||||
- create and manage your Ethereum accounts;
|
- create and manage your Ethereum accounts;
|
||||||
- manage your Ether and any Ethereum tokens;
|
- manage your Ether and any Ethereum tokens;
|
||||||
- create and register your own tokens;
|
- create and register your own tokens;
|
||||||
- and much more.
|
- 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
|
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.
|
||||||
of RPC APIs.
|
|
||||||
|
|
||||||
If you run into an issue while using parity, feel free to file one in this repository
|
If you run into an issue while using parity, feel free to file one in this repository or hop on our [gitter chat room](https://gitter.im/paritytech/parity) to ask a question. We are glad to help!
|
||||||
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
|
|
||||||
|
|
||||||
Parity's current release is 1.5. You can download it at https://ethcore.io/parity.html or follow the instructions
|
**For security-critical issues**, please refer to the security policy outlined in `SECURITY.MD`.
|
||||||
below to build from source.
|
|
||||||
|
Parity's current beta-release is 1.7. You can download it at https://github.com/paritytech/parity/releases or follow the instructions below to build from source.
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Build dependencies
|
## Build dependencies
|
||||||
|
|
||||||
Parity is fully compatible with Stable Rust.
|
**Parity 1.7.x-beta requires Rust version 1.19.0 to build**.
|
||||||
|
|
||||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
||||||
|
|
||||||
@@ -60,7 +49,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
|||||||
```bash
|
```bash
|
||||||
$ curl https://sh.rustup.rs -sSf | sh
|
$ curl https://sh.rustup.rs -sSf | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl`, `libudev-dev` and `pkg-config` packages to be installed.
|
Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl`, `libudev-dev` and `pkg-config` packages to be installed.
|
||||||
- OSX:
|
- OSX:
|
||||||
```bash
|
```bash
|
||||||
@@ -80,19 +69,23 @@ Once you have rustup, install parity or download and build from source
|
|||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Quick install
|
## Install from the snap store
|
||||||
|
|
||||||
|
In any of the [supported Linux distros](https://snapcraft.io/docs/core/install):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install --git https://github.com/ethcore/parity.git parity
|
sudo snap install parity --edge
|
||||||
```
|
```
|
||||||
|
|
||||||
|
(Note that this is an experimental and unstable release, at the moment)
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Build from source
|
## Build from source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# download Parity code
|
# download Parity code
|
||||||
$ git clone https://github.com/ethcore/parity
|
$ git clone https://github.com/paritytech/parity
|
||||||
$ cd parity
|
$ cd parity
|
||||||
|
|
||||||
# build in release mode
|
# build in release mode
|
||||||
|
|||||||
54
SECURITY.md
Normal file
54
SECURITY.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
For security inquiries or vulnerability reports, please send a message to security@parity.io.
|
||||||
|
|
||||||
|
Please use a descriptive subject line so we can identify the report as such.
|
||||||
|
|
||||||
|
If you send a report, we will respond to the e-mail within 48 hours, and provide regular updates from that time onwards.
|
||||||
|
|
||||||
|
If you would like to encrypt your report, please use the PGP key provided below.
|
||||||
|
It is also reproduced [on MIT's key server](https://pgp.mit.edu/pks/lookup?op=get&search=0x5D0F03018D07DE73)
|
||||||
|
|
||||||
|
```
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQENBFlyIAwBCACe0keNPjgYzZ1Oy/8t3zj/Qw9bHHqrzx7FWy8NbXnYBM19NqOZ
|
||||||
|
DIP7Oe0DvCaf/uruBskCS0iVstHlEFQ2AYe0Ei0REt9lQdy61GylU/DEB3879IG+
|
||||||
|
6FO0SnFeYeerv1/hFI2K6uv8v7PyyVDiiJSW0I1KIs2OBwJicTKmWxLAeQsRgx9G
|
||||||
|
yRGalrVk4KP+6pWTA7k3DxmDZKZyfYV/Ej10NtuzmsemwDbv98HKeomp/kgFOfSy
|
||||||
|
3AZjeCpctlsNqpjUuXa0/HudmH2WLxZ0fz8XeoRh8XM9UudNIecjrDqmAFrt/btQ
|
||||||
|
/3guvlzhFCdhYPVGsUusKMECk/JG+Xx1/1ZjABEBAAG0LFBhcml0eSBTZWN1cml0
|
||||||
|
eSBDb250YWN0IDxzZWN1cml0eUBwYXJpdHkuaW8+iQFUBBMBCAA+FiEE2uUVYCjP
|
||||||
|
N6B8aTiDXQ8DAY0H3nMFAllyIAwCGwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwEC
|
||||||
|
HgECF4AACgkQXQ8DAY0H3nM60wgAkS3A36Zc+upiaxU7tumcGv+an17j7gin0sif
|
||||||
|
+0ELSjVfrXInM6ovai+NhUdcLkJ7tCrKS90fvlaELK5Sg9CXBWCTFccKN4A/B7ey
|
||||||
|
rOg2NPXUecnyBB/XqQgKYH7ujYlOlqBDXMfz6z8Hj6WToxg9PPMGGomyMGh8AWxM
|
||||||
|
3yRPFs5RKt0VKgN++5N00oly5Y8ri5pgCidDvCLYMGTVDHFKwkuc9w6BlWlu1R1e
|
||||||
|
/hXFWUFAP1ffTAul3QwyKhjPn2iotCdxXjvt48KaU8DN4iL7aMBN/ZBKqGS7yRdF
|
||||||
|
D/JbJyaaJ0ZRvFSTSXy/sWY3z1B5mtCPBxco8hqqNfRkCwuZ6LkBDQRZciAMAQgA
|
||||||
|
8BP8xrwe12TOUTqL/Vrbxv/FLdhKh53J6TrPKvC2TEEKOrTNo5ahRq+XOS5E7G2N
|
||||||
|
x3b+fq8gR9BzFcldAx0XWUtGs/Wv++ulaSNqTBxj13J3G3WGsUfMKxRgj//piCUD
|
||||||
|
bCFLQfGZdKk0M1o9QkPVARwwmvCNiNB/l++xGqPtfc44H5jWj3GoGvL2MkShPzrN
|
||||||
|
yN/bJ+m+R5gtFGdInqa5KXBuxxuW25eDKJ+LzjbgUgeC76wNcfOiQHTdMkcupjdO
|
||||||
|
bbGFwo10hcbRAOcZEv6//Zrlmk/6nPxEd2hN20St2bSN0+FqfZ267mWEu3ejsgF8
|
||||||
|
ArdCpv5h4fBvJyNwiTZwIQARAQABiQE8BBgBCAAmFiEE2uUVYCjPN6B8aTiDXQ8D
|
||||||
|
AY0H3nMFAllyIAwCGwwFCQPCZwAACgkQXQ8DAY0H3nNisggAl4fqhRlA34wIb190
|
||||||
|
sqXHVxiCuzPaqS6krE9xAa1+gncX485OtcJNqnjugHm2rFE48lv7oasviuPXuInE
|
||||||
|
/OgVFnXYv9d/Xx2JUeDs+bFTLouCDRY2Unh7KJZasfqnMcCHWcxHx5FvRNZRssaB
|
||||||
|
WTZVo6sizPurGUtbpYe4/OLFhadBqAE0EUmVRFEUMc1YTnu4eLaRBzoWN4d2UWwi
|
||||||
|
LN25RSrVSke7LTSFbgn9ntQrQ2smXSR+cdNkkfRCjFcpUaecvFl9HwIqoyVbT4Ym
|
||||||
|
0hbpbbX/cJdc91tKa+psa29uMeGL/cgL9fAu19yNFRyOTMxjZnvql1X/WE1pLmoP
|
||||||
|
ETBD1Q==
|
||||||
|
=K9Qw
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
```
|
||||||
|
|
||||||
|
Important Legal Information:
|
||||||
|
|
||||||
|
Your submission might be eligible for a bug bounty. The bug bounty program is an experimental and discretionary rewards program for the Parity community to reward those who are helping to improve the Parity software. Rewards are at the sole discretion of Parity Technologies Ltd..
|
||||||
|
|
||||||
|
We are not able to issue rewards to individuals who are on sanctions lists or who are in countries on sanctions lists (e.g. North Korea, Iran, etc).
|
||||||
|
|
||||||
|
You are responsible for all taxes. All rewards are subject to applicable law.
|
||||||
|
|
||||||
|
Finally, your testing must not violate any law or compromise any data that is not yours.
|
||||||
35
build.rs
Normal file
35
build.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// 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 rustc_version;
|
||||||
|
|
||||||
|
const MIN_RUSTC_VERSION: &'static str = "1.15.1";
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
@@ -1,45 +1,48 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Parity Dapps crate"
|
description = "Parity Dapps crate"
|
||||||
name = "ethcore-dapps"
|
name = "parity-dapps"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.3"
|
|
||||||
log = "0.3"
|
|
||||||
env_logger = "0.3"
|
|
||||||
futures = "0.1"
|
|
||||||
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
|
|
||||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
|
|
||||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
|
||||||
unicase = "1.3"
|
|
||||||
url = "1.0"
|
|
||||||
rustc-serialize = "0.3"
|
|
||||||
serde = "0.9"
|
|
||||||
serde_json = "0.9"
|
|
||||||
serde_derive = "0.9"
|
|
||||||
linked-hash-map = "0.3"
|
|
||||||
parity-dapps-glue = "1.4"
|
|
||||||
base32 = "0.3"
|
base32 = "0.3"
|
||||||
|
futures = "0.1"
|
||||||
|
linked-hash-map = "0.3"
|
||||||
|
log = "0.3"
|
||||||
|
parity-dapps-glue = "1.7"
|
||||||
mime = "0.2"
|
mime = "0.2"
|
||||||
mime_guess = "1.6.1"
|
mime_guess = "1.6.1"
|
||||||
|
rand = "0.3"
|
||||||
|
rustc-hex = "1.0"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
serde_json = "1.0"
|
||||||
time = "0.1.35"
|
time = "0.1.35"
|
||||||
|
unicase = "1.3"
|
||||||
|
url = "1.0"
|
||||||
zip = { version = "0.1", default-features = false }
|
zip = { version = "0.1", default-features = false }
|
||||||
ethcore-devtools = { path = "../devtools" }
|
|
||||||
ethcore-rpc = { path = "../rpc" }
|
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-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
fetch = { path = "../util/fetch" }
|
fetch = { path = "../util/fetch" }
|
||||||
parity-ui = { path = "./ui" }
|
node-health = { path = "./node-health" }
|
||||||
parity-hash-fetch = { path = "../hash-fetch" }
|
parity-hash-fetch = { path = "../hash-fetch" }
|
||||||
parity-reactor = { path = "../util/reactor" }
|
parity-reactor = { path = "../util/reactor" }
|
||||||
|
parity-ui = { path = "./ui" }
|
||||||
|
|
||||||
clippy = { version = "0.0.103", optional = true}
|
clippy = { version = "0.0.103", optional = true}
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
env_logger = "0.4"
|
||||||
|
ethcore-devtools = { path = "../devtools" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
dev = ["clippy", "ethcore-util/dev"]
|
||||||
|
|
||||||
ui = ["parity-ui/no-precompiled-js"]
|
ui = ["parity-ui/no-precompiled-js"]
|
||||||
ui-precompiled = ["parity-ui/use-precompiled-js"]
|
ui-precompiled = ["parity-ui/use-precompiled-js"]
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Base Package for all Parity built-in dapps"
|
description = "Base Package for all Parity built-in dapps"
|
||||||
name = "parity-dapps-glue"
|
name = "parity-dapps-glue"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
quasi_codegen = { version = "0.11", optional = true }
|
quasi_codegen = { version = "0.32", optional = true }
|
||||||
syntex = { version = "0.33", optional = true }
|
syntex = { version = "0.58", optional = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glob = { version = "0.2.11" }
|
glob = { version = "0.2.11" }
|
||||||
mime_guess = { version = "1.6.1" }
|
mime_guess = { version = "1.6.1" }
|
||||||
aster = { version = "0.17", default-features = false }
|
aster = { version = "0.41", default-features = false }
|
||||||
quasi = { version = "0.11", default-features = false }
|
quasi = { version = "0.32", default-features = false }
|
||||||
quasi_macros = { version = "0.11", optional = true }
|
quasi_macros = { version = "0.32", optional = true }
|
||||||
syntex = { version = "0.33", optional = true }
|
syntex = { version = "0.58", optional = true }
|
||||||
syntex_syntax = { version = "0.33", optional = true }
|
syntex_syntax = { version = "0.58", optional = true }
|
||||||
clippy = { version = "0.0.90", optional = true }
|
clippy = { version = "0.0.90", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Code generator to simplify creating a built-in Parity Dapp
|
|||||||
1. Clone this repository.
|
1. Clone this repository.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/ethcore/parity.git
|
$ git clone https://github.com/paritytech/parity.git
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Create a new directory for your Dapp. (`./myapp`)
|
1. Create a new directory for your Dapp. (`./myapp`)
|
||||||
@@ -29,10 +29,10 @@ Code generator to simplify creating a built-in Parity Dapp
|
|||||||
|
|
||||||
The `inject.js` script will create global `web3` instance with proper provider that should be used by your dapp.
|
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/ethcore/parity-ui/blob/master/status/Cargo.toml).
|
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
|
```bash
|
||||||
$ git clone https://github.com/ethcore/parity-ui.git
|
$ git clone https://github.com/paritytech/parity-ui.git
|
||||||
$ cd ./parity-ui/
|
$ cd ./parity-ui/
|
||||||
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
$ cp ./home/Cargo.toml ../parity/dapps/myapp/Cargo.toml
|
||||||
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
$ cp ./home/build.rs ../parity/dapps/myapp/build.rs
|
||||||
|
|||||||
@@ -25,13 +25,11 @@ mod inner {
|
|||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
let mut registry = syntex::Registry::new();
|
|
||||||
quasi_codegen::register(&mut registry);
|
|
||||||
|
|
||||||
let src = Path::new("src/lib.rs.in");
|
let src = Path::new("src/lib.rs.in");
|
||||||
let dst = Path::new(&out_dir).join("lib.rs");
|
let dst = Path::new(&out_dir).join("lib.rs");
|
||||||
|
|
||||||
registry.expand("", &src, &dst).unwrap();
|
quasi_codegen::expand(&src, &dst).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,8 @@ pub mod inner {
|
|||||||
|
|
||||||
impl fold::Folder for StripAttributeFolder {
|
impl fold::Folder for StripAttributeFolder {
|
||||||
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||||
match attr.node.value.node {
|
if &*attr.value.name.as_str() == "webapp" {
|
||||||
ast::MetaItemKind::List(ref n, _) if n == &"webapp" => { return None; }
|
return None;
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(attr)
|
Some(attr)
|
||||||
|
|||||||
@@ -22,16 +22,12 @@ use self::mime_guess::guess_mime_type;
|
|||||||
use std::path::{self, Path, PathBuf};
|
use std::path::{self, Path, PathBuf};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use syntax::ast::{MetaItem, Item};
|
|
||||||
|
|
||||||
use syntax::ast;
|
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
|
use syntax::ast::{self, MetaItem, Item};
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
use syntax::ptr::P;
|
use syntax::print::pprust::lit_to_string;
|
||||||
use syntax::print::pprust::{lit_to_string};
|
use syntax::symbol::InternedString;
|
||||||
use syntax::parse::token::{InternedString};
|
|
||||||
|
|
||||||
|
|
||||||
pub fn expand_webapp_implementation(
|
pub fn expand_webapp_implementation(
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
@@ -48,7 +44,7 @@ pub fn expand_webapp_implementation(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
let builder = aster::AstBuilder::new().span(span);
|
let builder = aster::AstBuilder::new().span(span);
|
||||||
implement_webapp(cx, &builder, &item, push);
|
implement_webapp(cx, &builder, item, push);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
|
fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push: &mut FnMut(Annotatable)) {
|
||||||
@@ -117,11 +113,12 @@ fn implement_webapp(cx: &ExtCtxt, builder: &aster::AstBuilder, item: &Item, push
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
||||||
for meta_items in item.attrs().iter().filter_map(webapp_meta_items) {
|
for meta_items in item.attrs.iter().filter_map(webapp_meta_items) {
|
||||||
for meta_item in meta_items {
|
for meta_item in meta_items {
|
||||||
|
let is_path = &*meta_item.name.as_str() == "path";
|
||||||
match meta_item.node {
|
match meta_item.node {
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"path" => {
|
ast::MetaItemKind::NameValue(ref lit) if is_path => {
|
||||||
if let Some(s) = get_str_from_lit(cx, name, lit) {
|
if let Some(s) = get_str_from_lit(cx, lit) {
|
||||||
return s.deref().to_owned();
|
return s.deref().to_owned();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -134,14 +131,32 @@ fn extract_path(cx: &ExtCtxt, item: &Item) -> String {
|
|||||||
"web".to_owned()
|
"web".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Option<InternedString> {
|
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 {
|
match lit.node {
|
||||||
ast::LitKind::Str(ref s, _) => Some(s.clone()),
|
ast::LitKind::Str(ref s, _) => Some(s.clone().as_str()),
|
||||||
_ => {
|
_ => {
|
||||||
cx.span_err(
|
cx.span_err(
|
||||||
lit.span,
|
lit.span,
|
||||||
&format!("webapp annotation `{}` must be a string, not `{}`",
|
&format!("webapp annotation path must be a string, not `{}`",
|
||||||
name,
|
|
||||||
lit_to_string(lit)
|
lit_to_string(lit)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -150,16 +165,6 @@ fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Option<Interned
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn webapp_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
|
||||||
match attr.node.value.node {
|
|
||||||
ast::MetaItemKind::List(ref name, ref items) if name == &"webapp" => {
|
|
||||||
attr::mark_used(&attr);
|
|
||||||
Some(items)
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_uri(path: &Path) -> String {
|
fn as_uri(path: &Path) -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
for component in path.iter() {
|
for component in path.iter() {
|
||||||
@@ -169,7 +174,6 @@ fn as_uri(path: &Path) -> String {
|
|||||||
s[0..s.len()-1].into()
|
s[0..s.len()-1].into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_convert_path_separators_on_all_platforms() {
|
fn should_convert_path_separators_on_all_platforms() {
|
||||||
// given
|
// given
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
extern crate syntex;
|
extern crate syntex;
|
||||||
|
|
||||||
#[cfg(feature = "with-syntex")]
|
#[cfg(feature = "with-syntex")]
|
||||||
#[macro_use]
|
|
||||||
extern crate syntex_syntax as syntax;
|
extern crate syntex_syntax as syntax;
|
||||||
|
|
||||||
#[cfg(feature = "with-syntex")]
|
#[cfg(feature = "with-syntex")]
|
||||||
|
|||||||
18
dapps/node-health/Cargo.toml
Normal file
18
dapps/node-health/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "node-health"
|
||||||
|
description = "Node's health status"
|
||||||
|
version = "0.1.0"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
futures = "0.1"
|
||||||
|
futures-cpupool = "0.1"
|
||||||
|
log = "0.3"
|
||||||
|
ntp = "0.2.0"
|
||||||
|
parking_lot = "0.4"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
time = "0.1.35"
|
||||||
|
|
||||||
|
parity-reactor = { path = "../../util/reactor" }
|
||||||
122
dapps/node-health/src/health.rs
Normal file
122
dapps/node-health/src/health.rs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Reporting node's health.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time;
|
||||||
|
use futures::{Future, BoxFuture};
|
||||||
|
use futures::sync::oneshot;
|
||||||
|
use types::{HealthInfo, HealthStatus, Health};
|
||||||
|
use time::{TimeChecker, MAX_DRIFT};
|
||||||
|
use parity_reactor::Remote;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use {SyncStatus};
|
||||||
|
|
||||||
|
const TIMEOUT_SECS: u64 = 5;
|
||||||
|
const PROOF: &str = "Only one closure is invoked.";
|
||||||
|
|
||||||
|
/// A struct enabling you to query for node's health.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NodeHealth {
|
||||||
|
sync_status: Arc<SyncStatus>,
|
||||||
|
time: TimeChecker,
|
||||||
|
remote: Remote,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeHealth {
|
||||||
|
/// Creates new `NodeHealth`.
|
||||||
|
pub fn new(sync_status: Arc<SyncStatus>, time: TimeChecker, remote: Remote) -> Self {
|
||||||
|
NodeHealth { sync_status, time, remote, }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query latest health report.
|
||||||
|
pub fn health(&self) -> BoxFuture<Health, ()> {
|
||||||
|
trace!(target: "dapps", "Checking node health.");
|
||||||
|
// Check timediff
|
||||||
|
let sync_status = self.sync_status.clone();
|
||||||
|
let time = self.time.time_drift();
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
let tx = Arc::new(Mutex::new(Some(tx)));
|
||||||
|
let tx2 = tx.clone();
|
||||||
|
self.remote.spawn_with_timeout(
|
||||||
|
move || time.then(move |result| {
|
||||||
|
let _ = tx.lock().take().expect(PROOF).send(Ok(result));
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
time::Duration::from_secs(TIMEOUT_SECS),
|
||||||
|
move || {
|
||||||
|
let _ = tx2.lock().take().expect(PROOF).send(Err(()));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
rx.map_err(|err| {
|
||||||
|
warn!(target: "dapps", "Health request cancelled: {:?}", err);
|
||||||
|
}).and_then(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 = {
|
||||||
|
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, }
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Health { peers, sync, time})
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
49
dapps/node-health/src/lib.rs
Normal file
49
dapps/node-health/src/lib.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Node Health status reporting.
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
extern crate futures;
|
||||||
|
extern crate futures_cpupool;
|
||||||
|
extern crate ntp;
|
||||||
|
extern crate time as time_crate;
|
||||||
|
extern crate parity_reactor;
|
||||||
|
extern crate parking_lot;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
mod health;
|
||||||
|
mod time;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
pub use futures_cpupool::CpuPool;
|
||||||
|
pub use health::NodeHealth;
|
||||||
|
pub use types::{Health, HealthInfo, HealthStatus};
|
||||||
|
pub use time::{TimeChecker, Error};
|
||||||
|
|
||||||
|
/// Indicates sync status
|
||||||
|
pub trait SyncStatus: ::std::fmt::Debug + 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);
|
||||||
|
}
|
||||||
355
dapps/node-health/src/time.rs
Normal file
355
dapps/node-health/src/time.rs
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
// 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::sync::Arc;
|
||||||
|
use std::sync::atomic::{self, AtomicUsize};
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use futures::{self, Future, BoxFuture};
|
||||||
|
use futures::future::{self, IntoFuture};
|
||||||
|
use futures_cpupool::{CpuPool, CpuFuture};
|
||||||
|
use ntp;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use time_crate::{Duration, Timespec};
|
||||||
|
|
||||||
|
/// Time checker error.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Error {
|
||||||
|
/// No servers are currently available for a query.
|
||||||
|
NoServersAvailable,
|
||||||
|
/// There was an error when trying to reach the NTP server.
|
||||||
|
Ntp(String),
|
||||||
|
/// IO error when reading NTP response.
|
||||||
|
Io(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::Error::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
NoServersAvailable => write!(fmt, "No NTP servers available"),
|
||||||
|
Ntp(ref err) => write!(fmt, "NTP error: {}", err),
|
||||||
|
Io(ref err) => write!(fmt, "Connection Error: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/// Returned Future.
|
||||||
|
type Future: IntoFuture<Item=Duration, Error=Error>;
|
||||||
|
|
||||||
|
/// Returns the current time drift.
|
||||||
|
fn drift(&self) -> Self::Future;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SERVER_MAX_POLL_INTERVAL_SECS: u64 = 60;
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Server {
|
||||||
|
pub address: String,
|
||||||
|
next_call: RwLock<time::Instant>,
|
||||||
|
failures: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Server {
|
||||||
|
pub fn is_available(&self) -> bool {
|
||||||
|
*self.next_call.read() < time::Instant::now()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn report_success(&self) {
|
||||||
|
self.failures.store(0, atomic::Ordering::SeqCst);
|
||||||
|
self.update_next_call(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn report_failure(&self) {
|
||||||
|
let errors = self.failures.fetch_add(1, atomic::Ordering::SeqCst);
|
||||||
|
self.update_next_call(1 << errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_next_call(&self, delay: usize) {
|
||||||
|
*self.next_call.write() = time::Instant::now() + time::Duration::from_secs(delay as u64 * SERVER_MAX_POLL_INTERVAL_SECS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<str>> From<T> for Server {
|
||||||
|
fn from(t: T) -> Self {
|
||||||
|
Server {
|
||||||
|
address: t.as_ref().to_owned(),
|
||||||
|
next_call: RwLock::new(time::Instant::now()),
|
||||||
|
failures: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NTP client using the SNTP algorithm for calculating drift.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SimpleNtp {
|
||||||
|
addresses: Vec<Arc<Server>>,
|
||||||
|
pool: CpuPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SimpleNtp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f
|
||||||
|
.debug_struct("SimpleNtp")
|
||||||
|
.field("addresses", &self.addresses)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleNtp {
|
||||||
|
fn new<T: AsRef<str>>(addresses: &[T], pool: CpuPool) -> SimpleNtp {
|
||||||
|
SimpleNtp {
|
||||||
|
addresses: addresses.iter().map(Server::from).map(Arc::new).collect(),
|
||||||
|
pool: pool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ntp for SimpleNtp {
|
||||||
|
type Future = future::Either<
|
||||||
|
CpuFuture<Duration, Error>,
|
||||||
|
future::FutureResult<Duration, Error>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
fn drift(&self) -> Self::Future {
|
||||||
|
use self::future::Either::{A, B};
|
||||||
|
|
||||||
|
let server = self.addresses.iter().find(|server| server.is_available());
|
||||||
|
server.map(|server| {
|
||||||
|
let server = server.clone();
|
||||||
|
A(self.pool.spawn_fn(move || {
|
||||||
|
debug!(target: "dapps", "Fetching time from {}.", server.address);
|
||||||
|
|
||||||
|
match ntp::request(&server.address) {
|
||||||
|
Ok(packet) => {
|
||||||
|
let dest_time = ::time_crate::now_utc().to_timespec();
|
||||||
|
let orig_time = Timespec::from(packet.orig_time);
|
||||||
|
let recv_time = Timespec::from(packet.recv_time);
|
||||||
|
let transmit_time = Timespec::from(packet.transmit_time);
|
||||||
|
|
||||||
|
let drift = ((recv_time - orig_time) + (transmit_time - dest_time)) / 2;
|
||||||
|
|
||||||
|
server.report_success();
|
||||||
|
Ok(drift)
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
server.report_failure();
|
||||||
|
Err(err.into())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}).unwrap_or_else(|| B(future::err(Error::NoServersAvailable)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE In a positive scenario first results will be seen after:
|
||||||
|
// MAX_RESULTS * UPDATE_TIMEOUT_INCOMPLETE_SECS seconds.
|
||||||
|
const MAX_RESULTS: usize = 4;
|
||||||
|
const UPDATE_TIMEOUT_OK_SECS: u64 = 6 * 60 * 60;
|
||||||
|
const UPDATE_TIMEOUT_WARN_SECS: u64 = 15 * 60;
|
||||||
|
const UPDATE_TIMEOUT_ERR_SECS: u64 = 60;
|
||||||
|
const UPDATE_TIMEOUT_INCOMPLETE_SECS: u64 = 10;
|
||||||
|
|
||||||
|
/// Maximal valid time drift.
|
||||||
|
pub const MAX_DRIFT: i64 = 500;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
/// A time checker.
|
||||||
|
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<T: AsRef<str>>(ntp_addresses: &[T], pool: CpuPool) -> Self {
|
||||||
|
let last_result = Arc::new(RwLock::new(
|
||||||
|
// Assume everything is ok at the very beginning.
|
||||||
|
(time::Instant::now(), vec![Ok(0)].into())
|
||||||
|
));
|
||||||
|
|
||||||
|
let ntp = SimpleNtp::new(ntp_addresses, pool);
|
||||||
|
|
||||||
|
TimeChecker {
|
||||||
|
ntp,
|
||||||
|
last_result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Ntp> TimeChecker<N> where <N::Future as IntoFuture>::Future: Send + 'static {
|
||||||
|
/// Updates the time
|
||||||
|
pub fn update(&self) -> BoxFuture<i64, Error> {
|
||||||
|
trace!(target: "dapps", "Updating time from NTP.");
|
||||||
|
let last_result = self.last_result.clone();
|
||||||
|
self.ntp.drift().into_future().then(move |res| {
|
||||||
|
let res = res.map(|d| d.num_milliseconds());
|
||||||
|
|
||||||
|
if let Err(Error::NoServersAvailable) = res {
|
||||||
|
debug!(target: "dapps", "No NTP servers available. Selecting an older result.");
|
||||||
|
return select_result(last_result.read().1.iter());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the results.
|
||||||
|
let mut results = mem::replace(&mut last_result.write().1, VecDeque::new());
|
||||||
|
let has_all_results = results.len() >= MAX_RESULTS;
|
||||||
|
let valid_till = time::Instant::now() + time::Duration::from_secs(
|
||||||
|
match res {
|
||||||
|
Ok(time) if has_all_results && time < MAX_DRIFT => UPDATE_TIMEOUT_OK_SECS,
|
||||||
|
Ok(_) if has_all_results => UPDATE_TIMEOUT_WARN_SECS,
|
||||||
|
Err(_) if has_all_results => UPDATE_TIMEOUT_ERR_SECS,
|
||||||
|
_ => UPDATE_TIMEOUT_INCOMPLETE_SECS,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
trace!(target: "dapps", "New time drift received: {:?}", res);
|
||||||
|
// Push the result.
|
||||||
|
results.push_back(res);
|
||||||
|
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::{future, Future};
|
||||||
|
use super::{Ntp, TimeChecker, Error};
|
||||||
|
use parking_lot::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 {
|
||||||
|
type Future = future::FutureResult<Duration, Error>;
|
||||||
|
|
||||||
|
fn drift(&self) -> Self::Future {
|
||||||
|
self.1.set(self.1.get() + 1);
|
||||||
|
future::ok(self.0.borrow_mut().pop().expect("Unexpected call to drift()."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn time_checker() -> TimeChecker<FakeNtp> {
|
||||||
|
let last_result = Arc::new(RwLock::new(
|
||||||
|
(Instant::now(), vec![Err(Error::Ntp("NTP server unavailable".into()))].into())
|
||||||
|
));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
dapps/node-health/src/types.rs
Normal file
57
dapps/node-health/src/types.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Base health types.
|
||||||
|
|
||||||
|
/// 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>,
|
||||||
|
}
|
||||||
@@ -15,52 +15,47 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use unicase::UniCase;
|
|
||||||
use hyper::{server, net, Decoder, Encoder, Next, Control};
|
use hyper::{server, net, Decoder, Encoder, Next, Control};
|
||||||
use hyper::header;
|
|
||||||
use hyper::method::Method;
|
use hyper::method::Method;
|
||||||
use hyper::header::AccessControlAllowOrigin;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use api::types::{App, ApiError};
|
use api::{response, types};
|
||||||
use api::response;
|
|
||||||
use apps::fetcher::Fetcher;
|
use apps::fetcher::Fetcher;
|
||||||
|
use handlers::{self, extract_url};
|
||||||
use handlers::extract_url;
|
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||||
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
use node_health::{NodeHealth, HealthStatus, Health};
|
||||||
use jsonrpc_http_server::cors;
|
use parity_reactor::Remote;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RestApi {
|
pub struct RestApi {
|
||||||
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
|
|
||||||
endpoints: Arc<Endpoints>,
|
|
||||||
fetcher: Arc<Fetcher>,
|
fetcher: Arc<Fetcher>,
|
||||||
|
health: NodeHealth,
|
||||||
|
remote: Remote,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestApi {
|
impl RestApi {
|
||||||
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
|
pub fn new(
|
||||||
|
fetcher: Arc<Fetcher>,
|
||||||
|
health: NodeHealth,
|
||||||
|
remote: Remote,
|
||||||
|
) -> Box<Endpoint> {
|
||||||
Box::new(RestApi {
|
Box::new(RestApi {
|
||||||
cors_domains: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
|
fetcher,
|
||||||
endpoints: endpoints,
|
health,
|
||||||
fetcher: fetcher,
|
remote,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_apps(&self) -> Vec<App> {
|
|
||||||
self.endpoints.iter().filter_map(|(ref k, ref e)| {
|
|
||||||
e.info().map(|ref info| App::from_info(k, info))
|
|
||||||
}).collect()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for RestApi {
|
impl Endpoint for RestApi {
|
||||||
fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<Handler> {
|
fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<Handler> {
|
||||||
Box::new(RestApiRouter::new(self.clone(), path, control))
|
Box::new(RestApiRouter::new((*self).clone(), path, control))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RestApiRouter {
|
struct RestApiRouter {
|
||||||
api: RestApi,
|
api: RestApi,
|
||||||
origin: Option<String>,
|
|
||||||
path: Option<EndpointPath>,
|
path: Option<EndpointPath>,
|
||||||
control: Option<Control>,
|
control: Option<Control>,
|
||||||
handler: Box<Handler>,
|
handler: Box<Handler>,
|
||||||
@@ -70,18 +65,18 @@ impl RestApiRouter {
|
|||||||
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
|
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
|
||||||
RestApiRouter {
|
RestApiRouter {
|
||||||
path: Some(path),
|
path: Some(path),
|
||||||
origin: None,
|
|
||||||
control: Some(control),
|
control: Some(control),
|
||||||
api: api,
|
api: api,
|
||||||
handler: response::as_json_error(&ApiError {
|
handler: Box::new(response::as_json_error(StatusCode::NotFound, &types::ApiError {
|
||||||
code: "404".into(),
|
code: "404".into(),
|
||||||
title: "Not Found".into(),
|
title: "Not Found".into(),
|
||||||
detail: "Resource you requested has not been 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>> {
|
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 {
|
match hash {
|
||||||
Some(hash) if self.api.fetcher.contains(hash) => {
|
Some(hash) if self.api.fetcher.contains(hash) => {
|
||||||
Some(self.api.fetcher.to_async_handler(path, control))
|
Some(self.api.fetcher.to_async_handler(path, control))
|
||||||
@@ -90,34 +85,29 @@ impl RestApiRouter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns basic headers for a response (it may be overwritten by the handler)
|
fn health(&self, control: Control) -> Box<Handler> {
|
||||||
fn response_headers(&self) -> header::Headers {
|
let map = move |health: Result<Result<Health, ()>, ()>| {
|
||||||
let mut headers = header::Headers::new();
|
let status = match health {
|
||||||
headers.set(header::AccessControlAllowCredentials);
|
Ok(Ok(ref health)) => {
|
||||||
headers.set(header::AccessControlAllowMethods(vec![
|
if [&health.peers.status, &health.sync.status].iter().any(|x| *x != &HealthStatus::Ok) {
|
||||||
Method::Options,
|
StatusCode::PreconditionFailed // HTTP 412
|
||||||
Method::Post,
|
} else {
|
||||||
Method::Get,
|
StatusCode::Ok // HTTP 200
|
||||||
]));
|
}
|
||||||
headers.set(header::AccessControlAllowHeaders(vec![
|
},
|
||||||
UniCase("origin".to_owned()),
|
_ => StatusCode::ServiceUnavailable, // HTTP 503
|
||||||
UniCase("content-type".to_owned()),
|
};
|
||||||
UniCase("accept".to_owned()),
|
|
||||||
]));
|
|
||||||
|
|
||||||
if let Some(cors_header) = cors::get_cors_header(&self.api.cors_domains, &self.origin) {
|
response::as_json(status, &health)
|
||||||
headers.set(cors_header);
|
};
|
||||||
}
|
let health = self.api.health.health();
|
||||||
|
let remote = self.api.remote.clone();
|
||||||
headers
|
Box::new(handlers::AsyncHandler::new(health, map, remote, control))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl server::Handler<net::HttpStream> for RestApiRouter {
|
impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||||
|
|
||||||
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||||
self.origin = cors::read_origin(&request);
|
|
||||||
|
|
||||||
if let Method::Options = *request.method() {
|
if let Method::Options = *request.method() {
|
||||||
self.handler = response::empty();
|
self.handler = response::empty();
|
||||||
return Next::write();
|
return Next::write();
|
||||||
@@ -141,8 +131,8 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
|
|||||||
if let Some(ref hash) = hash { path.app_id = hash.clone().to_owned() }
|
if let Some(ref hash) = hash { path.app_id = hash.clone().to_owned() }
|
||||||
|
|
||||||
let handler = endpoint.and_then(|v| match v {
|
let handler = endpoint.and_then(|v| match v {
|
||||||
"apps" => Some(response::as_json(&self.api.list_apps())),
|
|
||||||
"ping" => Some(response::ping()),
|
"ping" => Some(response::ping()),
|
||||||
|
"health" => Some(self.health(control)),
|
||||||
"content" => self.resolve_content(hash, path, control),
|
"content" => self.resolve_content(hash, path, control),
|
||||||
_ => None
|
_ => None
|
||||||
});
|
});
|
||||||
@@ -160,12 +150,10 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
*res.headers_mut() = self.response_headers();
|
|
||||||
self.handler.on_response(res)
|
self.handler.on_response(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
|
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
|
||||||
self.handler.on_response_writable(encoder)
|
self.handler.on_response_writable(encoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,4 +21,3 @@ mod response;
|
|||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use self::api::RestApi;
|
pub use self::api::RestApi;
|
||||||
pub use self::types::App;
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use endpoint::Handler;
|
use endpoint::Handler;
|
||||||
use handlers::{ContentHandler, EchoHandler};
|
use handlers::{ContentHandler, EchoHandler};
|
||||||
|
|
||||||
@@ -23,16 +25,16 @@ pub fn empty() -> Box<Handler> {
|
|||||||
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
|
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_json<T: Serialize>(val: &T) -> Box<Handler> {
|
pub fn as_json<T: Serialize>(status: StatusCode, val: &T) -> ContentHandler {
|
||||||
let json = serde_json::to_string(val)
|
let json = serde_json::to_string(val)
|
||||||
.expect("serialization to string is infallible; qed");
|
.expect("serialization to string is infallible; qed");
|
||||||
Box::new(ContentHandler::ok(json, mime!(Application/Json)))
|
ContentHandler::new(status, json, mime!(Application/Json))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_json_error<T: Serialize>(val: &T) -> Box<Handler> {
|
pub fn as_json_error<T: Serialize>(status: StatusCode, val: &T) -> ContentHandler {
|
||||||
let json = serde_json::to_string(val)
|
let json = serde_json::to_string(val)
|
||||||
.expect("serialization to string is infallible; qed");
|
.expect("serialization to string is infallible; qed");
|
||||||
Box::new(ContentHandler::not_found(json, mime!(Application/Json)))
|
ContentHandler::new(status, json, mime!(Application/Json))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ping() -> Box<Handler> {
|
pub fn ping() -> Box<Handler> {
|
||||||
|
|||||||
@@ -14,51 +14,14 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use endpoint::EndpointInfo;
|
/// A structure representing any error in REST API.
|
||||||
|
|
||||||
#[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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ApiError {
|
pub struct ApiError {
|
||||||
|
/// Error code.
|
||||||
pub code: String,
|
pub code: String,
|
||||||
|
/// Human-readable error summary.
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
/// More technical error details.
|
||||||
pub detail: String,
|
pub detail: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
55
dapps/src/apps/app.rs
Normal file
55
dapps/src/apps/app.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ use util::sha3::sha3;
|
|||||||
use page::{LocalPageEndpoint, PageCache};
|
use page::{LocalPageEndpoint, PageCache};
|
||||||
use handlers::{ContentValidator, ValidatorResponse};
|
use handlers::{ContentValidator, ValidatorResponse};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
||||||
|
|
||||||
@@ -116,16 +117,16 @@ pub struct Dapp {
|
|||||||
id: String,
|
id: String,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
on_done: OnDone,
|
on_done: OnDone,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dapp {
|
impl Dapp {
|
||||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable) -> Self {
|
||||||
Dapp {
|
Dapp {
|
||||||
id: id,
|
id,
|
||||||
dapps_path: dapps_path,
|
dapps_path,
|
||||||
on_done: on_done,
|
on_done,
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ mod installers;
|
|||||||
use std::{fs, env};
|
use std::{fs, env};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
use fetch::{Client as FetchClient, Fetch};
|
use fetch::{Client as FetchClient, Fetch};
|
||||||
use hash_fetch::urlhint::{URLHintContract, URLHint, URLHintResult};
|
use hash_fetch::urlhint::{URLHintContract, URLHint, URLHintResult};
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
@@ -31,7 +31,7 @@ use parity_reactor::Remote;
|
|||||||
use hyper;
|
use hyper;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use {SyncStatus, random_filename};
|
use {Embeddable, SyncStatus, random_filename};
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
use page::LocalPageEndpoint;
|
use page::LocalPageEndpoint;
|
||||||
use handlers::{ContentHandler, ContentFetcherHandler};
|
use handlers::{ContentHandler, ContentFetcherHandler};
|
||||||
@@ -47,46 +47,73 @@ pub trait Fetcher: Send + Sync + 'static {
|
|||||||
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler>;
|
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + Send + Sync + 'static = URLHintContract> {
|
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHintContract> {
|
||||||
dapps_path: PathBuf,
|
cache_path: PathBuf,
|
||||||
resolver: R,
|
resolver: R,
|
||||||
cache: Arc<Mutex<ContentCache>>,
|
cache: Arc<Mutex<ContentCache>>,
|
||||||
sync: Arc<SyncStatus>,
|
sync: Arc<SyncStatus>,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
|
only_content: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: URLHint + Send + Sync + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
|
impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Clear cache path
|
// Clear cache path
|
||||||
let _ = fs::remove_dir_all(&self.dapps_path);
|
let _ = fs::remove_dir_all(&self.cache_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: URLHint + Send + Sync + 'static, F: Fetch> ContentFetcher<F, R> {
|
impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
||||||
|
|
||||||
pub fn new(resolver: R, sync_status: Arc<SyncStatus>, embeddable_on: Option<(String, u16)>, remote: Remote, fetch: F) -> Self {
|
pub fn new(
|
||||||
let mut dapps_path = env::temp_dir();
|
resolver: R,
|
||||||
dapps_path.push(random_filename());
|
sync_status: Arc<SyncStatus>,
|
||||||
|
remote: Remote,
|
||||||
|
fetch: F,
|
||||||
|
) -> Self {
|
||||||
|
let mut cache_path = env::temp_dir();
|
||||||
|
cache_path.push(random_filename());
|
||||||
|
|
||||||
ContentFetcher {
|
ContentFetcher {
|
||||||
dapps_path: dapps_path,
|
cache_path: cache_path,
|
||||||
resolver: resolver,
|
resolver: resolver,
|
||||||
sync: sync_status,
|
sync: sync_status,
|
||||||
cache: Arc::new(Mutex::new(ContentCache::default())),
|
cache: Arc::new(Mutex::new(ContentCache::default())),
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on: None,
|
||||||
remote: remote,
|
remote: remote,
|
||||||
fetch: fetch,
|
fetch: fetch,
|
||||||
|
only_content: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
|
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(
|
Box::new(ContentHandler::error(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
"Sync In Progress",
|
"Sync In Progress",
|
||||||
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
||||||
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
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,
|
address,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -95,9 +122,19 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> ContentFetcher<F, R> {
|
|||||||
fn set_status(&self, content_id: &str, status: ContentStatus) {
|
fn set_status(&self, content_id: &str, status: ContentStatus) {
|
||||||
self.cache.lock().insert(content_id.to_owned(), status);
|
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 + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||||
fn contains(&self, content_id: &str) -> bool {
|
fn contains(&self, content_id: &str) -> bool {
|
||||||
{
|
{
|
||||||
let mut cache = self.cache.lock();
|
let mut cache = self.cache.lock();
|
||||||
@@ -108,10 +145,8 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
|||||||
}
|
}
|
||||||
// fallback to resolver
|
// fallback to resolver
|
||||||
if let Ok(content_id) = content_id.from_hex() {
|
if let Ok(content_id) = content_id.from_hex() {
|
||||||
// else try to resolve the app_id
|
|
||||||
let has_content = self.resolver.resolve(content_id).is_some();
|
|
||||||
// if there is content or we are syncing return true
|
// if there is content or we are syncing return true
|
||||||
has_content || self.sync.is_major_importing()
|
self.sync.is_major_importing() || self.resolve(content_id).is_some()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -137,7 +172,7 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
|||||||
_ => {
|
_ => {
|
||||||
trace!(target: "dapps", "Content unavailable. Fetching... {:?}", content_id);
|
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_hex = content_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
||||||
let content = self.resolver.resolve(content_hex);
|
let content = self.resolve(content_hex);
|
||||||
|
|
||||||
let cache = self.cache.clone();
|
let cache = self.cache.clone();
|
||||||
let id = content_id.clone();
|
let id = content_id.clone();
|
||||||
@@ -155,6 +190,9 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
|||||||
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
|
Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => {
|
||||||
(None, Self::still_syncing(self.embeddable_on.clone()))
|
(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)) => {
|
Some(URLHintResult::Dapp(dapp)) => {
|
||||||
let handler = ContentFetcherHandler::new(
|
let handler = ContentFetcherHandler::new(
|
||||||
dapp.url(),
|
dapp.url(),
|
||||||
@@ -162,7 +200,7 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
|||||||
control,
|
control,
|
||||||
installers::Dapp::new(
|
installers::Dapp::new(
|
||||||
content_id.clone(),
|
content_id.clone(),
|
||||||
self.dapps_path.clone(),
|
self.cache_path.clone(),
|
||||||
Box::new(on_done),
|
Box::new(on_done),
|
||||||
self.embeddable_on.clone(),
|
self.embeddable_on.clone(),
|
||||||
),
|
),
|
||||||
@@ -181,7 +219,7 @@ impl<R: URLHint + Send + Sync + 'static, F: Fetch> Fetcher for ContentFetcher<F,
|
|||||||
installers::Content::new(
|
installers::Content::new(
|
||||||
content_id.clone(),
|
content_id.clone(),
|
||||||
content.mime,
|
content.mime,
|
||||||
self.dapps_path.clone(),
|
self.cache_path.clone(),
|
||||||
Box::new(on_done),
|
Box::new(on_done),
|
||||||
),
|
),
|
||||||
self.embeddable_on.clone(),
|
self.embeddable_on.clone(),
|
||||||
@@ -225,6 +263,7 @@ mod tests {
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use util::Bytes;
|
use util::Bytes;
|
||||||
use fetch::{Fetch, Client};
|
use fetch::{Fetch, Client};
|
||||||
|
use futures::{future, Future, BoxFuture};
|
||||||
use hash_fetch::urlhint::{URLHint, URLHintResult};
|
use hash_fetch::urlhint::{URLHint, URLHintResult};
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
|
|
||||||
@@ -232,19 +271,29 @@ mod tests {
|
|||||||
use endpoint::EndpointInfo;
|
use endpoint::EndpointInfo;
|
||||||
use page::LocalPageEndpoint;
|
use page::LocalPageEndpoint;
|
||||||
use super::{ContentFetcher, Fetcher};
|
use super::{ContentFetcher, Fetcher};
|
||||||
|
use {SyncStatus};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct FakeResolver;
|
struct FakeResolver;
|
||||||
impl URLHint for FakeResolver {
|
impl URLHint for FakeResolver {
|
||||||
fn resolve(&self, _id: Bytes) -> Option<URLHintResult> {
|
fn resolve(&self, _id: Bytes) -> BoxFuture<Option<URLHintResult>, String> {
|
||||||
None
|
future::ok(None).boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FakeSync(bool);
|
||||||
|
impl SyncStatus for FakeSync {
|
||||||
|
fn is_major_importing(&self) -> bool { self.0 }
|
||||||
|
fn peers(&self) -> (usize, usize) { (0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_true_if_contains_the_app() {
|
fn should_true_if_contains_the_app() {
|
||||||
// given
|
// given
|
||||||
let path = env::temp_dir();
|
let path = env::temp_dir();
|
||||||
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), None, Remote::new_sync(), Client::new().unwrap());
|
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(FakeSync(false)), Remote::new_sync(), Client::new().unwrap())
|
||||||
|
.allow_dapps(true);
|
||||||
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
||||||
name: "fake".into(),
|
name: "fake".into(),
|
||||||
description: "".into(),
|
description: "".into(),
|
||||||
|
|||||||
@@ -14,13 +14,15 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use page::{LocalPageEndpoint, PageCache};
|
use page::{LocalPageEndpoint, PageCache};
|
||||||
use endpoint::{Endpoints, EndpointInfo};
|
use endpoint::{Endpoint, EndpointInfo};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
struct LocalDapp {
|
struct LocalDapp {
|
||||||
id: String,
|
id: String,
|
||||||
@@ -59,14 +61,14 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
|||||||
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
||||||
/// Parses the path to extract last component (for name).
|
/// Parses the path to extract last component (for name).
|
||||||
/// `None` is returned when path is invalid or non-existent.
|
/// `None` is returned when path is invalid or non-existent.
|
||||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, signer_address: Option<(String, u16)>) -> Option<(String, Box<LocalPageEndpoint>)> {
|
pub fn local_endpoint<P: AsRef<Path>>(path: P, embeddable: Embeddable) -> Option<(String, Box<LocalPageEndpoint>)> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
path.canonicalize().ok().and_then(|path| {
|
path.canonicalize().ok().and_then(|path| {
|
||||||
let name = path.file_name().and_then(|name| name.to_str());
|
let name = path.file_name().and_then(|name| name.to_str());
|
||||||
name.map(|name| {
|
name.map(|name| {
|
||||||
let dapp = local_dapp(name.into(), path.clone());
|
let dapp = local_dapp(name.into(), path.clone());
|
||||||
(dapp.id, Box::new(LocalPageEndpoint::new(
|
(dapp.id, Box::new(LocalPageEndpoint::new(
|
||||||
dapp.path, dapp.info, PageCache::Disabled, signer_address.clone())
|
dapp.path, dapp.info, PageCache::Disabled, embeddable.clone())
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -85,12 +87,12 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
|
|||||||
|
|
||||||
/// Returns endpoints for Local Dapps found for given filesystem path.
|
/// Returns endpoints for Local Dapps found for given filesystem path.
|
||||||
/// Scans the directory and collects `LocalPageEndpoints`.
|
/// Scans the directory and collects `LocalPageEndpoints`.
|
||||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> Endpoints {
|
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, embeddable: Embeddable) -> BTreeMap<String, Box<Endpoint>> {
|
||||||
let mut pages = Endpoints::new();
|
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
|
||||||
for dapp in local_dapps(dapps_path.as_ref()) {
|
for dapp in local_dapps(dapps_path.as_ref()) {
|
||||||
pages.insert(
|
pages.insert(
|
||||||
dapp.id,
|
dapp.id,
|
||||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
|
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, embeddable.clone()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pages
|
pages
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
pub use api::App as Manifest;
|
pub use apps::App as Manifest;
|
||||||
|
|
||||||
pub const MANIFEST_FILENAME: &'static str = "manifest.json";
|
pub const MANIFEST_FILENAME: &'static str = "manifest.json";
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,10 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use endpoint::{Endpoints, Endpoint};
|
use endpoint::{Endpoints, Endpoint};
|
||||||
use page::PageEndpoint;
|
use page::PageEndpoint;
|
||||||
use proxypac::ProxyPac;
|
use proxypac::ProxyPac;
|
||||||
@@ -23,17 +25,19 @@ use web::Web;
|
|||||||
use fetch::Fetch;
|
use fetch::Fetch;
|
||||||
use parity_dapps::WebApp;
|
use parity_dapps::WebApp;
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
use {WebProxyTokens};
|
use parity_ui;
|
||||||
|
use {WebProxyTokens, ParentFrameSettings};
|
||||||
|
|
||||||
|
mod app;
|
||||||
mod cache;
|
mod cache;
|
||||||
mod fs;
|
mod fs;
|
||||||
|
mod ui;
|
||||||
pub mod fetcher;
|
pub mod fetcher;
|
||||||
pub mod manifest;
|
pub mod manifest;
|
||||||
|
|
||||||
extern crate parity_ui;
|
pub use self::app::App;
|
||||||
|
|
||||||
pub const HOME_PAGE: &'static str = "parity";
|
pub const HOME_PAGE: &'static str = "home";
|
||||||
pub const DAPPS_DOMAIN: &'static str = ".web3.site";
|
|
||||||
pub const RPC_PATH: &'static str = "rpc";
|
pub const RPC_PATH: &'static str = "rpc";
|
||||||
pub const API_PATH: &'static str = "api";
|
pub const API_PATH: &'static str = "api";
|
||||||
pub const UTILS_PATH: &'static str = "parity-utils";
|
pub const UTILS_PATH: &'static str = "parity-utils";
|
||||||
@@ -44,18 +48,27 @@ pub fn utils() -> Box<Endpoint> {
|
|||||||
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
|
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>(
|
pub fn all_endpoints<F: Fetch>(
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
extra_dapps: Vec<PathBuf>,
|
extra_dapps: Vec<PathBuf>,
|
||||||
signer_address: Option<(String, u16)>,
|
dapps_domain: &str,
|
||||||
|
embeddable: Option<ParentFrameSettings>,
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Endpoints {
|
) -> Endpoints {
|
||||||
// fetch fs dapps at first to avoid overwriting builtins
|
// fetch fs dapps at first to avoid overwriting builtins
|
||||||
let mut pages = fs::local_endpoints(dapps_path, signer_address.clone());
|
let mut pages = fs::local_endpoints(dapps_path, embeddable.clone());
|
||||||
for path in extra_dapps {
|
for path in extra_dapps {
|
||||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), signer_address.clone()) {
|
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone()) {
|
||||||
pages.insert(id, endpoint);
|
pages.insert(id, endpoint);
|
||||||
} else {
|
} else {
|
||||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
||||||
@@ -63,14 +76,14 @@ pub fn all_endpoints<F: Fetch>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(signer_address.clone()));
|
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(embeddable.clone()));
|
||||||
pages.insert("proxy".into(), ProxyPac::boxed(signer_address.clone()));
|
pages.insert("proxy".into(), ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned()));
|
||||||
pages.insert(WEB_PATH.into(), Web::boxed(signer_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
||||||
|
|
||||||
pages
|
Arc::new(pages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
|
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 {
|
pages.insert(id.to_owned(), Box::new(match embed_at {
|
||||||
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
|
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
|
||||||
Embeddable::No => PageEndpoint::new(T::default()),
|
Embeddable::No => PageEndpoint::new(T::default()),
|
||||||
@@ -78,7 +91,7 @@ fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum Embeddable {
|
enum Embeddable {
|
||||||
Yes(Option<(String, u16)>),
|
Yes(Option<ParentFrameSettings>),
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|||||||
55
dapps/src/apps/ui.rs
Normal file
55
dapps/src/apps/ui.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// 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,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
//! URL Endpoint traits
|
//! URL Endpoint traits
|
||||||
|
|
||||||
use hyper::{self, server, net};
|
use std::sync::Arc;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use hyper::{self, server, net};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default, Clone)]
|
#[derive(Debug, PartialEq, Default, Clone)]
|
||||||
pub struct EndpointPath {
|
pub struct EndpointPath {
|
||||||
pub app_id: String,
|
pub app_id: String,
|
||||||
@@ -37,7 +39,7 @@ pub struct EndpointInfo {
|
|||||||
pub icon_url: String,
|
pub icon_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
|
pub type Endpoints = Arc<BTreeMap<String, Box<Endpoint>>>;
|
||||||
pub type Handler = server::Handler<net::HttpStream> + Send;
|
pub type Handler = server::Handler<net::HttpStream> + Send;
|
||||||
|
|
||||||
pub trait Endpoint : Send + Sync {
|
pub trait Endpoint : Send + Sync {
|
||||||
|
|||||||
112
dapps/src/handlers/async.rs
Normal file
112
dapps/src/handlers/async.rs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,44 +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/>.
|
|
||||||
|
|
||||||
//! Authorization Handlers
|
|
||||||
|
|
||||||
use hyper::{server, Decoder, Encoder, Next};
|
|
||||||
use hyper::net::HttpStream;
|
|
||||||
use hyper::status::StatusCode;
|
|
||||||
|
|
||||||
pub struct AuthRequiredHandler;
|
|
||||||
|
|
||||||
impl server::Handler<HttpStream> for AuthRequiredHandler {
|
|
||||||
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(StatusCode::Unauthorized);
|
|
||||||
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
|
|
||||||
Next::write()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
|
||||||
Next::end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ use hyper::status::StatusCode;
|
|||||||
use util::version;
|
use util::version;
|
||||||
|
|
||||||
use handlers::add_security_headers;
|
use handlers::add_security_headers;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ContentHandler {
|
pub struct ContentHandler {
|
||||||
@@ -31,7 +32,7 @@ pub struct ContentHandler {
|
|||||||
content: String,
|
content: String,
|
||||||
mimetype: Mime,
|
mimetype: Mime,
|
||||||
write_pos: usize,
|
write_pos: usize,
|
||||||
safe_to_embed_on: Option<(String, u16)>,
|
safe_to_embed_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContentHandler {
|
impl ContentHandler {
|
||||||
@@ -39,15 +40,17 @@ impl ContentHandler {
|
|||||||
Self::new(StatusCode::Ok, content, mimetype)
|
Self::new(StatusCode::Ok, content, mimetype)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not_found(content: String, mimetype: Mime) -> Self {
|
pub fn html(code: StatusCode, content: String, embeddable_on: Embeddable) -> Self {
|
||||||
Self::new(StatusCode::NotFound, content, mimetype)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn html(code: StatusCode, content: String, embeddable_on: Option<(String, u16)>) -> Self {
|
|
||||||
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
|
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn error(
|
||||||
|
code: StatusCode,
|
||||||
|
title: &str,
|
||||||
|
message: &str,
|
||||||
|
details: Option<&str>,
|
||||||
|
embeddable_on: Embeddable,
|
||||||
|
) -> Self {
|
||||||
Self::html(code, format!(
|
Self::html(code, format!(
|
||||||
include_str!("../error_tpl.html"),
|
include_str!("../error_tpl.html"),
|
||||||
title=title,
|
title=title,
|
||||||
@@ -61,13 +64,18 @@ impl ContentHandler {
|
|||||||
Self::new_embeddable(code, content, mimetype, None)
|
Self::new_embeddable(code, content, mimetype, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new_embeddable(
|
||||||
|
code: StatusCode,
|
||||||
|
content: String,
|
||||||
|
mimetype: Mime,
|
||||||
|
safe_to_embed_on: Embeddable,
|
||||||
|
) -> Self {
|
||||||
ContentHandler {
|
ContentHandler {
|
||||||
code: code,
|
code,
|
||||||
content: content,
|
content,
|
||||||
mimetype: mimetype,
|
mimetype,
|
||||||
write_pos: 0,
|
write_pos: 0,
|
||||||
safe_to_embed_on: embeddable_on,
|
safe_to_embed_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +92,7 @@ impl server::Handler<HttpStream> for ContentHandler {
|
|||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
res.set_status(self.code);
|
res.set_status(self.code);
|
||||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ use hyper::status::StatusCode;
|
|||||||
use endpoint::EndpointPath;
|
use endpoint::EndpointPath;
|
||||||
use handlers::{ContentHandler, StreamingHandler};
|
use handlers::{ContentHandler, StreamingHandler};
|
||||||
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
||||||
|
use {Embeddable};
|
||||||
|
|
||||||
const FETCH_TIMEOUT: u64 = 300;
|
const FETCH_TIMEOUT: u64 = 300;
|
||||||
|
|
||||||
@@ -179,7 +180,7 @@ impl server::Handler<HttpStream> for WaitingHandler {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Errors {
|
struct Errors {
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Errors {
|
impl Errors {
|
||||||
@@ -241,20 +242,20 @@ impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
|||||||
path: EndpointPath,
|
path: EndpointPath,
|
||||||
control: Control,
|
control: Control,
|
||||||
installer: H,
|
installer: H,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ContentFetcherHandler {
|
ContentFetcherHandler {
|
||||||
fetch_control: FetchControl::default(),
|
fetch_control: FetchControl::default(),
|
||||||
control: control,
|
control,
|
||||||
remote: remote,
|
remote,
|
||||||
fetch: fetch,
|
fetch,
|
||||||
status: FetchState::NotStarted(url),
|
status: FetchState::NotStarted(url),
|
||||||
installer: Some(installer),
|
installer: Some(installer),
|
||||||
path: path,
|
path,
|
||||||
errors: Errors {
|
errors: Errors {
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,39 +16,105 @@
|
|||||||
|
|
||||||
//! Hyper handlers implementations.
|
//! Hyper handlers implementations.
|
||||||
|
|
||||||
mod auth;
|
mod async;
|
||||||
mod content;
|
mod content;
|
||||||
mod echo;
|
mod echo;
|
||||||
mod fetch;
|
mod fetch;
|
||||||
mod redirect;
|
mod redirect;
|
||||||
mod streaming;
|
mod streaming;
|
||||||
|
|
||||||
pub use self::auth::AuthRequiredHandler;
|
pub use self::async::AsyncHandler;
|
||||||
pub use self::content::ContentHandler;
|
pub use self::content::ContentHandler;
|
||||||
pub use self::echo::EchoHandler;
|
pub use self::echo::EchoHandler;
|
||||||
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
||||||
pub use self::redirect::Redirection;
|
pub use self::redirect::Redirection;
|
||||||
pub use self::streaming::StreamingHandler;
|
pub use self::streaming::StreamingHandler;
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
use util::Itertools;
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use hyper::{server, header, net, uri};
|
use hyper::{server, header, net, uri};
|
||||||
use address;
|
use {apps, address, Embeddable};
|
||||||
|
|
||||||
/// Adds security-related headers to the Response.
|
/// Adds security-related headers to the Response.
|
||||||
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option<(String, u16)>) {
|
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-XSS-Protection", vec![b"1; mode=block".to_vec()]);
|
||||||
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
||||||
|
|
||||||
// Embedding header:
|
// Embedding header:
|
||||||
if let Some(embeddable_on) = embeddable_on {
|
if let None = embeddable_on {
|
||||||
headers.set_raw(
|
|
||||||
"X-Frame-Options",
|
|
||||||
vec![format!("ALLOW-FROM http://{}", address(&embeddable_on)).into_bytes()]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// TODO [ToDr] Should we be more strict here (DENY?)?
|
|
||||||
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
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)
|
||||||
|
{
|
||||||
|
let script_src = embeddable_on.as_ref()
|
||||||
|
.map(|e| e.extra_script_src.iter()
|
||||||
|
.map(|&(ref host, port)| address(host, port))
|
||||||
|
.join(" ")
|
||||||
|
).unwrap_or_default();
|
||||||
|
format!(
|
||||||
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' {};",
|
||||||
|
script_src
|
||||||
|
).into_bytes()
|
||||||
|
},
|
||||||
|
// Same restrictions as script-src with additional
|
||||||
|
// blob: that is required for camera access (worker)
|
||||||
|
b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' https: blob:;".to_vec(),
|
||||||
|
// Restrict everything else to the same origin.
|
||||||
|
b"default-src 'self';".to_vec(),
|
||||||
|
// Run in sandbox mode (although it's not fully safe since we allow same-origin and script)
|
||||||
|
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)| address(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(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -87,4 +153,3 @@ pub fn convert_uri_to_url(uri: &uri::RequestUri, host: Option<&header::Host>) ->
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ use hyper::mime::Mime;
|
|||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use handlers::add_security_headers;
|
use handlers::add_security_headers;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 1024;
|
const BUFFER_SIZE: usize = 1024;
|
||||||
|
|
||||||
@@ -33,11 +34,11 @@ pub struct StreamingHandler<R: io::Read> {
|
|||||||
status: StatusCode,
|
status: StatusCode,
|
||||||
content: io::BufReader<R>,
|
content: io::BufReader<R>,
|
||||||
mimetype: Mime,
|
mimetype: Mime,
|
||||||
safe_to_embed_on: Option<(String, u16)>,
|
safe_to_embed_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: io::Read> StreamingHandler<R> {
|
impl<R: io::Read> StreamingHandler<R> {
|
||||||
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Embeddable) -> Self {
|
||||||
StreamingHandler {
|
StreamingHandler {
|
||||||
buffer: [0; BUFFER_SIZE],
|
buffer: [0; BUFFER_SIZE],
|
||||||
buffer_leftover: 0,
|
buffer_leftover: 0,
|
||||||
@@ -68,7 +69,7 @@ impl<R: io::Read> server::Handler<HttpStream> for StreamingHandler<R> {
|
|||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
res.set_status(self.status);
|
res.set_status(self.status);
|
||||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
488
dapps/src/lib.rs
488
dapps/src/lib.rs
@@ -20,26 +20,28 @@
|
|||||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||||
|
|
||||||
extern crate base32;
|
extern crate base32;
|
||||||
extern crate hyper;
|
extern crate futures;
|
||||||
extern crate time;
|
extern crate linked_hash_map;
|
||||||
extern crate url as url_lib;
|
extern crate mime_guess;
|
||||||
extern crate unicase;
|
extern crate rand;
|
||||||
|
extern crate rustc_hex;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
extern crate time;
|
||||||
|
extern crate unicase;
|
||||||
|
extern crate url as url_lib;
|
||||||
extern crate zip;
|
extern crate zip;
|
||||||
extern crate rand;
|
|
||||||
extern crate jsonrpc_core;
|
extern crate jsonrpc_core;
|
||||||
extern crate jsonrpc_http_server;
|
extern crate jsonrpc_http_server;
|
||||||
extern crate mime_guess;
|
|
||||||
extern crate rustc_serialize;
|
|
||||||
extern crate ethcore_rpc;
|
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate parity_hash_fetch as hash_fetch;
|
|
||||||
extern crate linked_hash_map;
|
|
||||||
extern crate fetch;
|
extern crate fetch;
|
||||||
|
extern crate node_health;
|
||||||
extern crate parity_dapps_glue as parity_dapps;
|
extern crate parity_dapps_glue as parity_dapps;
|
||||||
extern crate futures;
|
extern crate parity_hash_fetch as hash_fetch;
|
||||||
extern crate parity_reactor;
|
extern crate parity_reactor;
|
||||||
|
extern crate parity_ui;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
@@ -53,13 +55,11 @@ extern crate ethcore_devtools as devtools;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
|
|
||||||
|
|
||||||
mod endpoint;
|
mod endpoint;
|
||||||
mod apps;
|
mod apps;
|
||||||
mod page;
|
mod page;
|
||||||
mod router;
|
mod router;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod rpc;
|
|
||||||
mod api;
|
mod api;
|
||||||
mod proxypac;
|
mod proxypac;
|
||||||
mod url;
|
mod url;
|
||||||
@@ -67,316 +67,197 @@ mod web;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::Arc;
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use ethcore_rpc::{Metadata};
|
use jsonrpc_http_server::{self as http, hyper, Origin};
|
||||||
use fetch::{Fetch, Client as FetchClient};
|
|
||||||
use hash_fetch::urlhint::ContractClient;
|
use fetch::Fetch;
|
||||||
use jsonrpc_core::Middleware;
|
use node_health::NodeHealth;
|
||||||
use jsonrpc_core::reactor::RpcHandler;
|
|
||||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
|
|
||||||
use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
|
pub use hash_fetch::urlhint::ContractClient;
|
||||||
|
pub use node_health::SyncStatus;
|
||||||
|
|
||||||
/// Indicates sync status
|
|
||||||
pub trait SyncStatus: Send + Sync {
|
|
||||||
/// Returns true if there is a major sync happening.
|
|
||||||
fn is_major_importing(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
|
||||||
fn is_major_importing(&self) -> bool { self() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates Web Proxy tokens
|
/// Validates Web Proxy tokens
|
||||||
pub trait WebProxyTokens: Send + Sync {
|
pub trait WebProxyTokens: Send + Sync {
|
||||||
/// Should return true if token is a valid web proxy access token.
|
/// Should return a domain allowed to be accessed by this token or `None` if the token is not valid
|
||||||
fn is_web_proxy_token_valid(&self, token: &str) -> bool;
|
fn domain(&self, token: &str) -> Option<Origin>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> WebProxyTokens for F where F: Fn(String) -> bool + Send + Sync {
|
impl<F> WebProxyTokens for F where F: Fn(String) -> Option<Origin> + Send + Sync {
|
||||||
fn is_web_proxy_token_valid(&self, token: &str) -> bool { self(token.to_owned()) }
|
fn domain(&self, token: &str) -> Option<Origin> { self(token.to_owned()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Webapps HTTP+RPC server build.
|
/// Current supported endpoints.
|
||||||
pub struct ServerBuilder<T: Fetch = FetchClient> {
|
pub struct Endpoints {
|
||||||
dapps_path: PathBuf,
|
endpoints: endpoint::Endpoints,
|
||||||
extra_dapps: Vec<PathBuf>,
|
|
||||||
registrar: Arc<ContractClient>,
|
|
||||||
sync_status: Arc<SyncStatus>,
|
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
|
||||||
signer_address: Option<(String, u16)>,
|
|
||||||
allowed_hosts: Option<Vec<String>>,
|
|
||||||
remote: Remote,
|
|
||||||
fetch: Option<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerBuilder {
|
impl Endpoints {
|
||||||
/// Construct new dapps server
|
/// Returns a current list of app endpoints.
|
||||||
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>, remote: Remote) -> Self {
|
pub fn list(&self) -> Vec<apps::App> {
|
||||||
ServerBuilder {
|
self.endpoints.iter().filter_map(|(ref k, ref e)| {
|
||||||
dapps_path: dapps_path.as_ref().to_owned(),
|
e.info().map(|ref info| apps::App::from_info(k, info))
|
||||||
extra_dapps: vec![],
|
}).collect()
|
||||||
registrar: registrar,
|
|
||||||
sync_status: Arc::new(|| false),
|
|
||||||
web_proxy_tokens: Arc::new(|_| false),
|
|
||||||
signer_address: None,
|
|
||||||
allowed_hosts: Some(vec![]),
|
|
||||||
remote: remote,
|
|
||||||
fetch: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Fetch> ServerBuilder<T> {
|
/// Dapps server as `jsonrpc-http-server` request middleware.
|
||||||
/// Set a fetch client to use.
|
pub struct Middleware {
|
||||||
pub fn fetch<X: Fetch>(self, fetch: X) -> ServerBuilder<X> {
|
router: router::Router,
|
||||||
ServerBuilder {
|
endpoints: endpoint::Endpoints,
|
||||||
dapps_path: self.dapps_path,
|
}
|
||||||
extra_dapps: vec![],
|
|
||||||
registrar: self.registrar,
|
impl Middleware {
|
||||||
sync_status: self.sync_status,
|
/// Get local endpoints handle.
|
||||||
web_proxy_tokens: self.web_proxy_tokens,
|
pub fn endpoints(&self) -> Endpoints {
|
||||||
signer_address: self.signer_address,
|
Endpoints {
|
||||||
allowed_hosts: self.allowed_hosts,
|
endpoints: self.endpoints.clone(),
|
||||||
remote: self.remote,
|
|
||||||
fetch: Some(fetch),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change default sync status.
|
/// Creates new middleware for UI server.
|
||||||
pub fn sync_status(mut self, status: Arc<SyncStatus>) -> Self {
|
pub fn ui<F: Fetch>(
|
||||||
self.sync_status = status;
|
health: NodeHealth,
|
||||||
self
|
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(
|
||||||
|
health,
|
||||||
|
content_fetcher.clone(),
|
||||||
|
remote.clone(),
|
||||||
|
);
|
||||||
|
special.insert(router::SpecialEndpoint::Home, Some(apps::ui()));
|
||||||
|
special
|
||||||
|
};
|
||||||
|
let router = router::Router::new(
|
||||||
|
content_fetcher,
|
||||||
|
None,
|
||||||
|
special,
|
||||||
|
None,
|
||||||
|
dapps_domain.to_owned(),
|
||||||
|
);
|
||||||
|
|
||||||
/// Change default web proxy tokens validator.
|
Middleware {
|
||||||
pub fn web_proxy_tokens(mut self, tokens: Arc<WebProxyTokens>) -> Self {
|
router: router,
|
||||||
self.web_proxy_tokens = tokens;
|
endpoints: Default::default(),
|
||||||
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: Option<Vec<String>>) -> Self {
|
|
||||||
self.allowed_hosts = allowed_hosts;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change extra dapps paths (apart from `dapps_path`)
|
|
||||||
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
|
|
||||||
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously start server with no authentication,
|
|
||||||
/// returns result with `Server` handle on success or an error.
|
|
||||||
pub fn start_unsecured_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, handler: RpcHandler<Metadata, S>) -> Result<Server, ServerError> {
|
|
||||||
let fetch = self.fetch_client()?;
|
|
||||||
Server::start_http(
|
|
||||||
addr,
|
|
||||||
self.allowed_hosts,
|
|
||||||
NoAuth,
|
|
||||||
handler,
|
|
||||||
self.dapps_path,
|
|
||||||
self.extra_dapps,
|
|
||||||
self.signer_address,
|
|
||||||
self.registrar,
|
|
||||||
self.sync_status,
|
|
||||||
self.web_proxy_tokens,
|
|
||||||
self.remote,
|
|
||||||
fetch,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
|
||||||
/// return result with `Server` handle on success or an error.
|
|
||||||
pub fn start_basic_auth_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, username: &str, password: &str, handler: RpcHandler<Metadata, S>) -> Result<Server, ServerError> {
|
|
||||||
let fetch = self.fetch_client()?;
|
|
||||||
Server::start_http(
|
|
||||||
addr,
|
|
||||||
self.allowed_hosts,
|
|
||||||
HttpBasicAuth::single_user(username, password),
|
|
||||||
handler,
|
|
||||||
self.dapps_path,
|
|
||||||
self.extra_dapps,
|
|
||||||
self.signer_address,
|
|
||||||
self.registrar,
|
|
||||||
self.sync_status,
|
|
||||||
self.web_proxy_tokens,
|
|
||||||
self.remote,
|
|
||||||
fetch,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_client(&self) -> Result<T, ServerError> {
|
|
||||||
match self.fetch.clone() {
|
|
||||||
Some(fetch) => Ok(fetch),
|
|
||||||
None => T::new().map_err(|_| ServerError::FetchInitialization),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Webapps HTTP server.
|
|
||||||
pub struct Server {
|
|
||||||
server: Option<hyper::server::Listening>,
|
|
||||||
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Server {
|
|
||||||
/// Returns a list of allowed hosts or `None` if all hosts are allowed.
|
|
||||||
fn allowed_hosts(hosts: Option<Vec<String>>, bind_address: String) -> Option<Vec<String>> {
|
|
||||||
let mut allowed = Vec::new();
|
|
||||||
|
|
||||||
match hosts {
|
|
||||||
Some(hosts) => allowed.extend_from_slice(&hosts),
|
|
||||||
None => return None,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add localhost domain as valid too if listening on loopback interface.
|
|
||||||
allowed.push(bind_address.replace("127.0.0.1", "localhost").into());
|
|
||||||
allowed.push(bind_address.into());
|
|
||||||
Some(allowed)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a list of CORS domains for API endpoint.
|
|
||||||
fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<String> {
|
|
||||||
match signer_address {
|
|
||||||
Some(signer_address) => vec![
|
|
||||||
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
|
||||||
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
|
||||||
format!("http://{}", address(&signer_address)),
|
|
||||||
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
|
||||||
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
|
||||||
format!("https://{}", address(&signer_address)),
|
|
||||||
|
|
||||||
],
|
|
||||||
None => vec![],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
|
/// Creates new Dapps server middleware.
|
||||||
addr: &SocketAddr,
|
pub fn dapps<F: Fetch>(
|
||||||
hosts: Option<Vec<String>>,
|
health: NodeHealth,
|
||||||
authorization: A,
|
remote: Remote,
|
||||||
handler: RpcHandler<Metadata, T>,
|
ui_address: Option<(String, u16)>,
|
||||||
|
extra_embed_on: Vec<(String, u16)>,
|
||||||
|
extra_script_src: Vec<(String, u16)>,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
extra_dapps: Vec<PathBuf>,
|
extra_dapps: Vec<PathBuf>,
|
||||||
signer_address: Option<(String, u16)>,
|
dapps_domain: &str,
|
||||||
registrar: Arc<ContractClient>,
|
registrar: Arc<ContractClient>,
|
||||||
sync_status: Arc<SyncStatus>,
|
sync_status: Arc<SyncStatus>,
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
remote: Remote,
|
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Result<Server, ServerError> {
|
) -> Self {
|
||||||
let panic_handler = Arc::new(Mutex::new(None));
|
let embeddable = as_embeddable(ui_address, extra_embed_on, extra_script_src, dapps_domain);
|
||||||
let authorization = Arc::new(authorization);
|
|
||||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||||
sync_status,
|
sync_status.clone(),
|
||||||
signer_address.clone(),
|
|
||||||
remote.clone(),
|
remote.clone(),
|
||||||
fetch.clone(),
|
fetch.clone(),
|
||||||
));
|
).embeddable_on(embeddable.clone()).allow_dapps(true));
|
||||||
let endpoints = Arc::new(apps::all_endpoints(
|
let endpoints = apps::all_endpoints(
|
||||||
dapps_path,
|
dapps_path,
|
||||||
extra_dapps,
|
extra_dapps,
|
||||||
signer_address.clone(),
|
dapps_domain,
|
||||||
|
embeddable.clone(),
|
||||||
web_proxy_tokens,
|
web_proxy_tokens,
|
||||||
remote.clone(),
|
remote.clone(),
|
||||||
fetch.clone(),
|
fetch.clone(),
|
||||||
));
|
);
|
||||||
let cors_domains = Self::cors_domains(signer_address.clone());
|
|
||||||
|
|
||||||
let special = Arc::new({
|
let special = {
|
||||||
let mut special = HashMap::new();
|
let mut special = special_endpoints(
|
||||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, cors_domains.clone(), panic_handler.clone()));
|
health,
|
||||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
content_fetcher.clone(),
|
||||||
|
remote.clone(),
|
||||||
|
);
|
||||||
special.insert(
|
special.insert(
|
||||||
router::SpecialEndpoint::Api,
|
router::SpecialEndpoint::Home,
|
||||||
api::RestApi::new(cors_domains, endpoints.clone(), content_fetcher.clone())
|
Some(apps::ui_redirection(embeddable.clone())),
|
||||||
);
|
);
|
||||||
special
|
special
|
||||||
});
|
};
|
||||||
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
|
||||||
|
|
||||||
hyper::Server::http(addr)?
|
let router = router::Router::new(
|
||||||
.handle(move |ctrl| router::Router::new(
|
content_fetcher,
|
||||||
ctrl,
|
Some(endpoints.clone()),
|
||||||
signer_address.clone(),
|
special,
|
||||||
content_fetcher.clone(),
|
embeddable,
|
||||||
endpoints.clone(),
|
dapps_domain.to_owned(),
|
||||||
special.clone(),
|
);
|
||||||
authorization.clone(),
|
|
||||||
hosts.clone(),
|
|
||||||
))
|
|
||||||
.map(|(l, srv)| {
|
|
||||||
|
|
||||||
::std::thread::spawn(move || {
|
Middleware {
|
||||||
srv.run();
|
router: router,
|
||||||
});
|
endpoints: endpoints,
|
||||||
|
|
||||||
Server {
|
|
||||||
server: Some(l),
|
|
||||||
panic_handler: panic_handler,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map_err(ServerError::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set callback for panics.
|
|
||||||
pub fn set_panic_handler<F>(&self, handler: F) where F : Fn() -> () + Send + 'static {
|
|
||||||
*self.panic_handler.lock().unwrap() = Some(Box::new(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
/// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Webapp Server startup error
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ServerError {
|
|
||||||
/// Wrapped `std::io::Error`
|
|
||||||
IoError(std::io::Error),
|
|
||||||
/// Other `hyper` error
|
|
||||||
Other(hyper::error::Error),
|
|
||||||
/// Fetch service initialization error
|
|
||||||
FetchInitialization,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<hyper::error::Error> for ServerError {
|
|
||||||
fn from(err: hyper::error::Error) -> Self {
|
|
||||||
match err {
|
|
||||||
hyper::error::Error::Io(e) => ServerError::IoError(e),
|
|
||||||
e => ServerError::Other(e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
||||||
|
health: NodeHealth,
|
||||||
|
content_fetcher: Arc<apps::fetcher::Fetcher>,
|
||||||
|
remote: Remote,
|
||||||
|
) -> 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,
|
||||||
|
health,
|
||||||
|
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)>,
|
||||||
|
extra_script_src: Vec<(String, u16)>,
|
||||||
|
dapps_domain: &str,
|
||||||
|
) -> Option<ParentFrameSettings> {
|
||||||
|
ui_address.map(|(host, port)| ParentFrameSettings {
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
extra_embed_on,
|
||||||
|
extra_script_src,
|
||||||
|
dapps_domain: dapps_domain.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Random filename
|
/// Random filename
|
||||||
fn random_filename() -> String {
|
fn random_filename() -> String {
|
||||||
use ::rand::Rng;
|
use ::rand::Rng;
|
||||||
@@ -384,48 +265,19 @@ fn random_filename() -> String {
|
|||||||
rng.gen_ascii_chars().take(12).collect()
|
rng.gen_ascii_chars().take(12).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address(address: &(String, u16)) -> String {
|
type Embeddable = Option<ParentFrameSettings>;
|
||||||
format!("{}:{}", address.0, address.1)
|
|
||||||
}
|
/// Parent frame host and port allowed to embed the content.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
#[cfg(test)]
|
pub struct ParentFrameSettings {
|
||||||
mod util_tests {
|
/// Hostname
|
||||||
use super::Server;
|
pub host: String,
|
||||||
|
/// Port
|
||||||
#[test]
|
pub port: u16,
|
||||||
fn should_return_allowed_hosts() {
|
/// Additional URLs the dapps can be embedded on.
|
||||||
// given
|
pub extra_embed_on: Vec<(String, u16)>,
|
||||||
let bind_address = "127.0.0.1".to_owned();
|
/// Additional URLs the dapp scripts can be loaded from.
|
||||||
|
pub extra_script_src: Vec<(String, u16)>,
|
||||||
// when
|
/// Dapps Domain (web3.site)
|
||||||
let all = Server::allowed_hosts(None, bind_address.clone());
|
pub dapps_domain: String,
|
||||||
let address = Server::allowed_hosts(Some(Vec::new()), bind_address.clone());
|
|
||||||
let some = Server::allowed_hosts(Some(vec!["ethcore.io".into()]), bind_address.clone());
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(all, None);
|
|
||||||
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
|
|
||||||
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_cors_domains() {
|
|
||||||
// given
|
|
||||||
|
|
||||||
// when
|
|
||||||
let none = Server::cors_domains(None);
|
|
||||||
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)));
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(none, Vec::<String>::new());
|
|
||||||
assert_eq!(some, vec![
|
|
||||||
"http://parity.web3.site".to_owned(),
|
|
||||||
"http://parity.web3.site:18180".into(),
|
|
||||||
"http://127.0.0.1:18180".into(),
|
|
||||||
"https://parity.web3.site".into(),
|
|
||||||
"https://parity.web3.site:18180".into(),
|
|
||||||
"https://127.0.0.1:18180".into()
|
|
||||||
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use page::{handler, PageCache};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||||
use parity_dapps::{WebApp, File, Info};
|
use parity_dapps::{WebApp, File, Info};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
pub struct PageEndpoint<T : WebApp + 'static> {
|
pub struct PageEndpoint<T : WebApp + 'static> {
|
||||||
/// Content of the files
|
/// Content of the files
|
||||||
@@ -25,8 +26,9 @@ pub struct PageEndpoint<T : WebApp + 'static> {
|
|||||||
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||||
pub prefix: Option<String>,
|
pub prefix: Option<String>,
|
||||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||||
safe_to_embed_on: Option<(String, u16)>,
|
safe_to_embed_on: Embeddable,
|
||||||
info: EndpointInfo,
|
info: EndpointInfo,
|
||||||
|
fallback_to_index_html: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: WebApp + 'static> PageEndpoint<T> {
|
impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||||
@@ -38,6 +40,20 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
|||||||
prefix: None,
|
prefix: None,
|
||||||
safe_to_embed_on: None,
|
safe_to_embed_on: None,
|
||||||
info: EndpointInfo::from(info),
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,19 +67,21 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
|||||||
prefix: Some(prefix),
|
prefix: Some(prefix),
|
||||||
safe_to_embed_on: None,
|
safe_to_embed_on: None,
|
||||||
info: EndpointInfo::from(info),
|
info: EndpointInfo::from(info),
|
||||||
|
fallback_to_index_html: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new `PageEndpoint` which can be safely used in iframe
|
/// Creates new `PageEndpoint` which can be safely used in iframe
|
||||||
/// even from different origin. It might be dangerous (clickjacking).
|
/// even from different origin. It might be dangerous (clickjacking).
|
||||||
/// Use wisely!
|
/// Use wisely!
|
||||||
pub fn new_safe_to_embed(app: T, address: Option<(String, u16)>) -> Self {
|
pub fn new_safe_to_embed(app: T, address: Embeddable) -> Self {
|
||||||
let info = app.info();
|
let info = app.info();
|
||||||
PageEndpoint {
|
PageEndpoint {
|
||||||
app: Arc::new(app),
|
app: Arc::new(app),
|
||||||
prefix: None,
|
prefix: None,
|
||||||
safe_to_embed_on: address,
|
safe_to_embed_on: address,
|
||||||
info: EndpointInfo::from(info),
|
info: EndpointInfo::from(info),
|
||||||
|
fallback_to_index_html: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +94,7 @@ impl<T: WebApp> Endpoint for PageEndpoint<T> {
|
|||||||
|
|
||||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||||
Box::new(handler::PageHandler {
|
Box::new(handler::PageHandler {
|
||||||
app: BuiltinDapp::new(self.app.clone()),
|
app: BuiltinDapp::new(self.app.clone(), self.fallback_to_index_html),
|
||||||
prefix: self.prefix.clone(),
|
prefix: self.prefix.clone(),
|
||||||
path: path,
|
path: path,
|
||||||
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
|
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
|
||||||
@@ -100,12 +118,14 @@ impl From<Info> for EndpointInfo {
|
|||||||
|
|
||||||
struct BuiltinDapp<T: WebApp + 'static> {
|
struct BuiltinDapp<T: WebApp + 'static> {
|
||||||
app: Arc<T>,
|
app: Arc<T>,
|
||||||
|
fallback_to_index_html: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: WebApp + 'static> BuiltinDapp<T> {
|
impl<T: WebApp + 'static> BuiltinDapp<T> {
|
||||||
fn new(app: Arc<T>) -> Self {
|
fn new(app: Arc<T>, fallback_to_index_html: bool) -> Self {
|
||||||
BuiltinDapp {
|
BuiltinDapp {
|
||||||
app: app,
|
app: app,
|
||||||
|
fallback_to_index_html: fallback_to_index_html,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,13 +134,19 @@ impl<T: WebApp + 'static> handler::Dapp for BuiltinDapp<T> {
|
|||||||
type DappFile = BuiltinDappFile<T>;
|
type DappFile = BuiltinDappFile<T>;
|
||||||
|
|
||||||
fn file(&self, path: &str) -> Option<Self::DappFile> {
|
fn file(&self, path: &str) -> Option<Self::DappFile> {
|
||||||
self.app.file(path).map(|_| {
|
let file = |path| self.app.file(path).map(|_| {
|
||||||
BuiltinDappFile {
|
BuiltinDappFile {
|
||||||
app: self.app.clone(),
|
app: self.app.clone(),
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
write_pos: 0,
|
write_pos: 0,
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
let res = file(path);
|
||||||
|
if self.fallback_to_index_html {
|
||||||
|
res.or_else(|| file("index.html"))
|
||||||
|
} else {
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ use hyper::status::StatusCode;
|
|||||||
use hyper::{Decoder, Encoder, Next};
|
use hyper::{Decoder, Encoder, Next};
|
||||||
use endpoint::EndpointPath;
|
use endpoint::EndpointPath;
|
||||||
use handlers::{ContentHandler, add_security_headers};
|
use handlers::{ContentHandler, add_security_headers};
|
||||||
|
use {Embeddable};
|
||||||
|
|
||||||
/// Represents a file that can be sent to client.
|
/// Represents a file that can be sent to client.
|
||||||
/// Implementation should keep track of bytes already sent internally.
|
/// Implementation should keep track of bytes already sent internally.
|
||||||
@@ -59,7 +60,7 @@ pub enum ServedFile<T: Dapp> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Dapp> ServedFile<T> {
|
impl<T: Dapp> ServedFile<T> {
|
||||||
pub fn new(embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new(embeddable_on: Embeddable) -> Self {
|
||||||
ServedFile::Error(ContentHandler::error(
|
ServedFile::Error(ContentHandler::error(
|
||||||
StatusCode::NotFound,
|
StatusCode::NotFound,
|
||||||
"404 Not Found",
|
"404 Not Found",
|
||||||
@@ -102,7 +103,7 @@ pub struct PageHandler<T: Dapp> {
|
|||||||
/// Requested path.
|
/// Requested path.
|
||||||
pub path: EndpointPath,
|
pub path: EndpointPath,
|
||||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||||
pub safe_to_embed_on: Option<(String, u16)>,
|
pub safe_to_embed_on: Embeddable,
|
||||||
/// Cache settings for this page.
|
/// Cache settings for this page.
|
||||||
pub cache: PageCache,
|
pub cache: PageCache,
|
||||||
}
|
}
|
||||||
@@ -174,7 +175,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Security headers:
|
// Security headers:
|
||||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||||
Next::write()
|
Next::write()
|
||||||
},
|
},
|
||||||
ServedFile::Error(ref mut handler) => {
|
ServedFile::Error(ref mut handler) => {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use page::handler::{self, PageCache, PageHandlerWaiting};
|
use page::handler::{self, PageCache, PageHandlerWaiting};
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LocalPageEndpoint {
|
pub struct LocalPageEndpoint {
|
||||||
@@ -28,11 +29,11 @@ pub struct LocalPageEndpoint {
|
|||||||
mime: Option<Mime>,
|
mime: Option<Mime>,
|
||||||
info: Option<EndpointInfo>,
|
info: Option<EndpointInfo>,
|
||||||
cache: PageCache,
|
cache: PageCache,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalPageEndpoint {
|
impl LocalPageEndpoint {
|
||||||
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self {
|
||||||
LocalPageEndpoint {
|
LocalPageEndpoint {
|
||||||
path: path,
|
path: path,
|
||||||
mime: None,
|
mime: None,
|
||||||
|
|||||||
@@ -18,37 +18,36 @@
|
|||||||
|
|
||||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||||
use handlers::ContentHandler;
|
use handlers::ContentHandler;
|
||||||
use apps::{HOME_PAGE, DAPPS_DOMAIN};
|
use apps::HOME_PAGE;
|
||||||
use address;
|
use {address, Embeddable};
|
||||||
|
|
||||||
pub struct ProxyPac {
|
pub struct ProxyPac {
|
||||||
signer_address: Option<(String, u16)>,
|
embeddable: Embeddable,
|
||||||
|
dapps_domain: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProxyPac {
|
impl ProxyPac {
|
||||||
pub fn boxed(signer_address: Option<(String, u16)>) -> Box<Endpoint> {
|
pub fn boxed(embeddable: Embeddable, dapps_domain: String) -> Box<Endpoint> {
|
||||||
Box::new(ProxyPac {
|
Box::new(ProxyPac { embeddable, dapps_domain })
|
||||||
signer_address: signer_address
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for ProxyPac {
|
impl Endpoint for ProxyPac {
|
||||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||||
let signer = self.signer_address
|
let ui = self.embeddable
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(address)
|
.map(|ref parent| address(&parent.host, parent.port))
|
||||||
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
r#"
|
r#"
|
||||||
function FindProxyForURL(url, host) {{
|
function FindProxyForURL(url, host) {{
|
||||||
if (shExpMatch(host, "{0}{1}"))
|
if (shExpMatch(host, "{0}.{1}"))
|
||||||
{{
|
{{
|
||||||
return "PROXY {4}";
|
return "PROXY {4}";
|
||||||
}}
|
}}
|
||||||
|
|
||||||
if (shExpMatch(host, "*{1}"))
|
if (shExpMatch(host, "*.{1}"))
|
||||||
{{
|
{{
|
||||||
return "PROXY {2}:{3}";
|
return "PROXY {2}:{3}";
|
||||||
}}
|
}}
|
||||||
@@ -56,7 +55,7 @@ function FindProxyForURL(url, host) {{
|
|||||||
return "DIRECT";
|
return "DIRECT";
|
||||||
}}
|
}}
|
||||||
"#,
|
"#,
|
||||||
HOME_PAGE, DAPPS_DOMAIN, path.host, path.port, signer);
|
HOME_PAGE, self.dapps_domain, path.host, path.port, ui);
|
||||||
|
|
||||||
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,23 +15,22 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Router implementation
|
//! Router implementation
|
||||||
//! Processes request handling authorization and dispatching it to proper application.
|
//! Dispatch requests to proper application.
|
||||||
|
|
||||||
pub mod auth;
|
|
||||||
mod host_validation;
|
|
||||||
|
|
||||||
use address;
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use url::{Url, Host};
|
use url::{Url, Host};
|
||||||
use hyper::{self, server, header, Next, Encoder, Decoder, Control, StatusCode};
|
use hyper::{self, server, header, Control};
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
use apps::{self, DAPPS_DOMAIN};
|
use jsonrpc_http_server as http;
|
||||||
|
|
||||||
|
use apps;
|
||||||
use apps::fetcher::Fetcher;
|
use apps::fetcher::Fetcher;
|
||||||
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
|
||||||
use handlers::{self, Redirection, ContentHandler};
|
use handlers;
|
||||||
use self::auth::{Authorization, Authorized};
|
use Embeddable;
|
||||||
|
|
||||||
/// Special endpoints are accessible on every domain (every dapp)
|
/// Special endpoints are accessible on every domain (every dapp)
|
||||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||||
@@ -39,60 +38,38 @@ pub enum SpecialEndpoint {
|
|||||||
Rpc,
|
Rpc,
|
||||||
Api,
|
Api,
|
||||||
Utils,
|
Utils,
|
||||||
|
Home,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Router<A: Authorization + 'static> {
|
pub struct Router {
|
||||||
control: Option<Control>,
|
endpoints: Option<Endpoints>,
|
||||||
signer_address: Option<(String, u16)>,
|
|
||||||
endpoints: Arc<Endpoints>,
|
|
||||||
fetch: Arc<Fetcher>,
|
fetch: Arc<Fetcher>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
embeddable_on: Embeddable,
|
||||||
allowed_hosts: Option<Vec<String>>,
|
dapps_domain: String,
|
||||||
handler: Box<server::Handler<HttpStream> + Send>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
impl http::RequestMiddleware for Router {
|
||||||
|
fn on_request(&self, req: &server::Request<HttpStream>, control: &Control) -> http::RequestMiddlewareAction {
|
||||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
|
||||||
// Choose proper handler depending on path / domain
|
// Choose proper handler depending on path / domain
|
||||||
let url = handlers::extract_url(&req);
|
let url = handlers::extract_url(req);
|
||||||
let endpoint = extract_endpoint(&url);
|
let endpoint = extract_endpoint(&url, &self.dapps_domain);
|
||||||
let referer = extract_referer_endpoint(&req);
|
let referer = extract_referer_endpoint(req, &self.dapps_domain);
|
||||||
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
|
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_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);
|
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
|
||||||
|
|
||||||
// Validate Host header
|
let control = control.clone();
|
||||||
if let Some(ref hosts) = self.allowed_hosts {
|
|
||||||
trace!(target: "dapps", "Validating host headers against: {:?}", hosts);
|
|
||||||
let is_valid = is_utils || host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect());
|
|
||||||
if !is_valid {
|
|
||||||
debug!(target: "dapps", "Rejecting invalid host header.");
|
|
||||||
self.handler = host_validation::host_invalid_response();
|
|
||||||
return self.handler.on_request(req);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!(target: "dapps", "Checking authorization.");
|
|
||||||
// Check authorization
|
|
||||||
let auth = self.authorization.is_authorized(&req);
|
|
||||||
if let Authorized::No(handler) = auth {
|
|
||||||
debug!(target: "dapps", "Authorization denied.");
|
|
||||||
self.handler = handler;
|
|
||||||
return self.handler.on_request(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let control = self.control.take().expect("on_request is called only once; control is always defined at start; qed");
|
|
||||||
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
|
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
|
||||||
self.handler = match (endpoint.0, endpoint.1, referer) {
|
let handler: Option<Box<Handler>> = match (endpoint.0, endpoint.1, referer) {
|
||||||
// Handle invalid web requests that we can recover from
|
// Handle invalid web requests that we can recover from
|
||||||
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
|
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
|
||||||
if referer.app_id == apps::WEB_PATH
|
if referer.app_id == apps::WEB_PATH
|
||||||
&& self.endpoints.contains_key(apps::WEB_PATH)
|
&& self.endpoints.as_ref().map(|ep| ep.contains_key(apps::WEB_PATH)).unwrap_or(false)
|
||||||
&& !is_web_endpoint(path)
|
&& !is_web_endpoint(path)
|
||||||
=>
|
=>
|
||||||
{
|
{
|
||||||
@@ -100,110 +77,87 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
let len = cmp::min(referer_url.path.len(), 2); // /web/<encoded>/
|
let len = cmp::min(referer_url.path.len(), 2); // /web/<encoded>/
|
||||||
let base = referer_url.path[..len].join("/");
|
let base = referer_url.path[..len].join("/");
|
||||||
let requested = url.map(|u| u.path.join("/")).unwrap_or_default();
|
let requested = url.map(|u| u.path.join("/")).unwrap_or_default();
|
||||||
Redirection::boxed(&format!("/{}/{}", base, requested))
|
Some(handlers::Redirection::boxed(&format!("/{}/{}", base, requested)))
|
||||||
},
|
},
|
||||||
// First check special endpoints
|
// First check special endpoints
|
||||||
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
|
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
|
||||||
trace!(target: "dapps", "Resolving to special endpoint.");
|
trace!(target: "dapps", "Resolving to special endpoint.");
|
||||||
self.special.get(endpoint)
|
self.special.get(endpoint)
|
||||||
.expect("special known to contain key; qed")
|
.expect("special known to contain key; qed")
|
||||||
.to_async_handler(path.clone().unwrap_or_default(), control)
|
.as_ref()
|
||||||
|
.map(|special| special.to_async_handler(path.clone().unwrap_or_default(), control))
|
||||||
},
|
},
|
||||||
// Then delegate to dapp
|
// Then delegate to dapp
|
||||||
(Some(ref path), _, _) if self.endpoints.contains_key(&path.app_id) => {
|
(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.");
|
trace!(target: "dapps", "Resolving to local/builtin dapp.");
|
||||||
self.endpoints.get(&path.app_id)
|
Some(self.endpoints
|
||||||
|
.as_ref()
|
||||||
|
.expect("endpoints known to be set; qed")
|
||||||
|
.get(&path.app_id)
|
||||||
.expect("endpoints known to contain key; qed")
|
.expect("endpoints known to contain key; qed")
|
||||||
.to_async_handler(path.clone(), control)
|
.to_async_handler(path.clone(), control))
|
||||||
},
|
},
|
||||||
// Try to resolve and fetch the dapp
|
// Try to resolve and fetch the dapp
|
||||||
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
|
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
|
||||||
trace!(target: "dapps", "Resolving to fetchable content.");
|
trace!(target: "dapps", "Resolving to fetchable content.");
|
||||||
self.fetch.to_async_handler(path.clone(), control)
|
Some(self.fetch.to_async_handler(path.clone(), control))
|
||||||
},
|
},
|
||||||
// NOTE [todr] /home is redirected to home page since some users may have the redirection cached
|
// 404 for non-existent content (only if serving endpoints and not homepage)
|
||||||
// (in the past we used 301 instead of 302)
|
(Some(ref path), _, _)
|
||||||
// It should be safe to remove it in (near) future.
|
if (is_get_request || is_head_request)
|
||||||
//
|
&& self.endpoints.is_some()
|
||||||
// 404 for non-existent content
|
&& path.app_id != apps::HOME_PAGE
|
||||||
(Some(ref path), _, _) if is_get_request && path.app_id != "home" => {
|
=>
|
||||||
|
{
|
||||||
trace!(target: "dapps", "Resolving to 404.");
|
trace!(target: "dapps", "Resolving to 404.");
|
||||||
Box::new(ContentHandler::error(
|
Some(Box::new(handlers::ContentHandler::error(
|
||||||
StatusCode::NotFound,
|
hyper::StatusCode::NotFound,
|
||||||
"404 Not Found",
|
"404 Not Found",
|
||||||
"Requested content was not found.",
|
"Requested content was not found.",
|
||||||
None,
|
None,
|
||||||
self.signer_address.clone(),
|
self.embeddable_on.clone(),
|
||||||
))
|
)))
|
||||||
},
|
},
|
||||||
// Redirect any other GET request to signer.
|
// Any other GET|HEAD requests to home page.
|
||||||
_ if is_get_request => {
|
_ if (is_get_request || is_head_request) && self.special.contains_key(&SpecialEndpoint::Home) => {
|
||||||
if let Some(ref signer_address) = self.signer_address {
|
self.special.get(&SpecialEndpoint::Home)
|
||||||
trace!(target: "dapps", "Redirecting to signer interface.");
|
.expect("special known to contain key; qed")
|
||||||
Redirection::boxed(&format!("http://{}", address(signer_address)))
|
.as_ref()
|
||||||
} else {
|
.map(|special| special.to_async_handler(Default::default(), control))
|
||||||
trace!(target: "dapps", "Signer disabled, returning 404.");
|
|
||||||
Box::new(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."),
|
|
||||||
self.signer_address.clone(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// RPC by default
|
// RPC by default
|
||||||
_ => {
|
_ => {
|
||||||
trace!(target: "dapps", "Resolving to RPC call.");
|
trace!(target: "dapps", "Resolving to RPC call.");
|
||||||
self.special.get(&SpecialEndpoint::Rpc)
|
None
|
||||||
.expect("RPC endpoint always stored; qed")
|
|
||||||
.to_async_handler(EndpointPath::default(), control)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Delegate on_request to proper handler
|
match handler {
|
||||||
self.handler.on_request(req)
|
Some(handler) => http::RequestMiddlewareAction::Respond {
|
||||||
}
|
should_validate_hosts: !is_utils,
|
||||||
|
handler: handler,
|
||||||
/// This event occurs each time the `Request` is ready to be read from.
|
},
|
||||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
None => http::RequestMiddlewareAction::Proceed {
|
||||||
self.handler.on_request_readable(decoder)
|
should_continue_on_invalid_cors: !is_origin_set,
|
||||||
}
|
},
|
||||||
|
}
|
||||||
/// This event occurs after the first time this handled signals `Next::write()`.
|
|
||||||
fn on_response(&mut self, response: &mut server::Response) -> Next {
|
|
||||||
self.handler.on_response(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This event occurs each time the `Response` is ready to be written to.
|
|
||||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
|
||||||
self.handler.on_response_writable(encoder)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Authorization> Router<A> {
|
impl Router {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
control: Control,
|
|
||||||
signer_address: Option<(String, u16)>,
|
|
||||||
content_fetcher: Arc<Fetcher>,
|
content_fetcher: Arc<Fetcher>,
|
||||||
endpoints: Arc<Endpoints>,
|
endpoints: Option<Endpoints>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
embeddable_on: Embeddable,
|
||||||
allowed_hosts: Option<Vec<String>>,
|
dapps_domain: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
||||||
let handler = special.get(&SpecialEndpoint::Utils)
|
|
||||||
.expect("Utils endpoint always stored; qed")
|
|
||||||
.to_handler(EndpointPath::default());
|
|
||||||
Router {
|
Router {
|
||||||
control: Some(control),
|
|
||||||
signer_address: signer_address,
|
|
||||||
endpoints: endpoints,
|
endpoints: endpoints,
|
||||||
fetch: content_fetcher,
|
fetch: content_fetcher,
|
||||||
special: special,
|
special: special,
|
||||||
authorization: authorization,
|
embeddable_on: embeddable_on,
|
||||||
allowed_hosts: allowed_hosts,
|
dapps_domain: format!(".{}", dapps_domain),
|
||||||
handler: handler,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,19 +169,19 @@ fn is_web_endpoint(path: &Option<EndpointPath>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_referer_endpoint(req: &server::Request<HttpStream>) -> Option<(EndpointPath, Url)> {
|
fn extract_referer_endpoint(req: &server::Request<HttpStream>, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
|
||||||
let referer = req.headers().get::<header::Referer>();
|
let referer = req.headers().get::<header::Referer>();
|
||||||
|
|
||||||
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
|
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
|
||||||
url.and_then(|url| {
|
url.and_then(|url| {
|
||||||
let option = Some(url);
|
let option = Some(url);
|
||||||
extract_url_referer_endpoint(&option).or_else(|| {
|
extract_url_referer_endpoint(&option, dapps_domain).or_else(|| {
|
||||||
extract_endpoint(&option).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
|
extract_endpoint(&option, dapps_domain).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_url_referer_endpoint(url: &Option<Url>) -> Option<(EndpointPath, Url)> {
|
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());
|
let query = url.as_ref().and_then(|url| url.query.as_ref());
|
||||||
match (url, query) {
|
match (url, query) {
|
||||||
(&Some(ref url), Some(ref query)) if query.starts_with(apps::URL_REFERER) => {
|
(&Some(ref url), Some(ref query)) if query.starts_with(apps::URL_REFERER) => {
|
||||||
@@ -235,7 +189,7 @@ fn extract_url_referer_endpoint(url: &Option<Url>) -> Option<(EndpointPath, Url)
|
|||||||
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
|
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
|
||||||
|
|
||||||
let referer_url = Url::parse(&referer_url).ok();
|
let referer_url = Url::parse(&referer_url).ok();
|
||||||
extract_endpoint(&referer_url).0.map(|endpoint| {
|
extract_endpoint(&referer_url, dapps_domain).0.map(|endpoint| {
|
||||||
(endpoint, referer_url.expect("Endpoint returned only when url `is_some`").clone())
|
(endpoint, referer_url.expect("Endpoint returned only when url `is_some`").clone())
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -243,7 +197,7 @@ fn extract_url_referer_endpoint(url: &Option<Url>) -> Option<(EndpointPath, Url)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
|
fn extract_endpoint(url: &Option<Url>, dapps_domain: &str) -> (Option<EndpointPath>, SpecialEndpoint) {
|
||||||
fn special_endpoint(url: &Url) -> SpecialEndpoint {
|
fn special_endpoint(url: &Url) -> SpecialEndpoint {
|
||||||
if url.path.len() <= 1 {
|
if url.path.len() <= 1 {
|
||||||
return SpecialEndpoint::None;
|
return SpecialEndpoint::None;
|
||||||
@@ -253,14 +207,15 @@ fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint
|
|||||||
apps::RPC_PATH => SpecialEndpoint::Rpc,
|
apps::RPC_PATH => SpecialEndpoint::Rpc,
|
||||||
apps::API_PATH => SpecialEndpoint::Api,
|
apps::API_PATH => SpecialEndpoint::Api,
|
||||||
apps::UTILS_PATH => SpecialEndpoint::Utils,
|
apps::UTILS_PATH => SpecialEndpoint::Utils,
|
||||||
|
apps::HOME_PAGE => SpecialEndpoint::Home,
|
||||||
_ => SpecialEndpoint::None,
|
_ => SpecialEndpoint::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match *url {
|
match *url {
|
||||||
Some(ref url) => match url.host {
|
Some(ref url) => match url.host {
|
||||||
Host::Domain(ref domain) if domain.ends_with(DAPPS_DOMAIN) => {
|
Host::Domain(ref domain) if domain.ends_with(dapps_domain) => {
|
||||||
let id = &domain[0..(domain.len() - DAPPS_DOMAIN.len())];
|
let id = &domain[0..(domain.len() - dapps_domain.len())];
|
||||||
let (id, params) = if let Some(split) = id.rfind('.') {
|
let (id, params) = if let Some(split) = id.rfind('.') {
|
||||||
let (params, id) = id.split_at(split);
|
let (params, id) = id.split_at(split);
|
||||||
(id[1..].to_owned(), [params.to_owned()].into_iter().chain(&url.path).cloned().collect())
|
(id[1..].to_owned(), [params.to_owned()].into_iter().chain(&url.path).cloned().collect())
|
||||||
@@ -294,11 +249,12 @@ fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_extract_endpoint() {
|
fn should_extract_endpoint() {
|
||||||
assert_eq!(extract_endpoint(&None), (None, SpecialEndpoint::None));
|
let dapps_domain = ".web3.site";
|
||||||
|
assert_eq!(extract_endpoint(&None, dapps_domain), (None, SpecialEndpoint::None));
|
||||||
|
|
||||||
// With path prefix
|
// With path prefix
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok()),
|
extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok(), dapps_domain),
|
||||||
(Some(EndpointPath {
|
(Some(EndpointPath {
|
||||||
app_id: "status".to_owned(),
|
app_id: "status".to_owned(),
|
||||||
app_params: vec!["index.html".to_owned()],
|
app_params: vec!["index.html".to_owned()],
|
||||||
@@ -310,7 +266,7 @@ fn should_extract_endpoint() {
|
|||||||
|
|
||||||
// With path prefix
|
// With path prefix
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok()),
|
extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok(), dapps_domain),
|
||||||
(Some(EndpointPath {
|
(Some(EndpointPath {
|
||||||
app_id: "rpc".to_owned(),
|
app_id: "rpc".to_owned(),
|
||||||
app_params: vec!["".to_owned()],
|
app_params: vec!["".to_owned()],
|
||||||
@@ -321,7 +277,7 @@ fn should_extract_endpoint() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok()),
|
extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok(), dapps_domain),
|
||||||
(Some(EndpointPath {
|
(Some(EndpointPath {
|
||||||
app_id: "status".to_owned(),
|
app_id: "status".to_owned(),
|
||||||
app_params: vec!["my".to_owned(), "parity-utils".into(), "inject.js".into()],
|
app_params: vec!["my".to_owned(), "parity-utils".into(), "inject.js".into()],
|
||||||
@@ -333,7 +289,7 @@ fn should_extract_endpoint() {
|
|||||||
|
|
||||||
// By Subdomain
|
// By Subdomain
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok()),
|
extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok(), dapps_domain),
|
||||||
(Some(EndpointPath {
|
(Some(EndpointPath {
|
||||||
app_id: "status".to_owned(),
|
app_id: "status".to_owned(),
|
||||||
app_params: vec!["test.html".to_owned()],
|
app_params: vec!["test.html".to_owned()],
|
||||||
@@ -345,7 +301,7 @@ fn should_extract_endpoint() {
|
|||||||
|
|
||||||
// RPC by subdomain
|
// RPC by subdomain
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok()),
|
extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok(), dapps_domain),
|
||||||
(Some(EndpointPath {
|
(Some(EndpointPath {
|
||||||
app_id: "status".to_owned(),
|
app_id: "status".to_owned(),
|
||||||
app_params: vec!["my".to_owned(), "rpc".into(), "".into()],
|
app_params: vec!["my".to_owned(), "rpc".into(), "".into()],
|
||||||
@@ -357,7 +313,7 @@ fn should_extract_endpoint() {
|
|||||||
|
|
||||||
// API by subdomain
|
// API by subdomain
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok()),
|
extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok(), dapps_domain),
|
||||||
(Some(EndpointPath {
|
(Some(EndpointPath {
|
||||||
app_id: "status".to_owned(),
|
app_id: "status".to_owned(),
|
||||||
app_params: vec!["my".to_owned(), "api".into(), "".into()],
|
app_params: vec!["my".to_owned(), "api".into(), "".into()],
|
||||||
@@ -1,106 +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 Authorization implementations
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use hyper::{server, net, header, status};
|
|
||||||
use endpoint::Handler;
|
|
||||||
use handlers::{AuthRequiredHandler, ContentHandler};
|
|
||||||
|
|
||||||
/// Authorization result
|
|
||||||
pub enum Authorized {
|
|
||||||
/// Authorization was successful.
|
|
||||||
Yes,
|
|
||||||
/// Unsuccessful authorization. Handler for further work is returned.
|
|
||||||
No(Box<Handler>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Authorization interface
|
|
||||||
pub trait Authorization : Send + Sync {
|
|
||||||
/// Checks if authorization is valid.
|
|
||||||
fn is_authorized(&self, req: &server::Request<net::HttpStream>)-> Authorized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HTTP Basic Authorization handler
|
|
||||||
pub struct HttpBasicAuth {
|
|
||||||
users: HashMap<String, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// No-authorization implementation (authorization disabled)
|
|
||||||
pub struct NoAuth;
|
|
||||||
|
|
||||||
impl Authorization for NoAuth {
|
|
||||||
fn is_authorized(&self, _req: &server::Request<net::HttpStream>)-> Authorized {
|
|
||||||
Authorized::Yes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Authorization for HttpBasicAuth {
|
|
||||||
fn is_authorized(&self, req: &server::Request<net::HttpStream>) -> Authorized {
|
|
||||||
let auth = self.check_auth(&req);
|
|
||||||
|
|
||||||
match auth {
|
|
||||||
Access::Denied => {
|
|
||||||
Authorized::No(Box::new(ContentHandler::error(
|
|
||||||
status::StatusCode::Unauthorized,
|
|
||||||
"Unauthorized",
|
|
||||||
"You need to provide valid credentials to access this page.",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)))
|
|
||||||
},
|
|
||||||
Access::AuthRequired => {
|
|
||||||
Authorized::No(Box::new(AuthRequiredHandler))
|
|
||||||
},
|
|
||||||
Access::Granted => {
|
|
||||||
Authorized::Yes
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Access {
|
|
||||||
Granted,
|
|
||||||
Denied,
|
|
||||||
AuthRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HttpBasicAuth {
|
|
||||||
/// Creates `HttpBasicAuth` instance with only one user.
|
|
||||||
pub fn single_user(username: &str, password: &str) -> Self {
|
|
||||||
let mut users = HashMap::new();
|
|
||||||
users.insert(username.to_owned(), password.to_owned());
|
|
||||||
HttpBasicAuth {
|
|
||||||
users: users
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_authorized(&self, username: &str, password: &str) -> bool {
|
|
||||||
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_auth(&self, req: &server::Request<net::HttpStream>) -> Access {
|
|
||||||
match req.headers().get::<header::Authorization<header::Basic>>() {
|
|
||||||
Some(&header::Authorization(
|
|
||||||
header::Basic { ref username, password: Some(ref password) }
|
|
||||||
)) if self.is_authorized(username, password) => Access::Granted,
|
|
||||||
Some(_) => Access::Denied,
|
|
||||||
None => Access::AuthRequired,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +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 apps::DAPPS_DOMAIN;
|
|
||||||
use hyper::{server, header, StatusCode};
|
|
||||||
use hyper::net::HttpStream;
|
|
||||||
|
|
||||||
use jsonrpc_http_server::{is_host_header_valid};
|
|
||||||
use handlers::ContentHandler;
|
|
||||||
|
|
||||||
pub fn is_valid(request: &server::Request<HttpStream>, allowed_hosts: &[String], endpoints: Vec<String>) -> bool {
|
|
||||||
let mut endpoints = endpoints.iter()
|
|
||||||
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
endpoints.extend_from_slice(allowed_hosts);
|
|
||||||
|
|
||||||
let header_valid = is_host_header_valid(request, &endpoints);
|
|
||||||
|
|
||||||
match (header_valid, request.headers().get::<header::Host>()) {
|
|
||||||
(true, _) => true,
|
|
||||||
(_, Some(host)) => host.hostname.ends_with(DAPPS_DOMAIN),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
|
|
||||||
Box::new(ContentHandler::error(StatusCode::Forbidden,
|
|
||||||
"Current Host Is Disallowed",
|
|
||||||
"You are trying to access your node using incorrect address.",
|
|
||||||
Some("Use allowed URL or specify different <code>hosts</code> CLI options."),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
@@ -1,83 +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, Mutex};
|
|
||||||
use hyper;
|
|
||||||
|
|
||||||
use ethcore_rpc::{Metadata, Origin};
|
|
||||||
use jsonrpc_core::Middleware;
|
|
||||||
use jsonrpc_core::reactor::RpcHandler;
|
|
||||||
use jsonrpc_http_server::{Rpc, ServerHandler, PanicHandler, AccessControlAllowOrigin, HttpMetaExtractor};
|
|
||||||
use endpoint::{Endpoint, EndpointPath, Handler};
|
|
||||||
|
|
||||||
pub fn rpc<T: Middleware<Metadata>>(
|
|
||||||
handler: RpcHandler<Metadata, T>,
|
|
||||||
cors_domains: Vec<String>,
|
|
||||||
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
|
||||||
) -> Box<Endpoint> {
|
|
||||||
Box::new(RpcEndpoint {
|
|
||||||
handler: handler,
|
|
||||||
meta_extractor: Arc::new(MetadataExtractor),
|
|
||||||
panic_handler: panic_handler,
|
|
||||||
cors_domain: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
|
|
||||||
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
|
|
||||||
allowed_hosts: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RpcEndpoint<T: Middleware<Metadata>> {
|
|
||||||
handler: RpcHandler<Metadata, T>,
|
|
||||||
meta_extractor: Arc<HttpMetaExtractor<Metadata>>,
|
|
||||||
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
|
||||||
cors_domain: Option<Vec<AccessControlAllowOrigin>>,
|
|
||||||
allowed_hosts: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Middleware<Metadata>> Endpoint for RpcEndpoint<T> {
|
|
||||||
fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box<Handler> {
|
|
||||||
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
|
|
||||||
Box::new(ServerHandler::new(
|
|
||||||
Rpc::new(self.handler.clone(), self.meta_extractor.clone()),
|
|
||||||
self.cors_domain.clone(),
|
|
||||||
self.allowed_hosts.clone(),
|
|
||||||
panic_handler,
|
|
||||||
control,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MetadataExtractor;
|
|
||||||
impl HttpMetaExtractor<Metadata> for MetadataExtractor {
|
|
||||||
fn read_metadata(&self, request: &hyper::server::Request<hyper::net::HttpStream>) -> Metadata {
|
|
||||||
let dapp_id = request.headers().get::<hyper::header::Origin>()
|
|
||||||
.map(|origin| format!("{}://{}", origin.scheme, origin.host))
|
|
||||||
.or_else(|| {
|
|
||||||
// fallback to custom header, but only if origin is null
|
|
||||||
request.headers().get_raw("origin")
|
|
||||||
.and_then(|raw| raw.one())
|
|
||||||
.and_then(|raw| if raw == "null".as_bytes() {
|
|
||||||
request.headers().get_raw("x-parity-origin")
|
|
||||||
.and_then(|raw| raw.one())
|
|
||||||
.map(|raw| String::from_utf8_lossy(raw).into_owned())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
});
|
|
||||||
Metadata {
|
|
||||||
origin: Origin::Dapps(dapp_id.map(Into::into).unwrap_or_default()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,35 +33,12 @@ fn should_return_error() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
response.assert_status("HTTP/1.1 404 Not Found");
|
||||||
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
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_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);
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_serve_apps() {
|
|
||||||
// given
|
|
||||||
let server = serve();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /api/apps 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());
|
|
||||||
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
|
||||||
assert!(response.body.contains("Parity UI"), response.body);
|
|
||||||
assert_security_headers(&response.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_handle_ping() {
|
fn should_handle_ping() {
|
||||||
// given
|
// given
|
||||||
@@ -79,8 +56,8 @@ fn should_handle_ping() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
response.assert_status("HTTP/1.1 200 OK");
|
||||||
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
|
response.assert_header("Content-Type", "application/json");
|
||||||
assert_eq!(response.body, "0\n\n".to_owned());
|
assert_eq!(response.body, "0\n\n".to_owned());
|
||||||
assert_security_headers(&response.headers);
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
@@ -102,113 +79,7 @@ fn should_try_to_resolve_dapp() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
response.assert_status("HTTP/1.1 404 Not Found");
|
||||||
assert_eq!(registrar.calls.lock().len(), 2);
|
assert_eq!(registrar.calls.lock().len(), 2);
|
||||||
assert_security_headers(&response.headers);
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_signer_port_cors_headers() {
|
|
||||||
// given
|
|
||||||
let server = serve();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
POST /api/ping HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Origin: http://127.0.0.1:18180\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
{}
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
|
||||||
assert!(
|
|
||||||
response.headers_raw.contains("Access-Control-Allow-Origin: http://127.0.0.1:18180"),
|
|
||||||
"CORS header for signer missing: {:?}",
|
|
||||||
response.headers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_signer_port_cors_headers_for_home_parity() {
|
|
||||||
// given
|
|
||||||
let server = serve();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
POST /api/ping HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Origin: http://parity.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
{}
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
|
||||||
assert!(
|
|
||||||
response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site"),
|
|
||||||
"CORS header for parity.web3.site missing: {:?}",
|
|
||||||
response.headers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_signer_port_cors_headers_for_home_parity_with_https() {
|
|
||||||
// given
|
|
||||||
let server = serve();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
POST /api/ping HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Origin: https://parity.web3.site\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
{}
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
|
||||||
assert!(
|
|
||||||
response.headers_raw.contains("Access-Control-Allow-Origin: https://parity.web3.site"),
|
|
||||||
"CORS header for parity.web3.site missing: {:?}",
|
|
||||||
response.headers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
|
|
||||||
// given
|
|
||||||
let server = serve();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
POST /api/ping HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Origin: http://parity.web3.site:18180\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
{}
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
|
||||||
assert!(
|
|
||||||
response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site:18180"),
|
|
||||||
"CORS header for parity.web3.site missing: {:?}",
|
|
||||||
response.headers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,80 +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_with_auth, request, assert_security_headers_for_embed};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_require_authorization() {
|
|
||||||
// given
|
|
||||||
let server = serve_with_auth("test", "test");
|
|
||||||
|
|
||||||
// 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 401 Unauthorized".to_owned());
|
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "WWW-Authenticate: Basic realm=\"Parity\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_reject_on_invalid_auth() {
|
|
||||||
// given
|
|
||||||
let server = serve_with_auth("test", "test");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET / HTTP/1.1\r\n\
|
|
||||||
Host: 127.0.0.1:8080\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(response.status, "HTTP/1.1 401 Unauthorized".to_owned());
|
|
||||||
assert!(response.body.contains("Unauthorized"), response.body);
|
|
||||||
assert_eq!(response.headers_raw.contains("WWW-Authenticate"), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_allow_on_valid_auth() {
|
|
||||||
// given
|
|
||||||
let server = serve_with_auth("Aladdin", "OpenSesame");
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /ui/ HTTP/1.1\r\n\
|
|
||||||
Host: 127.0.0.1:8080\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l\r\n
|
|
||||||
\r\n\
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
|
||||||
assert_security_headers_for_embed(&response.headers);
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use devtools::http_client;
|
use devtools::http_client;
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
use tests::helpers::{
|
use tests::helpers::{
|
||||||
serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch,
|
serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch,
|
||||||
serve_with_registrar_and_fetch, serve_with_registrar_and_fetch_and_threads,
|
serve_with_registrar_and_fetch, serve_with_registrar_and_fetch_and_threads,
|
||||||
@@ -60,7 +60,7 @@ fn should_return_503_when_syncing_but_should_make_the_calls() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
response.assert_status("HTTP/1.1 503 Service Unavailable");
|
response.assert_status("HTTP/1.1 503 Service Unavailable");
|
||||||
assert_eq!(registrar.calls.lock().len(), 4);
|
assert_eq!(registrar.calls.lock().len(), 2);
|
||||||
assert_security_headers_for_embed(&response.headers);
|
assert_security_headers_for_embed(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ fn should_encode_and_decode_base32() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_stream_web_content() {
|
fn should_stream_web_content() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("token");
|
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
@@ -335,7 +335,7 @@ fn should_stream_web_content() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_support_base32_encoded_web_urls() {
|
fn should_support_base32_encoded_web_urls() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("token");
|
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
@@ -358,7 +358,7 @@ fn should_support_base32_encoded_web_urls() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_correctly_handle_long_label_when_splitted() {
|
fn should_correctly_handle_long_label_when_splitted() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("xolrg9fePeQyKLnL");
|
let (server, fetch) = serve_with_fetch("xolrg9fePeQyKLnL", "https://contribution.melonport.com");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
@@ -382,7 +382,7 @@ fn should_correctly_handle_long_label_when_splitted() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_support_base32_encoded_web_urls_as_path() {
|
fn should_support_base32_encoded_web_urls_as_path() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("token");
|
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
@@ -402,10 +402,32 @@ fn should_support_base32_encoded_web_urls_as_path() {
|
|||||||
fetch.assert_no_more_requests();
|
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]
|
#[test]
|
||||||
fn should_return_error_on_invalid_token() {
|
fn should_return_error_on_invalid_token() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("test");
|
let (server, fetch) = serve_with_fetch("test", "https://parity.io");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
@@ -427,7 +449,7 @@ fn should_return_error_on_invalid_token() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_return_error_on_invalid_protocol() {
|
fn should_return_error_on_invalid_protocol() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("token");
|
let (server, fetch) = serve_with_fetch("token", "ftp://parity.io");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
@@ -449,7 +471,7 @@ fn should_return_error_on_invalid_protocol() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_disallow_non_get_requests() {
|
fn should_disallow_non_get_requests() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("token");
|
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
@@ -474,7 +496,7 @@ fn should_disallow_non_get_requests() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_fix_absolute_requests_based_on_referer() {
|
fn should_fix_absolute_requests_based_on_referer() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("token");
|
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
@@ -497,7 +519,7 @@ fn should_fix_absolute_requests_based_on_referer() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn should_fix_absolute_requests_based_on_referer_in_url() {
|
fn should_fix_absolute_requests_based_on_referer_in_url() {
|
||||||
// given
|
// given
|
||||||
let (server, fetch) = serve_with_fetch("token");
|
let (server, fetch) = serve_with_fetch("token", "https://parity.io");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let response = request(server,
|
let response = request(server,
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ impl Fetch for FakeFetch {
|
|||||||
|
|
||||||
let data = response.lock().take().unwrap_or(b"Some content");
|
let data = response.lock().take().unwrap_or(b"Some content");
|
||||||
let cursor = io::Cursor::new(data);
|
let cursor = io::Cursor::new(data);
|
||||||
tx.complete(fetch::Response::from_reader(cursor));
|
tx.send(fetch::Response::from_reader(cursor)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
rx.map_err(|_| fetch::Error::Aborted).boxed()
|
rx.map_err(|_| fetch::Error::Aborted).boxed()
|
||||||
|
|||||||
@@ -16,19 +16,21 @@
|
|||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::ops::Deref;
|
use std::net::SocketAddr;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use env_logger::LogBuilder;
|
use env_logger::LogBuilder;
|
||||||
use ethcore_rpc::Metadata;
|
use jsonrpc_core::IoHandler;
|
||||||
use jsonrpc_core::MetaIoHandler;
|
use jsonrpc_http_server::{self as http, Host, DomainsValidation};
|
||||||
use jsonrpc_core::reactor::RpcEventLoop;
|
|
||||||
|
|
||||||
use ServerBuilder;
|
|
||||||
use Server;
|
|
||||||
use fetch::Fetch;
|
|
||||||
use devtools::http_client;
|
use devtools::http_client;
|
||||||
|
use hash_fetch::urlhint::ContractClient;
|
||||||
|
use fetch::{Fetch, Client as FetchClient};
|
||||||
|
use node_health::{NodeHealth, TimeChecker, CpuPool};
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
|
|
||||||
|
use {Middleware, SyncStatus, WebProxyTokens};
|
||||||
|
|
||||||
mod registrar;
|
mod registrar;
|
||||||
mod fetch;
|
mod fetch;
|
||||||
|
|
||||||
@@ -37,6 +39,13 @@ use self::fetch::FakeFetch;
|
|||||||
|
|
||||||
const SIGNER_PORT: u16 = 18180;
|
const SIGNER_PORT: u16 = 18180;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
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() {
|
fn init_logger() {
|
||||||
// Initialize logger
|
// Initialize logger
|
||||||
if let Ok(log) = env::var("RUST_LOG") {
|
if let Ok(log) = env::var("RUST_LOG") {
|
||||||
@@ -46,20 +55,7 @@ fn init_logger() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ServerLoop {
|
pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (Server, Arc<FakeRegistrar>) where
|
||||||
pub server: Server,
|
|
||||||
pub event_loop: RpcEventLoop,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for ServerLoop {
|
|
||||||
type Target = Server;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.server
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_server<F, B>(process: F, io: MetaIoHandler<Metadata>, remote: Remote) -> (ServerLoop, Arc<FakeRegistrar>) where
|
|
||||||
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
|
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
|
||||||
B: Fetch,
|
B: Fetch,
|
||||||
{
|
{
|
||||||
@@ -68,91 +64,69 @@ pub fn init_server<F, B>(process: F, io: MetaIoHandler<Metadata>, remote: Remote
|
|||||||
let mut dapps_path = env::temp_dir();
|
let mut dapps_path = env::temp_dir();
|
||||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||||
|
|
||||||
// TODO [ToDr] When https://github.com/ethcore/jsonrpc/issues/26 is resolved
|
|
||||||
// this additional EventLoop wouldn't be needed, we should be able to re-use remote.
|
|
||||||
let event_loop = RpcEventLoop::spawn();
|
|
||||||
let handler = event_loop.handler(Arc::new(io));
|
|
||||||
let server = process(ServerBuilder::new(
|
let server = process(ServerBuilder::new(
|
||||||
&dapps_path, registrar.clone(), remote,
|
&dapps_path, registrar.clone(), remote,
|
||||||
))
|
))
|
||||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
||||||
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), handler).unwrap();
|
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
|
||||||
(
|
(
|
||||||
ServerLoop { server: server, event_loop: event_loop },
|
server,
|
||||||
registrar,
|
registrar,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_with_auth(user: &str, pass: &str) -> ServerLoop {
|
pub fn serve_with_rpc(io: IoHandler) -> Server {
|
||||||
init_logger();
|
init_server(|builder| builder, io, Remote::new_sync()).0
|
||||||
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 event_loop = RpcEventLoop::spawn();
|
|
||||||
let handler = event_loop.handler(Arc::new(MetaIoHandler::default()));
|
|
||||||
let server = ServerBuilder::new(&dapps_path, registrar, Remote::new(event_loop.remote()))
|
|
||||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
|
||||||
.allowed_hosts(None)
|
|
||||||
.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), user, pass, handler).unwrap();
|
|
||||||
ServerLoop {
|
|
||||||
server: server,
|
|
||||||
event_loop: event_loop,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_with_rpc(io: MetaIoHandler<Metadata>) -> ServerLoop {
|
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
|
||||||
init_server(|builder| builder.allowed_hosts(None), io, Remote::new_sync()).0
|
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_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
|
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
||||||
init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0
|
init_server(|builder| builder, Default::default(), Remote::new_sync())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
|
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
|
||||||
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serve_with_registrar_and_sync() -> (ServerLoop, Arc<FakeRegistrar>) {
|
|
||||||
init_server(|builder| {
|
init_server(|builder| {
|
||||||
builder
|
builder.sync_status(Arc::new(FakeSync(true)))
|
||||||
.sync_status(Arc::new(|| true))
|
|
||||||
.allowed_hosts(None)
|
|
||||||
}, Default::default(), Remote::new_sync())
|
}, Default::default(), Remote::new_sync())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_with_registrar_and_fetch() -> (ServerLoop, FakeFetch, Arc<FakeRegistrar>) {
|
pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc<FakeRegistrar>) {
|
||||||
serve_with_registrar_and_fetch_and_threads(false)
|
serve_with_registrar_and_fetch_and_threads(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (ServerLoop, FakeFetch, Arc<FakeRegistrar>) {
|
pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Server, FakeFetch, Arc<FakeRegistrar>) {
|
||||||
let fetch = FakeFetch::default();
|
let fetch = FakeFetch::default();
|
||||||
let f = fetch.clone();
|
let f = fetch.clone();
|
||||||
let (server, reg) = init_server(move |builder| {
|
let (server, reg) = init_server(move |builder| {
|
||||||
builder.allowed_hosts(None).fetch(f.clone())
|
builder.fetch(f.clone())
|
||||||
}, Default::default(), if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() });
|
}, Default::default(), if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() });
|
||||||
|
|
||||||
(server, fetch, reg)
|
(server, fetch, reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
|
pub fn serve_with_fetch(web_token: &'static str, domain: &'static str) -> (Server, FakeFetch) {
|
||||||
let fetch = FakeFetch::default();
|
let fetch = FakeFetch::default();
|
||||||
let f = fetch.clone();
|
let f = fetch.clone();
|
||||||
let (server, _) = init_server(move |builder| {
|
let (server, _) = init_server(move |builder| {
|
||||||
builder
|
builder
|
||||||
.allowed_hosts(None)
|
|
||||||
.fetch(f.clone())
|
.fetch(f.clone())
|
||||||
.web_proxy_tokens(Arc::new(move |token| &token == web_token))
|
.web_proxy_tokens(Arc::new(move |token| {
|
||||||
|
if &token == web_token { Some(domain.into()) } else { None }
|
||||||
|
}))
|
||||||
}, Default::default(), Remote::new_sync());
|
}, Default::default(), Remote::new_sync());
|
||||||
|
|
||||||
(server, fetch)
|
(server, fetch)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve() -> ServerLoop {
|
pub fn serve() -> Server {
|
||||||
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync()).0
|
init_server(|builder| builder, Default::default(), Remote::new_sync()).0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request(server: ServerLoop, request: &str) -> http_client::Response {
|
pub fn request(server: Server, request: &str) -> http_client::Response {
|
||||||
http_client::request(server.addr(), request)
|
http_client::request(server.addr(), request)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,3 +136,173 @@ pub fn assert_security_headers(headers: &[String]) {
|
|||||||
pub fn assert_security_headers_for_embed(headers: &[String]) {
|
pub fn assert_security_headers_for_embed(headers: &[String]) {
|
||||||
http_client::assert_security_headers_present(headers, Some(SIGNER_PORT))
|
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 health = NodeHealth::new(
|
||||||
|
sync_status.clone(),
|
||||||
|
TimeChecker::new::<String>(&[], CpuPool::new(1)),
|
||||||
|
remote.clone(),
|
||||||
|
);
|
||||||
|
let middleware = Middleware::dapps(
|
||||||
|
health,
|
||||||
|
remote,
|
||||||
|
signer_address,
|
||||||
|
vec![],
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_hex::FromHex;
|
||||||
|
|
||||||
use hash_fetch::urlhint::ContractClient;
|
use hash_fetch::urlhint::ContractClient;
|
||||||
use util::{Bytes, Address, Mutex, H256, ToPretty};
|
use util::{Bytes, Address, Mutex, H256, ToPretty};
|
||||||
@@ -64,9 +64,10 @@ impl ContractClient for FakeRegistrar {
|
|||||||
Ok(REGISTRAR.parse().unwrap())
|
Ok(REGISTRAR.parse().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
|
fn call(&self, address: Address, data: Bytes) -> ::futures::BoxFuture<Bytes, String> {
|
||||||
let call = (address.to_hex(), data.to_hex());
|
let call = (address.to_hex(), data.to_hex());
|
||||||
self.calls.lock().push(call.clone());
|
self.calls.lock().push(call.clone());
|
||||||
self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call))
|
let res = self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call));
|
||||||
|
Box::new(::futures::future::done(res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod authorization;
|
|
||||||
mod fetch;
|
mod fetch;
|
||||||
mod redirection;
|
mod redirection;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
|
|||||||
@@ -32,7 +32,27 @@ fn should_redirect_to_home() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
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");
|
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,27 +72,7 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
|
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_for_users_with_cached_redirection() {
|
|
||||||
// given
|
|
||||||
let server = serve();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
GET /home/ 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 302 Found".to_owned());
|
|
||||||
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ fn should_display_404_on_invalid_dapp() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
response.assert_status("HTTP/1.1 404 Not Found");
|
||||||
assert_security_headers_for_embed(&response.headers);
|
assert_security_headers_for_embed(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ fn should_display_404_on_invalid_dapp_with_domain() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
|
response.assert_status("HTTP/1.1 404 Not Found");
|
||||||
assert_security_headers_for_embed(&response.headers);
|
assert_security_headers_for_embed(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,8 +134,8 @@ fn should_serve_rpc() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
response.assert_status("HTTP/1.1 200 OK");
|
||||||
assert_eq!(response.body, format!("58\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#));
|
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -156,8 +156,8 @@ fn should_serve_rpc_at_slash_rpc() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
response.assert_status("HTTP/1.1 200 OK");
|
||||||
assert_eq!(response.body, format!("58\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#));
|
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -178,8 +178,8 @@ fn should_serve_proxy_pac() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
response.assert_status("HTTP/1.1 200 OK");
|
||||||
assert_eq!(response.body, "DD\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"parity.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_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);
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ fn should_serve_utils() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
response.assert_status("HTTP/1.1 200 OK");
|
||||||
assert_eq!(response.body.contains("function(){"), true);
|
assert_eq!(response.body.contains("function(){"), true);
|
||||||
assert_security_headers(&response.headers);
|
assert_security_headers(&response.headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,14 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use futures::{future, Future};
|
use jsonrpc_core::{IoHandler, Value};
|
||||||
use ethcore_rpc::{Metadata, Origin};
|
|
||||||
use jsonrpc_core::{MetaIoHandler, Value};
|
|
||||||
|
|
||||||
use tests::helpers::{serve_with_rpc, request};
|
use tests::helpers::{serve_with_rpc, request};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_serve_rpc() {
|
fn should_serve_rpc() {
|
||||||
// given
|
// given
|
||||||
let mut io = MetaIoHandler::default();
|
let mut io = IoHandler::default();
|
||||||
io.add_method("rpc_test", |_| {
|
io.add_method("rpc_test", |_| {
|
||||||
Ok(Value::String("Hello World!".into()))
|
Ok(Value::String("Hello World!".into()))
|
||||||
});
|
});
|
||||||
@@ -49,71 +47,3 @@ fn should_serve_rpc() {
|
|||||||
response.assert_status("HTTP/1.1 200 OK");
|
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());
|
assert_eq!(response.body, "31\n{\"jsonrpc\":\"2.0\",\"result\":\"Hello World!\",\"id\":1}\n\n0\n\n".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_extract_metadata() {
|
|
||||||
// given
|
|
||||||
let mut io = MetaIoHandler::default();
|
|
||||||
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
|
|
||||||
assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into()));
|
|
||||||
assert_eq!(meta.dapp_id(), "https://parity.io/".into());
|
|
||||||
future::ok(Value::String("Hello World!".into())).boxed()
|
|
||||||
});
|
|
||||||
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\
|
|
||||||
Origin: https://parity.io/\r\n\
|
|
||||||
X-Parity-Origin: https://this.should.be.ignored\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());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_extract_metadata_from_custom_header() {
|
|
||||||
// given
|
|
||||||
let mut io = MetaIoHandler::default();
|
|
||||||
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
|
|
||||||
assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into()));
|
|
||||||
assert_eq!(meta.dapp_id(), "https://parity.io/".into());
|
|
||||||
future::ok(Value::String("Hello World!".into())).boxed()
|
|
||||||
});
|
|
||||||
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\
|
|
||||||
Origin: null\r\n\
|
|
||||||
X-Parity-Origin: https://parity.io/\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());
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ fn should_reject_invalid_host() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
|
||||||
assert!(response.body.contains("Current Host Is Disallowed"), response.body);
|
assert!(response.body.contains("Provided Host header is not whitelisted."), response.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -97,31 +97,3 @@ fn should_allow_parity_utils_even_on_invalid_domain() {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_not_return_cors_headers_for_rpc() {
|
|
||||||
// given
|
|
||||||
let server = serve_hosts(Some(vec!["localhost:8080".into()]));
|
|
||||||
|
|
||||||
// when
|
|
||||||
let response = request(server,
|
|
||||||
"\
|
|
||||||
POST /rpc HTTP/1.1\r\n\
|
|
||||||
Host: localhost:8080\r\n\
|
|
||||||
Origin: null\r\n\
|
|
||||||
Content-Type: application/json\r\n\
|
|
||||||
Connection: close\r\n\
|
|
||||||
\r\n\
|
|
||||||
{}
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
|
||||||
assert!(
|
|
||||||
!response.headers_raw.contains("Access-Control-Allow-Origin"),
|
|
||||||
"CORS headers were not expected: {:?}",
|
|
||||||
response.headers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ use handlers::{
|
|||||||
StreamingHandler, extract_url,
|
StreamingHandler, extract_url,
|
||||||
};
|
};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use WebProxyTokens;
|
use {Embeddable, WebProxyTokens};
|
||||||
|
|
||||||
pub type Embeddable = Option<(String, u16)>;
|
|
||||||
|
|
||||||
pub struct Web<F> {
|
pub struct Web<F> {
|
||||||
embeddable_on: Embeddable,
|
embeddable_on: Embeddable,
|
||||||
@@ -43,12 +41,17 @@ pub struct Web<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Fetch> Web<F> {
|
impl<F: Fetch> Web<F> {
|
||||||
pub fn boxed(embeddable_on: Embeddable, web_proxy_tokens: Arc<WebProxyTokens>, remote: Remote, fetch: F) -> Box<Endpoint> {
|
pub fn boxed(
|
||||||
|
embeddable_on: Embeddable,
|
||||||
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
|
remote: Remote,
|
||||||
|
fetch: F,
|
||||||
|
) -> Box<Endpoint> {
|
||||||
Box::new(Web {
|
Box::new(Web {
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
web_proxy_tokens: web_proxy_tokens,
|
web_proxy_tokens,
|
||||||
remote: remote,
|
remote,
|
||||||
fetch: fetch,
|
fetch,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,14 +136,14 @@ impl<F: Fetch> WebHandler<F> {
|
|||||||
let target_url = token_it.next();
|
let target_url = token_it.next();
|
||||||
|
|
||||||
// Check if token supplied in URL is correct.
|
// Check if token supplied in URL is correct.
|
||||||
match token {
|
let domain = match token.and_then(|token| self.web_proxy_tokens.domain(token)) {
|
||||||
Some(token) if self.web_proxy_tokens.is_web_proxy_token_valid(token) => {},
|
Some(domain) => domain,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(State::Error(ContentHandler::error(
|
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()
|
StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), self.embeddable_on.clone()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Validate protocol
|
// Validate protocol
|
||||||
let mut target_url = match target_url {
|
let mut target_url = match target_url {
|
||||||
@@ -152,6 +155,12 @@ impl<F: Fetch> WebHandler<F> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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("/") {
|
if !target_url.ends_with("/") {
|
||||||
target_url = format!("{}/", target_url);
|
target_url = format!("{}/", target_url);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ description = "Ethcore Parity UI"
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "parity-ui"
|
name = "parity-ui"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
@@ -11,7 +11,8 @@ rustc_version = "0.1"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parity-ui-dev = { path = "../../js", optional = true }
|
parity-ui-dev = { path = "../../js", optional = true }
|
||||||
parity-ui-precompiled = { git = "https://github.com/ethcore/js-precompiled.git", 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]
|
[features]
|
||||||
no-precompiled-js = ["parity-ui-dev"]
|
no-precompiled-js = ["parity-ui-dev"]
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
[package]
|
|
||||||
description = "Ethcore Database"
|
|
||||||
homepage = "http://parity.io"
|
|
||||||
license = "GPL-3.0"
|
|
||||||
name = "ethcore-db"
|
|
||||||
version = "1.6.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
clippy = { version = "0.0.103", optional = true}
|
|
||||||
ethcore-devtools = { path = "../devtools" }
|
|
||||||
ethcore-ipc = { path = "../ipc/rpc" }
|
|
||||||
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
|
|
||||||
semver = "0.5"
|
|
||||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
|
||||||
nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" }
|
|
||||||
crossbeam = "0.2"
|
|
||||||
ethcore-util = { path = "../util" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
dev = ["clippy"]
|
|
||||||
@@ -1,565 +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 rocksdb ipc service
|
|
||||||
|
|
||||||
use traits::*;
|
|
||||||
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator, IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
|
|
||||||
use std::sync::{RwLock, Arc};
|
|
||||||
use std::convert::From;
|
|
||||||
use ipc::IpcConfig;
|
|
||||||
use std::mem;
|
|
||||||
use ipc::binary::BinaryConvertError;
|
|
||||||
use std::collections::{VecDeque, HashMap, BTreeMap};
|
|
||||||
|
|
||||||
enum WriteCacheEntry {
|
|
||||||
Remove,
|
|
||||||
Write(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WriteCache {
|
|
||||||
entries: HashMap<Vec<u8>, WriteCacheEntry>,
|
|
||||||
preferred_len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
const FLUSH_BATCH_SIZE: usize = 4096;
|
|
||||||
|
|
||||||
impl WriteCache {
|
|
||||||
fn new(cache_len: usize) -> WriteCache {
|
|
||||||
WriteCache {
|
|
||||||
entries: HashMap::new(),
|
|
||||||
preferred_len: cache_len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, key: Vec<u8>, val: Vec<u8>) {
|
|
||||||
self.entries.insert(key, WriteCacheEntry::Write(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove(&mut self, key: Vec<u8>) {
|
|
||||||
self.entries.insert(key, WriteCacheEntry::Remove);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
|
|
||||||
self.entries.get(key).and_then(
|
|
||||||
|vec_ref| match vec_ref {
|
|
||||||
&WriteCacheEntry::Write(ref val) => Some(val.clone()),
|
|
||||||
&WriteCacheEntry::Remove => None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// WriteCache should be locked for this
|
|
||||||
fn flush(&mut self, db: &DB, amount: usize) -> Result<(), Error> {
|
|
||||||
let batch = WriteBatch::new();
|
|
||||||
let mut removed_so_far = 0;
|
|
||||||
while removed_so_far < amount {
|
|
||||||
if self.entries.len() == 0 { break; }
|
|
||||||
let removed_key = {
|
|
||||||
let (key, cache_entry) = self.entries.iter().nth(0)
|
|
||||||
.expect("if entries.len == 0, we should have break in the loop, still we got here somehow");
|
|
||||||
|
|
||||||
match *cache_entry {
|
|
||||||
WriteCacheEntry::Write(ref val) => {
|
|
||||||
batch.put(&key, val)?;
|
|
||||||
},
|
|
||||||
WriteCacheEntry::Remove => {
|
|
||||||
batch.delete(&key)?;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
key.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.entries.remove(&removed_key);
|
|
||||||
|
|
||||||
removed_so_far = removed_so_far + 1;
|
|
||||||
}
|
|
||||||
if removed_so_far > 0 {
|
|
||||||
db.write(batch)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// flushes until cache is empty
|
|
||||||
fn flush_all(&mut self, db: &DB) -> Result<(), Error> {
|
|
||||||
while !self.is_empty() { self.flush(db, FLUSH_BATCH_SIZE)?; }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.entries.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_shrink(&mut self, db: &DB) -> Result<(), Error> {
|
|
||||||
if self.entries.len() > self.preferred_len {
|
|
||||||
self.flush(db, FLUSH_BATCH_SIZE)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Database {
|
|
||||||
db: RwLock<Option<DB>>,
|
|
||||||
/// Iterators - dont't use between threads!
|
|
||||||
iterators: RwLock<BTreeMap<IteratorHandle, DBIterator>>,
|
|
||||||
write_cache: RwLock<WriteCache>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for Database {}
|
|
||||||
unsafe impl Sync for Database {}
|
|
||||||
|
|
||||||
impl Database {
|
|
||||||
pub fn new() -> Database {
|
|
||||||
Database {
|
|
||||||
db: RwLock::new(None),
|
|
||||||
iterators: RwLock::new(BTreeMap::new()),
|
|
||||||
write_cache: RwLock::new(WriteCache::new(DEFAULT_CACHE_LEN)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn flush(&self) -> Result<(), Error> {
|
|
||||||
let mut cache_lock = self.write_cache.write();
|
|
||||||
let db_lock = self.db.read();
|
|
||||||
if db_lock.is_none() { return Ok(()); }
|
|
||||||
let db = db_lock.as_ref().unwrap();
|
|
||||||
|
|
||||||
cache_lock.try_shrink(&db)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn flush_all(&self) -> Result<(), Error> {
|
|
||||||
let mut cache_lock = self.write_cache.write();
|
|
||||||
let db_lock = self.db.read();
|
|
||||||
if db_lock.is_none() { return Ok(()); }
|
|
||||||
let db = db_lock.as_ref().expect("we should have exited with Ok(()) on the previous step");
|
|
||||||
|
|
||||||
cache_lock.flush_all(&db)?;
|
|
||||||
Ok(())
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Database {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.flush().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[ipc]
|
|
||||||
impl DatabaseService for Database {
|
|
||||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error> {
|
|
||||||
let mut db = self.db.write();
|
|
||||||
if db.is_some() { return Err(Error::AlreadyOpen); }
|
|
||||||
|
|
||||||
let mut opts = Options::new();
|
|
||||||
opts.set_max_open_files(256);
|
|
||||||
opts.create_if_missing(true);
|
|
||||||
opts.set_use_fsync(false);
|
|
||||||
opts.set_compaction_style(DBCompactionStyle::DBUniversalCompaction);
|
|
||||||
if let Some(size) = config.prefix_size {
|
|
||||||
let mut block_opts = BlockBasedOptions::new();
|
|
||||||
block_opts.set_index_type(IndexType::HashSearch);
|
|
||||||
opts.set_block_based_table_factory(&block_opts);
|
|
||||||
opts.set_prefix_extractor_fixed_size(size);
|
|
||||||
}
|
|
||||||
*db = Some(DB::open(&opts, &path)?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Opens database in the specified path with the default config
|
|
||||||
fn open_default(&self, path: String) -> Result<(), Error> {
|
|
||||||
self.open(DatabaseConfig::default(), path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close(&self) -> Result<(), Error> {
|
|
||||||
self.flush_all()?;
|
|
||||||
|
|
||||||
let mut db = self.db.write();
|
|
||||||
if db.is_none() { return Err(Error::IsClosed); }
|
|
||||||
|
|
||||||
*db = None;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error> {
|
|
||||||
let mut cache_lock = self.write_cache.write();
|
|
||||||
cache_lock.write(key.to_vec(), value.to_vec());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete(&self, key: &[u8]) -> Result<(), Error> {
|
|
||||||
let mut cache_lock = self.write_cache.write();
|
|
||||||
cache_lock.remove(key.to_vec());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&self, transaction: DBTransaction) -> Result<(), Error> {
|
|
||||||
let mut cache_lock = self.write_cache.write();
|
|
||||||
|
|
||||||
let mut writes = transaction.writes.borrow_mut();
|
|
||||||
for kv in writes.drain(..) {
|
|
||||||
cache_lock.write(kv.key, kv.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut removes = transaction.removes.borrow_mut();
|
|
||||||
for k in removes.drain(..) {
|
|
||||||
cache_lock.remove(k);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
|
||||||
{
|
|
||||||
let key_vec = key.to_vec();
|
|
||||||
let cache_hit = self.write_cache.read().get(&key_vec);
|
|
||||||
|
|
||||||
if cache_hit.is_some() {
|
|
||||||
return Ok(Some(cache_hit.expect("cache_hit.is_some() = true, still there is none somehow here")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let db_lock = self.db.read();
|
|
||||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
|
||||||
|
|
||||||
match db.get(key)? {
|
|
||||||
Some(db_vec) => {
|
|
||||||
Ok(Some(db_vec.to_vec()))
|
|
||||||
},
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
|
||||||
let db_lock = self.db.read();
|
|
||||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
|
||||||
|
|
||||||
let mut iter = db.iterator(IteratorMode::From(prefix, Direction::Forward));
|
|
||||||
match iter.next() {
|
|
||||||
// TODO: use prefix_same_as_start read option (not availabele in C API currently)
|
|
||||||
Some((k, v)) => if k[0 .. prefix.len()] == prefix[..] { Ok(Some(v.to_vec())) } else { Ok(None) },
|
|
||||||
_ => Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_empty(&self) -> Result<bool, Error> {
|
|
||||||
let db_lock = self.db.read();
|
|
||||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
|
||||||
|
|
||||||
Ok(db.iterator(IteratorMode::Start).next().is_none())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter(&self) -> Result<IteratorHandle, Error> {
|
|
||||||
let db_lock = self.db.read();
|
|
||||||
let db = db_lock.as_ref().ok_or(Error::IsClosed)?;
|
|
||||||
|
|
||||||
let mut iterators = self.iterators.write();
|
|
||||||
let next_iterator = iterators.keys().last().unwrap_or(&0) + 1;
|
|
||||||
iterators.insert(next_iterator, db.iterator(IteratorMode::Start));
|
|
||||||
Ok(next_iterator)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn iter_next(&self, handle: IteratorHandle) -> Option<KeyValue> {
|
|
||||||
let mut iterators = self.iterators.write();
|
|
||||||
let mut iterator = match iterators.get_mut(&handle) {
|
|
||||||
Some(some_iterator) => some_iterator,
|
|
||||||
None => { return None; },
|
|
||||||
};
|
|
||||||
|
|
||||||
iterator.next().and_then(|(some_key, some_val)| {
|
|
||||||
Some(KeyValue {
|
|
||||||
key: some_key.to_vec(),
|
|
||||||
value: some_val.to_vec(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error> {
|
|
||||||
let mut iterators = self.iterators.write();
|
|
||||||
iterators.remove(&handle);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO : put proper at compile-time
|
|
||||||
impl IpcConfig for Database {}
|
|
||||||
|
|
||||||
/// Database iterator
|
|
||||||
pub struct DatabaseIterator {
|
|
||||||
client: Arc<DatabaseClient<::nanomsg::Socket>>,
|
|
||||||
handle: IteratorHandle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for DatabaseIterator {
|
|
||||||
type Item = (Vec<u8>, Vec<u8>);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.client.iter_next(self.handle).and_then(|kv| Some((kv.key, kv.value)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for DatabaseIterator {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.client.dispose_iter(self.handle).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
|
|
||||||
use super::Database;
|
|
||||||
use traits::*;
|
|
||||||
use devtools::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_be_created() {
|
|
||||||
let db = Database::new();
|
|
||||||
assert!(db.is_empty().is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_be_open_empty() {
|
|
||||||
let db = Database::new();
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
db.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
|
|
||||||
assert!(db.is_empty().is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_store_key() {
|
|
||||||
let db = Database::new();
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
db.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
|
|
||||||
db.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
|
||||||
db.flush_all().unwrap();
|
|
||||||
assert!(!db.is_empty().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_retrieve() {
|
|
||||||
let db = Database::new();
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
db.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
db.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
|
||||||
db.close().unwrap();
|
|
||||||
|
|
||||||
db.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
assert_eq!(db.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod write_cache_tests {
|
|
||||||
use super::Database;
|
|
||||||
use traits::*;
|
|
||||||
use devtools::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn cache_write_flush() {
|
|
||||||
let db = Database::new();
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
|
|
||||||
db.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
db.put("100500".as_bytes(), "1".as_bytes()).unwrap();
|
|
||||||
db.delete("100500".as_bytes()).unwrap();
|
|
||||||
db.flush_all().unwrap();
|
|
||||||
|
|
||||||
let val = db.get("100500".as_bytes()).unwrap();
|
|
||||||
assert!(val.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod client_tests {
|
|
||||||
use super::{DatabaseClient, Database};
|
|
||||||
use traits::*;
|
|
||||||
use devtools::*;
|
|
||||||
use nanoipc;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::{Ordering, AtomicBool};
|
|
||||||
use crossbeam;
|
|
||||||
use run_worker;
|
|
||||||
|
|
||||||
fn init_worker(addr: &str) -> nanoipc::Worker<Database> {
|
|
||||||
let mut worker = nanoipc::Worker::<Database>::new(&Arc::new(Database::new()));
|
|
||||||
worker.add_duplex(addr).unwrap();
|
|
||||||
worker
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_call_handshake() {
|
|
||||||
let url = "ipc:///tmp/parity-db-ipc-test-10.ipc";
|
|
||||||
let worker_should_exit = Arc::new(AtomicBool::new(false));
|
|
||||||
let worker_is_ready = Arc::new(AtomicBool::new(false));
|
|
||||||
let c_worker_should_exit = worker_should_exit.clone();
|
|
||||||
let c_worker_is_ready = worker_is_ready.clone();
|
|
||||||
|
|
||||||
::std::thread::spawn(move || {
|
|
||||||
let mut worker = init_worker(url);
|
|
||||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
|
||||||
worker.poll();
|
|
||||||
c_worker_is_ready.store(true, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
while !worker_is_ready.load(Ordering::Relaxed) { }
|
|
||||||
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
|
|
||||||
|
|
||||||
let hs = client.handshake();
|
|
||||||
|
|
||||||
worker_should_exit.store(true, Ordering::Relaxed);
|
|
||||||
assert!(hs.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_open_db() {
|
|
||||||
let url = "ipc:///tmp/parity-db-ipc-test-20.ipc";
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
|
|
||||||
let worker_should_exit = Arc::new(AtomicBool::new(false));
|
|
||||||
let worker_is_ready = Arc::new(AtomicBool::new(false));
|
|
||||||
let c_worker_should_exit = worker_should_exit.clone();
|
|
||||||
let c_worker_is_ready = worker_is_ready.clone();
|
|
||||||
|
|
||||||
::std::thread::spawn(move || {
|
|
||||||
let mut worker = init_worker(url);
|
|
||||||
while !c_worker_should_exit.load(Ordering::Relaxed) {
|
|
||||||
worker.poll();
|
|
||||||
c_worker_is_ready.store(true, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
while !worker_is_ready.load(Ordering::Relaxed) { }
|
|
||||||
let client = nanoipc::init_duplex_client::<DatabaseClient<_>>(url).unwrap();
|
|
||||||
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
assert!(client.is_empty().unwrap());
|
|
||||||
worker_should_exit.store(true, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_put() {
|
|
||||||
let url = "ipc:///tmp/parity-db-ipc-test-30.ipc";
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
|
|
||||||
crossbeam::scope(move |scope| {
|
|
||||||
let stop = Arc::new(AtomicBool::new(false));
|
|
||||||
run_worker(scope, stop.clone(), url);
|
|
||||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
|
||||||
client.close().unwrap();
|
|
||||||
|
|
||||||
stop.store(true, Ordering::Relaxed);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_put_and_read() {
|
|
||||||
let url = "ipc:///tmp/parity-db-ipc-test-40.ipc";
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
|
|
||||||
crossbeam::scope(move |scope| {
|
|
||||||
let stop = Arc::new(AtomicBool::new(false));
|
|
||||||
run_worker(scope, stop.clone(), url);
|
|
||||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
|
||||||
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
client.put("xxx".as_bytes(), "1".as_bytes()).unwrap();
|
|
||||||
client.close().unwrap();
|
|
||||||
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
|
||||||
|
|
||||||
stop.store(true, Ordering::Relaxed);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_read_empty() {
|
|
||||||
let url = "ipc:///tmp/parity-db-ipc-test-45.ipc";
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
|
|
||||||
crossbeam::scope(move |scope| {
|
|
||||||
let stop = Arc::new(AtomicBool::new(false));
|
|
||||||
run_worker(scope, stop.clone(), url);
|
|
||||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
|
||||||
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
assert!(client.get("xxx".as_bytes()).unwrap().is_none());
|
|
||||||
|
|
||||||
stop.store(true, Ordering::Relaxed);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_commit_client_transaction() {
|
|
||||||
let url = "ipc:///tmp/parity-db-ipc-test-60.ipc";
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
|
|
||||||
crossbeam::scope(move |scope| {
|
|
||||||
let stop = Arc::new(AtomicBool::new(false));
|
|
||||||
run_worker(scope, stop.clone(), url);
|
|
||||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
|
|
||||||
let transaction = DBTransaction::new();
|
|
||||||
transaction.put("xxx".as_bytes(), "1".as_bytes());
|
|
||||||
client.write(transaction).unwrap();
|
|
||||||
|
|
||||||
client.close().unwrap();
|
|
||||||
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec());
|
|
||||||
|
|
||||||
stop.store(true, Ordering::Relaxed);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn key_write_read_ipc() {
|
|
||||||
let url = "ipc:///tmp/parity-db-ipc-test-70.ipc";
|
|
||||||
let path = RandomTempPath::create_dir();
|
|
||||||
|
|
||||||
crossbeam::scope(|scope| {
|
|
||||||
let stop = StopGuard::new();
|
|
||||||
run_worker(&scope, stop.share(), url);
|
|
||||||
|
|
||||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(url).unwrap();
|
|
||||||
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
let mut batch = Vec::new();
|
|
||||||
for _ in 0..100 {
|
|
||||||
batch.push((random_str(256).as_bytes().to_vec(), random_str(256).as_bytes().to_vec()));
|
|
||||||
batch.push((random_str(256).as_bytes().to_vec(), random_str(2048).as_bytes().to_vec()));
|
|
||||||
batch.push((random_str(2048).as_bytes().to_vec(), random_str(2048).as_bytes().to_vec()));
|
|
||||||
batch.push((random_str(2048).as_bytes().to_vec(), random_str(256).as_bytes().to_vec()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for &(ref k, ref v) in batch.iter() {
|
|
||||||
client.put(k, v).unwrap();
|
|
||||||
}
|
|
||||||
client.close().unwrap();
|
|
||||||
|
|
||||||
client.open_default(path.as_str().to_owned()).unwrap();
|
|
||||||
for &(ref k, ref v) in batch.iter() {
|
|
||||||
assert_eq!(v, &client.get(k).unwrap().unwrap());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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/>.
|
|
||||||
|
|
||||||
extern crate ethcore_ipc as ipc;
|
|
||||||
extern crate rocksdb;
|
|
||||||
extern crate ethcore_devtools as devtools;
|
|
||||||
extern crate semver;
|
|
||||||
extern crate ethcore_ipc_nano as nanoipc;
|
|
||||||
extern crate nanomsg;
|
|
||||||
extern crate crossbeam;
|
|
||||||
extern crate ethcore_util as util;
|
|
||||||
|
|
||||||
pub mod database;
|
|
||||||
pub mod traits;
|
|
||||||
|
|
||||||
pub use traits::{DatabaseService, DBTransaction, Error};
|
|
||||||
pub use database::{Database, DatabaseClient, DatabaseIterator};
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::atomic::*;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
pub type DatabaseNanoClient = DatabaseClient<::nanomsg::Socket>;
|
|
||||||
pub type DatabaseConnection = nanoipc::GuardedSocket<DatabaseNanoClient>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ServiceError {
|
|
||||||
Io(std::io::Error),
|
|
||||||
Socket(nanoipc::SocketError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<std::io::Error> for ServiceError {
|
|
||||||
fn from(io_error: std::io::Error) -> ServiceError { ServiceError::Io(io_error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<nanoipc::SocketError> for ServiceError {
|
|
||||||
fn from(socket_error: nanoipc::SocketError) -> ServiceError { ServiceError::Socket(socket_error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn blocks_service_url(db_path: &str) -> Result<String, std::io::Error> {
|
|
||||||
let mut path = PathBuf::from(db_path);
|
|
||||||
::std::fs::create_dir_all(db_path)?;
|
|
||||||
path.push("blocks.ipc");
|
|
||||||
Ok(format!("ipc://{}", path.to_str().unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extras_service_url(db_path: &str) -> Result<String, ::std::io::Error> {
|
|
||||||
let mut path = PathBuf::from(db_path);
|
|
||||||
::std::fs::create_dir_all(db_path)?;
|
|
||||||
path.push("extras.ipc");
|
|
||||||
Ok(format!("ipc://{}", path.to_str().unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn blocks_client(db_path: &str) -> Result<DatabaseConnection, ServiceError> {
|
|
||||||
let url = blocks_service_url(db_path)?;
|
|
||||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(&url)?;
|
|
||||||
Ok(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extras_client(db_path: &str) -> Result<DatabaseConnection, ServiceError> {
|
|
||||||
let url = extras_service_url(db_path)?;
|
|
||||||
let client = nanoipc::generic_client::<DatabaseClient<_>>(&url)?;
|
|
||||||
Ok(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
// for tests
|
|
||||||
pub fn run_worker(scope: &crossbeam::Scope, stop: Arc<AtomicBool>, socket_path: &str) {
|
|
||||||
let socket_path = socket_path.to_owned();
|
|
||||||
scope.spawn(move || {
|
|
||||||
let mut worker = nanoipc::Worker::new(&Arc::new(Database::new()));
|
|
||||||
worker.add_reqrep(&socket_path).unwrap();
|
|
||||||
while !stop.load(Ordering::Relaxed) {
|
|
||||||
worker.poll();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
135
db/src/traits.rs
135
db/src/traits.rs
@@ -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/>.
|
|
||||||
|
|
||||||
//! Ethcore database trait
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
pub type IteratorHandle = u32;
|
|
||||||
|
|
||||||
pub const DEFAULT_CACHE_LEN: usize = 12288;
|
|
||||||
|
|
||||||
#[derive(Binary)]
|
|
||||||
pub struct KeyValue {
|
|
||||||
pub key: Vec<u8>,
|
|
||||||
pub value: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Binary)]
|
|
||||||
pub enum Error {
|
|
||||||
AlreadyOpen,
|
|
||||||
IsClosed,
|
|
||||||
RocksDb(String),
|
|
||||||
TransactionUnknown,
|
|
||||||
IteratorUnknown,
|
|
||||||
UncommitedTransactions,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Error {
|
|
||||||
fn from(s: String) -> Error {
|
|
||||||
Error::RocksDb(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Database configuration
|
|
||||||
#[derive(Binary)]
|
|
||||||
pub struct DatabaseConfig {
|
|
||||||
/// Optional prefix size in bytes. Allows lookup by partial key.
|
|
||||||
pub prefix_size: Option<usize>,
|
|
||||||
/// write cache length
|
|
||||||
pub cache: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DatabaseConfig {
|
|
||||||
fn default() -> DatabaseConfig {
|
|
||||||
DatabaseConfig {
|
|
||||||
prefix_size: None,
|
|
||||||
cache: DEFAULT_CACHE_LEN,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DatabaseConfig {
|
|
||||||
fn with_prefix(prefix: usize) -> DatabaseConfig {
|
|
||||||
DatabaseConfig {
|
|
||||||
prefix_size: Some(prefix),
|
|
||||||
cache: DEFAULT_CACHE_LEN,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait DatabaseService : Sized {
|
|
||||||
/// Opens database in the specified path
|
|
||||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Opens database in the specified path with the default config
|
|
||||||
fn open_default(&self, path: String) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Closes database
|
|
||||||
fn close(&self) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Insert a key-value pair in the transaction. Any existing value value will be overwritten.
|
|
||||||
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Delete value by key.
|
|
||||||
fn delete(&self, key: &[u8]) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Get value by key.
|
|
||||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>;
|
|
||||||
|
|
||||||
/// Get value by partial key. Prefix size should match configured prefix size.
|
|
||||||
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error>;
|
|
||||||
|
|
||||||
/// Check if there is anything in the database.
|
|
||||||
fn is_empty(&self) -> Result<bool, Error>;
|
|
||||||
|
|
||||||
/// Get handle to iterate through keys
|
|
||||||
fn iter(&self) -> Result<IteratorHandle, Error>;
|
|
||||||
|
|
||||||
/// Next key-value for the the given iterator
|
|
||||||
fn iter_next(&self, iterator: IteratorHandle) -> Option<KeyValue>;
|
|
||||||
|
|
||||||
/// Dispose iteration that is no longer needed
|
|
||||||
fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Write client transaction
|
|
||||||
fn write(&self, transaction: DBTransaction) -> Result<(), Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Binary)]
|
|
||||||
pub struct DBTransaction {
|
|
||||||
pub writes: RefCell<Vec<KeyValue>>,
|
|
||||||
pub removes: RefCell<Vec<Vec<u8>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DBTransaction {
|
|
||||||
pub fn new() -> DBTransaction {
|
|
||||||
DBTransaction {
|
|
||||||
writes: RefCell::new(Vec::new()),
|
|
||||||
removes: RefCell::new(Vec::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn put(&self, key: &[u8], value: &[u8]) {
|
|
||||||
let mut brw = self.writes.borrow_mut();
|
|
||||||
brw.push(KeyValue { key: key.to_vec(), value: value.to_vec() });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(&self, key: &[u8]) {
|
|
||||||
let mut brw = self.removes.borrow_mut();
|
|
||||||
brw.push(key.to_vec());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ description = "Ethcore development/test/build tools"
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "ethcore-devtools"
|
name = "ethcore-devtools"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@@ -102,12 +102,7 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
|||||||
|
|
||||||
/// Check if all required security headers are present
|
/// Check if all required security headers are present
|
||||||
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||||
if let Some(port) = port {
|
if let None = port {
|
||||||
assert!(
|
|
||||||
headers.iter().find(|header| header.as_str() == &format!("X-Frame-Options: ALLOW-FROM http://127.0.0.1:{}", port)).is_some(),
|
|
||||||
"X-Frame-Options: ALLOW-FROM missing: {:?}", headers
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
assert!(
|
assert!(
|
||||||
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
||||||
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
||||||
@@ -121,4 +116,8 @@ pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
|||||||
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
|
headers.iter().find(|header| header.as_str() == "X-Content-Type-Options: nosniff").is_some(),
|
||||||
"X-Content-Type-Options missing: {:?}", headers
|
"X-Content-Type-Options missing: {:?}", headers
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
headers.iter().find(|header| header.starts_with("Content-Security-Policy: ")).is_some(),
|
||||||
|
"Content-Security-Policy missing: {:?}", headers
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ impl Drop for RandomTempPath {
|
|||||||
|
|
||||||
pub struct GuardedTempResult<T> {
|
pub struct GuardedTempResult<T> {
|
||||||
pub result: Option<T>,
|
pub result: Option<T>,
|
||||||
pub _temp: RandomTempPath
|
pub _temp: RandomTempPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> GuardedTempResult<T> {
|
impl<T> GuardedTempResult<T> {
|
||||||
|
|||||||
3
docker/README.md
Normal file
3
docker/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Usage
|
||||||
|
|
||||||
|
```docker build -f docker/ubuntu/Dockerfile --tag ethcore/parity:branch_or_tag_name .```
|
||||||
@@ -1,29 +1,32 @@
|
|||||||
FROM centos:latest
|
FROM centos:latest
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# install tools and dependencies
|
# install tools and dependencies
|
||||||
RUN yum -y update&& \
|
RUN yum -y update&& \
|
||||||
yum install -y git make gcc-c++ gcc file binutils
|
yum install -y git make gcc-c++ gcc file binutils
|
||||||
|
|
||||||
# install rustup
|
# install rustup
|
||||||
RUN curl -sSf https://static.rust-lang.org/rustup.sh -o rustup.sh &&\
|
RUN curl -sSf https://static.rust-lang.org/rustup.sh -o rustup.sh &&\
|
||||||
ls&&\
|
ls&&\
|
||||||
sh rustup.sh -s -- --disable-sudo
|
sh rustup.sh --disable-sudo
|
||||||
|
|
||||||
# show backtraces
|
# show backtraces
|
||||||
ENV RUST_BACKTRACE 1
|
ENV RUST_BACKTRACE 1
|
||||||
|
|
||||||
# set compiler
|
# set compiler
|
||||||
ENV CXX g++
|
ENV CXX g++
|
||||||
ENV CC gcc
|
ENV CC gcc
|
||||||
|
|
||||||
# show tools
|
# show tools
|
||||||
RUN rustc -vV && \
|
RUN rustc -vV && \
|
||||||
cargo -V && \
|
cargo -V && \
|
||||||
gcc -v &&\
|
gcc -v &&\
|
||||||
g++ -v
|
g++ -v
|
||||||
|
|
||||||
# build parity
|
# build parity
|
||||||
RUN git clone https://github.com/ethcore/parity && \
|
ADD . /build/parity
|
||||||
cd parity&&\
|
RUN cd parity&&\
|
||||||
git checkout beta && \
|
cargo build --release --verbose && \
|
||||||
git pull && \
|
|
||||||
ls -a&&\
|
|
||||||
cargo build --release --verbose && \
|
|
||||||
ls /build/parity/target/release/parity && \
|
ls /build/parity/target/release/parity && \
|
||||||
strip /build/parity/target/release/parity
|
strip /build/parity/target/release/parity
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ WORKDIR /build
|
|||||||
#ENV for build TAG
|
#ENV for build TAG
|
||||||
ARG BUILD_TAG
|
ARG BUILD_TAG
|
||||||
ENV BUILD_TAG ${BUILD_TAG:-master}
|
ENV BUILD_TAG ${BUILD_TAG:-master}
|
||||||
RUN echo $BUILD_TAG
|
RUN echo "Build tag:" $BUILD_TAG
|
||||||
# install tools and dependencies
|
# install tools and dependencies
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --force-yes --no-install-recommends \
|
apt-get install -y --force-yes --no-install-recommends \
|
||||||
@@ -48,7 +48,7 @@ RUN apt-get update && \
|
|||||||
# show backtraces
|
# show backtraces
|
||||||
RUST_BACKTRACE=1 && \
|
RUST_BACKTRACE=1 && \
|
||||||
# build parity
|
# build parity
|
||||||
cd /build&&git clone https://github.com/ethcore/parity && \
|
cd /build&&git clone https://github.com/paritytech/parity && \
|
||||||
cd parity && \
|
cd parity && \
|
||||||
git pull&& \
|
git pull&& \
|
||||||
git checkout $BUILD_TAG && \
|
git checkout $BUILD_TAG && \
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
FROM ubuntu:14.04
|
FROM ubuntu:14.04
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# install tools and dependencies
|
# install tools and dependencies
|
||||||
RUN apt-get -y update && \
|
RUN apt-get -y update && \
|
||||||
apt-get install -y --force-yes --no-install-recommends \
|
apt-get install -y --force-yes --no-install-recommends \
|
||||||
@@ -24,14 +25,11 @@ RUN rustup target add aarch64-unknown-linux-gnu
|
|||||||
ENV RUST_BACKTRACE 1
|
ENV RUST_BACKTRACE 1
|
||||||
|
|
||||||
# show tools
|
# show tools
|
||||||
RUN rustc -vV && \
|
RUN rustc -vV && cargo -V
|
||||||
cargo -V
|
|
||||||
|
|
||||||
# build parity
|
# build parity
|
||||||
RUN git clone https://github.com/ethcore/parity && \
|
ADD . /build/parity
|
||||||
cd parity && \
|
RUN cd parity && \
|
||||||
git checkout beta && \
|
|
||||||
git pull && \
|
|
||||||
mkdir -p .cargo && \
|
mkdir -p .cargo && \
|
||||||
echo '[target.aarch64-unknown-linux-gnu]\n\
|
echo '[target.aarch64-unknown-linux-gnu]\n\
|
||||||
linker = "aarch64-linux-gnu-gcc"\n'\
|
linker = "aarch64-linux-gnu-gcc"\n'\
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
FROM ubuntu:14.04
|
FROM ubuntu:14.04
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# install tools and dependencies
|
# install tools and dependencies
|
||||||
RUN apt-get -y update && \
|
RUN apt-get -y update && \
|
||||||
apt-get install -y --force-yes --no-install-recommends \
|
apt-get install -y --force-yes --no-install-recommends \
|
||||||
@@ -23,16 +24,12 @@ RUN rustup target add armv7-unknown-linux-gnueabihf
|
|||||||
# show backtraces
|
# show backtraces
|
||||||
ENV RUST_BACKTRACE 1
|
ENV RUST_BACKTRACE 1
|
||||||
|
|
||||||
|
|
||||||
# show tools
|
# show tools
|
||||||
RUN rustc -vV && \
|
RUN rustc -vV && cargo -V
|
||||||
cargo -V
|
|
||||||
|
|
||||||
# build parity
|
# build parity
|
||||||
RUN git clone https://github.com/ethcore/parity && \
|
ADD . /build/parity
|
||||||
cd parity && \
|
RUN cd parity && \
|
||||||
git checkout beta && \
|
|
||||||
git pull && \
|
|
||||||
mkdir -p .cargo && \
|
mkdir -p .cargo && \
|
||||||
echo '[target.armv7-unknown-linux-gnueabihf]\n\
|
echo '[target.armv7-unknown-linux-gnueabihf]\n\
|
||||||
linker = "arm-linux-gnueabihf-gcc"\n'\
|
linker = "arm-linux-gnueabihf-gcc"\n'\
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
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 rustup
|
|
||||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
|
||||||
|
|
||||||
# rustup directory
|
|
||||||
ENV PATH /root/.cargo/bin:$PATH
|
|
||||||
|
|
||||||
# show backtraces
|
|
||||||
ENV RUST_BACKTRACE 1
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
FROM ubuntu:14.04
|
FROM ubuntu:14.04
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# install tools and dependencies
|
# install tools and dependencies
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y \
|
apt-get install -y \
|
||||||
@@ -45,10 +46,8 @@ gcc -v &&\
|
|||||||
g++ -v
|
g++ -v
|
||||||
|
|
||||||
# build parity
|
# build parity
|
||||||
RUN git clone https://github.com/ethcore/parity && \
|
ADD . /build/parity
|
||||||
cd parity && \
|
RUN cd parity && \
|
||||||
git checkout beta && \
|
|
||||||
git pull && \
|
|
||||||
cargo build --release --features ethcore/jit --verbose && \
|
cargo build --release --features ethcore/jit --verbose && \
|
||||||
ls /build/parity/target/release/parity && \
|
ls /build/parity/target/release/parity && \
|
||||||
strip /build/parity/target/release/parity
|
strip /build/parity/target/release/parity
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
FROM ubuntu:14.04
|
|
||||||
WORKDIR /build
|
|
||||||
# install tools and dependencies
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y \
|
|
||||||
build-essential \
|
|
||||||
g++ \
|
|
||||||
curl \
|
|
||||||
git \
|
|
||||||
file \
|
|
||||||
binutils
|
|
||||||
|
|
||||||
# install rustup
|
|
||||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
|
||||||
|
|
||||||
# rustup directory
|
|
||||||
ENV PATH /root/.cargo/bin:$PATH
|
|
||||||
|
|
||||||
# show backtraces
|
|
||||||
ENV RUST_BACKTRACE 1
|
|
||||||
|
|
||||||
# show tools
|
|
||||||
RUN rustc -vV && \
|
|
||||||
cargo -V && \
|
|
||||||
gcc -v &&\
|
|
||||||
g++ -v
|
|
||||||
|
|
||||||
# build parity
|
|
||||||
RUN git clone https://github.com/ethcore/parity && \
|
|
||||||
cd parity && \
|
|
||||||
git checkout stable && \
|
|
||||||
git pull && \
|
|
||||||
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"]
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
FROM ubuntu:14.04
|
FROM ubuntu:14.04
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
# install tools and dependencies
|
# install tools and dependencies
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y \
|
apt-get install -y \
|
||||||
@@ -8,7 +9,10 @@ RUN apt-get update && \
|
|||||||
curl \
|
curl \
|
||||||
git \
|
git \
|
||||||
file \
|
file \
|
||||||
binutils
|
binutils \
|
||||||
|
libssl-dev \
|
||||||
|
pkg-config \
|
||||||
|
libudev-dev
|
||||||
|
|
||||||
# install rustup
|
# install rustup
|
||||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||||
@@ -26,10 +30,8 @@ gcc -v &&\
|
|||||||
g++ -v
|
g++ -v
|
||||||
|
|
||||||
# build parity
|
# build parity
|
||||||
RUN git clone https://github.com/ethcore/parity && \
|
ADD . /build/parity
|
||||||
cd parity && \
|
RUN cd parity && \
|
||||||
git checkout beta && \
|
|
||||||
git pull && \
|
|
||||||
cargo build --release --verbose && \
|
cargo build --release --verbose && \
|
||||||
ls /build/parity/target/release/parity && \
|
ls /build/parity/target/release/parity && \
|
||||||
strip /build/parity/target/release/parity
|
strip /build/parity/target/release/parity
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ethash"
|
name = "ethash"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@@ -9,4 +9,4 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
log = "0.3"
|
log = "0.3"
|
||||||
sha3 = { path = "../util/sha3" }
|
sha3 = { path = "../util/sha3" }
|
||||||
primal = "0.2.3"
|
primal = "0.2.3"
|
||||||
parking_lot = "0.3"
|
parking_lot = "0.4"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use std::mem;
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use sha3;
|
use sha3;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
|
|
||||||
@@ -86,6 +86,7 @@ impl Node {
|
|||||||
pub type H256 = [u8; 32];
|
pub type H256 = [u8; 32];
|
||||||
|
|
||||||
pub struct Light {
|
pub struct Light {
|
||||||
|
cache_dir: PathBuf,
|
||||||
block_number: u64,
|
block_number: u64,
|
||||||
cache: Vec<Node>,
|
cache: Vec<Node>,
|
||||||
seed_compute: Mutex<SeedHashCompute>,
|
seed_compute: Mutex<SeedHashCompute>,
|
||||||
@@ -94,8 +95,8 @@ pub struct Light {
|
|||||||
/// Light cache structure
|
/// Light cache structure
|
||||||
impl Light {
|
impl Light {
|
||||||
/// Create a new light cache for a given block number
|
/// Create a new light cache for a given block number
|
||||||
pub fn new(block_number: u64) -> Light {
|
pub fn new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
|
||||||
light_new(block_number)
|
light_new(cache_dir, block_number)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the light boundary data
|
/// Calculate the light boundary data
|
||||||
@@ -105,17 +106,15 @@ impl Light {
|
|||||||
light_compute(self, header_hash, nonce)
|
light_compute(self, header_hash, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_path(seed_hash: H256) -> PathBuf {
|
pub fn file_path<T: AsRef<Path>>(cache_dir: T, seed_hash: H256) -> PathBuf {
|
||||||
let mut home = ::std::env::home_dir().unwrap();
|
let mut cache_dir = cache_dir.as_ref().to_path_buf();
|
||||||
home.push(".ethash");
|
cache_dir.push(to_hex(&seed_hash));
|
||||||
home.push("light");
|
cache_dir
|
||||||
home.push(to_hex(&seed_hash));
|
|
||||||
home
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_file(block_number: u64) -> io::Result<Light> {
|
pub fn from_file<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> io::Result<Light> {
|
||||||
let seed_compute = SeedHashCompute::new();
|
let seed_compute = SeedHashCompute::new();
|
||||||
let path = Light::file_path(seed_compute.get_seedhash(block_number));
|
let path = Light::file_path(&cache_dir, seed_compute.get_seedhash(block_number));
|
||||||
let mut file = File::open(path)?;
|
let mut file = File::open(path)?;
|
||||||
|
|
||||||
let cache_size = get_cache_size(block_number);
|
let cache_size = get_cache_size(block_number);
|
||||||
@@ -123,24 +122,27 @@ impl Light {
|
|||||||
return Err(io::Error::new(io::ErrorKind::Other, "Cache file size mismatch"));
|
return Err(io::Error::new(io::ErrorKind::Other, "Cache file size mismatch"));
|
||||||
}
|
}
|
||||||
let num_nodes = cache_size / NODE_BYTES;
|
let num_nodes = cache_size / NODE_BYTES;
|
||||||
let mut nodes: Vec<Node> = Vec::new();
|
let mut nodes: Vec<Node> = Vec::with_capacity(num_nodes);
|
||||||
nodes.resize(num_nodes, unsafe { mem::uninitialized() });
|
nodes.resize(num_nodes, unsafe { mem::uninitialized() });
|
||||||
let buf = unsafe { slice::from_raw_parts_mut(nodes.as_mut_ptr() as *mut u8, cache_size) };
|
let buf = unsafe { slice::from_raw_parts_mut(nodes.as_mut_ptr() as *mut u8, cache_size) };
|
||||||
file.read_exact(buf)?;
|
file.read_exact(buf)?;
|
||||||
Ok(Light {
|
Ok(Light {
|
||||||
|
block_number,
|
||||||
|
cache_dir: cache_dir.as_ref().to_path_buf(),
|
||||||
cache: nodes,
|
cache: nodes,
|
||||||
block_number: block_number,
|
|
||||||
seed_compute: Mutex::new(seed_compute),
|
seed_compute: Mutex::new(seed_compute),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_file(&self) -> io::Result<PathBuf> {
|
pub fn to_file(&self) -> io::Result<PathBuf> {
|
||||||
let seed_compute = self.seed_compute.lock();
|
let seed_compute = self.seed_compute.lock();
|
||||||
let path = Light::file_path(seed_compute.get_seedhash(self.block_number));
|
let path = Light::file_path(&self.cache_dir, seed_compute.get_seedhash(self.block_number));
|
||||||
|
|
||||||
if self.block_number >= ETHASH_EPOCH_LENGTH * 2 {
|
if self.block_number >= ETHASH_EPOCH_LENGTH * 2 {
|
||||||
let deprecated = Light::file_path(
|
let deprecated = Light::file_path(
|
||||||
seed_compute.get_seedhash(self.block_number - ETHASH_EPOCH_LENGTH * 2));
|
&self.cache_dir,
|
||||||
|
seed_compute.get_seedhash(self.block_number - ETHASH_EPOCH_LENGTH * 2)
|
||||||
|
);
|
||||||
|
|
||||||
if deprecated.exists() {
|
if deprecated.exists() {
|
||||||
debug!(target: "ethash", "removing: {:?}", &deprecated);
|
debug!(target: "ethash", "removing: {:?}", &deprecated);
|
||||||
@@ -341,15 +343,12 @@ fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn light_new(block_number: u64) -> Light {
|
fn light_new<T: AsRef<Path>>(cache_dir: T, block_number: u64) -> Light {
|
||||||
|
|
||||||
let seed_compute = SeedHashCompute::new();
|
let seed_compute = SeedHashCompute::new();
|
||||||
let seedhash = seed_compute.get_seedhash(block_number);
|
let seedhash = seed_compute.get_seedhash(block_number);
|
||||||
let cache_size = get_cache_size(block_number);
|
let cache_size = get_cache_size(block_number);
|
||||||
|
|
||||||
if cache_size % NODE_BYTES != 0 {
|
assert!(cache_size % NODE_BYTES == 0, "Unaligned cache size");
|
||||||
panic!("Unaligned cache size");
|
|
||||||
}
|
|
||||||
let num_nodes = cache_size / NODE_BYTES;
|
let num_nodes = cache_size / NODE_BYTES;
|
||||||
|
|
||||||
let mut nodes = Vec::with_capacity(num_nodes);
|
let mut nodes = Vec::with_capacity(num_nodes);
|
||||||
@@ -373,8 +372,9 @@ fn light_new(block_number: u64) -> Light {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Light {
|
Light {
|
||||||
|
block_number,
|
||||||
|
cache_dir: cache_dir.as_ref().to_path_buf(),
|
||||||
cache: nodes,
|
cache: nodes,
|
||||||
block_number: block_number,
|
|
||||||
seed_compute: Mutex::new(seed_compute),
|
seed_compute: Mutex::new(seed_compute),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,7 +433,7 @@ fn test_light_compute() {
|
|||||||
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 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;
|
let nonce = 0xd7b3ac70a301a249;
|
||||||
// difficulty = 0x085657254bd9u64;
|
// difficulty = 0x085657254bd9u64;
|
||||||
let light = Light::new(486382);
|
let light = Light::new(&::std::env::temp_dir(), 486382);
|
||||||
let result = light_compute(&light, &hash, nonce);
|
let result = light_compute(&light, &hash, nonce);
|
||||||
assert_eq!(result.mix_hash[..], mix_hash[..]);
|
assert_eq!(result.mix_hash[..], mix_hash[..]);
|
||||||
assert_eq!(result.value[..], boundary[..]);
|
assert_eq!(result.value[..], boundary[..]);
|
||||||
@@ -472,15 +472,16 @@ fn test_seed_compute_after_newer() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_drop_old_data() {
|
fn test_drop_old_data() {
|
||||||
let first = Light::new(0).to_file().unwrap();
|
let path = ::std::env::temp_dir();
|
||||||
|
let first = Light::new(&path, 0).to_file().unwrap();
|
||||||
|
|
||||||
let second = Light::new(ETHASH_EPOCH_LENGTH).to_file().unwrap();
|
let second = Light::new(&path, ETHASH_EPOCH_LENGTH).to_file().unwrap();
|
||||||
assert!(fs::metadata(&first).is_ok());
|
assert!(fs::metadata(&first).is_ok());
|
||||||
|
|
||||||
let _ = Light::new(ETHASH_EPOCH_LENGTH * 2).to_file();
|
let _ = Light::new(&path, ETHASH_EPOCH_LENGTH * 2).to_file();
|
||||||
assert!(fs::metadata(&first).is_err());
|
assert!(fs::metadata(&first).is_err());
|
||||||
assert!(fs::metadata(&second).is_ok());
|
assert!(fs::metadata(&second).is_ok());
|
||||||
|
|
||||||
let _ = Light::new(ETHASH_EPOCH_LENGTH * 3).to_file();
|
let _ = Light::new(&path, ETHASH_EPOCH_LENGTH * 3).to_file();
|
||||||
assert!(fs::metadata(&second).is_err());
|
assert!(fs::metadata(&second).is_err());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ extern crate log;
|
|||||||
mod compute;
|
mod compute;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use compute::Light;
|
use compute::Light;
|
||||||
pub use compute::{ETHASH_EPOCH_LENGTH, H256, ProofOfWork, SeedHashCompute, quick_get_difficulty, slow_get_seedhash};
|
pub use compute::{ETHASH_EPOCH_LENGTH, H256, ProofOfWork, SeedHashCompute, quick_get_difficulty, slow_get_seedhash};
|
||||||
|
|
||||||
@@ -41,12 +42,14 @@ struct LightCache {
|
|||||||
/// Light/Full cache manager.
|
/// Light/Full cache manager.
|
||||||
pub struct EthashManager {
|
pub struct EthashManager {
|
||||||
cache: Mutex<LightCache>,
|
cache: Mutex<LightCache>,
|
||||||
|
cache_dir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EthashManager {
|
impl EthashManager {
|
||||||
/// Create a new new instance of ethash manager
|
/// Create a new new instance of ethash manager
|
||||||
pub fn new() -> EthashManager {
|
pub fn new<T: AsRef<Path>>(cache_dir: T) -> EthashManager {
|
||||||
EthashManager {
|
EthashManager {
|
||||||
|
cache_dir: cache_dir.as_ref().to_path_buf(),
|
||||||
cache: Mutex::new(LightCache {
|
cache: Mutex::new(LightCache {
|
||||||
recent_epoch: None,
|
recent_epoch: None,
|
||||||
recent: None,
|
recent: None,
|
||||||
@@ -88,11 +91,11 @@ impl EthashManager {
|
|||||||
};
|
};
|
||||||
match light {
|
match light {
|
||||||
None => {
|
None => {
|
||||||
let light = match Light::from_file(block_number) {
|
let light = match Light::from_file(&self.cache_dir, block_number) {
|
||||||
Ok(light) => Arc::new(light),
|
Ok(light) => Arc::new(light),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("Light cache file not found for {}:{}", block_number, e);
|
debug!("Light cache file not found for {}:{}", block_number, e);
|
||||||
let light = Light::new(block_number);
|
let light = Light::new(&self.cache_dir, block_number);
|
||||||
if let Err(e) = light.to_file() {
|
if let Err(e) = light.to_file() {
|
||||||
warn!("Light cache file write error: {}", e);
|
warn!("Light cache file write error: {}", e);
|
||||||
}
|
}
|
||||||
@@ -112,7 +115,7 @@ impl EthashManager {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lru() {
|
fn test_lru() {
|
||||||
let ethash = EthashManager::new();
|
let ethash = EthashManager::new(&::std::env::temp_dir());
|
||||||
let hash = [0u8; 32];
|
let hash = [0u8; 32];
|
||||||
ethash.compute_light(1, &hash, 1);
|
ethash.compute_light(1, &hash, 1);
|
||||||
ethash.compute_light(50000, &hash, 1);
|
ethash.compute_light(50000, &hash, 1);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ description = "Ethcore library"
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "ethcore"
|
name = "ethcore"
|
||||||
version = "1.6.0"
|
version = "1.7.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
@@ -11,46 +11,55 @@ build = "build.rs"
|
|||||||
"ethcore-ipc-codegen" = { path = "../ipc/codegen" }
|
"ethcore-ipc-codegen" = { path = "../ipc/codegen" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
|
||||||
env_logger = "0.3"
|
|
||||||
rustc-serialize = "0.3"
|
|
||||||
rust-crypto = "0.2.34"
|
|
||||||
num_cpus = "0.2"
|
|
||||||
crossbeam = "0.2.9"
|
|
||||||
lazy_static = "0.2"
|
|
||||||
bloomchain = "0.1"
|
|
||||||
semver = "0.5"
|
|
||||||
bit-set = "0.4"
|
bit-set = "0.4"
|
||||||
time = "0.1"
|
bloomchain = "0.1"
|
||||||
rand = "0.3"
|
bn = { git = "https://github.com/paritytech/bn" }
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
transient-hashmap = "0.1"
|
|
||||||
linked-hash-map = "0.3.0"
|
|
||||||
lru-cache = "0.1.0"
|
|
||||||
ethabi = "1.0.0"
|
|
||||||
evmjit = { path = "../evmjit", optional = true }
|
|
||||||
clippy = { version = "0.0.103", optional = true}
|
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" }
|
ethash = { path = "../ethash" }
|
||||||
ethcore-util = { path = "../util" }
|
|
||||||
ethcore-io = { path = "../util/io" }
|
|
||||||
ethcore-devtools = { path = "../devtools" }
|
|
||||||
ethjson = { path = "../json" }
|
|
||||||
ethcore-ipc = { path = "../ipc/rpc" }
|
|
||||||
ethstore = { path = "../ethstore" }
|
|
||||||
ethkey = { path = "../ethkey" }
|
|
||||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
|
||||||
rlp = { path = "../util/rlp" }
|
|
||||||
ethcore-stratum = { path = "../stratum" }
|
|
||||||
ethcore-bloom-journal = { path = "../util/bloom" }
|
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" }
|
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"
|
||||||
|
rayon = "0.8"
|
||||||
|
rand = "0.3"
|
||||||
|
rlp = { path = "../util/rlp" }
|
||||||
|
rust-crypto = "0.2.34"
|
||||||
|
rustc-hex = "1.0"
|
||||||
|
rustc-serialize = "0.3"
|
||||||
|
semver = "0.6"
|
||||||
stats = { path = "../util/stats" }
|
stats = { path = "../util/stats" }
|
||||||
|
time = "0.1"
|
||||||
|
transient-hashmap = "0.4"
|
||||||
|
|
||||||
[dependencies.hyper]
|
[dev-dependencies]
|
||||||
git = "https://github.com/ethcore/hyper"
|
native-contracts = { path = "native_contracts", features = ["test_contracts"] }
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
jit = ["evmjit"]
|
jit = ["evm/jit"]
|
||||||
evm-debug = ["slow-blocks"]
|
evm-debug = ["slow-blocks"]
|
||||||
evm-debug-tests = ["evm-debug"]
|
evm-debug-tests = ["evm-debug"]
|
||||||
slow-blocks = [] # Use SLOW_TX_DURATION="50" (compile time!) to track transactions over 50ms
|
slow-blocks = [] # Use SLOW_TX_DURATION="50" (compile time!) to track transactions over 50ms
|
||||||
@@ -60,5 +69,3 @@ dev = ["clippy"]
|
|||||||
default = []
|
default = []
|
||||||
benches = []
|
benches = []
|
||||||
ipc = []
|
ipc = []
|
||||||
ethkey-cli = ["ethkey/cli"]
|
|
||||||
ethstore-cli = ["ethstore/cli"]
|
|
||||||
|
|||||||
97
ethcore/benches/evm.rs
Normal file
97
ethcore/benches/evm.rs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
extern crate ethcore_util as util;
|
||||||
|
extern crate rand;
|
||||||
|
extern crate bn;
|
||||||
|
extern crate crypto;
|
||||||
|
extern crate rustc_serialize;
|
||||||
|
extern crate ethkey;
|
||||||
|
|
||||||
|
use self::test::{Bencher};
|
||||||
|
use rand::{StdRng};
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bn_128_pairing(b: &mut Bencher) {
|
||||||
|
use bn::{pairing, G1, G2, Fr, Group};
|
||||||
|
|
||||||
|
let rng = &mut ::rand::thread_rng();
|
||||||
|
|
||||||
|
let sk0 = Fr::random(rng);
|
||||||
|
let sk1 = Fr::random(rng);
|
||||||
|
|
||||||
|
let pk0 = G1::one() * sk0;
|
||||||
|
let pk1 = G2::one() * sk1;
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let _ = pairing(pk0, pk1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bn_128_mul(b: &mut Bencher) {
|
||||||
|
use bn::{AffineG1, G1, Fr, Group};
|
||||||
|
|
||||||
|
let mut rng = StdRng::new().unwrap();
|
||||||
|
let p: G1 = G1::random(&mut rng);
|
||||||
|
let fr = Fr::random(&mut rng);
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let _ = AffineG1::from_jacobian(p * fr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn sha256(b: &mut Bencher) {
|
||||||
|
use crypto::sha2::Sha256;
|
||||||
|
use crypto::digest::Digest;
|
||||||
|
|
||||||
|
let mut input: [u8; 256] = [0; 256];
|
||||||
|
let mut out = [0; 32];
|
||||||
|
|
||||||
|
b.iter(|| {
|
||||||
|
let mut sha = Sha256::new();
|
||||||
|
sha.input(&input);
|
||||||
|
sha.result(&mut input[0..32]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn ecrecover(b: &mut Bencher) {
|
||||||
|
use rustc_serialize::hex::FromHex;
|
||||||
|
use ethkey::{Signature, recover as ec_recover};
|
||||||
|
use util::H256;
|
||||||
|
let input = FromHex::from_hex("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
|
||||||
|
let hash = H256::from_slice(&input[0..32]);
|
||||||
|
let v = H256::from_slice(&input[32..64]);
|
||||||
|
let r = H256::from_slice(&input[64..96]);
|
||||||
|
let s = H256::from_slice(&input[96..128]);
|
||||||
|
|
||||||
|
let bit = match v[31] {
|
||||||
|
27 | 28 if &v.0[..31] == &[0; 31] => v[31] - 27,
|
||||||
|
_ => { return; },
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = Signature::from_rsv(&r, &s, bit);
|
||||||
|
b.iter(|| {
|
||||||
|
let _ = ec_recover(&s, &hash);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
extern crate ethcore_ipc_codegen;
|
extern crate ethcore_ipc_codegen;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
|
|
||||||
ethcore_ipc_codegen::derive_ipc_cond("src/client/traits.rs", cfg!(feature="ipc")).unwrap();
|
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/snapshot/snapshot_service_trait.rs", cfg!(feature="ipc")).unwrap();
|
||||||
ethcore_ipc_codegen::derive_ipc_cond("src/client/chain_notify.rs", cfg!(feature="ipc")).unwrap();
|
ethcore_ipc_codegen::derive_ipc_cond("src/client/chain_notify.rs", cfg!(feature="ipc")).unwrap();
|
||||||
|
|||||||
23
ethcore/evm/Cargo.toml
Normal file
23
ethcore/evm/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
[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"]
|
||||||
@@ -15,11 +15,12 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Evm input params.
|
//! Evm input params.
|
||||||
use util::{Address, Bytes, Uint, U256};
|
use util::{Address, Bytes, U256};
|
||||||
use util::hash::{H256, FixedHash};
|
use util::hash::{H256};
|
||||||
use util::sha3::{Hashable, SHA3_EMPTY};
|
use util::sha3::{Hashable, SHA3_EMPTY};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
use types::executed::CallType;
|
|
||||||
|
use {CallType};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -39,6 +40,16 @@ impl ActionValue {
|
|||||||
ActionValue::Transfer(x) | ActionValue::Apparent(x) => x
|
ActionValue::Transfer(x) | ActionValue::Apparent(x) => x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the transfer action value of the U256-convertable raw value
|
||||||
|
pub fn transfer<T: Into<U256>>(transfer_value: T) -> ActionValue {
|
||||||
|
ActionValue::Transfer(transfer_value.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the apparent action value of the U256-convertable raw value
|
||||||
|
pub fn apparent<T: Into<U256>>(apparent_value: T) -> ActionValue {
|
||||||
|
ActionValue::Apparent(apparent_value.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should be a trait, possible to avoid cloning everything from a Transaction(/View).
|
// TODO: should be a trait, possible to avoid cloning everything from a Transaction(/View).
|
||||||
@@ -48,7 +59,7 @@ pub struct ActionParams {
|
|||||||
/// Address of currently executed code.
|
/// Address of currently executed code.
|
||||||
pub code_address: Address,
|
pub code_address: Address,
|
||||||
/// Hash of currently executed code.
|
/// Hash of currently executed code.
|
||||||
pub code_hash: H256,
|
pub code_hash: Option<H256>,
|
||||||
/// Receive address. Usually equal to code_address,
|
/// Receive address. Usually equal to code_address,
|
||||||
/// except when called using CALLCODE.
|
/// except when called using CALLCODE.
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
@@ -76,7 +87,7 @@ impl Default for ActionParams {
|
|||||||
fn default() -> ActionParams {
|
fn default() -> ActionParams {
|
||||||
ActionParams {
|
ActionParams {
|
||||||
code_address: Address::new(),
|
code_address: Address::new(),
|
||||||
code_hash: SHA3_EMPTY,
|
code_hash: Some(SHA3_EMPTY),
|
||||||
address: Address::new(),
|
address: Address::new(),
|
||||||
sender: Address::new(),
|
sender: Address::new(),
|
||||||
origin: Address::new(),
|
origin: Address::new(),
|
||||||
@@ -95,7 +106,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
|
|||||||
let address: Address = t.address.into();
|
let address: Address = t.address.into();
|
||||||
ActionParams {
|
ActionParams {
|
||||||
code_address: Address::new(),
|
code_address: Address::new(),
|
||||||
code_hash: (&*t.code).sha3(),
|
code_hash: Some((&*t.code).sha3()),
|
||||||
address: address,
|
address: address,
|
||||||
sender: t.sender.into(),
|
sender: t.sender.into(),
|
||||||
origin: t.origin.into(),
|
origin: t.origin.into(),
|
||||||
@@ -25,7 +25,7 @@ extern crate test;
|
|||||||
use self::test::{Bencher, black_box};
|
use self::test::{Bencher, black_box};
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use action_params::ActionParams;
|
use evm::action_params::ActionParams;
|
||||||
use evm::{self, Factory, VMType};
|
use evm::{self, Factory, VMType};
|
||||||
use evm::tests::FakeExt;
|
use evm::tests::FakeExt;
|
||||||
|
|
||||||
70
ethcore/evm/src/call_type.rs
Normal file
70
ethcore/evm/src/call_type.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
//! 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,10 +14,12 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Environment information for transaction execution.
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use util::{U256, Address, H256, Hashable};
|
use util::{U256, Address, H256, Hashable};
|
||||||
use header::BlockNumber;
|
use types::BlockNumber;
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
|
||||||
/// Simple vector of hashes, should be at most 256 items large, can be smaller if being used
|
/// Simple vector of hashes, should be at most 256 items large, can be smaller if being used
|
||||||
@@ -25,7 +27,7 @@ use ethjson;
|
|||||||
pub type LastHashes = Vec<H256>;
|
pub type LastHashes = Vec<H256>;
|
||||||
|
|
||||||
/// Information concerning the execution environment for a message-call/contract-creation.
|
/// Information concerning the execution environment for a message-call/contract-creation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EnvInfo {
|
pub struct EnvInfo {
|
||||||
/// The block number.
|
/// The block number.
|
||||||
pub number: BlockNumber,
|
pub number: BlockNumber,
|
||||||
@@ -74,15 +76,13 @@ impl From<ethjson::vm::Env> for EnvInfo {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate rustc_serialize;
|
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use super::*;
|
use super::*;
|
||||||
use util::{U256, Address};
|
use util::{U256, Address};
|
||||||
use ethjson;
|
use ethjson;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_serializes_form_json() {
|
fn it_serializes_from_json() {
|
||||||
let env_info = EnvInfo::from(ethjson::vm::Env {
|
let env_info = EnvInfo::from(ethjson::vm::Env {
|
||||||
author: ethjson::hash::Address(Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()),
|
author: ethjson::hash::Address(Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()),
|
||||||
number: ethjson::uint::Uint(U256::from(1_112_339)),
|
number: ethjson::uint::Uint(U256::from(1_112_339)),
|
||||||
@@ -17,9 +17,11 @@
|
|||||||
//! Evm interface.
|
//! Evm interface.
|
||||||
|
|
||||||
use std::{ops, cmp, fmt};
|
use std::{ops, cmp, fmt};
|
||||||
use util::{U128, U256, U512, Uint, trie};
|
use util::{U128, U256, U512, trie};
|
||||||
use action_params::ActionParams;
|
use action_params::ActionParams;
|
||||||
use evm::Ext;
|
use {Ext};
|
||||||
|
|
||||||
|
use super::wasm;
|
||||||
|
|
||||||
/// Evm errors.
|
/// Evm errors.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -59,9 +61,18 @@ pub enum Error {
|
|||||||
/// What was the stack limit
|
/// What was the stack limit
|
||||||
limit: usize
|
limit: usize
|
||||||
},
|
},
|
||||||
/// Returned on evm internal error. Should never be ignored during development.
|
/// 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.
|
/// Likely to cause consensus issues.
|
||||||
Internal(String),
|
Internal(String),
|
||||||
|
/// Wasm runtime error
|
||||||
|
Wasm(String),
|
||||||
|
/// Out of bounds access in RETURNDATACOPY.
|
||||||
|
OutOfBounds,
|
||||||
|
/// Execution has been reverted with REVERT.
|
||||||
|
Reverted,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Box<trie::TrieError>> for Error {
|
impl From<Box<trie::TrieError>> for Error {
|
||||||
@@ -70,32 +81,95 @@ impl From<Box<trie::TrieError>> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<wasm::RuntimeError> for Error {
|
||||||
|
fn from(err: wasm::RuntimeError) -> Self {
|
||||||
|
Error::Wasm(format!("Runtime error: {:?}", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use self::Error::*;
|
use self::Error::*;
|
||||||
let message = match *self {
|
match *self {
|
||||||
OutOfGas => "Out of gas",
|
OutOfGas => write!(f, "Out of gas"),
|
||||||
BadJumpDestination { .. } => "Bad jump destination",
|
BadJumpDestination { destination } => write!(f, "Bad jump destination {:x}", destination),
|
||||||
BadInstruction { .. } => "Bad instruction",
|
BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction),
|
||||||
StackUnderflow { .. } => "Stack underflow",
|
StackUnderflow { instruction, wanted, on_stack } => write!(f, "Stack underflow {} {}/{}", instruction, wanted, on_stack),
|
||||||
OutOfStack { .. } => "Out of stack",
|
OutOfStack { instruction, wanted, limit } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
|
||||||
Internal(ref msg) => msg,
|
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
|
||||||
};
|
Internal(ref msg) => write!(f, "Internal error: {}", msg),
|
||||||
message.fmt(f)
|
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
|
||||||
|
Wasm(ref msg) => write!(f, "Internal error: {}", msg),
|
||||||
|
OutOfBounds => write!(f, "Out of bounds"),
|
||||||
|
Reverted => write!(f, "Reverted"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specialized version of Result over EVM errors.
|
/// A specialized version of Result over EVM errors.
|
||||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
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
|
/// Gas Left: either it is a known value, or it needs to be computed by processing
|
||||||
/// a return instruction.
|
/// a return instruction.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug)]
|
||||||
pub enum GasLeft<'a> {
|
pub enum GasLeft {
|
||||||
/// Known gas left
|
/// Known gas left
|
||||||
Known(U256),
|
Known(U256),
|
||||||
/// Return instruction must be processed.
|
/// Return or Revert instruction must be processed.
|
||||||
NeedsReturn(U256, &'a [u8]),
|
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.
|
/// Types that can be "finalized" using an EVM.
|
||||||
@@ -103,15 +177,19 @@ pub enum GasLeft<'a> {
|
|||||||
/// In practice, this is just used to define an inherent impl on
|
/// In practice, this is just used to define an inherent impl on
|
||||||
/// `Reult<GasLeft<'a>>`.
|
/// `Reult<GasLeft<'a>>`.
|
||||||
pub trait Finalize {
|
pub trait Finalize {
|
||||||
/// Consume the externalities, call return if necessary, and produce a final amount of gas left.
|
/// Consume the externalities, call return if necessary, and produce call result.
|
||||||
fn finalize<E: Ext>(self, ext: E) -> Result<U256>;
|
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Finalize for Result<GasLeft<'a>> {
|
impl Finalize for Result<GasLeft> {
|
||||||
fn finalize<E: Ext>(self, ext: E) -> Result<U256> {
|
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
|
||||||
match self {
|
match self {
|
||||||
Ok(GasLeft::Known(gas)) => Ok(gas),
|
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult { gas_left: gas_left, apply_state: true, return_data: ReturnData::empty() }),
|
||||||
Ok(GasLeft::NeedsReturn(gas, ret_code)) => ext.ret(&gas, ret_code),
|
Ok(GasLeft::NeedsReturn {gas_left, data, apply_state}) => ext.ret(&gas_left, &data, apply_state).map(|gas_left| FinalizationResult {
|
||||||
|
gas_left: gas_left,
|
||||||
|
apply_state: apply_state,
|
||||||
|
return_data: data,
|
||||||
|
}),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,11 +229,11 @@ impl CostType for U256 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn overflow_add(self, other: Self) -> (Self, bool) {
|
fn overflow_add(self, other: Self) -> (Self, bool) {
|
||||||
Uint::overflowing_add(self, other)
|
self.overflowing_add(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||||
Uint::overflowing_mul(self, other)
|
self.overflowing_mul(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
|
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
|
||||||
@@ -220,7 +298,7 @@ pub trait Evm {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use util::{U256, Uint};
|
use util::U256;
|
||||||
use super::CostType;
|
use super::CostType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
//! Interface for Evm externalities.
|
//! Interface for Evm externalities.
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use evm::{self, Schedule};
|
use call_type::CallType;
|
||||||
use env_info::*;
|
use env_info::EnvInfo;
|
||||||
use types::executed::CallType;
|
use schedule::Schedule;
|
||||||
|
use evm::{self, ReturnData};
|
||||||
|
|
||||||
/// Result of externalities create function.
|
/// Result of externalities create function.
|
||||||
pub enum ContractCreateResult {
|
pub enum ContractCreateResult {
|
||||||
@@ -28,47 +29,65 @@ pub enum ContractCreateResult {
|
|||||||
Created(Address, U256),
|
Created(Address, U256),
|
||||||
/// Returned when contract creation failed.
|
/// Returned when contract creation failed.
|
||||||
/// VM doesn't have to know the reason.
|
/// VM doesn't have to know the reason.
|
||||||
Failed
|
Failed,
|
||||||
|
/// Returned when contract creation failed.
|
||||||
|
/// VM doesn't have to know the reason.
|
||||||
|
FailedInStaticCall,
|
||||||
|
/// Reverted with REVERT.
|
||||||
|
Reverted(U256, ReturnData),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of externalities call function.
|
/// Result of externalities call function.
|
||||||
pub enum MessageCallResult {
|
pub enum MessageCallResult {
|
||||||
/// Returned when message call was successfull.
|
/// Returned when message call was successfull.
|
||||||
/// Contains gas left.
|
/// Contains gas left and output data.
|
||||||
Success(U256),
|
Success(U256, ReturnData),
|
||||||
/// Returned when message call failed.
|
/// Returned when message call failed.
|
||||||
/// VM doesn't have to know the reason.
|
/// VM doesn't have to know the reason.
|
||||||
Failed
|
Failed,
|
||||||
|
/// Returned when message call was reverted.
|
||||||
|
/// Contains gas left and output data.
|
||||||
|
Reverted(U256, ReturnData),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies how an address is calculated for a new contract.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum CreateContractAddress {
|
||||||
|
/// Address is calculated from nonce and sender. Pre EIP-86 (Metropolis)
|
||||||
|
FromSenderAndNonce,
|
||||||
|
/// Address is calculated from code hash. Default since EIP-86
|
||||||
|
FromCodeHash,
|
||||||
|
/// Address is calculated from code hash and sender. Used by CREATE_P2SH instruction.
|
||||||
|
FromSenderAndCodeHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Externalities interface for EVMs
|
/// Externalities interface for EVMs
|
||||||
// TODO: [rob] associated error type instead of `trie::Result`. Not all EVMs are trie powered.
|
|
||||||
pub trait Ext {
|
pub trait Ext {
|
||||||
/// Returns a value for given key.
|
/// Returns a value for given key.
|
||||||
fn storage_at(&self, key: &H256) -> trie::Result<H256>;
|
fn storage_at(&self, key: &H256) -> evm::Result<H256>;
|
||||||
|
|
||||||
/// Stores a value for given key.
|
/// Stores a value for given key.
|
||||||
fn set_storage(&mut self, key: H256, value: H256) -> trie::Result<()>;
|
fn set_storage(&mut self, key: H256, value: H256) -> evm::Result<()>;
|
||||||
|
|
||||||
/// Determine whether an account exists.
|
/// Determine whether an account exists.
|
||||||
fn exists(&self, address: &Address) -> trie::Result<bool>;
|
fn exists(&self, address: &Address) -> evm::Result<bool>;
|
||||||
|
|
||||||
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
|
/// Determine whether an account exists and is not null (zero balance/nonce, no code).
|
||||||
fn exists_and_not_null(&self, address: &Address) -> trie::Result<bool>;
|
fn exists_and_not_null(&self, address: &Address) -> evm::Result<bool>;
|
||||||
|
|
||||||
/// Balance of the origin account.
|
/// Balance of the origin account.
|
||||||
fn origin_balance(&self) -> trie::Result<U256>;
|
fn origin_balance(&self) -> evm::Result<U256>;
|
||||||
|
|
||||||
/// Returns address balance.
|
/// Returns address balance.
|
||||||
fn balance(&self, address: &Address) -> trie::Result<U256>;
|
fn balance(&self, address: &Address) -> evm::Result<U256>;
|
||||||
|
|
||||||
/// Returns the hash of one of the 256 most recent complete blocks.
|
/// Returns the hash of one of the 256 most recent complete blocks.
|
||||||
fn blockhash(&self, number: &U256) -> H256;
|
fn blockhash(&mut self, number: &U256) -> H256;
|
||||||
|
|
||||||
/// Creates new contract.
|
/// Creates new contract.
|
||||||
///
|
///
|
||||||
/// Returns gas_left and contract address if contract creation was succesfull.
|
/// Returns gas_left and contract address if contract creation was succesfull.
|
||||||
fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult;
|
fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult;
|
||||||
|
|
||||||
/// Message call.
|
/// Message call.
|
||||||
///
|
///
|
||||||
@@ -88,21 +107,21 @@ pub trait Ext {
|
|||||||
) -> MessageCallResult;
|
) -> MessageCallResult;
|
||||||
|
|
||||||
/// Returns code at given address
|
/// Returns code at given address
|
||||||
fn extcode(&self, address: &Address) -> trie::Result<Arc<Bytes>>;
|
fn extcode(&self, address: &Address) -> evm::Result<Arc<Bytes>>;
|
||||||
|
|
||||||
/// Returns code size at given address
|
/// Returns code size at given address
|
||||||
fn extcodesize(&self, address: &Address) -> trie::Result<usize>;
|
fn extcodesize(&self, address: &Address) -> evm::Result<usize>;
|
||||||
|
|
||||||
/// Creates log entry with given topics and data
|
/// Creates log entry with given topics and data
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]);
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> evm::Result<()>;
|
||||||
|
|
||||||
/// Should be called when transaction calls `RETURN` opcode.
|
/// Should be called when transaction calls `RETURN` opcode.
|
||||||
/// Returns gas_left if cost of returning the data is not too high.
|
/// Returns gas_left if cost of returning the data is not too high.
|
||||||
fn ret(self, gas: &U256, data: &[u8]) -> evm::Result<U256> where Self: Sized;
|
fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> evm::Result<U256>;
|
||||||
|
|
||||||
/// Should be called when contract commits suicide.
|
/// Should be called when contract commits suicide.
|
||||||
/// Address to which funds should be refunded.
|
/// Address to which funds should be refunded.
|
||||||
fn suicide(&mut self, refund_address: &Address) -> trie::Result<()> ;
|
fn suicide(&mut self, refund_address: &Address) -> evm::Result<()> ;
|
||||||
|
|
||||||
/// Returns schedule.
|
/// Returns schedule.
|
||||||
fn schedule(&self) -> &Schedule;
|
fn schedule(&self) -> &Schedule;
|
||||||
@@ -119,9 +138,15 @@ pub trait Ext {
|
|||||||
/// Increments sstore refunds count by 1.
|
/// Increments sstore refunds count by 1.
|
||||||
fn inc_sstore_clears(&mut self);
|
fn inc_sstore_clears(&mut self);
|
||||||
|
|
||||||
|
/// Decide if any more operations should be traced. Passthrough for the VM trace.
|
||||||
|
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { false }
|
||||||
|
|
||||||
/// Prepare to trace an operation. Passthrough for the VM trace.
|
/// Prepare to trace an operation. Passthrough for the VM trace.
|
||||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
|
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {}
|
||||||
|
|
||||||
/// Trace the finalised execution of a single instruction.
|
/// Trace the finalised execution of a single instruction.
|
||||||
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
|
||||||
|
|
||||||
|
/// Check if running in static context.
|
||||||
|
fn is_static(&self) -> bool;
|
||||||
}
|
}
|
||||||
@@ -16,70 +16,11 @@
|
|||||||
|
|
||||||
//! Evm factory.
|
//! Evm factory.
|
||||||
//!
|
//!
|
||||||
//! TODO: consider spliting it into two separate files.
|
|
||||||
use std::fmt;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use evm::Evm;
|
use evm::Evm;
|
||||||
use util::{U256, Uint};
|
use util::U256;
|
||||||
use super::interpreter::SharedCache;
|
use super::interpreter::SharedCache;
|
||||||
|
use super::vmtype::VMType;
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
/// Type of EVM to use.
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evm factory. Creates appropriate Evm.
|
/// Evm factory. Creates appropriate Evm.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -23,6 +23,13 @@ pub fn is_push(i: Instruction) -> bool {
|
|||||||
i >= PUSH1 && i <= PUSH32
|
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
|
/// Returns number of bytes to read for `PUSHN` instruction
|
||||||
/// PUSH1 -> 1
|
/// PUSH1 -> 1
|
||||||
pub fn get_push_bytes(i: Instruction) -> usize {
|
pub fn get_push_bytes(i: Instruction) -> usize {
|
||||||
@@ -30,6 +37,15 @@ pub fn get_push_bytes(i: Instruction) -> usize {
|
|||||||
(i - PUSH1 + 1) as usize
|
(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]
|
#[test]
|
||||||
fn test_get_push_bytes() {
|
fn test_get_push_bytes() {
|
||||||
assert_eq!(get_push_bytes(PUSH1), 1);
|
assert_eq!(get_push_bytes(PUSH1), 1);
|
||||||
@@ -122,162 +138,170 @@ pub fn get_tier_idx (tier: GasPriceTier) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// EVM instruction information.
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
pub struct InstructionInfo {
|
pub struct InstructionInfo {
|
||||||
|
/// Mnemonic name.
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub additional: usize,
|
/// Number of stack arguments.
|
||||||
pub args: usize,
|
pub args: usize,
|
||||||
|
/// Number of returned stack items.
|
||||||
pub ret: usize,
|
pub ret: usize,
|
||||||
pub side_effects: bool,
|
/// Gas price tier.
|
||||||
pub tier: GasPriceTier
|
pub tier: GasPriceTier
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionInfo {
|
impl InstructionInfo {
|
||||||
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> Self {
|
/// Create new instruction info.
|
||||||
|
pub fn new(name: &'static str, args: usize, ret: usize, tier: GasPriceTier) -> Self {
|
||||||
InstructionInfo {
|
InstructionInfo {
|
||||||
name: name,
|
name: name,
|
||||||
additional: additional,
|
|
||||||
args: args,
|
args: args,
|
||||||
ret: ret,
|
ret: ret,
|
||||||
side_effects: side_effects,
|
|
||||||
tier: tier
|
tier: tier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
/// Static instruction table.
|
||||||
pub static ref INSTRUCTIONS: [InstructionInfo; 0x100] = {
|
pub static ref INSTRUCTIONS: [InstructionInfo; 0x100] = {
|
||||||
let mut arr = [InstructionInfo::default(); 0x100];
|
let mut arr = [InstructionInfo::default(); 0x100];
|
||||||
arr[STOP as usize] = InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero);
|
arr[STOP as usize] = InstructionInfo::new("STOP", 0, 0, GasPriceTier::Zero);
|
||||||
arr[ADD as usize] = InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[ADD as usize] = InstructionInfo::new("ADD", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[SUB as usize] = InstructionInfo::new("SUB", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[SUB as usize] = InstructionInfo::new("SUB", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[MUL as usize] = InstructionInfo::new("MUL", 0, 2, 1, false, GasPriceTier::Low);
|
arr[MUL as usize] = InstructionInfo::new("MUL", 2, 1, GasPriceTier::Low);
|
||||||
arr[DIV as usize] = InstructionInfo::new("DIV", 0, 2, 1, false, GasPriceTier::Low);
|
arr[DIV as usize] = InstructionInfo::new("DIV", 2, 1, GasPriceTier::Low);
|
||||||
arr[SDIV as usize] = InstructionInfo::new("SDIV", 0, 2, 1, false, GasPriceTier::Low);
|
arr[SDIV as usize] = InstructionInfo::new("SDIV", 2, 1, GasPriceTier::Low);
|
||||||
arr[MOD as usize] = InstructionInfo::new("MOD", 0, 2, 1, false, GasPriceTier::Low);
|
arr[MOD as usize] = InstructionInfo::new("MOD", 2, 1, GasPriceTier::Low);
|
||||||
arr[SMOD as usize] = InstructionInfo::new("SMOD", 0, 2, 1, false, GasPriceTier::Low);
|
arr[SMOD as usize] = InstructionInfo::new("SMOD", 2, 1, GasPriceTier::Low);
|
||||||
arr[EXP as usize] = InstructionInfo::new("EXP", 0, 2, 1, false, GasPriceTier::Special);
|
arr[EXP as usize] = InstructionInfo::new("EXP", 2, 1, GasPriceTier::Special);
|
||||||
arr[NOT as usize] = InstructionInfo::new("NOT", 0, 1, 1, false, GasPriceTier::VeryLow);
|
arr[NOT as usize] = InstructionInfo::new("NOT", 1, 1, GasPriceTier::VeryLow);
|
||||||
arr[LT as usize] = InstructionInfo::new("LT", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[LT as usize] = InstructionInfo::new("LT", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[GT as usize] = InstructionInfo::new("GT", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[GT as usize] = InstructionInfo::new("GT", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[SLT as usize] = InstructionInfo::new("SLT", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[SLT as usize] = InstructionInfo::new("SLT", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[SGT as usize] = InstructionInfo::new("SGT", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[SGT as usize] = InstructionInfo::new("SGT", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[EQ as usize] = InstructionInfo::new("EQ", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[EQ as usize] = InstructionInfo::new("EQ", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[ISZERO as usize] = InstructionInfo::new("ISZERO", 0, 1, 1, false, GasPriceTier::VeryLow);
|
arr[ISZERO as usize] = InstructionInfo::new("ISZERO", 1, 1, GasPriceTier::VeryLow);
|
||||||
arr[AND as usize] = InstructionInfo::new("AND", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[AND as usize] = InstructionInfo::new("AND", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[OR as usize] = InstructionInfo::new("OR", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[OR as usize] = InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[XOR as usize] = InstructionInfo::new("XOR", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[XOR as usize] = InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[BYTE as usize] = InstructionInfo::new("BYTE", 0, 2, 1, false, GasPriceTier::VeryLow);
|
arr[BYTE as usize] = InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow);
|
||||||
arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid);
|
arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid);
|
||||||
arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 0, 3, 1, false, GasPriceTier::Mid);
|
arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid);
|
||||||
arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low);
|
arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low);
|
||||||
arr[SHA3 as usize] = InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special);
|
arr[RETURNDATASIZE as usize] = InstructionInfo::new("RETURNDATASIZE", 0, 1, GasPriceTier::Base);
|
||||||
arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base);
|
arr[RETURNDATACOPY as usize] = InstructionInfo::new("RETURNDATACOPY", 3, 0, GasPriceTier::VeryLow);
|
||||||
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Special);
|
arr[SHA3 as usize] = InstructionInfo::new("SHA3", 2, 1, GasPriceTier::Special);
|
||||||
arr[ORIGIN as usize] = InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base);
|
arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 1, GasPriceTier::Base);
|
||||||
arr[CALLER as usize] = InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base);
|
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 1, 1, GasPriceTier::Special);
|
||||||
arr[CALLVALUE as usize] = InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base);
|
arr[ORIGIN as usize] = InstructionInfo::new("ORIGIN", 0, 1, GasPriceTier::Base);
|
||||||
arr[CALLDATALOAD as usize] = InstructionInfo::new("CALLDATALOAD", 0, 1, 1, false, GasPriceTier::VeryLow);
|
arr[CALLER as usize] = InstructionInfo::new("CALLER", 0, 1, GasPriceTier::Base);
|
||||||
arr[CALLDATASIZE as usize] = InstructionInfo::new("CALLDATASIZE", 0, 0, 1, false, GasPriceTier::Base);
|
arr[CALLVALUE as usize] = InstructionInfo::new("CALLVALUE", 0, 1, GasPriceTier::Base);
|
||||||
arr[CALLDATACOPY as usize] = InstructionInfo::new("CALLDATACOPY", 0, 3, 0, true, GasPriceTier::VeryLow);
|
arr[CALLDATALOAD as usize] = InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow);
|
||||||
arr[CODESIZE as usize] = InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base);
|
arr[CALLDATASIZE as usize] = InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base);
|
||||||
arr[CODECOPY as usize] = InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow);
|
arr[CALLDATACOPY as usize] = InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow);
|
||||||
arr[GASPRICE as usize] = InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base);
|
arr[CODESIZE as usize] = InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base);
|
||||||
arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Special);
|
arr[CODECOPY as usize] = InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow);
|
||||||
arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Special);
|
arr[GASPRICE as usize] = InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base);
|
||||||
arr[BLOCKHASH as usize] = InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext);
|
arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 1, 1, GasPriceTier::Special);
|
||||||
arr[COINBASE as usize] = InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base);
|
arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 4, 0, GasPriceTier::Special);
|
||||||
arr[TIMESTAMP as usize] = InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base);
|
arr[BLOCKHASH as usize] = InstructionInfo::new("BLOCKHASH", 1, 1, GasPriceTier::Ext);
|
||||||
arr[NUMBER as usize] = InstructionInfo::new("NUMBER", 0, 0, 1, false, GasPriceTier::Base);
|
arr[COINBASE as usize] = InstructionInfo::new("COINBASE", 0, 1, GasPriceTier::Base);
|
||||||
arr[DIFFICULTY as usize] = InstructionInfo::new("DIFFICULTY", 0, 0, 1, false, GasPriceTier::Base);
|
arr[TIMESTAMP as usize] = InstructionInfo::new("TIMESTAMP", 0, 1, GasPriceTier::Base);
|
||||||
arr[GASLIMIT as usize] = InstructionInfo::new("GASLIMIT", 0, 0, 1, false, GasPriceTier::Base);
|
arr[NUMBER as usize] = InstructionInfo::new("NUMBER", 0, 1, GasPriceTier::Base);
|
||||||
arr[POP as usize] = InstructionInfo::new("POP", 0, 1, 0, false, GasPriceTier::Base);
|
arr[DIFFICULTY as usize] = InstructionInfo::new("DIFFICULTY", 0, 1, GasPriceTier::Base);
|
||||||
arr[MLOAD as usize] = InstructionInfo::new("MLOAD", 0, 1, 1, false, GasPriceTier::VeryLow);
|
arr[GASLIMIT as usize] = InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base);
|
||||||
arr[MSTORE as usize] = InstructionInfo::new("MSTORE", 0, 2, 0, true, GasPriceTier::VeryLow);
|
arr[POP as usize] = InstructionInfo::new("POP", 1, 0, GasPriceTier::Base);
|
||||||
arr[MSTORE8 as usize] = InstructionInfo::new("MSTORE8", 0, 2, 0, true, GasPriceTier::VeryLow);
|
arr[MLOAD as usize] = InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow);
|
||||||
arr[SLOAD as usize] = InstructionInfo::new("SLOAD", 0, 1, 1, false, GasPriceTier::Special);
|
arr[MSTORE as usize] = InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow);
|
||||||
arr[SSTORE as usize] = InstructionInfo::new("SSTORE", 0, 2, 0, true, GasPriceTier::Special);
|
arr[MSTORE8 as usize] = InstructionInfo::new("MSTORE8", 2, 0, GasPriceTier::VeryLow);
|
||||||
arr[JUMP as usize] = InstructionInfo::new("JUMP", 0, 1, 0, true, GasPriceTier::Mid);
|
arr[SLOAD as usize] = InstructionInfo::new("SLOAD", 1, 1, GasPriceTier::Special);
|
||||||
arr[JUMPI as usize] = InstructionInfo::new("JUMPI", 0, 2, 0, true, GasPriceTier::High);
|
arr[SSTORE as usize] = InstructionInfo::new("SSTORE", 2, 0, GasPriceTier::Special);
|
||||||
arr[PC as usize] = InstructionInfo::new("PC", 0, 0, 1, false, GasPriceTier::Base);
|
arr[JUMP as usize] = InstructionInfo::new("JUMP", 1, 0, GasPriceTier::Mid);
|
||||||
arr[MSIZE as usize] = InstructionInfo::new("MSIZE", 0, 0, 1, false, GasPriceTier::Base);
|
arr[JUMPI as usize] = InstructionInfo::new("JUMPI", 2, 0, GasPriceTier::High);
|
||||||
arr[GAS as usize] = InstructionInfo::new("GAS", 0, 0, 1, false, GasPriceTier::Base);
|
arr[PC as usize] = InstructionInfo::new("PC", 0, 1, GasPriceTier::Base);
|
||||||
arr[JUMPDEST as usize] = InstructionInfo::new("JUMPDEST", 0, 0, 0, true, GasPriceTier::Special);
|
arr[MSIZE as usize] = InstructionInfo::new("MSIZE", 0, 1, GasPriceTier::Base);
|
||||||
arr[PUSH1 as usize] = InstructionInfo::new("PUSH1", 1, 0, 1, false, GasPriceTier::VeryLow);
|
arr[GAS as usize] = InstructionInfo::new("GAS", 0, 1, GasPriceTier::Base);
|
||||||
arr[PUSH2 as usize] = InstructionInfo::new("PUSH2", 2, 0, 1, false, GasPriceTier::VeryLow);
|
arr[JUMPDEST as usize] = InstructionInfo::new("JUMPDEST", 0, 0, GasPriceTier::Special);
|
||||||
arr[PUSH3 as usize] = InstructionInfo::new("PUSH3", 3, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH1 as usize] = InstructionInfo::new("PUSH1", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH4 as usize] = InstructionInfo::new("PUSH4", 4, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH2 as usize] = InstructionInfo::new("PUSH2", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH5 as usize] = InstructionInfo::new("PUSH5", 5, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH3 as usize] = InstructionInfo::new("PUSH3", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH6 as usize] = InstructionInfo::new("PUSH6", 6, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH4 as usize] = InstructionInfo::new("PUSH4", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH7 as usize] = InstructionInfo::new("PUSH7", 7, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH5 as usize] = InstructionInfo::new("PUSH5", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH8 as usize] = InstructionInfo::new("PUSH8", 8, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH6 as usize] = InstructionInfo::new("PUSH6", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH9 as usize] = InstructionInfo::new("PUSH9", 9, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH7 as usize] = InstructionInfo::new("PUSH7", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH10 as usize] = InstructionInfo::new("PUSH10", 10, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH8 as usize] = InstructionInfo::new("PUSH8", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH11 as usize] = InstructionInfo::new("PUSH11", 11, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH9 as usize] = InstructionInfo::new("PUSH9", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH12 as usize] = InstructionInfo::new("PUSH12", 12, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH10 as usize] = InstructionInfo::new("PUSH10", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH13 as usize] = InstructionInfo::new("PUSH13", 13, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH11 as usize] = InstructionInfo::new("PUSH11", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH14 as usize] = InstructionInfo::new("PUSH14", 14, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH12 as usize] = InstructionInfo::new("PUSH12", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH15 as usize] = InstructionInfo::new("PUSH15", 15, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH13 as usize] = InstructionInfo::new("PUSH13", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH16 as usize] = InstructionInfo::new("PUSH16", 16, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH14 as usize] = InstructionInfo::new("PUSH14", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH17 as usize] = InstructionInfo::new("PUSH17", 17, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH15 as usize] = InstructionInfo::new("PUSH15", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH18 as usize] = InstructionInfo::new("PUSH18", 18, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH16 as usize] = InstructionInfo::new("PUSH16", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH19 as usize] = InstructionInfo::new("PUSH19", 19, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH17 as usize] = InstructionInfo::new("PUSH17", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH20 as usize] = InstructionInfo::new("PUSH20", 20, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH18 as usize] = InstructionInfo::new("PUSH18", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH21 as usize] = InstructionInfo::new("PUSH21", 21, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH19 as usize] = InstructionInfo::new("PUSH19", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH22 as usize] = InstructionInfo::new("PUSH22", 22, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH20 as usize] = InstructionInfo::new("PUSH20", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH23 as usize] = InstructionInfo::new("PUSH23", 23, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH21 as usize] = InstructionInfo::new("PUSH21", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH24 as usize] = InstructionInfo::new("PUSH24", 24, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH22 as usize] = InstructionInfo::new("PUSH22", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH25 as usize] = InstructionInfo::new("PUSH25", 25, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH23 as usize] = InstructionInfo::new("PUSH23", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH26 as usize] = InstructionInfo::new("PUSH26", 26, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH24 as usize] = InstructionInfo::new("PUSH24", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH27 as usize] = InstructionInfo::new("PUSH27", 27, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH25 as usize] = InstructionInfo::new("PUSH25", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH28 as usize] = InstructionInfo::new("PUSH28", 28, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH26 as usize] = InstructionInfo::new("PUSH26", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH29 as usize] = InstructionInfo::new("PUSH29", 29, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH27 as usize] = InstructionInfo::new("PUSH27", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH30 as usize] = InstructionInfo::new("PUSH30", 30, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH28 as usize] = InstructionInfo::new("PUSH28", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH31 as usize] = InstructionInfo::new("PUSH31", 31, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH29 as usize] = InstructionInfo::new("PUSH29", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[PUSH32 as usize] = InstructionInfo::new("PUSH32", 32, 0, 1, false, GasPriceTier::VeryLow);
|
arr[PUSH30 as usize] = InstructionInfo::new("PUSH30", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[DUP1 as usize] = InstructionInfo::new("DUP1", 0, 1, 2, false, GasPriceTier::VeryLow);
|
arr[PUSH31 as usize] = InstructionInfo::new("PUSH31", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[DUP2 as usize] = InstructionInfo::new("DUP2", 0, 2, 3, false, GasPriceTier::VeryLow);
|
arr[PUSH32 as usize] = InstructionInfo::new("PUSH32", 0, 1, GasPriceTier::VeryLow);
|
||||||
arr[DUP3 as usize] = InstructionInfo::new("DUP3", 0, 3, 4, false, GasPriceTier::VeryLow);
|
arr[DUP1 as usize] = InstructionInfo::new("DUP1", 1, 2, GasPriceTier::VeryLow);
|
||||||
arr[DUP4 as usize] = InstructionInfo::new("DUP4", 0, 4, 5, false, GasPriceTier::VeryLow);
|
arr[DUP2 as usize] = InstructionInfo::new("DUP2", 2, 3, GasPriceTier::VeryLow);
|
||||||
arr[DUP5 as usize] = InstructionInfo::new("DUP5", 0, 5, 6, false, GasPriceTier::VeryLow);
|
arr[DUP3 as usize] = InstructionInfo::new("DUP3", 3, 4, GasPriceTier::VeryLow);
|
||||||
arr[DUP6 as usize] = InstructionInfo::new("DUP6", 0, 6, 7, false, GasPriceTier::VeryLow);
|
arr[DUP4 as usize] = InstructionInfo::new("DUP4", 4, 5, GasPriceTier::VeryLow);
|
||||||
arr[DUP7 as usize] = InstructionInfo::new("DUP7", 0, 7, 8, false, GasPriceTier::VeryLow);
|
arr[DUP5 as usize] = InstructionInfo::new("DUP5", 5, 6, GasPriceTier::VeryLow);
|
||||||
arr[DUP8 as usize] = InstructionInfo::new("DUP8", 0, 8, 9, false, GasPriceTier::VeryLow);
|
arr[DUP6 as usize] = InstructionInfo::new("DUP6", 6, 7, GasPriceTier::VeryLow);
|
||||||
arr[DUP9 as usize] = InstructionInfo::new("DUP9", 0, 9, 10, false, GasPriceTier::VeryLow);
|
arr[DUP7 as usize] = InstructionInfo::new("DUP7", 7, 8, GasPriceTier::VeryLow);
|
||||||
arr[DUP10 as usize] = InstructionInfo::new("DUP10", 0, 10, 11, false, GasPriceTier::VeryLow);
|
arr[DUP8 as usize] = InstructionInfo::new("DUP8", 8, 9, GasPriceTier::VeryLow);
|
||||||
arr[DUP11 as usize] = InstructionInfo::new("DUP11", 0, 11, 12, false, GasPriceTier::VeryLow);
|
arr[DUP9 as usize] = InstructionInfo::new("DUP9", 9, 10, GasPriceTier::VeryLow);
|
||||||
arr[DUP12 as usize] = InstructionInfo::new("DUP12", 0, 12, 13, false, GasPriceTier::VeryLow);
|
arr[DUP10 as usize] = InstructionInfo::new("DUP10", 10, 11, GasPriceTier::VeryLow);
|
||||||
arr[DUP13 as usize] = InstructionInfo::new("DUP13", 0, 13, 14, false, GasPriceTier::VeryLow);
|
arr[DUP11 as usize] = InstructionInfo::new("DUP11", 11, 12, GasPriceTier::VeryLow);
|
||||||
arr[DUP14 as usize] = InstructionInfo::new("DUP14", 0, 14, 15, false, GasPriceTier::VeryLow);
|
arr[DUP12 as usize] = InstructionInfo::new("DUP12", 12, 13, GasPriceTier::VeryLow);
|
||||||
arr[DUP15 as usize] = InstructionInfo::new("DUP15", 0, 15, 16, false, GasPriceTier::VeryLow);
|
arr[DUP13 as usize] = InstructionInfo::new("DUP13", 13, 14, GasPriceTier::VeryLow);
|
||||||
arr[DUP16 as usize] = InstructionInfo::new("DUP16", 0, 16, 17, false, GasPriceTier::VeryLow);
|
arr[DUP14 as usize] = InstructionInfo::new("DUP14", 14, 15, GasPriceTier::VeryLow);
|
||||||
arr[SWAP1 as usize] = InstructionInfo::new("SWAP1", 0, 2, 2, false, GasPriceTier::VeryLow);
|
arr[DUP15 as usize] = InstructionInfo::new("DUP15", 15, 16, GasPriceTier::VeryLow);
|
||||||
arr[SWAP2 as usize] = InstructionInfo::new("SWAP2", 0, 3, 3, false, GasPriceTier::VeryLow);
|
arr[DUP16 as usize] = InstructionInfo::new("DUP16", 16, 17, GasPriceTier::VeryLow);
|
||||||
arr[SWAP3 as usize] = InstructionInfo::new("SWAP3", 0, 4, 4, false, GasPriceTier::VeryLow);
|
arr[SWAP1 as usize] = InstructionInfo::new("SWAP1", 2, 2, GasPriceTier::VeryLow);
|
||||||
arr[SWAP4 as usize] = InstructionInfo::new("SWAP4", 0, 5, 5, false, GasPriceTier::VeryLow);
|
arr[SWAP2 as usize] = InstructionInfo::new("SWAP2", 3, 3, GasPriceTier::VeryLow);
|
||||||
arr[SWAP5 as usize] = InstructionInfo::new("SWAP5", 0, 6, 6, false, GasPriceTier::VeryLow);
|
arr[SWAP3 as usize] = InstructionInfo::new("SWAP3", 4, 4, GasPriceTier::VeryLow);
|
||||||
arr[SWAP6 as usize] = InstructionInfo::new("SWAP6", 0, 7, 7, false, GasPriceTier::VeryLow);
|
arr[SWAP4 as usize] = InstructionInfo::new("SWAP4", 5, 5, GasPriceTier::VeryLow);
|
||||||
arr[SWAP7 as usize] = InstructionInfo::new("SWAP7", 0, 8, 8, false, GasPriceTier::VeryLow);
|
arr[SWAP5 as usize] = InstructionInfo::new("SWAP5", 6, 6, GasPriceTier::VeryLow);
|
||||||
arr[SWAP8 as usize] = InstructionInfo::new("SWAP8", 0, 9, 9, false, GasPriceTier::VeryLow);
|
arr[SWAP6 as usize] = InstructionInfo::new("SWAP6", 7, 7, GasPriceTier::VeryLow);
|
||||||
arr[SWAP9 as usize] = InstructionInfo::new("SWAP9", 0, 10, 10, false, GasPriceTier::VeryLow);
|
arr[SWAP7 as usize] = InstructionInfo::new("SWAP7", 8, 8, GasPriceTier::VeryLow);
|
||||||
arr[SWAP10 as usize] = InstructionInfo::new("SWAP10", 0, 11, 11, false, GasPriceTier::VeryLow);
|
arr[SWAP8 as usize] = InstructionInfo::new("SWAP8", 9, 9, GasPriceTier::VeryLow);
|
||||||
arr[SWAP11 as usize] = InstructionInfo::new("SWAP11", 0, 12, 12, false, GasPriceTier::VeryLow);
|
arr[SWAP9 as usize] = InstructionInfo::new("SWAP9", 10, 10, GasPriceTier::VeryLow);
|
||||||
arr[SWAP12 as usize] = InstructionInfo::new("SWAP12", 0, 13, 13, false, GasPriceTier::VeryLow);
|
arr[SWAP10 as usize] = InstructionInfo::new("SWAP10", 11, 11, GasPriceTier::VeryLow);
|
||||||
arr[SWAP13 as usize] = InstructionInfo::new("SWAP13", 0, 14, 14, false, GasPriceTier::VeryLow);
|
arr[SWAP11 as usize] = InstructionInfo::new("SWAP11", 12, 12, GasPriceTier::VeryLow);
|
||||||
arr[SWAP14 as usize] = InstructionInfo::new("SWAP14", 0, 15, 15, false, GasPriceTier::VeryLow);
|
arr[SWAP12 as usize] = InstructionInfo::new("SWAP12", 13, 13, GasPriceTier::VeryLow);
|
||||||
arr[SWAP15 as usize] = InstructionInfo::new("SWAP15", 0, 16, 16, false, GasPriceTier::VeryLow);
|
arr[SWAP13 as usize] = InstructionInfo::new("SWAP13", 14, 14, GasPriceTier::VeryLow);
|
||||||
arr[SWAP16 as usize] = InstructionInfo::new("SWAP16", 0, 17, 17, false, GasPriceTier::VeryLow);
|
arr[SWAP14 as usize] = InstructionInfo::new("SWAP14", 15, 15, GasPriceTier::VeryLow);
|
||||||
arr[LOG0 as usize] = InstructionInfo::new("LOG0", 0, 2, 0, true, GasPriceTier::Special);
|
arr[SWAP15 as usize] = InstructionInfo::new("SWAP15", 16, 16, GasPriceTier::VeryLow);
|
||||||
arr[LOG1 as usize] = InstructionInfo::new("LOG1", 0, 3, 0, true, GasPriceTier::Special);
|
arr[SWAP16 as usize] = InstructionInfo::new("SWAP16", 17, 17, GasPriceTier::VeryLow);
|
||||||
arr[LOG2 as usize] = InstructionInfo::new("LOG2", 0, 4, 0, true, GasPriceTier::Special);
|
arr[LOG0 as usize] = InstructionInfo::new("LOG0", 2, 0, GasPriceTier::Special);
|
||||||
arr[LOG3 as usize] = InstructionInfo::new("LOG3", 0, 5, 0, true, GasPriceTier::Special);
|
arr[LOG1 as usize] = InstructionInfo::new("LOG1", 3, 0, GasPriceTier::Special);
|
||||||
arr[LOG4 as usize] = InstructionInfo::new("LOG4", 0, 6, 0, true, GasPriceTier::Special);
|
arr[LOG2 as usize] = InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special);
|
||||||
arr[CREATE as usize] = InstructionInfo::new("CREATE", 0, 3, 1, true, GasPriceTier::Special);
|
arr[LOG3 as usize] = InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special);
|
||||||
arr[CALL as usize] = InstructionInfo::new("CALL", 0, 7, 1, true, GasPriceTier::Special);
|
arr[LOG4 as usize] = InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special);
|
||||||
arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special);
|
arr[CREATE as usize] = InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special);
|
||||||
arr[RETURN as usize] = InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero);
|
arr[CALL as usize] = InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special);
|
||||||
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special);
|
arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special);
|
||||||
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, 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
|
arr
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -360,6 +384,10 @@ pub const GASPRICE: Instruction = 0x3a;
|
|||||||
pub const EXTCODESIZE: Instruction = 0x3b;
|
pub const EXTCODESIZE: Instruction = 0x3b;
|
||||||
/// copy external code (from another contract)
|
/// copy external code (from another contract)
|
||||||
pub const EXTCODECOPY: Instruction = 0x3c;
|
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
|
/// get hash of most recent complete block
|
||||||
pub const BLOCKHASH: Instruction = 0x40;
|
pub const BLOCKHASH: Instruction = 0x40;
|
||||||
@@ -553,6 +581,12 @@ pub const CALLCODE: Instruction = 0xf2;
|
|||||||
pub const RETURN: Instruction = 0xf3;
|
pub const RETURN: Instruction = 0xf3;
|
||||||
/// like CALLCODE but keeps caller's value and sender
|
/// like CALLCODE but keeps caller's value and sender
|
||||||
pub const DELEGATECALL: Instruction = 0xf4;
|
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
|
/// halt execution and register account for later deletion
|
||||||
pub const SUICIDE: Instruction = 0xff;
|
pub const SUICIDE: Instruction = 0xff;
|
||||||
|
|
||||||
@@ -16,10 +16,11 @@
|
|||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
use super::u256_to_address;
|
use super::u256_to_address;
|
||||||
use evm::{self, CostType};
|
|
||||||
use evm::instructions::{self, Instruction, InstructionInfo};
|
use {evm, ext};
|
||||||
use evm::interpreter::stack::Stack;
|
use instructions::{self, Instruction, InstructionInfo};
|
||||||
use evm::schedule::Schedule;
|
use interpreter::stack::Stack;
|
||||||
|
use schedule::Schedule;
|
||||||
|
|
||||||
macro_rules! overflowing {
|
macro_rules! overflowing {
|
||||||
($x: expr) => {{
|
($x: expr) => {{
|
||||||
@@ -30,26 +31,26 @@ macro_rules! overflowing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
#[cfg_attr(feature="dev", allow(enum_variant_names))]
|
||||||
enum Request<Cost: CostType> {
|
enum Request<Cost: ::evm::CostType> {
|
||||||
Gas(Cost),
|
Gas(Cost),
|
||||||
GasMem(Cost, Cost),
|
GasMem(Cost, Cost),
|
||||||
GasMemProvide(Cost, Cost, Option<U256>),
|
GasMemProvide(Cost, Cost, Option<U256>),
|
||||||
GasMemCopy(Cost, Cost, Cost)
|
GasMemCopy(Cost, Cost, Cost)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstructionRequirements<Cost: CostType> {
|
pub struct InstructionRequirements<Cost> {
|
||||||
pub gas_cost: Cost,
|
pub gas_cost: Cost,
|
||||||
pub provide_gas: Option<Cost>,
|
pub provide_gas: Option<Cost>,
|
||||||
pub memory_total_gas: Cost,
|
pub memory_total_gas: Cost,
|
||||||
pub memory_required_size: usize,
|
pub memory_required_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gasometer<Gas: CostType> {
|
pub struct Gasometer<Gas> {
|
||||||
pub current_gas: Gas,
|
pub current_gas: Gas,
|
||||||
pub current_mem_gas: Gas,
|
pub current_mem_gas: Gas,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Gas: CostType> Gasometer<Gas> {
|
impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||||
|
|
||||||
pub fn new(current_gas: Gas) -> Self {
|
pub fn new(current_gas: Gas) -> Self {
|
||||||
Gasometer {
|
Gasometer {
|
||||||
@@ -106,7 +107,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
/// it will be the amount of gas that the current context provides to the child context.
|
/// it will be the amount of gas that the current context provides to the child context.
|
||||||
pub fn requirements(
|
pub fn requirements(
|
||||||
&mut self,
|
&mut self,
|
||||||
ext: &evm::Ext,
|
ext: &ext::Ext,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
info: &InstructionInfo,
|
info: &InstructionInfo,
|
||||||
stack: &Stack<U256>,
|
stack: &Stack<U256>,
|
||||||
@@ -164,7 +165,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
instructions::MSTORE8 => {
|
instructions::MSTORE8 => {
|
||||||
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
|
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
|
||||||
},
|
},
|
||||||
instructions::RETURN => {
|
instructions::RETURN | instructions::REVERT => {
|
||||||
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||||
},
|
},
|
||||||
instructions::SHA3 => {
|
instructions::SHA3 => {
|
||||||
@@ -173,7 +174,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
|
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))?)
|
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
|
||||||
},
|
},
|
||||||
instructions::CALLDATACOPY | instructions::CODECOPY => {
|
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => {
|
||||||
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?)
|
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?)
|
||||||
},
|
},
|
||||||
instructions::EXTCODECOPY => {
|
instructions::EXTCODECOPY => {
|
||||||
@@ -213,7 +214,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
|
|
||||||
Request::GasMemProvide(gas, mem, Some(requested))
|
Request::GasMemProvide(gas, mem, Some(requested))
|
||||||
},
|
},
|
||||||
instructions::DELEGATECALL => {
|
instructions::DELEGATECALL | instructions::STATICCALL => {
|
||||||
let gas = Gas::from(schedule.call_gas);
|
let gas = Gas::from(schedule.call_gas);
|
||||||
let mem = cmp::max(
|
let mem = cmp::max(
|
||||||
mem_needed(stack.peek(4), stack.peek(5))?,
|
mem_needed(stack.peek(4), stack.peek(5))?,
|
||||||
@@ -223,7 +224,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
|
|
||||||
Request::GasMemProvide(gas, mem, Some(requested))
|
Request::GasMemProvide(gas, mem, Some(requested))
|
||||||
},
|
},
|
||||||
instructions::CREATE => {
|
instructions::CREATE | instructions::CREATE2 => {
|
||||||
let gas = Gas::from(schedule.create_gas);
|
let gas = Gas::from(schedule.create_gas);
|
||||||
let mem = mem_needed(stack.peek(1), stack.peek(2))?;
|
let mem = mem_needed(stack.peek(1), stack.peek(2))?;
|
||||||
|
|
||||||
@@ -235,6 +236,9 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
|
||||||
Request::Gas(gas)
|
Request::Gas(gas)
|
||||||
},
|
},
|
||||||
|
instructions::BLOCKHASH => {
|
||||||
|
Request::Gas(Gas::from(schedule.blockhash_gas))
|
||||||
|
},
|
||||||
_ => Request::Gas(default_gas),
|
_ => Request::Gas(default_gas),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -287,7 +291,7 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, Gas, usize)> {
|
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 gas_for_mem = |mem_size: Gas| {
|
||||||
let s = mem_size >> 5;
|
let s = mem_size >> 5;
|
||||||
// s * memory_gas + s * s / quad_coeff_div
|
// s * memory_gas + s * s / quad_coeff_div
|
||||||
@@ -315,12 +319,12 @@ impl<Gas: CostType> Gasometer<Gas> {
|
|||||||
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mem_needed_const<Gas: CostType>(mem: &U256, add: usize) -> evm::Result<Gas> {
|
fn mem_needed_const<Gas: evm::CostType>(mem: &U256, add: usize) -> evm::Result<Gas> {
|
||||||
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
|
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mem_needed<Gas: CostType>(offset: &U256, size: &U256) -> evm::Result<Gas> {
|
fn mem_needed<Gas: evm::CostType>(offset: &U256, size: &U256) -> evm::Result<Gas> {
|
||||||
if size.is_zero() {
|
if size.is_zero() {
|
||||||
return Ok(Gas::from(0));
|
return Ok(Gas::from(0));
|
||||||
}
|
}
|
||||||
@@ -329,7 +333,7 @@ fn mem_needed<Gas: CostType>(offset: &U256, size: &U256) -> evm::Result<Gas> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add_gas_usize<Gas: CostType>(value: Gas, num: usize) -> (Gas, bool) {
|
fn add_gas_usize<Gas: evm::CostType>(value: Gas, num: usize) -> (Gas, bool) {
|
||||||
value.overflow_add(Gas::from(num))
|
value.overflow_add(Gas::from(num))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +341,7 @@ fn add_gas_usize<Gas: CostType>(value: Gas, num: usize) -> (Gas, bool) {
|
|||||||
fn test_mem_gas_cost() {
|
fn test_mem_gas_cost() {
|
||||||
// given
|
// given
|
||||||
let gasometer = Gasometer::<U256>::new(U256::zero());
|
let gasometer = Gasometer::<U256>::new(U256::zero());
|
||||||
let schedule = evm::Schedule::default();
|
let schedule = Schedule::default();
|
||||||
let current_mem_size = 5;
|
let current_mem_size = 5;
|
||||||
let mem_size = !U256::zero();
|
let mem_size = !U256::zero();
|
||||||
|
|
||||||
@@ -354,7 +358,7 @@ fn test_mem_gas_cost() {
|
|||||||
fn test_calculate_mem_cost() {
|
fn test_calculate_mem_cost() {
|
||||||
// given
|
// given
|
||||||
let gasometer = Gasometer::<usize>::new(0);
|
let gasometer = Gasometer::<usize>::new(0);
|
||||||
let schedule = evm::Schedule::default();
|
let schedule = Schedule::default();
|
||||||
let current_mem_size = 0;
|
let current_mem_size = 0;
|
||||||
let mem_size = 5;
|
let mem_size = 5;
|
||||||
|
|
||||||
@@ -14,7 +14,10 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::{U256, Uint};
|
use util::U256;
|
||||||
|
use {ReturnData};
|
||||||
|
|
||||||
|
const MAX_RETURN_WASTE_BYTES: usize = 16384;
|
||||||
|
|
||||||
pub trait Memory {
|
pub trait Memory {
|
||||||
/// Retrieve current size of the memory
|
/// Retrieve current size of the memory
|
||||||
@@ -36,10 +39,12 @@ pub trait Memory {
|
|||||||
/// Retrieve writeable part of memory
|
/// Retrieve writeable part of memory
|
||||||
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
|
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8];
|
||||||
fn dump(&self);
|
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
|
/// Checks whether offset and size is valid memory range
|
||||||
fn is_valid_range(off: usize, size: usize) -> bool {
|
pub fn is_valid_range(off: usize, size: usize) -> bool {
|
||||||
// When size is zero we haven't actually expanded the memory
|
// When size is zero we haven't actually expanded the memory
|
||||||
let overflow = off.overflowing_add(size).1;
|
let overflow = off.overflowing_add(size).1;
|
||||||
size > 0 && !overflow
|
size > 0 && !overflow
|
||||||
@@ -84,11 +89,9 @@ impl Memory for Vec<u8> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
|
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
|
||||||
let off = offset.low_u64() as usize;
|
if !slice.is_empty() {
|
||||||
|
let off = offset.low_u64() as usize;
|
||||||
// TODO [todr] Optimize?
|
self[off..off+slice.len()].copy_from_slice(slice);
|
||||||
for pos in off..off+slice.len() {
|
|
||||||
self[pos] = slice[pos - off];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,33 +115,81 @@ impl Memory for Vec<u8> {
|
|||||||
Memory::resize(self, size)
|
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]
|
#[test]
|
||||||
fn test_memory_read_and_write() {
|
fn test_memory_read_and_write() {
|
||||||
// given
|
// given
|
||||||
let mem: &mut Memory = &mut vec![];
|
let mem: &mut Memory = &mut vec![];
|
||||||
mem.resize(0x80 + 32);
|
mem.resize(0x80 + 32);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
mem.write(U256::from(0x80), U256::from(0xabcdef));
|
mem.write(U256::from(0x80), U256::from(0xabcdef));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
|
assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_memory_read_and_write_byte() {
|
fn test_memory_read_and_write_byte() {
|
||||||
// given
|
// given
|
||||||
let mem: &mut Memory = &mut vec![];
|
let mem: &mut Memory = &mut vec![];
|
||||||
mem.resize(32);
|
mem.resize(32);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
mem.write_byte(U256::from(0x1d), U256::from(0xab));
|
mem.write_byte(U256::from(0x1d), U256::from(0xab));
|
||||||
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
|
mem.write_byte(U256::from(0x1e), U256::from(0xcd));
|
||||||
mem.write_byte(U256::from(0x1f), U256::from(0xef));
|
mem.write_byte(U256::from(0x1f), U256::from(0xef));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef));
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,9 +30,10 @@ pub use self::shared_cache::SharedCache;
|
|||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use action_params::{ActionParams, ActionValue};
|
use action_params::{ActionParams, ActionValue};
|
||||||
use types::executed::CallType;
|
use call_type::CallType;
|
||||||
use evm::instructions::{self, Instruction, InstructionInfo};
|
use instructions::{self, Instruction, InstructionInfo};
|
||||||
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
|
use evm::{self, GasLeft, CostType, ReturnData};
|
||||||
|
use ext::{self, MessageCallResult, ContractCreateResult, CreateContractAddress};
|
||||||
use bit_set::BitSet;
|
use bit_set::BitSet;
|
||||||
|
|
||||||
use util::*;
|
use util::*;
|
||||||
@@ -84,8 +85,16 @@ enum InstructionResult<Gas> {
|
|||||||
Ok,
|
Ok,
|
||||||
UnusedGas(Gas),
|
UnusedGas(Gas),
|
||||||
JumpToPosition(U256),
|
JumpToPosition(U256),
|
||||||
// gas left, init_orf, init_size
|
StopExecutionNeedsReturn {
|
||||||
StopExecutionNeedsReturn(Gas, U256, U256),
|
/// Gas left.
|
||||||
|
gas: Gas,
|
||||||
|
/// Return data offset.
|
||||||
|
init_off: U256,
|
||||||
|
/// Return data size.
|
||||||
|
init_size: U256,
|
||||||
|
/// Apply or revert state changes.
|
||||||
|
apply: bool,
|
||||||
|
},
|
||||||
StopExecution,
|
StopExecution,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,17 +103,19 @@ enum InstructionResult<Gas> {
|
|||||||
pub struct Interpreter<Cost: CostType> {
|
pub struct Interpreter<Cost: CostType> {
|
||||||
mem: Vec<u8>,
|
mem: Vec<u8>,
|
||||||
cache: Arc<SharedCache>,
|
cache: Arc<SharedCache>,
|
||||||
|
return_data: ReturnData,
|
||||||
_type: PhantomData<Cost>,
|
_type: PhantomData<Cost>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
||||||
fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result<GasLeft> {
|
fn exec(&mut self, params: ActionParams, ext: &mut ext::Ext) -> evm::Result<GasLeft> {
|
||||||
self.mem.clear();
|
self.mem.clear();
|
||||||
|
|
||||||
let mut informant = informant::EvmInformant::new(ext.depth());
|
let mut informant = informant::EvmInformant::new(ext.depth());
|
||||||
|
let mut do_trace = true;
|
||||||
|
|
||||||
let code = ¶ms.code.as_ref().expect("exec always called with code; qed");
|
let code = ¶ms.code.as_ref().expect("exec always called with code; qed");
|
||||||
let valid_jump_destinations = self.cache.jump_destinations(¶ms.code_hash, code);
|
let mut valid_jump_destinations = None;
|
||||||
|
|
||||||
let mut gasometer = Gasometer::<Cost>::new(Cost::from_u256(params.gas)?);
|
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 stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero());
|
||||||
@@ -115,13 +126,17 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
let instruction = code[reader.position];
|
let instruction = code[reader.position];
|
||||||
reader.position += 1;
|
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];
|
let info = &infos[instruction as usize];
|
||||||
self.verify_instruction(ext, instruction, info, &stack)?;
|
self.verify_instruction(ext, instruction, info, &stack)?;
|
||||||
|
|
||||||
// Calculate gas cost
|
// Calculate gas cost
|
||||||
let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?;
|
let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?;
|
||||||
// TODO: make compile-time removable if too much of a performance hit.
|
if do_trace {
|
||||||
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &requirements.gas_cost.as_u256());
|
ext.trace_prepare_execute(reader.position - 1, instruction, requirements.gas_cost.as_u256());
|
||||||
|
}
|
||||||
|
|
||||||
gasometer.verify_gas(&requirements.gas_cost)?;
|
gasometer.verify_gas(&requirements.gas_cost)?;
|
||||||
self.mem.expand(requirements.memory_required_size);
|
self.mem.expand(requirements.memory_required_size);
|
||||||
@@ -130,7 +145,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
|
|
||||||
evm_debug!({ informant.before_instruction(reader.position, instruction, info, &gasometer.current_gas, &stack) });
|
evm_debug!({ informant.before_instruction(reader.position, instruction, info, &gasometer.current_gas, &stack) });
|
||||||
|
|
||||||
let (mem_written, store_written) = match trace_executed {
|
let (mem_written, store_written) = match do_trace {
|
||||||
true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
|
true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
|
||||||
false => (None, None),
|
false => (None, None),
|
||||||
};
|
};
|
||||||
@@ -146,19 +161,34 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
|
|||||||
gasometer.current_gas = gasometer.current_gas + *gas;
|
gasometer.current_gas = gasometer.current_gas + *gas;
|
||||||
}
|
}
|
||||||
|
|
||||||
if trace_executed {
|
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);
|
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
|
// Advance
|
||||||
match result {
|
match result {
|
||||||
InstructionResult::JumpToPosition(position) => {
|
InstructionResult::JumpToPosition(position) => {
|
||||||
let pos = self.verify_jump(position, &valid_jump_destinations)?;
|
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;
|
reader.position = pos;
|
||||||
},
|
},
|
||||||
InstructionResult::StopExecutionNeedsReturn(gas, off, size) => {
|
InstructionResult::StopExecutionNeedsReturn {gas, init_off, init_size, apply} => {
|
||||||
informant.done();
|
informant.done();
|
||||||
return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size)));
|
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,
|
InstructionResult::StopExecution => break,
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -175,14 +205,20 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
Interpreter {
|
Interpreter {
|
||||||
mem: Vec::new(),
|
mem: Vec::new(),
|
||||||
cache: cache,
|
cache: cache,
|
||||||
|
return_data: ReturnData::empty(),
|
||||||
_type: PhantomData::default(),
|
_type: PhantomData::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
|
fn verify_instruction(&self, ext: &ext::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> evm::Result<()> {
|
||||||
let schedule = ext.schedule();
|
let schedule = ext.schedule();
|
||||||
|
|
||||||
if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL {
|
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 {
|
return Err(evm::Error::BadInstruction {
|
||||||
instruction: instruction
|
instruction: instruction
|
||||||
});
|
});
|
||||||
@@ -215,14 +251,20 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
stack: &Stack<U256>
|
stack: &Stack<U256>
|
||||||
) -> Option<(usize, usize)> {
|
) -> Option<(usize, usize)> {
|
||||||
match instruction {
|
let read = |pos| stack.peek(pos).low_u64() as usize;
|
||||||
instructions::MSTORE | instructions::MLOAD => Some((stack.peek(0).low_u64() as usize, 32)),
|
let written = match instruction {
|
||||||
instructions::MSTORE8 => Some((stack.peek(0).low_u64() as usize, 1)),
|
instructions::MSTORE | instructions::MLOAD => Some((read(0), 32)),
|
||||||
instructions::CALLDATACOPY | instructions::CODECOPY => Some((stack.peek(0).low_u64() as usize, stack.peek(2).low_u64() as usize)),
|
instructions::MSTORE8 => Some((read(0), 1)),
|
||||||
instructions::EXTCODECOPY => Some((stack.peek(1).low_u64() as usize, stack.peek(3).low_u64() as usize)),
|
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => Some((read(0), read(2))),
|
||||||
instructions::CALL | instructions::CALLCODE => Some((stack.peek(5).low_u64() as usize, stack.peek(6).low_u64() as usize)),
|
instructions::EXTCODECOPY => Some((read(1), read(3))),
|
||||||
instructions::DELEGATECALL => Some((stack.peek(4).low_u64() as usize, stack.peek(5).low_u64() as usize)),
|
instructions::CALL | instructions::CALLCODE => Some((read(5), read(6))),
|
||||||
|
instructions::DELEGATECALL | instructions::STATICCALL => Some((read(4), read(5))),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match written {
|
||||||
|
Some((offset, size)) if !memory::is_valid_range(offset, size) => None,
|
||||||
|
written => written,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +283,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
gas: Cost,
|
gas: Cost,
|
||||||
params: &ActionParams,
|
params: &ActionParams,
|
||||||
ext: &mut evm::Ext,
|
ext: &mut ext::Ext,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
code: &mut CodeReader,
|
code: &mut CodeReader,
|
||||||
stack: &mut Stack<U256>,
|
stack: &mut Stack<U256>,
|
||||||
@@ -266,34 +308,48 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
instructions::JUMPDEST => {
|
instructions::JUMPDEST => {
|
||||||
// ignore
|
// ignore
|
||||||
},
|
},
|
||||||
instructions::CREATE => {
|
instructions::CREATE | instructions::CREATE2 => {
|
||||||
let endowment = stack.pop_back();
|
let endowment = stack.pop_back();
|
||||||
let init_off = stack.pop_back();
|
let init_off = stack.pop_back();
|
||||||
let init_size = 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 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 contract_code = self.mem.read_slice(init_off, init_size);
|
||||||
let can_create = ext.balance(¶ms.address)? >= endowment && ext.depth() < ext.schedule().max_depth;
|
let can_create = ext.balance(¶ms.address)? >= endowment && ext.depth() < ext.schedule().max_depth;
|
||||||
|
|
||||||
|
// clear return data buffer before creating new call frame.
|
||||||
|
self.return_data = ReturnData::empty();
|
||||||
|
|
||||||
if !can_create {
|
if !can_create {
|
||||||
stack.push(U256::zero());
|
stack.push(U256::zero());
|
||||||
return Ok(InstructionResult::UnusedGas(create_gas));
|
return Ok(InstructionResult::UnusedGas(create_gas));
|
||||||
}
|
}
|
||||||
|
|
||||||
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code);
|
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme);
|
||||||
return match create_result {
|
return match create_result {
|
||||||
ContractCreateResult::Created(address, gas_left) => {
|
ContractCreateResult::Created(address, gas_left) => {
|
||||||
stack.push(address_to_u256(address));
|
stack.push(address_to_u256(address));
|
||||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||||
},
|
},
|
||||||
|
ContractCreateResult::Reverted(gas_left, return_data) => {
|
||||||
|
stack.push(U256::zero());
|
||||||
|
self.return_data = return_data;
|
||||||
|
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
|
||||||
|
},
|
||||||
ContractCreateResult::Failed => {
|
ContractCreateResult::Failed => {
|
||||||
stack.push(U256::zero());
|
stack.push(U256::zero());
|
||||||
Ok(InstructionResult::Ok)
|
Ok(InstructionResult::Ok)
|
||||||
}
|
},
|
||||||
|
ContractCreateResult::FailedInStaticCall => {
|
||||||
|
Err(evm::Error::MutableCallInStaticContext)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => {
|
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => {
|
||||||
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
|
assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible");
|
||||||
|
|
||||||
stack.pop_back();
|
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 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 = stack.pop_back();
|
||||||
@@ -301,6 +357,8 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
|
|
||||||
let value = if instruction == instructions::DELEGATECALL {
|
let value = if instruction == instructions::DELEGATECALL {
|
||||||
None
|
None
|
||||||
|
} else if instruction == instructions::STATICCALL {
|
||||||
|
Some(U256::zero())
|
||||||
} else {
|
} else {
|
||||||
Some(stack.pop_back())
|
Some(stack.pop_back())
|
||||||
};
|
};
|
||||||
@@ -319,6 +377,9 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
// Get sender & receive addresses, check if we have balance
|
// Get sender & receive addresses, check if we have balance
|
||||||
let (sender_address, receive_address, has_balance, call_type) = match instruction {
|
let (sender_address, receive_address, has_balance, call_type) = match instruction {
|
||||||
instructions::CALL => {
|
instructions::CALL => {
|
||||||
|
if ext.is_static() && value.map_or(false, |v| !v.is_zero()) {
|
||||||
|
return Err(evm::Error::MutableCallInStaticContext);
|
||||||
|
}
|
||||||
let has_balance = ext.balance(¶ms.address)? >= value.expect("value set for all but delegate call; qed");
|
let has_balance = ext.balance(¶ms.address)? >= value.expect("value set for all but delegate call; qed");
|
||||||
(¶ms.address, &code_address, has_balance, CallType::Call)
|
(¶ms.address, &code_address, has_balance, CallType::Call)
|
||||||
},
|
},
|
||||||
@@ -327,9 +388,13 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
(¶ms.address, ¶ms.address, has_balance, CallType::CallCode)
|
||||||
},
|
},
|
||||||
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall),
|
instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall),
|
||||||
|
instructions::STATICCALL => (¶ms.address, &code_address, true, CallType::StaticCall),
|
||||||
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
|
_ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// clear return data buffer before creating new call frame.
|
||||||
|
self.return_data = ReturnData::empty();
|
||||||
|
|
||||||
let can_call = has_balance && ext.depth() < ext.schedule().max_depth;
|
let can_call = has_balance && ext.depth() < ext.schedule().max_depth;
|
||||||
if !can_call {
|
if !can_call {
|
||||||
stack.push(U256::zero());
|
stack.push(U256::zero());
|
||||||
@@ -345,21 +410,33 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return match call_result {
|
return match call_result {
|
||||||
MessageCallResult::Success(gas_left) => {
|
MessageCallResult::Success(gas_left, data) => {
|
||||||
stack.push(U256::one());
|
stack.push(U256::one());
|
||||||
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one")))
|
self.return_data = data;
|
||||||
|
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one")))
|
||||||
|
},
|
||||||
|
MessageCallResult::Reverted(gas_left, data) => {
|
||||||
|
stack.push(U256::zero());
|
||||||
|
self.return_data = data;
|
||||||
|
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one")))
|
||||||
},
|
},
|
||||||
MessageCallResult::Failed => {
|
MessageCallResult::Failed => {
|
||||||
stack.push(U256::zero());
|
stack.push(U256::zero());
|
||||||
Ok(InstructionResult::Ok)
|
Ok(InstructionResult::Ok)
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
instructions::RETURN => {
|
instructions::RETURN => {
|
||||||
let init_off = stack.pop_back();
|
let init_off = stack.pop_back();
|
||||||
let init_size = stack.pop_back();
|
let init_size = stack.pop_back();
|
||||||
|
|
||||||
return Ok(InstructionResult::StopExecutionNeedsReturn(gas, init_off, init_size))
|
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 => {
|
instructions::STOP => {
|
||||||
return Ok(InstructionResult::StopExecution);
|
return Ok(InstructionResult::StopExecution);
|
||||||
@@ -378,7 +455,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(H256::from)
|
.map(H256::from)
|
||||||
.collect();
|
.collect();
|
||||||
ext.log(topics, self.mem.read_slice(offset, size));
|
ext.log(topics, self.mem.read_slice(offset, size))?;
|
||||||
},
|
},
|
||||||
instructions::PUSH1...instructions::PUSH32 => {
|
instructions::PUSH1...instructions::PUSH32 => {
|
||||||
let bytes = instructions::get_push_bytes(instruction);
|
let bytes = instructions::get_push_bytes(instruction);
|
||||||
@@ -472,21 +549,35 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
instructions::CODESIZE => {
|
instructions::CODESIZE => {
|
||||||
stack.push(U256::from(code.len()));
|
stack.push(U256::from(code.len()));
|
||||||
},
|
},
|
||||||
|
instructions::RETURNDATASIZE => {
|
||||||
|
stack.push(U256::from(self.return_data.len()))
|
||||||
|
},
|
||||||
instructions::EXTCODESIZE => {
|
instructions::EXTCODESIZE => {
|
||||||
let address = u256_to_address(&stack.pop_back());
|
let address = u256_to_address(&stack.pop_back());
|
||||||
let len = ext.extcodesize(&address)?;
|
let len = ext.extcodesize(&address)?;
|
||||||
stack.push(U256::from(len));
|
stack.push(U256::from(len));
|
||||||
},
|
},
|
||||||
instructions::CALLDATACOPY => {
|
instructions::CALLDATACOPY => {
|
||||||
self.copy_data_to_memory(stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
|
Self::copy_data_to_memory(&mut self.mem, stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
|
||||||
|
},
|
||||||
|
instructions::RETURNDATACOPY => {
|
||||||
|
{
|
||||||
|
let source_offset = stack.peek(1);
|
||||||
|
let size = stack.peek(2);
|
||||||
|
let return_data_len = U256::from(self.return_data.len());
|
||||||
|
if source_offset.overflow_add(*size).0 > return_data_len {
|
||||||
|
return Err(evm::Error::OutOfBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::copy_data_to_memory(&mut self.mem, stack, &*self.return_data);
|
||||||
},
|
},
|
||||||
instructions::CODECOPY => {
|
instructions::CODECOPY => {
|
||||||
self.copy_data_to_memory(stack, params.code.as_ref().map_or_else(|| &[] as &[u8], |c| &**c as &[u8]));
|
Self::copy_data_to_memory(&mut self.mem, stack, params.code.as_ref().map_or_else(|| &[] as &[u8], |c| &**c as &[u8]));
|
||||||
},
|
},
|
||||||
instructions::EXTCODECOPY => {
|
instructions::EXTCODECOPY => {
|
||||||
let address = u256_to_address(&stack.pop_back());
|
let address = u256_to_address(&stack.pop_back());
|
||||||
let code = ext.extcode(&address)?;
|
let code = ext.extcode(&address)?;
|
||||||
self.copy_data_to_memory(stack, &code);
|
Self::copy_data_to_memory(&mut self.mem, stack, &code);
|
||||||
},
|
},
|
||||||
instructions::GASPRICE => {
|
instructions::GASPRICE => {
|
||||||
stack.push(params.gas_price.clone());
|
stack.push(params.gas_price.clone());
|
||||||
@@ -518,7 +609,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
Ok(InstructionResult::Ok)
|
Ok(InstructionResult::Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_data_to_memory(&mut self, stack: &mut Stack<U256>, source: &[u8]) {
|
fn copy_data_to_memory(mem: &mut Vec<u8>, stack: &mut Stack<U256>, source: &[u8]) {
|
||||||
let dest_offset = stack.pop_back();
|
let dest_offset = stack.pop_back();
|
||||||
let source_offset = stack.pop_back();
|
let source_offset = stack.pop_back();
|
||||||
let size = stack.pop_back();
|
let size = stack.pop_back();
|
||||||
@@ -527,9 +618,9 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
|
let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size {
|
||||||
true => {
|
true => {
|
||||||
let zero_slice = if source_offset > source_size {
|
let zero_slice = if source_offset > source_size {
|
||||||
self.mem.writeable_slice(dest_offset, size)
|
mem.writeable_slice(dest_offset, size)
|
||||||
} else {
|
} else {
|
||||||
self.mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size)
|
mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size)
|
||||||
};
|
};
|
||||||
for i in zero_slice.iter_mut() {
|
for i in zero_slice.iter_mut() {
|
||||||
*i = 0;
|
*i = 0;
|
||||||
@@ -541,7 +632,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
|
|
||||||
if source_offset < source_size {
|
if source_offset < source_size {
|
||||||
let output_begin = source_offset.low_u64() as usize;
|
let output_begin = source_offset.low_u64() as usize;
|
||||||
self.mem.write_slice(dest_offset, &source[output_begin..output_end]);
|
mem.write_slice(dest_offset, &source[output_begin..output_end]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,3 +899,36 @@ fn address_to_u256(value: Address) -> U256 {
|
|||||||
U256::from(&*H256::from(value))
|
U256::from(&*H256::from(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
use rustc_hex::FromHex;
|
||||||
|
use vmtype::VMType;
|
||||||
|
use factory::Factory;
|
||||||
|
use vm::{self, ActionParams, ActionValue};
|
||||||
|
use vm::tests::{FakeExt, test_finalize};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_fail_on_tracing_mem() {
|
||||||
|
let code = "7feeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000620f120660406000601773945304eb96065b2a98b57a48a06ae28d285a71b56101f4f1600055".from_hex().unwrap();
|
||||||
|
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.address = 5.into();
|
||||||
|
params.gas = 300_000.into();
|
||||||
|
params.gas_price = 1.into();
|
||||||
|
params.value = ActionValue::Transfer(100_000.into());
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new();
|
||||||
|
ext.balances.insert(5.into(), 1_000_000_000.into());
|
||||||
|
ext.tracing = true;
|
||||||
|
|
||||||
|
let gas_left = {
|
||||||
|
let mut vm = Factory::new(VMType::Interpreter, 1).create(params.gas);
|
||||||
|
test_finalize(vm.exec(params, &mut ext)).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(ext.calls.len(), 1);
|
||||||
|
assert_eq!(gas_left, 248_212.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,7 +92,7 @@ impl Default for SharedCache {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_find_jump_destinations() {
|
fn test_find_jump_destinations() {
|
||||||
use util::FromHex;
|
use rustc_hex::FromHex;
|
||||||
// given
|
// given
|
||||||
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
|
let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap();
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use evm::instructions;
|
use instructions;
|
||||||
|
|
||||||
/// Stack trait with VM-friendly API
|
/// Stack trait with VM-friendly API
|
||||||
pub trait Stack<T> {
|
pub trait Stack<T> {
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
use util::*;
|
use util::*;
|
||||||
use evmjit;
|
use evmjit;
|
||||||
use evm::{self, GasLeft};
|
use evm::{self, GasLeft};
|
||||||
use types::executed::CallType;
|
use evm::CallType;
|
||||||
|
|
||||||
/// Should be used to convert jit types to ethcore
|
/// Should be used to convert jit types to ethcore
|
||||||
trait FromJit<T>: Sized {
|
trait FromJit<T>: Sized {
|
||||||
70
ethcore/evm/src/lib.rs
Normal file
70
ethcore/evm/src/lib.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// 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};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user