Compare commits
566 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64af073bd4 | ||
|
|
fe83046198 | ||
|
|
8031910892 | ||
|
|
73a3dac38c | ||
|
|
bbaf5ed4f5 | ||
|
|
9cf777510f | ||
|
|
9b398421ce | ||
|
|
a618dcaaf0 | ||
|
|
50021c7611 | ||
|
|
f62d2e6ea8 | ||
|
|
7da8e7926d | ||
|
|
5363e75f3a | ||
|
|
31d667a29a | ||
|
|
ca2bc92dda | ||
|
|
a0b71a9f52 | ||
|
|
300c0c120b | ||
|
|
b228dc7b54 | ||
|
|
df4d98001a | ||
|
|
2e4672ea13 | ||
|
|
7f7fe33bde | ||
|
|
3d313d592a | ||
|
|
abc6970d08 | ||
|
|
fa636d402d | ||
|
|
73d6666294 | ||
|
|
f16ccacd22 | ||
|
|
ba3de5d3b9 | ||
|
|
f16de5501a | ||
|
|
e298ab30e4 | ||
|
|
63d3a4dd59 | ||
|
|
0e55b6c6c9 | ||
|
|
4d115987cb | ||
|
|
2d623f14db | ||
|
|
1d1a3b9d02 | ||
|
|
1d69b0e124 | ||
|
|
3c59475be6 | ||
|
|
facab31551 | ||
|
|
c9cfcd2728 | ||
|
|
557dbb73b8 | ||
|
|
6f772a28e2 | ||
|
|
16f3119547 | ||
|
|
c3741640f7 | ||
|
|
decca7f698 | ||
|
|
fc53726c7f | ||
|
|
e2b81bc08d | ||
|
|
4d2d1eac1f | ||
|
|
5572e3dd1d | ||
|
|
0db1c0d336 | ||
|
|
905f04dc9d | ||
|
|
f560cf360c | ||
|
|
f5d48cbf2a | ||
|
|
d59e2ecbc6 | ||
|
|
a97ca5aaf4 | ||
|
|
df61b1b328 | ||
|
|
52a69d19e6 | ||
|
|
33084aaa07 | ||
|
|
2a82fa0a47 | ||
|
|
1ccc90108c | ||
|
|
e60c5b59a6 | ||
|
|
b199fbb6c6 | ||
|
|
d9d506db57 | ||
|
|
425e1ce04c | ||
|
|
8741d0d155 | ||
|
|
dfb2ddfdc2 | ||
|
|
52ac5a00f5 | ||
|
|
6336bdecf3 | ||
|
|
e72fc5398a | ||
|
|
2f52425387 | ||
|
|
46a988ddd4 | ||
|
|
76a7246369 | ||
|
|
725d32083a | ||
|
|
b6f827e3c8 | ||
|
|
05bfdc508e | ||
|
|
08f30fc1a8 | ||
|
|
b20833154e | ||
|
|
8702a29e6f | ||
|
|
aa59aa439d | ||
|
|
25aabe6e52 | ||
|
|
979f4e0617 | ||
|
|
03ba0cc498 | ||
|
|
ab079fd7bf | ||
|
|
dd60146c24 | ||
|
|
d1fc57d15c | ||
|
|
5da24e5dc3 | ||
|
|
8b3836a7d6 | ||
|
|
9bb9a84d40 | ||
|
|
7093651d70 | ||
|
|
52355855fc | ||
|
|
deceb5fd56 | ||
|
|
531bc79edc | ||
|
|
11b65ce53d | ||
|
|
40a304b177 | ||
|
|
8c88e2a8cc | ||
|
|
c5ffb5af79 | ||
|
|
6ba1e66d32 | ||
|
|
9fb5623569 | ||
|
|
087ebcf94e | ||
|
|
0c7ab34c32 | ||
|
|
9de579366a | ||
|
|
1b507e0147 | ||
|
|
b165059327 | ||
|
|
326a51a5c2 | ||
|
|
93b4b15c15 | ||
|
|
62df81f9fe | ||
|
|
21c65a99ea | ||
|
|
fbfcd48483 | ||
|
|
1809961f94 | ||
|
|
d8a3137ce8 | ||
|
|
86db5c08fc | ||
|
|
27d9964001 | ||
|
|
de531c9a1e | ||
|
|
d891e80ad7 | ||
|
|
b26f8c3f33 | ||
|
|
1732cc7a40 | ||
|
|
7b72463845 | ||
|
|
f7b7d317fe | ||
|
|
4e72608441 | ||
|
|
e99753d6e3 | ||
|
|
7dbf23829e | ||
|
|
f19b00b6ca | ||
|
|
beb6f651af | ||
|
|
7c015f023f | ||
|
|
034fa4d615 | ||
|
|
dbfc5cd31c | ||
|
|
b0b7a3aa39 | ||
|
|
489722b83f | ||
|
|
9e5e57fdcd | ||
|
|
bcf8cd6dc0 | ||
|
|
b29329c3c5 | ||
|
|
b672d51a74 | ||
|
|
53f1d7b6ff | ||
|
|
f56b89010d | ||
|
|
85c471b905 | ||
|
|
8c47240248 | ||
|
|
57faa37623 | ||
|
|
29e07755e9 | ||
|
|
5d4925d582 | ||
|
|
24c6561bc1 | ||
|
|
a44ef73b33 | ||
|
|
360ed1a1f1 | ||
|
|
b37086722a | ||
|
|
4304f4a2a4 | ||
|
|
e4f0c0b215 | ||
|
|
0934a283b2 | ||
|
|
297d25dd65 | ||
|
|
11cb544c24 | ||
|
|
b62a5c8f7d | ||
|
|
9746b944f1 | ||
|
|
f33cd60dc2 | ||
|
|
171244a471 | ||
|
|
80a7e4b964 | ||
|
|
3199576416 | ||
|
|
30ba10e10c | ||
|
|
6b1e722a6b | ||
|
|
eaa41ea568 | ||
|
|
b9a08c36aa | ||
|
|
ccb62d3b55 | ||
|
|
edda0b2380 | ||
|
|
02cf48681d | ||
|
|
4907c5028f | ||
|
|
c5ddacc3ee | ||
|
|
c65ee93542 | ||
|
|
4cb4344542 | ||
|
|
a9ae6e3b58 | ||
|
|
3f41186b2e | ||
|
|
01e33ffb61 | ||
|
|
7cf807d1b4 | ||
|
|
53a975d1dc | ||
|
|
6c15a47acc | ||
|
|
1cc6b2b976 | ||
|
|
22a87b1203 | ||
|
|
67a9a40243 | ||
|
|
f614a69929 | ||
|
|
856657e39a | ||
|
|
431ba5e260 | ||
|
|
d0e79be5c6 | ||
|
|
898f1410a5 | ||
|
|
b220e07feb | ||
|
|
226fe8e0bb | ||
|
|
435ba186f8 | ||
|
|
f048839a4b | ||
|
|
e734810293 | ||
|
|
8574bfd546 | ||
|
|
9cdd2d265a | ||
|
|
3f841cc578 | ||
|
|
a76981a61d | ||
|
|
1fbef3289b | ||
|
|
247495fba2 | ||
|
|
9a8fdeead9 | ||
|
|
aafb014d01 | ||
|
|
10bdfe6926 | ||
|
|
63dbb527cc | ||
|
|
ce00c13c7a | ||
|
|
1372f870d5 | ||
|
|
a1857bf139 | ||
|
|
77babe6226 | ||
|
|
8ab56ea3d1 | ||
|
|
7ae9e61d6c | ||
|
|
52b1bc5ff6 | ||
|
|
46b5801730 | ||
|
|
7dd29825c4 | ||
|
|
8a8cfb133f | ||
|
|
9c7395a6be | ||
|
|
b007770ba8 | ||
|
|
0cba70fba3 | ||
|
|
9c8b80f998 | ||
|
|
d67369a01c | ||
|
|
861c8d0701 | ||
|
|
3cc3dbef66 | ||
|
|
6e79a36ef6 | ||
|
|
615fca5dc5 | ||
|
|
f31e7d54af | ||
|
|
038862fa9d | ||
|
|
340f0b6f58 | ||
|
|
4e447ccc68 | ||
|
|
459dcbcef1 | ||
|
|
3ba3dd3805 | ||
|
|
18f16616fe | ||
|
|
028d6f6853 | ||
|
|
6a47fd176b | ||
|
|
3d00a912b5 | ||
|
|
3005b394f3 | ||
|
|
dd17c766b8 | ||
|
|
0d2f516ad7 | ||
|
|
34b7cf2e0a | ||
|
|
a2735308b9 | ||
|
|
a44e95b0ec | ||
|
|
5ab18d1313 | ||
|
|
5dba43178b | ||
|
|
9eed51ed20 | ||
|
|
c3ef5c10a3 | ||
|
|
77bbab009e | ||
|
|
d8472a8031 | ||
|
|
8e26977693 | ||
|
|
72a8f56e51 | ||
|
|
a74b72faa2 | ||
|
|
ac8db8f101 | ||
|
|
be25e930e5 | ||
|
|
fea05dc594 | ||
|
|
4cb1c906b0 | ||
|
|
64417594c7 | ||
|
|
98ddff1326 | ||
|
|
799c69c8d5 | ||
|
|
7b5d39e0a1 | ||
|
|
78007cf80b | ||
|
|
b1a67bf18f | ||
|
|
4a9d57d2ce | ||
|
|
507a4ea26c | ||
|
|
e74ab38e29 | ||
|
|
3d35529016 | ||
|
|
d14b6871a5 | ||
|
|
66f8038b6b | ||
|
|
d5d0cb0b47 | ||
|
|
517c705ab5 | ||
|
|
598833d1ea | ||
|
|
2b193f00d4 | ||
|
|
be7c771efd | ||
|
|
e0efe577b3 | ||
|
|
facbb93c62 | ||
|
|
06e8ae19e3 | ||
|
|
c60e02d151 | ||
|
|
f380340a9b | ||
|
|
2310ecb480 | ||
|
|
6c205067b1 | ||
|
|
44bc8a08fb | ||
|
|
80c734efe2 | ||
|
|
8d0e05adb7 | ||
|
|
50c43bd819 | ||
|
|
1053f3610c | ||
|
|
a7511b6b02 | ||
|
|
1b86f8368d | ||
|
|
36d3d0d7d7 | ||
|
|
9fd95e6694 | ||
|
|
4226c0f631 | ||
|
|
9a0a5b4c22 | ||
|
|
3ac33ceda0 | ||
|
|
0f83ef6a0b | ||
|
|
fc6e598b40 | ||
|
|
691e55227d | ||
|
|
6738fe8268 | ||
|
|
507a638762 | ||
|
|
80d59949ff | ||
|
|
29a5c1785c | ||
|
|
91ae781f05 | ||
|
|
e75274df66 | ||
|
|
e0f6709f05 | ||
|
|
02ecb6b37b | ||
|
|
29076da4b7 | ||
|
|
420f2ad6c4 | ||
|
|
3abe3e1fbc | ||
|
|
cc19f11c4d | ||
|
|
11cae70cdd | ||
|
|
b37ceccf02 | ||
|
|
ab44168e1c | ||
|
|
4cf8df87e5 | ||
|
|
a784240734 | ||
|
|
d956b7cea3 | ||
|
|
c3925a494d | ||
|
|
60302acb94 | ||
|
|
c26627d968 | ||
|
|
86df371a72 | ||
|
|
174e142ad9 | ||
|
|
61c405a014 | ||
|
|
fbc0e0039a | ||
|
|
636ecf306a | ||
|
|
2746c1c18c | ||
|
|
fd6d66fc86 | ||
|
|
c7a11418bb | ||
|
|
7200cfcbc9 | ||
|
|
92fd00f41e | ||
|
|
4269867ca4 | ||
|
|
29a2238299 | ||
|
|
7e13ce6185 | ||
|
|
cb2ace82db | ||
|
|
d9aeafa40e | ||
|
|
3159e20814 | ||
|
|
ab1c6cd655 | ||
|
|
d65a1d2b2a | ||
|
|
673cde6c7f | ||
|
|
248428657d | ||
|
|
9eeafecd76 | ||
|
|
c5ed363bba | ||
|
|
d3695d0b72 | ||
|
|
49ba117f13 | ||
|
|
3647628ae2 | ||
|
|
2d5d632d74 | ||
|
|
fbdde8f0e6 | ||
|
|
986c28efb7 | ||
|
|
2382d779ca | ||
|
|
24ec696062 | ||
|
|
cca4efb861 | ||
|
|
f1edd3d683 | ||
|
|
da9e9692dc | ||
|
|
da4b1c36cb | ||
|
|
edaf24a2ce | ||
|
|
b91d339bf2 | ||
|
|
c7af950992 | ||
|
|
3dd7ce5566 | ||
|
|
803682d9b8 | ||
|
|
002d808a8a | ||
|
|
bdf4446173 | ||
|
|
2ed09de38e | ||
|
|
c8b565805e | ||
|
|
e15f631ec7 | ||
|
|
d7caae2241 | ||
|
|
ae757afe15 | ||
|
|
895b027527 | ||
|
|
36dfa4743e | ||
|
|
ee253c7a3f | ||
|
|
32a4a060d6 | ||
|
|
716de21873 | ||
|
|
172553c81e | ||
|
|
2e24348c2d | ||
|
|
1f885254e1 | ||
|
|
9a70f717ad | ||
|
|
d4d63a5688 | ||
|
|
f48d030f0b | ||
|
|
b304ce5838 | ||
|
|
5fe14e172e | ||
|
|
9b1d1dc336 | ||
|
|
c648171f91 | ||
|
|
274e7767b4 | ||
|
|
ef0a41c7b3 | ||
|
|
9afbc9816d | ||
|
|
25e6b2b827 | ||
|
|
eef9586c57 | ||
|
|
3b0b1375f2 | ||
|
|
aaf9490c91 | ||
|
|
171e93752c | ||
|
|
fe027e872c | ||
|
|
a160adadaa | ||
|
|
598ad4d5da | ||
|
|
0467bfc9ed | ||
|
|
3e61d6f3f9 | ||
|
|
57c14eedfa | ||
|
|
43d931de12 | ||
|
|
f023d7a17f | ||
|
|
d7077c8ef7 | ||
|
|
b33cb32a11 | ||
|
|
3c046556b5 | ||
|
|
326ea59258 | ||
|
|
7af366c5b1 | ||
|
|
9f43526c88 | ||
|
|
8282c7dd50 | ||
|
|
3b662c285f | ||
|
|
1f1b420230 | ||
|
|
456ad9e21b | ||
|
|
f06cff48b9 | ||
|
|
b1ca41dea8 | ||
|
|
5dd5983568 | ||
|
|
2abf1df667 | ||
|
|
d7e225c0af | ||
|
|
1ac2987437 | ||
|
|
cd7b046d80 | ||
|
|
095e51c39c | ||
|
|
4a9b9dc305 | ||
|
|
baababea02 | ||
|
|
b25a37eb38 | ||
|
|
f8b56317b4 | ||
|
|
4b93773c4f | ||
|
|
aaad0e87ae | ||
|
|
a7103e2870 | ||
|
|
e151fbb071 | ||
|
|
e411399f13 | ||
|
|
0bbdcb13b3 | ||
|
|
9b28e97ca8 | ||
|
|
7df0aa2b60 | ||
|
|
b7d243368b | ||
|
|
21d5e105c1 | ||
|
|
908104e365 | ||
|
|
9783c93564 | ||
|
|
29de69e39d | ||
|
|
a8b26e2cb5 | ||
|
|
bcb63bce12 | ||
|
|
fa44532a1e | ||
|
|
a084969d55 | ||
|
|
cb1808d53d | ||
|
|
eb6617f9d4 | ||
|
|
fd62944efc | ||
|
|
488ff88562 | ||
|
|
b8b55be0be | ||
|
|
c26cfc1c5a | ||
|
|
d4c9c7cc23 | ||
|
|
1b759c1462 | ||
|
|
2ea7a8d666 | ||
|
|
747818a349 | ||
|
|
62b9c1b14f | ||
|
|
4c1b74a42e | ||
|
|
45d532368d | ||
|
|
fb0076d262 | ||
|
|
78e7101f85 | ||
|
|
8052824f3f | ||
|
|
c96686620a | ||
|
|
ce1a4c8952 | ||
|
|
a9a671dfc0 | ||
|
|
b58754cd26 | ||
|
|
b873d3befb | ||
|
|
bbf20c3637 | ||
|
|
a2b4997888 | ||
|
|
1ab2060afa | ||
|
|
a25eb4b046 | ||
|
|
d1e6e3d642 | ||
|
|
0242c66a1b | ||
|
|
548b4e164e | ||
|
|
24f09de60e | ||
|
|
39a46eee8f | ||
|
|
bb2a631885 | ||
|
|
cfcf061e41 | ||
|
|
465aae71e7 | ||
|
|
601ebcf3cc | ||
|
|
bb36a594c5 | ||
|
|
5c438cc58a | ||
|
|
1aee197d79 | ||
|
|
8f6e48e4c3 | ||
|
|
ff7fcd0992 | ||
|
|
8e98f06ce5 | ||
|
|
316fae2cc4 | ||
|
|
43027b36ea | ||
|
|
0f7b66f557 | ||
|
|
edb8663195 | ||
|
|
fa73ae17d9 | ||
|
|
46b0af6121 | ||
|
|
d91e8ccd34 | ||
|
|
789b903de6 | ||
|
|
8102fb9306 | ||
|
|
07521c17b4 | ||
|
|
d8a4cca817 | ||
|
|
0a513ad06e | ||
|
|
0907722fc8 | ||
|
|
d4ff3e51e9 | ||
|
|
b30e7c7ba4 | ||
|
|
d42ea6b69e | ||
|
|
654cd570b4 | ||
|
|
c71dff04c5 | ||
|
|
b53442953a | ||
|
|
7ad8599324 | ||
|
|
ef09902508 | ||
|
|
9f5abd01b5 | ||
|
|
52f4034bdd | ||
|
|
5c2ca9a0cb | ||
|
|
d4f0f2c80e | ||
|
|
456619001a | ||
|
|
ba38cc0ccc | ||
|
|
cdd1c6dd90 | ||
|
|
bad02d65a1 | ||
|
|
97eb311cb0 | ||
|
|
080b50809a | ||
|
|
95538ac42c | ||
|
|
ef8dd23254 | ||
|
|
08048a7377 | ||
|
|
eb56e74340 | ||
|
|
5c5f52c017 | ||
|
|
6e8df6a6ce | ||
|
|
6259a5a737 | ||
|
|
427d54f341 | ||
|
|
86c85bdd9e | ||
|
|
8fc03e2dc2 | ||
|
|
3c061857c4 | ||
|
|
2891b7b4ea | ||
|
|
d5be0fae54 | ||
|
|
f6ec1eae4a | ||
|
|
a14f2391ff | ||
|
|
f31ddec3a8 | ||
|
|
5337de8d42 | ||
|
|
562e591ed3 | ||
|
|
dec083a5ed | ||
|
|
3cca6c869e | ||
|
|
9aef8ba063 | ||
|
|
6ecd6eaa12 | ||
|
|
bfbcb8d551 | ||
|
|
6977d335e9 | ||
|
|
49024a4f28 | ||
|
|
f94f061f6a | ||
|
|
06cf2a3f41 | ||
|
|
7904464d24 | ||
|
|
36626f96a8 | ||
|
|
5ecbeaa82f | ||
|
|
f3a6da7c1e | ||
|
|
1b4f67151f | ||
|
|
3850ee64bb | ||
|
|
f3c1643090 | ||
|
|
e4867d7cb9 | ||
|
|
f45254d485 | ||
|
|
ce87691a2e | ||
|
|
0e917b9fa3 | ||
|
|
53db9921d6 | ||
|
|
7ca5c115d8 | ||
|
|
b38f33531e | ||
|
|
25f1f22c86 | ||
|
|
0e3a15cadb | ||
|
|
d7498c1dd5 | ||
|
|
a2bb3f2832 | ||
|
|
d26e038dc7 | ||
|
|
9badb310ae | ||
|
|
53b3a6f0a1 | ||
|
|
17a60920cb | ||
|
|
3290f393bd | ||
|
|
874bc808cb | ||
|
|
8ed2b9c7d9 | ||
|
|
dfb603dd08 | ||
|
|
16e58958c9 | ||
|
|
75013003f7 | ||
|
|
b88eef5374 | ||
|
|
835b158069 | ||
|
|
5cc6a681c9 | ||
|
|
5b6ea41d9d | ||
|
|
c36f47bbbf | ||
|
|
22a19819c6 | ||
|
|
6ec282fe98 | ||
|
|
98c7677ce1 | ||
|
|
446d59426a | ||
|
|
f478812441 | ||
|
|
4ded1cc3d4 | ||
|
|
10c2302b55 | ||
|
|
f400da405a | ||
|
|
d9743f166f | ||
|
|
e5ca5e0926 | ||
|
|
d696a66d71 | ||
|
|
c9846b8f48 | ||
|
|
5c252f2a60 | ||
|
|
3b1cca5622 | ||
|
|
b047eb29a1 | ||
|
|
f689792481 | ||
|
|
c6e83caddf | ||
|
|
f12add6958 | ||
|
|
997fd93016 | ||
|
|
5e0ba1c310 |
@@ -9,3 +9,8 @@ trim_trailing_whitespace=true
|
||||
max_line_length=120
|
||||
insert_final_newline=true
|
||||
|
||||
[.travis.yml]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=8
|
||||
end_of_line=lf
|
||||
|
||||
293
.gitlab-ci.yml
Normal file
293
.gitlab-ci.yml
Normal file
@@ -0,0 +1,293 @@
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
variables:
|
||||
GIT_DEPTH: "3"
|
||||
SIMPLECOV: "true"
|
||||
RUST_BACKTRACE: "1"
|
||||
cache:
|
||||
key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
|
||||
untracked: true
|
||||
linux-stable:
|
||||
stage: build
|
||||
image: ethcore/rust:stable
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/checksum --body checksum
|
||||
tags:
|
||||
- rust
|
||||
- rust-stable
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "stable-x86_64-unknown-linux-gnu_parity"
|
||||
linux-stable-14.04:
|
||||
stage: build
|
||||
image: ethcore/rust-14.04:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-ubuntu_14_04-gnu/checksum --body checksum
|
||||
tags:
|
||||
- rust
|
||||
- rust-14.04
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "stable-x86_64-unknown-ubuntu_14_04-gnu_parity"
|
||||
linux-beta:
|
||||
stage: build
|
||||
image: ethcore/rust:beta
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
tags:
|
||||
- rust
|
||||
- rust-beta
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "beta-x86_64-unknown-linux-gnu_parity"
|
||||
allow_failure: true
|
||||
linux-nightly:
|
||||
stage: build
|
||||
image: ethcore/rust:nightly
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
tags:
|
||||
- rust
|
||||
- rust-nightly
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "nigthly-x86_64-unknown-linux-gnu_parity"
|
||||
allow_failure: true
|
||||
linux-centos:
|
||||
stage: build
|
||||
image: ethcore/rust-centos:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- export CXX="g++"
|
||||
- export CC="gcc"
|
||||
- cargo build --release --verbose
|
||||
- strip target/release/parity
|
||||
- md5sum target/release/parity >> checksum
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- 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/checksum --body checksum
|
||||
tags:
|
||||
- rust
|
||||
- rust-centos
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "x86_64-unknown-centos-gnu_parity"
|
||||
linux-armv7:
|
||||
stage: build
|
||||
image: ethcore/rust-armv7:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target armv7-unknown-linux-gnueabihf --release --verbose
|
||||
- arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
- md5sum target/armv7-unknown-linux-gnueabihf/release/parity >> checksum
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/checksum --body checksum
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- target/armv7-unknown-linux-gnueabihf/release/parity
|
||||
name: "armv7_unknown_linux_gnueabihf_parity"
|
||||
allow_failure: true
|
||||
linux-arm:
|
||||
stage: build
|
||||
image: ethcore/rust-arm:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target arm-unknown-linux-gnueabihf --release --verbose
|
||||
- arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity
|
||||
- md5sum target/arm-unknown-linux-gnueabihf/release/parity >> checksum
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/checksum --body checksum
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- target/arm-unknown-linux-gnueabihf/release/parity
|
||||
name: "arm-unknown-linux-gnueabihf_parity"
|
||||
allow_failure: true
|
||||
linux-armv6:
|
||||
stage: build
|
||||
image: ethcore/rust-armv6:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config
|
||||
- echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target arm-unknown-linux-gnueabi --release --verbose
|
||||
- arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity
|
||||
- md5sum target/arm-unknown-linux-gnueabi/release/parity >> checksum
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity --body target/arm-unknown-linux-gnueabi/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/checksum --body checksum
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- target/arm-unknown-linux-gnueabi/release/parity
|
||||
name: "arm-unknown-linux-gnueabi_parity"
|
||||
allow_failure: true
|
||||
linux-aarch64:
|
||||
stage: build
|
||||
image: ethcore/rust-aarch64:latest
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- rm -rf .cargo
|
||||
- mkdir -p .cargo
|
||||
- echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config
|
||||
- echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config
|
||||
- cat .cargo/config
|
||||
- cargo build --target aarch64-unknown-linux-gnu --release --verbose
|
||||
- aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity
|
||||
- md5sum target/aarch64-unknown-linux-gnu/release/parity >> checksum
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/checksum --body checksum
|
||||
tags:
|
||||
- rust
|
||||
- rust-arm
|
||||
artifacts:
|
||||
paths:
|
||||
- target/aarch64-unknown-linux-gnu/release/parity
|
||||
name: "aarch64-unknown-linux-gnu_parity"
|
||||
allow_failure: true
|
||||
darwin:
|
||||
stage: build
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- cargo build --release --verbose
|
||||
- md5sum target/release/parity >> checksum
|
||||
- aws configure set aws_access_key_id $s3_key
|
||||
- aws configure set aws_secret_access_key $s3_secret
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity --body target/release/parity
|
||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/checksum --body checksum
|
||||
tags:
|
||||
- osx
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity
|
||||
name: "x86_64-apple-darwin_parity"
|
||||
windows:
|
||||
stage: build
|
||||
only:
|
||||
- master
|
||||
- beta
|
||||
- tags
|
||||
- stable
|
||||
script:
|
||||
- set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include;C:\vs2015\VC\include;C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt
|
||||
- set LIB=C:\vs2015\VC\lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.10240.0\ucrt\x64
|
||||
- set RUST_BACKTRACE=1
|
||||
- set RUSTFLAGS=-Zorbit=off
|
||||
- rustup default stable-x86_64-pc-windows-msvc
|
||||
- cargo build --release --verbose
|
||||
- cmd md5sum target\release\parity >> checksum
|
||||
- aws configure set aws_access_key_id %s3_key%
|
||||
- aws configure set aws_secret_access_key %s3_secret%
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity --body target\release\parity.exe
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity --body target\release\parity.pdb
|
||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/checksum --body checksum
|
||||
tags:
|
||||
- rust-windows
|
||||
artifacts:
|
||||
paths:
|
||||
- target/release/parity.exe
|
||||
- target/release/parity.pdb
|
||||
name: "x86_64-pc-windows-msvc_parity"
|
||||
test-linux:
|
||||
stage: test
|
||||
before_script:
|
||||
- git submodule update --init --recursive
|
||||
script:
|
||||
- export RUST_BACKTRACE=1
|
||||
- ./test.sh --verbose
|
||||
tags:
|
||||
- rust-test
|
||||
dependencies:
|
||||
- linux-stable
|
||||
103
.travis.yml
103
.travis.yml
@@ -1,6 +1,7 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: rust
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
@@ -8,101 +9,69 @@ branches:
|
||||
- /^stable-.*$/
|
||||
- /^beta$/
|
||||
- /^stable$/
|
||||
|
||||
git:
|
||||
depth: 3
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
include:
|
||||
- rust: stable
|
||||
env: FEATURES="--features travis-beta" RUN_TESTS="true"
|
||||
# - rust: beta
|
||||
# env: FEATURES="--features travis-beta" RUN_TESTS="true"
|
||||
- rust: stable
|
||||
env: FEATURES="--features travis-beta" RUN_BUILD="true"
|
||||
env: RUN_TESTS="true"
|
||||
- rust: beta
|
||||
env: FEATURES="--features travis-beta" RUN_BUILD="true"
|
||||
env: RUN_COVERAGE="true"
|
||||
- rust: stable
|
||||
env: FEATURES="--features travis-beta" RUN_COVERAGE="true"
|
||||
# - rust: nightly
|
||||
# env: FEATURES="--features travis-nightly" RUN_BENCHES="true"
|
||||
- rust: nightly
|
||||
env: FEATURES="--features travis-nightly" RUN_TESTS="true"
|
||||
env: RUN_DOCS="true"
|
||||
|
||||
env:
|
||||
global:
|
||||
# GH_TOKEN
|
||||
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
|
||||
- TARGETS="-p ethkey -p ethstore -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethjson -p ethcore-dapps -p ethcore-signer"
|
||||
- ARCHIVE_SUFFIX="-${TRAVIS_OS_NAME}-${TRAVIS_TAG}"
|
||||
- KCOV_FEATURES=""
|
||||
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov --exclude-pattern /usr/,/.cargo,/root/.multirust,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests,ethstore/tests target/kcov"
|
||||
- CXX="g++-4.8"
|
||||
- CC="gcc-4.8"
|
||||
- RUST_BACKTRACE="1"
|
||||
- RUN_TESTS="false"
|
||||
- RUN_COVERAGE="false"
|
||||
- RUN_BUILD="false"
|
||||
- RUN_BENCHES="false"
|
||||
- RUST_BACKTRACE="1"
|
||||
- RUN_DOCS="false"
|
||||
# GH_TOKEN for documentation
|
||||
- secure: bumJASbZSU8bxJ0EyPUJmu16AiV9EXOpyOj86Jlq/Ty9CfwGqsSXt96uDyE+OUJf34RUFQMsw0nk37/zC4lcn6kqk2wpuH3N/o85Zo/cVZY/NusBWLQqtT5VbYWsV+u2Ua4Tmmsw8yVYQhYwU2ZOejNpflL+Cs9XGgORp1L+/gMRMC2y5Se6ZhwnKPQlRJ8LGsG1dzjQULxzADIt3/zuspNBS8a2urJwlHfGMkvHDoUWCviP/GXoSqw3TZR7FmKyxE19I8n9+iSvm9+oZZquvcgfUxMHn8Gq/b44UbPvjtFOg2yam4xdWXF/RyWCHdc/R9EHorSABeCbefIsm+zcUF3/YQxwpSxM4IZEeH2rTiC7dcrsKw3XsO16xFQz5YI5Bay+CT/wTdMmJd7DdYz7Dyf+pOvcM9WOf/zorxYWSBOMYy0uzbusU2iyIghQ82s7E/Ahg+WARtPgkuTLSB5aL1oCTBKHqQscMr7lo5Ti6RpWLxEdTQMBznc+bMr+6dEtkEcG9zqc6cE9XX+ox3wTU6+HVMfQ1ltCntJ4UKcw3A6INEbw9wgocQa812CIASQ2fE+SCAbz6JxBjIAlFUnD1lUB7S8PdMPwn9plfQgKQ2A5YZqg6FnBdf0rQXIJYxQWKHXj/rBHSUCT0tHACDlzTA+EwWggvkP5AGIxRxm8jhw=
|
||||
- KCOV_CMD="./kcov-master/tmp/usr/local/bin/kcov"
|
||||
|
||||
cache:
|
||||
apt: true
|
||||
directories:
|
||||
- $TRAVIS_BUILD_DIR/target
|
||||
- $TRAVIS_BUILD_DIR/kcov-master
|
||||
- $HOME/.cargo
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
- gcc-4.8
|
||||
- g++-4.8
|
||||
|
||||
install:
|
||||
- ([ "$RUN_COVERAGE" = "false" ]) || (test -x $KCOV_CMD) || (
|
||||
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
|
||||
tar xzf master.tar.gz &&
|
||||
mkdir -p kcov-master/build &&
|
||||
cd kcov-master/build &&
|
||||
cmake .. &&
|
||||
make && make install DESTDIR=../tmp &&
|
||||
cd
|
||||
)
|
||||
|
||||
script:
|
||||
- if [ "$RUN_TESTS" = "true" ]; then cargo test --release --verbose ${FEATURES} ${TARGETS}; fi
|
||||
- if [ "$RUN_BENCHES" = "true" ]; then cargo bench --no-run ${FEATURES} ${TARGETS}; fi
|
||||
- if [ "$RUN_BUILD" = "true" ]; then cargo build --release --verbose ${FEATURES}; fi
|
||||
- if [ "$RUN_BUILD" = "true" ]; then tar cvzf parity${ARCHIVE_SUFFIX}.tar.gz -C target/release parity; fi
|
||||
- if [ "$RUN_TESTS" = "true" ]; then ./test.sh --verbose; fi
|
||||
- if [ "$RUN_COVERAGE" = "true" ]; then ./scripts/cov.sh "$KCOV_CMD"; fi
|
||||
|
||||
after_success: |
|
||||
[ "$RUN_COVERAGE" = "true" ] &&
|
||||
wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz &&
|
||||
tar xzf master.tar.gz && mkdir kcov-master/build && cd kcov-master/build && cmake .. && make && make install DESTDIR=../tmp && cd ../.. &&
|
||||
cargo test --no-run ${KCOV_FEATURES} ${TARGETS} &&
|
||||
$KCOV_CMD target/debug/deps/ethkey-* &&
|
||||
$KCOV_CMD target/debug/deps/ethstore-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore_util-* &&
|
||||
$KCOV_CMD target/debug/deps/ethash-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore-* &&
|
||||
$KCOV_CMD target/debug/deps/ethsync-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore_rpc-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore_dapps-* &&
|
||||
$KCOV_CMD target/debug/deps/ethcore_signer-* &&
|
||||
$KCOV_CMD target/debug/deps/ethjson-* &&
|
||||
$KCOV_CMD target/debug/parity-* &&
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
[ $TRAVIS_RUST_VERSION = stable ] &&
|
||||
cargo doc --no-deps --verbose ${KCOV_FEATURES} ${TARGETS} &&
|
||||
echo '<meta http-equiv=refresh content=0;url=ethcore/index.html>' > target/doc/index.html &&
|
||||
[ "$RUN_DOCS" = "true" ] &&
|
||||
./scripts/doc.sh &&
|
||||
pip install --user ghp-import &&
|
||||
/home/travis/.local/bin/ghp-import -n target/doc &&
|
||||
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "t+oGT/4lsy7IScw5s86Dpntl5Nyck4qG6nhHwMScc6FYzwLldgwgJaafL8Ej+HG+b7nFLriN+Snoa4YQ5o74X5ZlSWubVREOYQlL/fq7vcPB0DwAZ0Jufq1QW2R1M+3SwwF1eAwTv2W3G7A2K7dxjCVvENcy/gdxnZ36NeUPsqaCC9UcI2Yc7+4jyQwvx6ZfBvQeu+HbKENA0eUNs2ZQOID/1IPy0LJBvSyxAQYsysXdjTzGdNu4+Iba20E8uWYe4fAbgz+gwGarXg1L6D6gKyMlWkViqWjvXWBuDJJqMQZ3rw41AwZOoh3mKd2Lc0l6l4oZcEqPuob0yKTNjz1tuJy9xKTC2F2bDzsvUgk1IRfMK5ukXXXS09ZCZWuA9/GtnsqJ1xGTiwX+DhQzpVBHaBiseSNlYE1YN/3jNyGY+iSts1qut+1BwE7swmcTLsAPoAy8Ue+f7ErNoCg1lm71vq7VO2DLn7x2NqHyHUEuJ+7olDHSdE84G7d9otDRu/+TfMOw7GXwTaha6yJRInuNsnj4CFMLNVvYACzCC2idB7f7nUZoSFi9jf18S9fCMPVmazMrFj4g95HWrVHkjpV5zRTeUdTWw6DJl6pC9HFqORHdCvLv4Rc4dm5r3CmOcAQ0ZuiccV2oKzw4/Wic96daae8M5f5KSQ/WTr+h0wXZKp0="
|
||||
skip_cleanup: true
|
||||
file: parity${ARCHIVE_SUFFIX}.tar.gz
|
||||
on:
|
||||
tags: true
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://hooks.slack.com/services/${SLACK_WEBHOOK}
|
||||
on_success: always
|
||||
on_failure: always
|
||||
on_start: never
|
||||
|
||||
notifications:
|
||||
slack:
|
||||
rooms:
|
||||
- ethcore:4EGxt9WP6AS7uX4JKXSfR9vi#chatops
|
||||
|
||||
753
Cargo.lock
generated
753
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
43
Cargo.toml
43
Cargo.toml
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Ethcore client."
|
||||
name = "parity"
|
||||
version = "1.3.0"
|
||||
version = "1.3.4"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
@@ -10,6 +10,7 @@ build = "build.rs"
|
||||
rustc_version = "0.1"
|
||||
syntex = "*"
|
||||
ethcore-ipc-codegen = { path = "ipc/codegen" }
|
||||
ethcore-ipc-tests = { path = "ipc/tests" }
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
@@ -17,24 +18,30 @@ env_logger = "0.3"
|
||||
rustc-serialize = "0.3"
|
||||
docopt = "0.6"
|
||||
time = "0.1"
|
||||
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
|
||||
fdlimit = { path = "util/fdlimit" }
|
||||
num_cpus = "0.2"
|
||||
number_prefix = "0.2"
|
||||
rpassword = "0.2.1"
|
||||
clippy = { version = "0.0.77", optional = true}
|
||||
semver = "0.2"
|
||||
ansi_term = "0.7"
|
||||
lazy_static = "0.2"
|
||||
regex = "0.1"
|
||||
isatty = "0.1"
|
||||
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
|
||||
fdlimit = { path = "util/fdlimit" }
|
||||
ethcore = { path = "ethcore" }
|
||||
ethcore-util = { path = "util" }
|
||||
ethsync = { path = "sync" }
|
||||
ethcore-io = { path = "util/io" }
|
||||
ethcore-devtools = { path = "devtools" }
|
||||
ethcore-rpc = { path = "rpc", optional = true }
|
||||
ethcore-signer = { path = "signer", optional = true }
|
||||
ethcore-dapps = { path = "dapps", optional = true }
|
||||
semver = "0.2"
|
||||
ethcore-rpc = { path = "rpc" }
|
||||
ethcore-signer = { path = "signer" }
|
||||
ethcore-ipc-nano = { path = "ipc/nano" }
|
||||
ethcore-ipc = { path = "ipc/rpc" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||
ansi_term = "0.7"
|
||||
ethcore-ipc-hypervisor = { path = "ipc/hypervisor" }
|
||||
ethcore-logger = { path = "logger" }
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git", branch = "beta" }
|
||||
ethcore-dapps = { path = "dapps", optional = true }
|
||||
clippy = { version = "0.0.80", optional = true}
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = "0.2"
|
||||
@@ -47,18 +54,20 @@ version = "0.8"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["rpc", "dapps", "ethcore-signer"]
|
||||
rpc = ["ethcore-rpc"]
|
||||
default = ["ui", "use-precompiled-js"]
|
||||
ui = ["dapps", "ethcore-signer/ui"]
|
||||
use-precompiled-js = ["ethcore-dapps/use-precompiled-js", "ethcore-signer/use-precompiled-js"]
|
||||
dapps = ["ethcore-dapps"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev",
|
||||
"ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||
travis-beta = ["ethcore/json-tests"]
|
||||
travis-nightly = ["ethcore/json-tests", "dev"]
|
||||
ipc = ["ethcore/ipc"]
|
||||
jit = ["ethcore/jit"]
|
||||
dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev", "ethcore-rpc/dev", "ethcore-dapps/dev", "ethcore-signer/dev"]
|
||||
json-tests = ["ethcore/json-tests"]
|
||||
|
||||
[[bin]]
|
||||
path = "parity/main.rs"
|
||||
name = "parity"
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
debug = false
|
||||
lto = false
|
||||
|
||||
|
||||
12
README.md
12
README.md
@@ -1,7 +1,11 @@
|
||||
# [Parity](https://ethcore.io/parity.html)
|
||||
### Fast, light, and robust Ethereum implementation
|
||||
|
||||
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/trogdoro/xiki][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
|
||||
[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url]
|
||||
|
||||
[Internal Documentation][doc-url]
|
||||
|
||||
Be sure to check out [our wiki][wiki-url] for more information.
|
||||
|
||||
[travis-image]: https://travis-ci.org/ethcore/parity.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/ethcore/parity
|
||||
@@ -11,8 +15,8 @@
|
||||
[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]: http://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
[Internal Documentation](http://ethcore.github.io/parity/ethcore/index.html)
|
||||
[doc-url]: http://ethcore.github.io/parity/ethcore/index.html
|
||||
[wiki-url]: https://github.com/ethcore/parity/wiki
|
||||
|
||||
----
|
||||
|
||||
@@ -29,7 +33,7 @@ This includes a few useful Dapps, including Ethereum Wallet, Maker OTC, and a no
|
||||
In a near-future release, it will be easy to install Dapps and use them through this web interface.
|
||||
|
||||
If you run into an issue while using parity, feel free to file one in this repository
|
||||
or hop on our [gitter chat room]([gitter-url]) to ask a question. We are glad to help!
|
||||
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
|
||||
|
||||
Parity's current release is 1.2. You can download it at https://ethcore.io/parity.html or follow the instructions
|
||||
below to build from source.
|
||||
|
||||
@@ -17,13 +17,16 @@ branches:
|
||||
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.9.0-x86_64-pc-windows-msvc.exe"
|
||||
- ps: Install-Product node 6
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.10.0-x86_64-pc-windows-msvc.exe"
|
||||
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll
|
||||
- ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe
|
||||
- rust-1.9.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
- rust-1.10.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
- node -v
|
||||
- npm -v
|
||||
|
||||
build: off
|
||||
|
||||
|
||||
24
build.rs
24
build.rs
@@ -15,35 +15,11 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate rustc_version;
|
||||
extern crate syntex;
|
||||
extern crate ethcore_ipc_codegen as codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use rustc_version::{version_meta, Channel};
|
||||
|
||||
fn main() {
|
||||
if let Channel::Nightly = version_meta().channel {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
}
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
// ipc pass
|
||||
{
|
||||
let src = Path::new("parity/hypervisor/service.rs.in");
|
||||
let dst = Path::new(&out_dir).join("hypervisor_service_ipc.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
|
||||
// serialization pass
|
||||
{
|
||||
let src = Path::new(&out_dir).join("hypervisor_service_ipc.rs");
|
||||
let dst = Path::new(&out_dir).join("hypervisor_service_cg.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
43
cov.sh
43
cov.sh
@@ -1,43 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Installing KCOV under ubuntu
|
||||
# https://users.rust-lang.org/t/tutorial-how-to-collect-test-coverages-for-rust-project/650#
|
||||
### Install deps
|
||||
# $ sudo apt-get install libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc binutils-dev libiberty-dev
|
||||
#
|
||||
### Compile kcov
|
||||
# $ wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && tar xf master.tar.gz
|
||||
# $ cd kcov-master && mkdir build && cd build
|
||||
# $ cmake .. && make && sudo make install
|
||||
|
||||
### Running coverage
|
||||
if ! type kcov > /dev/null; then
|
||||
echo "Install kcov first (details inside this file). Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cargo test \
|
||||
-p ethkey \
|
||||
-p ethstore \
|
||||
-p ethash \
|
||||
-p ethcore-util \
|
||||
-p ethcore \
|
||||
-p ethsync \
|
||||
-p ethcore-rpc \
|
||||
-p parity \
|
||||
-p ethcore-signer \
|
||||
-p ethcore-dapps \
|
||||
--no-run || exit $?
|
||||
rm -rf target/coverage
|
||||
mkdir -p target/coverage
|
||||
|
||||
EXCLUDE="~/.multirust,rocksdb,secp256k1,src/tests,util/json-tests,util/src/network/tests,sync/src/tests,ethcore/src/tests,ethcore/src/evm/tests,ethstore/tests"
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethkey-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethstore-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethash-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_util-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethsync-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_rpc-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_signer-*
|
||||
kcov --exclude-pattern $EXCLUDE --include-pattern src --verify target/coverage target/debug/deps/ethcore_dapps-*
|
||||
xdg-open target/coverage/index.html
|
||||
@@ -10,9 +10,10 @@ build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
jsonrpc-core = "2.0"
|
||||
jsonrpc-core = "2.1"
|
||||
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
|
||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||
unicase = "1.3"
|
||||
url = "1.0"
|
||||
rustc-serialize = "0.3"
|
||||
serde = "0.7.0"
|
||||
@@ -20,15 +21,13 @@ serde_json = "0.7.0"
|
||||
serde_macros = { version = "0.7.0", optional = true }
|
||||
ethcore-rpc = { path = "../rpc" }
|
||||
ethcore-util = { path = "../util" }
|
||||
parity-dapps = { git = "https://github.com/ethcore/parity-dapps-rs.git", version = "0.3" }
|
||||
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||
# List of apps
|
||||
parity-dapps-status = { git = "https://github.com/ethcore/parity-dapps-status-rs.git", version = "0.5.1" }
|
||||
parity-dapps-builtins = { git = "https://github.com/ethcore/parity-dapps-builtins-rs.git", version = "0.5.2" }
|
||||
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-dapps-wallet-rs.git", version = "0.6.0", optional = true }
|
||||
parity-dapps-dao = { git = "https://github.com/ethcore/parity-dapps-dao-rs.git", version = "0.4.0", optional = true }
|
||||
parity-dapps-makerotc = { git = "https://github.com/ethcore/parity-dapps-makerotc-rs.git", version = "0.3.0", optional = true }
|
||||
parity-dapps-status = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||
parity-dapps-home = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||
parity-dapps-wallet = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4", optional = true }
|
||||
mime_guess = { version = "1.6.1" }
|
||||
clippy = { version = "0.0.77", optional = true}
|
||||
clippy = { version = "0.0.80", optional = true}
|
||||
|
||||
[build-dependencies]
|
||||
serde_codegen = { version = "0.7.0", optional = true }
|
||||
@@ -39,3 +38,9 @@ default = ["serde_codegen", "extra-dapps"]
|
||||
extra-dapps = ["parity-dapps-wallet"]
|
||||
nightly = ["serde_macros"]
|
||||
dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"]
|
||||
|
||||
use-precompiled-js = [
|
||||
"parity-dapps-status/use-precompiled-js",
|
||||
"parity-dapps-home/use-precompiled-js",
|
||||
"parity-dapps-wallet/use-precompiled-js"
|
||||
]
|
||||
|
||||
@@ -25,8 +25,8 @@ mod inner {
|
||||
pub fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
|
||||
let src = Path::new("./src/api/mod.rs.in");
|
||||
let dst = Path::new(&out_dir).join("mod.rs");
|
||||
let src = Path::new("./src/api/types.rs.in");
|
||||
let dst = Path::new(&out_dir).join("types.rs");
|
||||
|
||||
let mut registry = syntex::Registry::new();
|
||||
|
||||
|
||||
@@ -15,42 +15,23 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath};
|
||||
|
||||
use api::response::as_json;
|
||||
use hyper::{server, net, Decoder, Encoder, Next};
|
||||
use api::types::{App, ApiError};
|
||||
use api::response::{as_json, as_json_error, ping_response};
|
||||
use handlers::extract_url;
|
||||
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RestApi {
|
||||
local_domain: String,
|
||||
endpoints: Arc<Endpoints>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
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 {
|
||||
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 RestApi {
|
||||
pub fn new(endpoints: Arc<Endpoints>) -> Box<Endpoint> {
|
||||
pub fn new(local_domain: String, endpoints: Arc<Endpoints>) -> Box<Endpoint> {
|
||||
Box::new(RestApi {
|
||||
endpoints: endpoints
|
||||
local_domain: local_domain,
|
||||
endpoints: endpoints,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,7 +44,58 @@ impl RestApi {
|
||||
|
||||
impl Endpoint for RestApi {
|
||||
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
||||
as_json(&self.list_apps())
|
||||
Box::new(RestApiRouter {
|
||||
api: self.clone(),
|
||||
handler: as_json_error(&ApiError {
|
||||
code: "404".into(),
|
||||
title: "Not Found".into(),
|
||||
detail: "Resource you requested has not been found.".into(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct RestApiRouter {
|
||||
api: RestApi,
|
||||
handler: Box<Handler>,
|
||||
}
|
||||
|
||||
impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||
|
||||
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||
let url = extract_url(&request);
|
||||
if url.is_none() {
|
||||
// Just return 404 if we can't parse URL
|
||||
return Next::write();
|
||||
}
|
||||
|
||||
let url = url.expect("Check for None is above; qed");
|
||||
let endpoint = url.path.get(1).map(|v| v.as_str());
|
||||
|
||||
let handler = endpoint.and_then(|v| match v {
|
||||
"apps" => Some(as_json(&self.api.list_apps())),
|
||||
"ping" => Some(ping_response(&self.api.local_domain)),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
// Overwrite default
|
||||
if let Some(h) = handler {
|
||||
self.handler = h;
|
||||
}
|
||||
|
||||
self.handler.on_request(request)
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
|
||||
self.handler.on_request_readable(decoder)
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
self.handler.on_response(res)
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
|
||||
self.handler.on_response_writable(encoder)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
//! REST API
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))]
|
||||
#![cfg_attr(feature="nightly", plugin(serde_macros, clippy))]
|
||||
|
||||
#[cfg(feature = "serde_macros")]
|
||||
include!("mod.rs.in");
|
||||
|
||||
#[cfg(not(feature = "serde_macros"))]
|
||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||
mod api;
|
||||
mod response;
|
||||
mod types;
|
||||
|
||||
pub use self::api::RestApi;
|
||||
pub use self::types::App;
|
||||
|
||||
@@ -16,8 +16,21 @@
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
use endpoint::{ContentHandler, Handler};
|
||||
use endpoint::Handler;
|
||||
use handlers::{ContentHandler, EchoHandler};
|
||||
|
||||
pub fn as_json<T : Serialize>(val: &T) -> Box<Handler> {
|
||||
Box::new(ContentHandler::new(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
||||
Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
||||
}
|
||||
|
||||
pub fn as_json_error<T : Serialize>(val: &T) -> Box<Handler> {
|
||||
Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned()))
|
||||
}
|
||||
|
||||
pub fn ping_response(local_domain: &str) -> Box<Handler> {
|
||||
Box::new(EchoHandler::cors(vec![
|
||||
format!("http://{}", local_domain),
|
||||
// Allow CORS calls also for localhost
|
||||
format!("http://{}", local_domain.replace("127.0.0.1", "localhost")),
|
||||
]))
|
||||
}
|
||||
|
||||
21
dapps/src/api/types.rs
Normal file
21
dapps/src/api/types.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(feature = "serde_macros")]
|
||||
include!("types.rs.in");
|
||||
|
||||
#[cfg(not(feature = "serde_macros"))]
|
||||
include!(concat!(env!("OUT_DIR"), "/types.rs"));
|
||||
50
dapps/src/api/types.rs.in
Normal file
50
dapps/src/api/types.rs.in
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2015, 2016 Ethcore (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, Serialize, Deserialize)]
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ApiError {
|
||||
pub code: String,
|
||||
pub title: String,
|
||||
pub detail: String,
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use parity_dapps::WebApp;
|
||||
mod fs;
|
||||
|
||||
extern crate parity_dapps_status;
|
||||
extern crate parity_dapps_builtins;
|
||||
extern crate parity_dapps_home;
|
||||
|
||||
pub const DAPPS_DOMAIN : &'static str = ".parity";
|
||||
pub const RPC_PATH : &'static str = "rpc";
|
||||
@@ -34,7 +34,7 @@ pub fn main_page() -> &'static str {
|
||||
}
|
||||
|
||||
pub fn utils() -> Box<Endpoint> {
|
||||
Box::new(PageEndpoint::with_prefix(parity_dapps_builtins::App::default(), UTILS_PATH.to_owned()))
|
||||
Box::new(PageEndpoint::with_prefix(parity_dapps_home::App::default(), UTILS_PATH.to_owned()))
|
||||
}
|
||||
|
||||
pub fn all_endpoints(dapps_path: String) -> Endpoints {
|
||||
@@ -44,7 +44,7 @@ pub fn all_endpoints(dapps_path: String) -> Endpoints {
|
||||
// because we use Cross-Origin LocalStorage.
|
||||
// TODO [ToDr] Account naming should be moved to parity.
|
||||
pages.insert("home".into(), Box::new(
|
||||
PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default())
|
||||
PageEndpoint::new_safe_to_embed(parity_dapps_home::App::default())
|
||||
));
|
||||
pages.insert("proxy".into(), ProxyPac::boxed());
|
||||
insert::<parity_dapps_status::App>(&mut pages, "parity");
|
||||
@@ -52,8 +52,6 @@ pub fn all_endpoints(dapps_path: String) -> Endpoints {
|
||||
|
||||
// Optional dapps
|
||||
wallet_page(&mut pages);
|
||||
daodapp_page(&mut pages);
|
||||
makerotc_page(&mut pages);
|
||||
|
||||
pages
|
||||
}
|
||||
@@ -66,22 +64,6 @@ fn wallet_page(pages: &mut Endpoints) {
|
||||
#[cfg(not(feature = "parity-dapps-wallet"))]
|
||||
fn wallet_page(_pages: &mut Endpoints) {}
|
||||
|
||||
#[cfg(feature = "parity-dapps-dao")]
|
||||
fn daodapp_page(pages: &mut Endpoints) {
|
||||
extern crate parity_dapps_dao;
|
||||
insert::<parity_dapps_dao::App>(pages, "dao");
|
||||
}
|
||||
#[cfg(not(feature = "parity-dapps-dao"))]
|
||||
fn daodapp_page(_pages: &mut Endpoints) {}
|
||||
|
||||
#[cfg(feature = "parity-dapps-makerotc")]
|
||||
fn makerotc_page(pages: &mut Endpoints) {
|
||||
extern crate parity_dapps_makerotc;
|
||||
insert::<parity_dapps_makerotc::App>(pages, "makerotc");
|
||||
}
|
||||
#[cfg(not(feature = "parity-dapps-makerotc"))]
|
||||
fn makerotc_page(_pages: &mut Endpoints) {}
|
||||
|
||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str) {
|
||||
pages.insert(id.to_owned(), Box::new(PageEndpoint::new(T::default())));
|
||||
}
|
||||
|
||||
@@ -16,11 +16,7 @@
|
||||
|
||||
//! URL Endpoint traits
|
||||
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
|
||||
use std::io::Write;
|
||||
use hyper::{server, net};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Clone)]
|
||||
@@ -42,58 +38,8 @@ pub struct EndpointInfo {
|
||||
pub trait Endpoint : Send + Sync {
|
||||
fn info(&self) -> Option<&EndpointInfo> { None }
|
||||
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<HttpStream> + Send>;
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<server::Handler<net::HttpStream> + Send>;
|
||||
}
|
||||
|
||||
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
|
||||
pub type Handler = server::Handler<HttpStream> + Send;
|
||||
|
||||
pub struct ContentHandler {
|
||||
content: String,
|
||||
mimetype: String,
|
||||
write_pos: usize,
|
||||
}
|
||||
|
||||
impl ContentHandler {
|
||||
pub fn new(content: String, mimetype: String) -> Self {
|
||||
ContentHandler {
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for ContentHandler {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(StatusCode::Ok);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
let bytes = self.content.as_bytes();
|
||||
if self.write_pos == bytes.len() {
|
||||
return Next::end();
|
||||
}
|
||||
|
||||
match encoder.write(&bytes[self.write_pos..]) {
|
||||
Ok(bytes) => {
|
||||
self.write_pos += bytes;
|
||||
Next::write()
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type Handler = server::Handler<net::HttpStream> + Send;
|
||||
|
||||
44
dapps/src/handlers/auth.rs
Normal file
44
dapps/src/handlers/auth.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015, 2016 Ethcore (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()
|
||||
}
|
||||
}
|
||||
|
||||
101
dapps/src/handlers/content.rs
Normal file
101
dapps/src/handlers/content.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Simple Content Handler
|
||||
|
||||
use std::io::Write;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
pub struct ContentHandler {
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: String,
|
||||
write_pos: usize,
|
||||
}
|
||||
|
||||
impl ContentHandler {
|
||||
pub fn ok(content: String, mimetype: String) -> Self {
|
||||
ContentHandler {
|
||||
code: StatusCode::Ok,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forbidden(content: String, mimetype: String) -> Self {
|
||||
ContentHandler {
|
||||
code: StatusCode::Forbidden,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not_found(content: String, mimetype: String) -> Self {
|
||||
ContentHandler {
|
||||
code: StatusCode::NotFound,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(code: StatusCode, content: String, mimetype: String) -> Self {
|
||||
ContentHandler {
|
||||
code: code,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
write_pos: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for ContentHandler {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(self.code);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap()));
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
let bytes = self.content.as_bytes();
|
||||
if self.write_pos == bytes.len() {
|
||||
return Next::end();
|
||||
}
|
||||
|
||||
match encoder.write(&bytes[self.write_pos..]) {
|
||||
Ok(bytes) => {
|
||||
self.write_pos += bytes;
|
||||
Next::write()
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
148
dapps/src/handlers/echo.rs
Normal file
148
dapps/src/handlers/echo.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Echo Handler
|
||||
|
||||
use std::io::Read;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::method::Method;
|
||||
use hyper::net::HttpStream;
|
||||
use unicase::UniCase;
|
||||
use super::ContentHandler;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
/// Type of Cross-Origin request
|
||||
enum Cors {
|
||||
/// Not a Cross-Origin request - no headers needed
|
||||
No,
|
||||
/// Cross-Origin request with valid Origin
|
||||
Allowed(String),
|
||||
/// Cross-Origin request with invalid Origin
|
||||
Forbidden,
|
||||
}
|
||||
|
||||
pub struct EchoHandler {
|
||||
safe_origins: Vec<String>,
|
||||
content: String,
|
||||
cors: Cors,
|
||||
handler: Option<ContentHandler>,
|
||||
}
|
||||
|
||||
impl EchoHandler {
|
||||
|
||||
pub fn cors(safe_origins: Vec<String>) -> Self {
|
||||
EchoHandler {
|
||||
safe_origins: safe_origins,
|
||||
content: String::new(),
|
||||
cors: Cors::Forbidden,
|
||||
handler: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn cors_header(&self, origin: Option<String>) -> Cors {
|
||||
fn origin_is_allowed(origin: &str, safe_origins: &[String]) -> bool {
|
||||
for safe in safe_origins {
|
||||
if origin.starts_with(safe) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
match origin {
|
||||
Some(ref origin) if origin_is_allowed(origin, &self.safe_origins) => {
|
||||
Cors::Allowed(origin.clone())
|
||||
},
|
||||
None => Cors::No,
|
||||
_ => Cors::Forbidden,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for EchoHandler {
|
||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||
let origin = request.headers().get_raw("origin")
|
||||
.and_then(|list| list.get(0))
|
||||
.and_then(|origin| String::from_utf8(origin.clone()).ok());
|
||||
|
||||
self.cors = self.cors_header(origin);
|
||||
|
||||
// Don't even read the payload if origin is forbidden!
|
||||
if let Cors::Forbidden = self.cors {
|
||||
self.handler = Some(ContentHandler::ok(String::new(), "text/plain".into()));
|
||||
Next::write()
|
||||
} else {
|
||||
Next::read()
|
||||
}
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
match decoder.read_to_string(&mut self.content) {
|
||||
Ok(0) => {
|
||||
self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into()));
|
||||
Next::write()
|
||||
},
|
||||
Ok(_) => Next::read(),
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::read(),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
if let Cors::Allowed(ref domain) = self.cors {
|
||||
let mut headers = res.headers_mut();
|
||||
headers.set(header::Allow(vec![Method::Options, Method::Post, Method::Get]));
|
||||
headers.set(header::AccessControlAllowHeaders(vec![
|
||||
UniCase("origin".to_owned()),
|
||||
UniCase("content-type".to_owned()),
|
||||
UniCase("accept".to_owned()),
|
||||
]));
|
||||
headers.set(header::AccessControlAllowOrigin::Value(domain.clone()));
|
||||
}
|
||||
self.handler.as_mut().unwrap().on_response(res)
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
self.handler.as_mut().unwrap().on_response_writable(encoder)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_correct_cors_value() {
|
||||
// given
|
||||
let safe_origins = vec!["chrome-extension://".to_owned(), "http://localhost:8080".to_owned()];
|
||||
let cut = EchoHandler {
|
||||
safe_origins: safe_origins,
|
||||
content: String::new(),
|
||||
cors: Cors::No,
|
||||
handler: None,
|
||||
};
|
||||
|
||||
// when
|
||||
let res1 = cut.cors_header(Some("http://ethcore.io".into()));
|
||||
let res2 = cut.cors_header(Some("http://localhost:8080".into()));
|
||||
let res3 = cut.cors_header(Some("chrome-extension://deadbeefcafe".into()));
|
||||
let res4 = cut.cors_header(None);
|
||||
|
||||
|
||||
// then
|
||||
assert_eq!(res1, Cors::Forbidden);
|
||||
assert_eq!(res2, Cors::Allowed("http://localhost:8080".into()));
|
||||
assert_eq!(res3, Cors::Allowed("chrome-extension://deadbeefcafe".into()));
|
||||
assert_eq!(res4, Cors::No);
|
||||
}
|
||||
57
dapps/src/handlers/mod.rs
Normal file
57
dapps/src/handlers/mod.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Hyper handlers implementations.
|
||||
|
||||
mod auth;
|
||||
mod echo;
|
||||
mod content;
|
||||
mod redirect;
|
||||
|
||||
pub use self::auth::AuthRequiredHandler;
|
||||
pub use self::echo::EchoHandler;
|
||||
pub use self::content::ContentHandler;
|
||||
pub use self::redirect::Redirection;
|
||||
|
||||
use url::Url;
|
||||
use hyper::{server, header, net, uri};
|
||||
|
||||
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
||||
match *req.uri() {
|
||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||
match Url::from_generic_url(url.clone()) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
uri::RequestUri::AbsolutePath(ref path) => {
|
||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||
let url_string = match req.headers().get::<header::Host>() {
|
||||
Some(ref host) => {
|
||||
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
||||
match Url::parse(&url_string) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,27 +45,32 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate url;
|
||||
extern crate url as url_lib;
|
||||
extern crate hyper;
|
||||
extern crate unicase;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_http_server;
|
||||
extern crate parity_dapps;
|
||||
extern crate ethcore_rpc;
|
||||
extern crate ethcore_util;
|
||||
extern crate mime_guess;
|
||||
|
||||
mod endpoint;
|
||||
mod apps;
|
||||
mod page;
|
||||
mod router;
|
||||
mod handlers;
|
||||
mod rpc;
|
||||
mod api;
|
||||
mod proxypac;
|
||||
mod url;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::net::SocketAddr;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use jsonrpc_core::{IoHandler, IoDelegate};
|
||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||
use ethcore_rpc::Extendable;
|
||||
@@ -95,14 +100,26 @@ impl ServerBuilder {
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// returns result with `Server` handle on success or an error.
|
||||
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
||||
Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone())
|
||||
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||
Server::start_http(
|
||||
addr,
|
||||
hosts,
|
||||
NoAuth,
|
||||
self.handler.clone(),
|
||||
self.dapps_path.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||
/// return result with `Server` handle on success or an error.
|
||||
pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone())
|
||||
pub fn start_basic_auth_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||
Server::start_http(
|
||||
addr,
|
||||
hosts,
|
||||
HttpBasicAuth::single_user(username, password),
|
||||
self.handler.clone(),
|
||||
self.dapps_path.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,17 +130,39 @@ pub struct Server {
|
||||
}
|
||||
|
||||
impl Server {
|
||||
fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>, dapps_path: String) -> Result<Server, ServerError> {
|
||||
/// 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)
|
||||
}
|
||||
|
||||
fn start_http<A: Authorization + 'static>(
|
||||
addr: &SocketAddr,
|
||||
hosts: Option<Vec<String>>,
|
||||
authorization: A,
|
||||
handler: Arc<IoHandler>,
|
||||
dapps_path: String,
|
||||
) -> Result<Server, ServerError> {
|
||||
let panic_handler = Arc::new(Mutex::new(None));
|
||||
let authorization = Arc::new(authorization);
|
||||
let endpoints = Arc::new(apps::all_endpoints(dapps_path));
|
||||
let special = Arc::new({
|
||||
let mut special = HashMap::new();
|
||||
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone()));
|
||||
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(endpoints.clone()));
|
||||
special.insert(router::SpecialEndpoint::Api, api::RestApi::new(format!("{}", addr), endpoints.clone()));
|
||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||
special
|
||||
});
|
||||
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
||||
|
||||
try!(hyper::Server::http(addr))
|
||||
.handle(move |_| router::Router::new(
|
||||
@@ -131,6 +170,7 @@ impl Server {
|
||||
endpoints.clone(),
|
||||
special.clone(),
|
||||
authorization.clone(),
|
||||
hosts.clone(),
|
||||
))
|
||||
.map(|(l, srv)| {
|
||||
|
||||
@@ -175,3 +215,24 @@ impl From<hyper::error::Error> for ServerError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Server;
|
||||
|
||||
#[test]
|
||||
fn should_return_allowed_hosts() {
|
||||
// given
|
||||
let bind_address = "127.0.0.1".to_owned();
|
||||
|
||||
// when
|
||||
let all = Server::allowed_hosts(None, bind_address.clone());
|
||||
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()]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
//! Serving ProxyPac file
|
||||
|
||||
use endpoint::{Endpoint, Handler, ContentHandler, EndpointPath};
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use handlers::ContentHandler;
|
||||
use apps::DAPPS_DOMAIN;
|
||||
|
||||
pub struct ProxyPac;
|
||||
@@ -41,7 +42,7 @@ function FindProxyForURL(url, host) {{
|
||||
}}
|
||||
"#,
|
||||
DAPPS_DOMAIN, path.host, path.port);
|
||||
Box::new(ContentHandler::new(content, "application/javascript".to_owned()))
|
||||
Box::new(ContentHandler::ok(content, "application/javascript".to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,24 +16,23 @@
|
||||
|
||||
//! HTTP Authorization implementations
|
||||
|
||||
use std::io::Write;
|
||||
use std::collections::HashMap;
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
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<server::Handler<HttpStream> + Send>),
|
||||
No(Box<Handler>),
|
||||
}
|
||||
|
||||
/// Authorization interface
|
||||
pub trait Authorization : Send + Sync {
|
||||
/// Checks if authorization is valid.
|
||||
fn is_authorized(&self, req: &server::Request<HttpStream>)-> Authorized;
|
||||
fn is_authorized(&self, req: &server::Request<net::HttpStream>)-> Authorized;
|
||||
}
|
||||
|
||||
/// HTTP Basic Authorization handler
|
||||
@@ -45,18 +44,22 @@ pub struct HttpBasicAuth {
|
||||
pub struct NoAuth;
|
||||
|
||||
impl Authorization for NoAuth {
|
||||
fn is_authorized(&self, _req: &server::Request<HttpStream>)-> Authorized {
|
||||
fn is_authorized(&self, _req: &server::Request<net::HttpStream>)-> Authorized {
|
||||
Authorized::Yes
|
||||
}
|
||||
}
|
||||
|
||||
impl Authorization for HttpBasicAuth {
|
||||
fn is_authorized(&self, req: &server::Request<HttpStream>) -> Authorized {
|
||||
fn is_authorized(&self, req: &server::Request<net::HttpStream>) -> Authorized {
|
||||
let auth = self.check_auth(&req);
|
||||
|
||||
match auth {
|
||||
Access::Denied => {
|
||||
Authorized::No(Box::new(UnauthorizedHandler { write_pos: 0 }))
|
||||
Authorized::No(Box::new(ContentHandler::new(
|
||||
status::StatusCode::Unauthorized,
|
||||
"<h1>Unauthorized</h1>".into(),
|
||||
"text/html".into(),
|
||||
)))
|
||||
},
|
||||
Access::AuthRequired => {
|
||||
Authorized::No(Box::new(AuthRequiredHandler))
|
||||
@@ -89,7 +92,7 @@ impl HttpBasicAuth {
|
||||
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
|
||||
}
|
||||
|
||||
fn check_auth(&self, req: &server::Request<HttpStream>) -> Access {
|
||||
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) }
|
||||
@@ -99,63 +102,3 @@ impl HttpBasicAuth {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnauthorizedHandler {
|
||||
write_pos: usize,
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for UnauthorizedHandler {
|
||||
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);
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
let response = "Unauthorized".as_bytes();
|
||||
|
||||
if self.write_pos == response.len() {
|
||||
return Next::end();
|
||||
}
|
||||
|
||||
match encoder.write(&response[self.write_pos..]) {
|
||||
Ok(bytes) => {
|
||||
self.write_pos += bytes;
|
||||
Next::write()
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
42
dapps/src/router/host_validation.rs
Normal file
42
dapps/src/router/host_validation.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 DAPPS_DOMAIN;
|
||||
use hyper::server;
|
||||
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);
|
||||
|
||||
is_host_header_valid(request, &endpoints)
|
||||
}
|
||||
|
||||
pub fn host_invalid_response() -> Box<server::Handler<HttpStream> + Send> {
|
||||
Box::new(ContentHandler::forbidden(
|
||||
r#"
|
||||
<h1>Request with disallowed <code>Host</code> header has been blocked.</h1>
|
||||
<p>Check the URL in your browser address bar.</p>
|
||||
"#.into(),
|
||||
"text/html".into()
|
||||
))
|
||||
}
|
||||
@@ -17,23 +17,19 @@
|
||||
//! Router implementation
|
||||
//! Processes request handling authorization and dispatching it to proper application.
|
||||
|
||||
mod url;
|
||||
mod redirect;
|
||||
pub mod auth;
|
||||
mod host_validation;
|
||||
|
||||
use DAPPS_DOMAIN;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use url::Host;
|
||||
use hyper;
|
||||
use hyper::{server, uri, header};
|
||||
use hyper::{Next, Encoder, Decoder};
|
||||
use url::{Url, Host};
|
||||
use hyper::{self, server, Next, Encoder, Decoder};
|
||||
use hyper::net::HttpStream;
|
||||
use apps;
|
||||
use endpoint::{Endpoint, Endpoints, EndpointPath};
|
||||
use self::url::Url;
|
||||
use handlers::{Redirection, extract_url};
|
||||
use self::auth::{Authorization, Authorized};
|
||||
use self::redirect::Redirection;
|
||||
|
||||
/// Special endpoints are accessible on every domain (every dapp)
|
||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||
@@ -49,40 +45,48 @@ pub struct Router<A: Authorization + 'static> {
|
||||
endpoints: Arc<Endpoints>,
|
||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||
authorization: Arc<A>,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
handler: Box<server::Handler<HttpStream> + Send>,
|
||||
}
|
||||
|
||||
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
|
||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||
// Validate Host header
|
||||
if let Some(ref hosts) = self.allowed_hosts {
|
||||
if !host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect()) {
|
||||
self.handler = host_validation::host_invalid_response();
|
||||
return self.handler.on_request(req);
|
||||
}
|
||||
}
|
||||
|
||||
// Check authorization
|
||||
let auth = self.authorization.is_authorized(&req);
|
||||
if let Authorized::No(handler) = auth {
|
||||
self.handler = handler;
|
||||
return self.handler.on_request(req);
|
||||
}
|
||||
|
||||
// Choose proper handler depending on path / domain
|
||||
self.handler = match auth {
|
||||
Authorized::No(handler) => handler,
|
||||
Authorized::Yes => {
|
||||
let url = extract_url(&req);
|
||||
let endpoint = extract_endpoint(&url);
|
||||
let url = extract_url(&req);
|
||||
let endpoint = extract_endpoint(&url);
|
||||
|
||||
match endpoint {
|
||||
// First check special endpoints
|
||||
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
||||
self.special.get(endpoint).unwrap().to_handler(path.clone().unwrap_or_default())
|
||||
},
|
||||
// Then delegate to dapp
|
||||
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
||||
self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone())
|
||||
},
|
||||
// Redirection to main page
|
||||
_ if *req.method() == hyper::method::Method::Get => {
|
||||
Redirection::new(self.main_page)
|
||||
},
|
||||
// RPC by default
|
||||
_ => {
|
||||
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default())
|
||||
}
|
||||
}
|
||||
self.handler = match endpoint {
|
||||
// First check special endpoints
|
||||
(ref path, ref endpoint) if self.special.contains_key(endpoint) => {
|
||||
self.special.get(endpoint).unwrap().to_handler(path.clone().unwrap_or_default())
|
||||
},
|
||||
// Then delegate to dapp
|
||||
(Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => {
|
||||
self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone())
|
||||
},
|
||||
// Redirection to main page
|
||||
_ if *req.method() == hyper::method::Method::Get => {
|
||||
Redirection::new(self.main_page)
|
||||
},
|
||||
// RPC by default
|
||||
_ => {
|
||||
self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default())
|
||||
}
|
||||
};
|
||||
|
||||
@@ -111,7 +115,9 @@ impl<A: Authorization> Router<A> {
|
||||
main_page: &'static str,
|
||||
endpoints: Arc<Endpoints>,
|
||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||
authorization: Arc<A>) -> Self {
|
||||
authorization: Arc<A>,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
) -> Self {
|
||||
|
||||
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
||||
Router {
|
||||
@@ -119,37 +125,12 @@ impl<A: Authorization> Router<A> {
|
||||
endpoints: endpoints,
|
||||
special: special,
|
||||
authorization: authorization,
|
||||
allowed_hosts: allowed_hosts,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_url(req: &server::Request<HttpStream>) -> Option<Url> {
|
||||
match *req.uri() {
|
||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||
match Url::from_generic_url(url.clone()) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
uri::RequestUri::AbsolutePath(ref path) => {
|
||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||
let url_string = match req.headers().get::<header::Host>() {
|
||||
Some(ref host) => {
|
||||
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
||||
match Url::parse(&url_string) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint) {
|
||||
fn special_endpoint(url: &Url) -> SpecialEndpoint {
|
||||
if url.path.len() <= 1 {
|
||||
|
||||
@@ -23,19 +23,22 @@ pub fn rpc(handler: Arc<IoHandler>, panic_handler: Arc<Mutex<Option<Box<Fn() ->
|
||||
Box::new(RpcEndpoint {
|
||||
handler: handler,
|
||||
panic_handler: panic_handler,
|
||||
cors_domain: vec![AccessControlAllowOrigin::Null],
|
||||
cors_domain: Some(vec![AccessControlAllowOrigin::Null]),
|
||||
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
|
||||
allowed_hosts: None,
|
||||
})
|
||||
}
|
||||
|
||||
struct RpcEndpoint {
|
||||
handler: Arc<IoHandler>,
|
||||
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
|
||||
cors_domain: Vec<AccessControlAllowOrigin>,
|
||||
cors_domain: Option<Vec<AccessControlAllowOrigin>>,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Endpoint for RpcEndpoint {
|
||||
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
||||
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
|
||||
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone(), panic_handler))
|
||||
Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone(), self.allowed_hosts.clone(), panic_handler))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
|
||||
|
||||
use url::Host;
|
||||
use url::{self};
|
||||
use url_lib::{self};
|
||||
pub use url_lib::Host;
|
||||
|
||||
/// HTTP/HTTPS URL type for Iron.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Url {
|
||||
/// Raw url of url
|
||||
pub raw: url::Url,
|
||||
pub raw: url_lib::Url,
|
||||
|
||||
/// The host field of the URL, probably a domain.
|
||||
pub host: Host,
|
||||
@@ -62,14 +62,14 @@ impl Url {
|
||||
/// See: http://url.spec.whatwg.org/#special-scheme
|
||||
pub fn parse(input: &str) -> Result<Url, String> {
|
||||
// Parse the string using rust-url, then convert.
|
||||
match url::Url::parse(input) {
|
||||
match url_lib::Url::parse(input) {
|
||||
Ok(raw_url) => Url::from_generic_url(raw_url),
|
||||
Err(e) => Err(format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Url` from a `rust-url` `Url`.
|
||||
pub fn from_generic_url(raw_url: url::Url) -> Result<Url, String> {
|
||||
pub fn from_generic_url(raw_url: url_lib::Url) -> Result<Url, String> {
|
||||
// Map empty usernames to None.
|
||||
let username = match raw_url.username() {
|
||||
"" => None,
|
||||
@@ -12,7 +12,7 @@ syntex = "*"
|
||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
clippy = { version = "0.0.77", optional = true}
|
||||
clippy = { version = "0.0.80", optional = true}
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" }
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
//! Ethcore rocksdb ipc service
|
||||
|
||||
use traits::*;
|
||||
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator,
|
||||
IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
|
||||
use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator, IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction};
|
||||
use std::sync::{RwLock, Arc};
|
||||
use std::convert::From;
|
||||
use ipc::IpcConfig;
|
||||
@@ -26,12 +25,6 @@ use std::mem;
|
||||
use ipc::binary::BinaryConvertError;
|
||||
use std::collections::{VecDeque, HashMap, BTreeMap};
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(s: String) -> Error {
|
||||
Error::RocksDb(s)
|
||||
}
|
||||
}
|
||||
|
||||
enum WriteCacheEntry {
|
||||
Remove,
|
||||
Write(Vec<u8>),
|
||||
@@ -60,7 +53,7 @@ impl WriteCache {
|
||||
self.entries.insert(key, WriteCacheEntry::Remove);
|
||||
}
|
||||
|
||||
fn get(&self, key: &Vec<u8>) -> Option<Vec<u8>> {
|
||||
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()),
|
||||
@@ -137,8 +130,8 @@ impl Database {
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write().unwrap();
|
||||
let db_lock = self.db.read().unwrap();
|
||||
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();
|
||||
|
||||
@@ -147,8 +140,8 @@ impl Database {
|
||||
}
|
||||
|
||||
pub fn flush_all(&self) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write().unwrap();
|
||||
let db_lock = self.db.read().unwrap();
|
||||
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");
|
||||
|
||||
@@ -167,7 +160,7 @@ impl Drop for Database {
|
||||
#[derive(Ipc)]
|
||||
impl DatabaseService for Database {
|
||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error> {
|
||||
let mut db = self.db.write().unwrap();
|
||||
let mut db = self.db.write();
|
||||
if db.is_some() { return Err(Error::AlreadyOpen); }
|
||||
|
||||
let mut opts = Options::new();
|
||||
@@ -194,7 +187,7 @@ impl DatabaseService for Database {
|
||||
fn close(&self) -> Result<(), Error> {
|
||||
try!(self.flush_all());
|
||||
|
||||
let mut db = self.db.write().unwrap();
|
||||
let mut db = self.db.write();
|
||||
if db.is_none() { return Err(Error::IsClosed); }
|
||||
|
||||
*db = None;
|
||||
@@ -202,19 +195,19 @@ impl DatabaseService for Database {
|
||||
}
|
||||
|
||||
fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error> {
|
||||
let mut cache_lock = self.write_cache.write().unwrap();
|
||||
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().unwrap();
|
||||
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().unwrap();
|
||||
let mut cache_lock = self.write_cache.write();
|
||||
|
||||
let mut writes = transaction.writes.borrow_mut();
|
||||
for kv in writes.drain(..) {
|
||||
@@ -231,13 +224,13 @@ impl DatabaseService for Database {
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
||||
{
|
||||
let key_vec = key.to_vec();
|
||||
let cache_hit = self.write_cache.read().unwrap().get(&key_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().unwrap();
|
||||
let db_lock = self.db.read();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
|
||||
match try!(db.get(key)) {
|
||||
@@ -249,7 +242,7 @@ impl DatabaseService for Database {
|
||||
}
|
||||
|
||||
fn get_by_prefix(&self, prefix: &[u8]) -> Result<Option<Vec<u8>>, Error> {
|
||||
let db_lock = self.db.read().unwrap();
|
||||
let db_lock = self.db.read();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
|
||||
let mut iter = db.iterator(IteratorMode::From(prefix, Direction::Forward));
|
||||
@@ -261,17 +254,17 @@ impl DatabaseService for Database {
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> Result<bool, Error> {
|
||||
let db_lock = self.db.read().unwrap();
|
||||
let db_lock = self.db.read();
|
||||
let db = try!(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().unwrap();
|
||||
let db_lock = self.db.read();
|
||||
let db = try!(db_lock.as_ref().ok_or(Error::IsClosed));
|
||||
|
||||
let mut iterators = self.iterators.write().unwrap();
|
||||
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)
|
||||
@@ -279,7 +272,7 @@ impl DatabaseService for Database {
|
||||
|
||||
fn iter_next(&self, handle: IteratorHandle) -> Option<KeyValue>
|
||||
{
|
||||
let mut iterators = self.iterators.write().unwrap();
|
||||
let mut iterators = self.iterators.write();
|
||||
let mut iterator = match iterators.get_mut(&handle) {
|
||||
Some(some_iterator) => some_iterator,
|
||||
None => { return None; },
|
||||
@@ -294,7 +287,7 @@ impl DatabaseService for Database {
|
||||
}
|
||||
|
||||
fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error> {
|
||||
let mut iterators = self.iterators.write().unwrap();
|
||||
let mut iterators = self.iterators.write();
|
||||
iterators.remove(&handle);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ pub struct KeyValue {
|
||||
pub value: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Binary)]
|
||||
pub enum Error {
|
||||
#[derive(Debug, Binary)]
|
||||
pub enum Error {
|
||||
AlreadyOpen,
|
||||
IsClosed,
|
||||
RocksDb(String),
|
||||
@@ -41,6 +41,12 @@ pub struct KeyValue {
|
||||
UncommitedTransactions,
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(s: String) -> Error {
|
||||
Error::RocksDb(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Database configuration
|
||||
#[derive(Binary)]
|
||||
pub struct DatabaseConfig {
|
||||
@@ -68,7 +74,7 @@ impl DatabaseConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DatabaseService : Sized {
|
||||
pub trait DatabaseService : Sized {
|
||||
/// Opens database in the specified path
|
||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>;
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
extern crate rand;
|
||||
|
||||
pub mod random_path;
|
||||
pub mod test_socket;
|
||||
pub mod stop_guard;
|
||||
mod random_path;
|
||||
mod test_socket;
|
||||
mod stop_guard;
|
||||
|
||||
pub use random_path::*;
|
||||
pub use test_socket::*;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
use std::path::*;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use rand::random;
|
||||
|
||||
pub struct RandomTempPath {
|
||||
@@ -74,6 +75,35 @@ impl Drop for RandomTempPath {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GuardedTempResult<T> {
|
||||
pub result: Option<T>,
|
||||
pub _temp: RandomTempPath
|
||||
}
|
||||
|
||||
impl<T> GuardedTempResult<T> {
|
||||
pub fn reference(&self) -> &T {
|
||||
self.result.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn reference_mut(&mut self) -> &mut T {
|
||||
self.result.as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> T {
|
||||
self.result.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for GuardedTempResult<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T { self.result.as_ref().unwrap() }
|
||||
}
|
||||
|
||||
impl<T> DerefMut for GuardedTempResult<T> {
|
||||
fn deref_mut(&mut self) -> &mut T { self.result.as_mut().unwrap() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creates_dir() {
|
||||
let temp = RandomTempPath::create_dir();
|
||||
|
||||
@@ -62,6 +62,7 @@ impl TestSocket {
|
||||
impl Read for TestSocket {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let end_position = cmp::min(self.read_buffer.len(), self.cursor+buf.len());
|
||||
if self.cursor > end_position { return Ok(0) }
|
||||
let len = cmp::max(end_position - self.cursor, 0);
|
||||
match len {
|
||||
0 => Ok(0),
|
||||
@@ -69,7 +70,7 @@ impl Read for TestSocket {
|
||||
for i in self.cursor..end_position {
|
||||
buf[i-self.cursor] = self.read_buffer[i];
|
||||
}
|
||||
self.cursor = self.cursor + buf.len();
|
||||
self.cursor = end_position;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
14
doc.sh
14
doc.sh
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
# generate documentation only for partiy and ethcore libraries
|
||||
|
||||
cargo doc --no-deps --verbose \
|
||||
-p ethkey \
|
||||
-p ethstore \
|
||||
-p ethash \
|
||||
-p ethcore-util \
|
||||
-p ethcore \
|
||||
-p ethsync \
|
||||
-p ethcore-rpc \
|
||||
-p ethcore-signer \
|
||||
-p ethcore-dapps \
|
||||
-p parity \
|
||||
@@ -23,15 +23,9 @@ RUN rustup target add aarch64-unknown-linux-gnu
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# set compilers
|
||||
ENV CXX aarch64-linux-gnu-g++
|
||||
ENV CC aarch64-linux-gnu-gcc
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && \
|
||||
cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
cargo -V
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
|
||||
@@ -23,15 +23,9 @@ RUN rustup target add armv7-unknown-linux-gnueabihf
|
||||
# show backtraces
|
||||
ENV RUST_BACKTRACE 1
|
||||
|
||||
# set compilers
|
||||
ENV CXX arm-linux-gnueabihf-g++
|
||||
ENV CC arm-linux-gnueabihf-gcc
|
||||
|
||||
# show tools
|
||||
RUN rustc -vV && \
|
||||
cargo -V && \
|
||||
gcc -v &&\
|
||||
g++ -v
|
||||
cargo -V
|
||||
|
||||
# build parity
|
||||
RUN git clone https://github.com/ethcore/parity && \
|
||||
|
||||
@@ -32,4 +32,5 @@ RUN git clone https://github.com/ethcore/parity && \
|
||||
cargo build --release --verbose && \
|
||||
ls /build/parity/target/release/parity && \
|
||||
strip /build/parity/target/release/parity
|
||||
RUN file /build/parity/target/release/parity
|
||||
|
||||
ENTRYPOINT ["/build/parity/target/release/parity"]
|
||||
|
||||
@@ -9,3 +9,4 @@ authors = ["arkpar <arkadiy@ethcore.io"]
|
||||
log = "0.3"
|
||||
sha3 = { path = "../util/sha3" }
|
||||
primal = "0.2.3"
|
||||
parking_lot = "0.2.6"
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
use primal::is_prime;
|
||||
use std::cell::Cell;
|
||||
use std::sync::Mutex;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use sha3;
|
||||
@@ -30,6 +29,8 @@ use std::path::PathBuf;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::fs::{self, File};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
pub const ETHASH_EPOCH_LENGTH: u64 = 30000;
|
||||
pub const ETHASH_CACHE_ROUNDS: usize = 3;
|
||||
pub const ETHASH_MIX_BYTES: usize = 128;
|
||||
@@ -134,7 +135,7 @@ impl Light {
|
||||
}
|
||||
|
||||
pub fn to_file(&self) -> io::Result<()> {
|
||||
let seed_compute = self.seed_compute.lock().unwrap();
|
||||
let seed_compute = self.seed_compute.lock();
|
||||
let path = Light::file_path(seed_compute.get_seedhash(self.block_number));
|
||||
try!(fs::create_dir_all(path.parent().unwrap()));
|
||||
let mut file = try!(File::create(path));
|
||||
@@ -270,11 +271,12 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
||||
|
||||
let page_size = 4 * MIX_WORDS;
|
||||
let num_full_pages = (full_size / page_size) as u32;
|
||||
let cache: &[Node] = &light.cache; // deref once for better performance
|
||||
|
||||
for i in 0..(ETHASH_ACCESSES as u32) {
|
||||
let index = fnv_hash(f_mix.get_unchecked(0).as_words().get_unchecked(0) ^ i, *mix.get_unchecked(0).as_words().get_unchecked((i as usize) % MIX_WORDS)) % num_full_pages;
|
||||
for n in 0..MIX_NODES {
|
||||
let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, light);
|
||||
let tmp_node = calculate_dag_item(index * MIX_NODES as u32 + n as u32, cache);
|
||||
for w in 0..NODE_WORDS {
|
||||
*mix.get_unchecked_mut(n).as_words_mut().get_unchecked_mut(w) = fnv_hash(*mix.get_unchecked(n).as_words().get_unchecked(w), *tmp_node.as_words().get_unchecked(w));
|
||||
}
|
||||
@@ -305,18 +307,17 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_dag_item(node_index: u32, light: &Light) -> Node {
|
||||
fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
|
||||
unsafe {
|
||||
let num_parent_nodes = light.cache.len();
|
||||
let cache_nodes = &light.cache;
|
||||
let init = cache_nodes.get_unchecked(node_index as usize % num_parent_nodes);
|
||||
let num_parent_nodes = cache.len();
|
||||
let init = cache.get_unchecked(node_index as usize % num_parent_nodes);
|
||||
let mut ret = init.clone();
|
||||
*ret.as_words_mut().get_unchecked_mut(0) ^= node_index;
|
||||
sha3::sha3_512(ret.bytes.as_mut_ptr(), ret.bytes.len(), ret.bytes.as_ptr(), ret.bytes.len());
|
||||
|
||||
for i in 0..ETHASH_DATASET_PARENTS {
|
||||
let parent_index = fnv_hash(node_index ^ i, *ret.as_words().get_unchecked(i as usize % NODE_WORDS)) % num_parent_nodes as u32;
|
||||
let parent = cache_nodes.get_unchecked(parent_index as usize);
|
||||
let parent = cache.get_unchecked(parent_index as usize);
|
||||
for w in 0..NODE_WORDS {
|
||||
*ret.as_words_mut().get_unchecked_mut(w) = fnv_hash(*ret.as_words().get_unchecked(w), *parent.as_words().get_unchecked(w));
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
//! See https://github.com/ethereum/wiki/wiki/Ethash
|
||||
extern crate primal;
|
||||
extern crate sha3;
|
||||
extern crate parking_lot;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
mod compute;
|
||||
@@ -26,7 +28,8 @@ use std::mem;
|
||||
use compute::Light;
|
||||
pub use compute::{ETHASH_EPOCH_LENGTH, H256, ProofOfWork, SeedHashCompute, quick_get_difficulty};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
struct LightCache {
|
||||
recent_epoch: Option<u64>,
|
||||
@@ -61,7 +64,7 @@ impl EthashManager {
|
||||
pub fn compute_light(&self, block_number: u64, header_hash: &H256, nonce: u64) -> ProofOfWork {
|
||||
let epoch = block_number / ETHASH_EPOCH_LENGTH;
|
||||
let light = {
|
||||
let mut lights = self.cache.lock().unwrap();
|
||||
let mut lights = self.cache.lock();
|
||||
let light = match lights.recent_epoch.clone() {
|
||||
Some(ref e) if *e == epoch => lights.recent.clone(),
|
||||
_ => match lights.prev_epoch.clone() {
|
||||
@@ -108,12 +111,12 @@ fn test_lru() {
|
||||
let hash = [0u8; 32];
|
||||
ethash.compute_light(1, &hash, 1);
|
||||
ethash.compute_light(50000, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 1);
|
||||
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 0);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 1);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
|
||||
ethash.compute_light(1, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 0);
|
||||
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 1);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 0);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 1);
|
||||
ethash.compute_light(70000, &hash, 1);
|
||||
assert_eq!(ethash.cache.lock().unwrap().recent_epoch.unwrap(), 2);
|
||||
assert_eq!(ethash.cache.lock().unwrap().prev_epoch.unwrap(), 0);
|
||||
assert_eq!(ethash.cache.lock().recent_epoch.unwrap(), 2);
|
||||
assert_eq!(ethash.cache.lock().prev_epoch.unwrap(), 0);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ authors = ["Ethcore <admin@ethcore.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
syntex = "*"
|
||||
"ethcore-ipc-codegen" = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
@@ -17,20 +16,28 @@ env_logger = "0.3"
|
||||
rustc-serialize = "0.3"
|
||||
heapsize = "0.3"
|
||||
rust-crypto = "0.2.34"
|
||||
time = "0.1"
|
||||
ethcore-util = { path = "../util" }
|
||||
evmjit = { path = "../evmjit", optional = true }
|
||||
ethash = { path = "../ethash" }
|
||||
num_cpus = "0.2"
|
||||
clippy = { version = "0.0.77", optional = true}
|
||||
crossbeam = "0.2.9"
|
||||
lazy_static = "0.2"
|
||||
bloomchain = "0.1"
|
||||
rayon = "0.4"
|
||||
semver = "0.2"
|
||||
bit-set = "0.4"
|
||||
time = "0.1"
|
||||
evmjit = { path = "../evmjit", optional = true }
|
||||
clippy = { version = "0.0.80", optional = true}
|
||||
ethash = { path = "../ethash" }
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore-io = { path = "../util/io" }
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethjson = { path = "../json" }
|
||||
bloomchain = "0.1"
|
||||
"ethcore-ipc" = { path = "../ipc/rpc" }
|
||||
rayon = "0.3.1"
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
ethstore = { path = "../ethstore" }
|
||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||
rand = "0.3"
|
||||
lru-cache = "0.0.7"
|
||||
bloomfilter = { git = "https://github.com/ethcore/rust-bloom-filter" }
|
||||
byteorder = "0.5"
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/ethcore/hyper"
|
||||
@@ -43,3 +50,5 @@ json-tests = []
|
||||
test-heavy = []
|
||||
dev = ["clippy"]
|
||||
default = []
|
||||
benches = []
|
||||
ipc = []
|
||||
|
||||
@@ -14,20 +14,10 @@
|
||||
// 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 syntex;
|
||||
extern crate ethcore_ipc_codegen as codegen;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
extern crate ethcore_ipc_codegen;
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
// serialization pass
|
||||
{
|
||||
let src = Path::new("src/types/mod.rs.in");
|
||||
let dst = Path::new(&out_dir).join("types.rs");
|
||||
let mut registry = syntex::Registry::new();
|
||||
codegen::register(&mut registry);
|
||||
registry.expand("", &src, &dst).unwrap();
|
||||
}
|
||||
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc("src/client/traits.rs").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc("src/client/chain_notify.rs").unwrap();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "Frontier/Homestead",
|
||||
"name": "Ethereum Classic",
|
||||
"forkName": "classic",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
@@ -9,8 +10,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"daoRescueSoftFork": true
|
||||
"frontierCompatibilityModeLimit": "0x118c30"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18,7 +18,9 @@
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
"networkID" : "0x1",
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
@@ -36,10 +38,10 @@
|
||||
"stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"
|
||||
},
|
||||
"nodes": [
|
||||
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
||||
"enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303",
|
||||
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
|
||||
"enode://248f12bc8b18d5289358085520ac78cd8076485211e6d96ab0bc93d6cd25442db0ce3a937dc404f64f207b0b9aed50e25e98ce32af5ac7cb321ff285b97de485@zero.parity.io:30303"
|
||||
"enode://e809c4a2fec7daed400e5e28564e23693b23b2cc5a019b612505631bbe7b9ccf709c1796d2a3d29ef2b045f210caf51e3c4f5b6d3587d43ad5d6397526fa6179@174.112.32.157:30303",
|
||||
"enode://687be94c3a7beaa3d2fde82fa5046cdeb3e8198354e05b29d6e0d4e276713e3707ac10f784a7904938b06b46c764875c241b0337dd853385a4d8bfcbf8190647@95.183.51.229:30303",
|
||||
"enode://6e538e7c1280f0a31ff08b382db5302480f775480b8e68f8febca0ceff81e4b19153c6f8bf60313b93bef2cc34d34e1df41317de0ce613a201d1660a788a03e2@52.206.67.235:30303",
|
||||
"enode://217ebe27e89bf4fec8ce06509323ff095b1014378deb75ab2e5f6759a4e8750a3bd8254b8c6833136e4d5e58230d65ee8ab34a5db5abf0640408c4288af3c8a7@188.138.1.237:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
162
ethcore/res/ethereum/daohardfork_test.json
Normal file
162
ethcore/res/ethereum/daohardfork_test.json
Normal file
@@ -0,0 +1,162 @@
|
||||
{
|
||||
"name": "DAO hard-fork consensus test",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x5",
|
||||
"daoHardforkTransition": "0x8",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
"0xd4fe7bc31cedb7bfb8a345f31e668033056b2728",
|
||||
"0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425",
|
||||
"0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f",
|
||||
"0xecd135fa4f61a655311e86238c92adcd779555d2",
|
||||
"0x1975bd06d486162d5dc297798dfc41edd5d160a7",
|
||||
"0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6",
|
||||
"0x319f70bab6845585f412ec7724b744fec6095c85",
|
||||
"0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936",
|
||||
"0x5c8536898fbb74fc7445814902fd08422eac56d0",
|
||||
"0x6966ab0d485353095148a2155858910e0965b6f9",
|
||||
"0x779543a0491a837ca36ce8c635d6154e3c4911a6",
|
||||
"0x2a5ed960395e2a49b1c758cef4aa15213cfd874c",
|
||||
"0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5",
|
||||
"0x9c50426be05db97f5d64fc54bf89eff947f0a321",
|
||||
"0x200450f06520bdd6c527622a273333384d870efb",
|
||||
"0xbe8539bfe837b67d1282b2b1d61c3f723966f049",
|
||||
"0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb",
|
||||
"0xf1385fb24aad0cd7432824085e42aff90886fef5",
|
||||
"0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091",
|
||||
"0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd",
|
||||
"0x51e0ddd9998364a2eb38588679f0d2c42653e4a6",
|
||||
"0x627a0a960c079c21c34f7612d5d230e01b4ad4c7",
|
||||
"0xf0b1aa0eb660754448a7937c022e30aa692fe0c5",
|
||||
"0x24c4d950dfd4dd1902bbed3508144a54542bba94",
|
||||
"0x9f27daea7aca0aa0446220b98d028715e3bc803d",
|
||||
"0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90",
|
||||
"0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b",
|
||||
"0x63ed5a272de2f6d968408b4acb9024f4cc208ebf",
|
||||
"0x6f6704e5a10332af6672e50b3d9754dc460dfa4d",
|
||||
"0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6",
|
||||
"0x492ea3bb0f3315521c31f273e565b868fc090f17",
|
||||
"0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00",
|
||||
"0x9ea779f907f0b315b364b0cfc39a0fde5b02a416",
|
||||
"0xceaeb481747ca6c540a000c1f3641f8cef161fa7",
|
||||
"0xcc34673c6c40e791051898567a1222daf90be287",
|
||||
"0x579a80d909f346fbfb1189493f521d7f48d52238",
|
||||
"0xe308bd1ac5fda103967359b2712dd89deffb7973",
|
||||
"0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c",
|
||||
"0xac1ecab32727358dba8962a0f3b261731aad9723",
|
||||
"0x4fd6ace747f06ece9c49699c7cabc62d02211f75",
|
||||
"0x440c59b325d2997a134c2c7c60a8c61611212bad",
|
||||
"0x4486a3d68fac6967006d7a517b889fd3f98c102b",
|
||||
"0x9c15b54878ba618f494b38f0ae7443db6af648ba",
|
||||
"0x27b137a85656544b1ccb5a0f2e561a5703c6a68f",
|
||||
"0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241",
|
||||
"0x23b75c2f6791eef49c69684db4c6c1f93bf49a50",
|
||||
"0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b",
|
||||
"0xb9637156d330c0d605a791f1c31ba5890582fe1c",
|
||||
"0x6131c42fa982e56929107413a9d526fd99405560",
|
||||
"0x1591fc0f688c81fbeb17f5426a162a7024d430c2",
|
||||
"0x542a9515200d14b68e934e9830d91645a980dd7a",
|
||||
"0xc4bbd073882dd2add2424cf47d35213405b01324",
|
||||
"0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4",
|
||||
"0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb",
|
||||
"0x3ba4d81db016dc2890c81f3acec2454bff5aada5",
|
||||
"0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab",
|
||||
"0xe4ae1efdfc53b73893af49113d8694a057b9c0d1",
|
||||
"0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5",
|
||||
"0x0737a6b837f97f46ebade41b9bc3e1c509c85c53",
|
||||
"0x97f43a37f595ab5dd318fb46e7a155eae057317a",
|
||||
"0x52c5317c848ba20c7504cb2c8052abd1fde29d03",
|
||||
"0x4863226780fe7c0356454236d3b1c8792785748d",
|
||||
"0x5d2b2e6fcbe3b11d26b525e085ff818dae332479",
|
||||
"0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c",
|
||||
"0x057b56736d32b86616a10f619859c6cd6f59092a",
|
||||
"0x9aa008f65de0b923a2a4f02012ad034a5e2e2192",
|
||||
"0x304a554a310c7e546dfe434669c62820b7d83490",
|
||||
"0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79",
|
||||
"0x4deb0033bb26bc534b197e61d19e0733e5679784",
|
||||
"0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a",
|
||||
"0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b",
|
||||
"0x4fa802324e929786dbda3b8820dc7834e9134a2a",
|
||||
"0x9da397b9e80755301a3b32173283a91c0ef6c87e",
|
||||
"0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6",
|
||||
"0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9",
|
||||
"0x5dc28b15dffed94048d73806ce4b7a4612a1d48f",
|
||||
"0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76",
|
||||
"0x12e626b0eebfe86a56d633b9864e389b45dcb260",
|
||||
"0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7",
|
||||
"0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5",
|
||||
"0xd164b088bd9108b60d0ca3751da4bceb207b0782",
|
||||
"0x6231b6d0d5e77fe001c2a460bd9584fee60d409b",
|
||||
"0x1cba23d343a983e9b5cfd19496b9a9701ada385f",
|
||||
"0xa82f360a8d3455c5c41366975bde739c37bfeb8a",
|
||||
"0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339",
|
||||
"0x005f5cee7a43331d5a3d3eec71305925a62f34b6",
|
||||
"0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d",
|
||||
"0xd131637d5275fd1a68a3200f4ad25c71a2a9522e",
|
||||
"0xbc07118b9ac290e4622f5e77a0853539789effbe",
|
||||
"0x47e7aa56d6bdf3f36be34619660de61275420af8",
|
||||
"0xacd87e28b0c9d1254e868b81cba4cc20d9a32225",
|
||||
"0xadf80daec7ba8dcf15392f1ac611fff65d94f880",
|
||||
"0x5524c55fb03cf21f549444ccbecb664d0acad706",
|
||||
"0x40b803a9abce16f50f36a77ba41180eb90023925",
|
||||
"0xfe24cdd8648121a43a7c86d289be4dd2951ed49f",
|
||||
"0x17802f43a0137c506ba92291391a8a8f207f487d",
|
||||
"0x253488078a4edf4d6f42f113d1e62836a942cf1a",
|
||||
"0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915",
|
||||
"0xb136707642a4ea12fb4bae820f03d2562ebff487",
|
||||
"0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940",
|
||||
"0xf14c14075d6c4ed84b86798af0956deef67365b5",
|
||||
"0xca544e5c4687d109611d0f8f928b53a25af72448",
|
||||
"0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c",
|
||||
"0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7",
|
||||
"0x6d87578288b6cb5549d5076a207456a1f6a63dc0",
|
||||
"0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e",
|
||||
"0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6",
|
||||
"0x2b3455ec7fedf16e646268bf88846bd7a2319bb2",
|
||||
"0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a",
|
||||
"0xd343b217de44030afaa275f54d31a9317c7f441e",
|
||||
"0x84ef4b2357079cd7a7c69fd7a37cd0609a679106",
|
||||
"0xda2fef9e4a3230988ff17df2165440f37e8b1708",
|
||||
"0xf4c64518ea10f995918a454158c6b61407ea345c",
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,126 @@
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"daoRescueSoftFork": false
|
||||
"daoHardforkTransition": "0x1d4c00",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
"0xd4fe7bc31cedb7bfb8a345f31e668033056b2728",
|
||||
"0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425",
|
||||
"0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f",
|
||||
"0xecd135fa4f61a655311e86238c92adcd779555d2",
|
||||
"0x1975bd06d486162d5dc297798dfc41edd5d160a7",
|
||||
"0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6",
|
||||
"0x319f70bab6845585f412ec7724b744fec6095c85",
|
||||
"0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936",
|
||||
"0x5c8536898fbb74fc7445814902fd08422eac56d0",
|
||||
"0x6966ab0d485353095148a2155858910e0965b6f9",
|
||||
"0x779543a0491a837ca36ce8c635d6154e3c4911a6",
|
||||
"0x2a5ed960395e2a49b1c758cef4aa15213cfd874c",
|
||||
"0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5",
|
||||
"0x9c50426be05db97f5d64fc54bf89eff947f0a321",
|
||||
"0x200450f06520bdd6c527622a273333384d870efb",
|
||||
"0xbe8539bfe837b67d1282b2b1d61c3f723966f049",
|
||||
"0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb",
|
||||
"0xf1385fb24aad0cd7432824085e42aff90886fef5",
|
||||
"0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091",
|
||||
"0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd",
|
||||
"0x51e0ddd9998364a2eb38588679f0d2c42653e4a6",
|
||||
"0x627a0a960c079c21c34f7612d5d230e01b4ad4c7",
|
||||
"0xf0b1aa0eb660754448a7937c022e30aa692fe0c5",
|
||||
"0x24c4d950dfd4dd1902bbed3508144a54542bba94",
|
||||
"0x9f27daea7aca0aa0446220b98d028715e3bc803d",
|
||||
"0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90",
|
||||
"0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b",
|
||||
"0x63ed5a272de2f6d968408b4acb9024f4cc208ebf",
|
||||
"0x6f6704e5a10332af6672e50b3d9754dc460dfa4d",
|
||||
"0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6",
|
||||
"0x492ea3bb0f3315521c31f273e565b868fc090f17",
|
||||
"0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00",
|
||||
"0x9ea779f907f0b315b364b0cfc39a0fde5b02a416",
|
||||
"0xceaeb481747ca6c540a000c1f3641f8cef161fa7",
|
||||
"0xcc34673c6c40e791051898567a1222daf90be287",
|
||||
"0x579a80d909f346fbfb1189493f521d7f48d52238",
|
||||
"0xe308bd1ac5fda103967359b2712dd89deffb7973",
|
||||
"0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c",
|
||||
"0xac1ecab32727358dba8962a0f3b261731aad9723",
|
||||
"0x4fd6ace747f06ece9c49699c7cabc62d02211f75",
|
||||
"0x440c59b325d2997a134c2c7c60a8c61611212bad",
|
||||
"0x4486a3d68fac6967006d7a517b889fd3f98c102b",
|
||||
"0x9c15b54878ba618f494b38f0ae7443db6af648ba",
|
||||
"0x27b137a85656544b1ccb5a0f2e561a5703c6a68f",
|
||||
"0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241",
|
||||
"0x23b75c2f6791eef49c69684db4c6c1f93bf49a50",
|
||||
"0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b",
|
||||
"0xb9637156d330c0d605a791f1c31ba5890582fe1c",
|
||||
"0x6131c42fa982e56929107413a9d526fd99405560",
|
||||
"0x1591fc0f688c81fbeb17f5426a162a7024d430c2",
|
||||
"0x542a9515200d14b68e934e9830d91645a980dd7a",
|
||||
"0xc4bbd073882dd2add2424cf47d35213405b01324",
|
||||
"0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4",
|
||||
"0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb",
|
||||
"0x3ba4d81db016dc2890c81f3acec2454bff5aada5",
|
||||
"0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab",
|
||||
"0xe4ae1efdfc53b73893af49113d8694a057b9c0d1",
|
||||
"0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5",
|
||||
"0x0737a6b837f97f46ebade41b9bc3e1c509c85c53",
|
||||
"0x97f43a37f595ab5dd318fb46e7a155eae057317a",
|
||||
"0x52c5317c848ba20c7504cb2c8052abd1fde29d03",
|
||||
"0x4863226780fe7c0356454236d3b1c8792785748d",
|
||||
"0x5d2b2e6fcbe3b11d26b525e085ff818dae332479",
|
||||
"0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c",
|
||||
"0x057b56736d32b86616a10f619859c6cd6f59092a",
|
||||
"0x9aa008f65de0b923a2a4f02012ad034a5e2e2192",
|
||||
"0x304a554a310c7e546dfe434669c62820b7d83490",
|
||||
"0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79",
|
||||
"0x4deb0033bb26bc534b197e61d19e0733e5679784",
|
||||
"0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a",
|
||||
"0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b",
|
||||
"0x4fa802324e929786dbda3b8820dc7834e9134a2a",
|
||||
"0x9da397b9e80755301a3b32173283a91c0ef6c87e",
|
||||
"0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6",
|
||||
"0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9",
|
||||
"0x5dc28b15dffed94048d73806ce4b7a4612a1d48f",
|
||||
"0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76",
|
||||
"0x12e626b0eebfe86a56d633b9864e389b45dcb260",
|
||||
"0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7",
|
||||
"0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5",
|
||||
"0xd164b088bd9108b60d0ca3751da4bceb207b0782",
|
||||
"0x6231b6d0d5e77fe001c2a460bd9584fee60d409b",
|
||||
"0x1cba23d343a983e9b5cfd19496b9a9701ada385f",
|
||||
"0xa82f360a8d3455c5c41366975bde739c37bfeb8a",
|
||||
"0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339",
|
||||
"0x005f5cee7a43331d5a3d3eec71305925a62f34b6",
|
||||
"0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d",
|
||||
"0xd131637d5275fd1a68a3200f4ad25c71a2a9522e",
|
||||
"0xbc07118b9ac290e4622f5e77a0853539789effbe",
|
||||
"0x47e7aa56d6bdf3f36be34619660de61275420af8",
|
||||
"0xacd87e28b0c9d1254e868b81cba4cc20d9a32225",
|
||||
"0xadf80daec7ba8dcf15392f1ac611fff65d94f880",
|
||||
"0x5524c55fb03cf21f549444ccbecb664d0acad706",
|
||||
"0x40b803a9abce16f50f36a77ba41180eb90023925",
|
||||
"0xfe24cdd8648121a43a7c86d289be4dd2951ed49f",
|
||||
"0x17802f43a0137c506ba92291391a8a8f207f487d",
|
||||
"0x253488078a4edf4d6f42f113d1e62836a942cf1a",
|
||||
"0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915",
|
||||
"0xb136707642a4ea12fb4bae820f03d2562ebff487",
|
||||
"0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940",
|
||||
"0xf14c14075d6c4ed84b86798af0956deef67365b5",
|
||||
"0xca544e5c4687d109611d0f8f928b53a25af72448",
|
||||
"0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c",
|
||||
"0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7",
|
||||
"0x6d87578288b6cb5549d5076a207456a1f6a63dc0",
|
||||
"0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e",
|
||||
"0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6",
|
||||
"0x2b3455ec7fedf16e646268bf88846bd7a2319bb2",
|
||||
"0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a",
|
||||
"0xd343b217de44030afaa275f54d31a9317c7f441e",
|
||||
"0x84ef4b2357079cd7a7c69fd7a37cd0609a679106",
|
||||
"0xda2fef9e4a3230988ff17df2165440f37e8b1708",
|
||||
"0xf4c64518ea10f995918a454158c6b61407ea345c",
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18,7 +137,9 @@
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
"networkID" : "0x1",
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
||||
@@ -10,7 +10,126 @@
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"daoRescueSoftFork": false
|
||||
"daoHardforkTransition": "0x1d4c00",
|
||||
"daoHardforkBeneficiary": "0xbf4ed7b27f1d666546e30d74d50d173d20bca754",
|
||||
"daoHardforkAccounts": [
|
||||
"0xd4fe7bc31cedb7bfb8a345f31e668033056b2728",
|
||||
"0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425",
|
||||
"0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f",
|
||||
"0xecd135fa4f61a655311e86238c92adcd779555d2",
|
||||
"0x1975bd06d486162d5dc297798dfc41edd5d160a7",
|
||||
"0xa3acf3a1e16b1d7c315e23510fdd7847b48234f6",
|
||||
"0x319f70bab6845585f412ec7724b744fec6095c85",
|
||||
"0x06706dd3f2c9abf0a21ddcc6941d9b86f0596936",
|
||||
"0x5c8536898fbb74fc7445814902fd08422eac56d0",
|
||||
"0x6966ab0d485353095148a2155858910e0965b6f9",
|
||||
"0x779543a0491a837ca36ce8c635d6154e3c4911a6",
|
||||
"0x2a5ed960395e2a49b1c758cef4aa15213cfd874c",
|
||||
"0x5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5",
|
||||
"0x9c50426be05db97f5d64fc54bf89eff947f0a321",
|
||||
"0x200450f06520bdd6c527622a273333384d870efb",
|
||||
"0xbe8539bfe837b67d1282b2b1d61c3f723966f049",
|
||||
"0x6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb",
|
||||
"0xf1385fb24aad0cd7432824085e42aff90886fef5",
|
||||
"0xd1ac8b1ef1b69ff51d1d401a476e7e612414f091",
|
||||
"0x8163e7fb499e90f8544ea62bbf80d21cd26d9efd",
|
||||
"0x51e0ddd9998364a2eb38588679f0d2c42653e4a6",
|
||||
"0x627a0a960c079c21c34f7612d5d230e01b4ad4c7",
|
||||
"0xf0b1aa0eb660754448a7937c022e30aa692fe0c5",
|
||||
"0x24c4d950dfd4dd1902bbed3508144a54542bba94",
|
||||
"0x9f27daea7aca0aa0446220b98d028715e3bc803d",
|
||||
"0xa5dc5acd6a7968a4554d89d65e59b7fd3bff0f90",
|
||||
"0xd9aef3a1e38a39c16b31d1ace71bca8ef58d315b",
|
||||
"0x63ed5a272de2f6d968408b4acb9024f4cc208ebf",
|
||||
"0x6f6704e5a10332af6672e50b3d9754dc460dfa4d",
|
||||
"0x77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6",
|
||||
"0x492ea3bb0f3315521c31f273e565b868fc090f17",
|
||||
"0x0ff30d6de14a8224aa97b78aea5388d1c51c1f00",
|
||||
"0x9ea779f907f0b315b364b0cfc39a0fde5b02a416",
|
||||
"0xceaeb481747ca6c540a000c1f3641f8cef161fa7",
|
||||
"0xcc34673c6c40e791051898567a1222daf90be287",
|
||||
"0x579a80d909f346fbfb1189493f521d7f48d52238",
|
||||
"0xe308bd1ac5fda103967359b2712dd89deffb7973",
|
||||
"0x4cb31628079fb14e4bc3cd5e30c2f7489b00960c",
|
||||
"0xac1ecab32727358dba8962a0f3b261731aad9723",
|
||||
"0x4fd6ace747f06ece9c49699c7cabc62d02211f75",
|
||||
"0x440c59b325d2997a134c2c7c60a8c61611212bad",
|
||||
"0x4486a3d68fac6967006d7a517b889fd3f98c102b",
|
||||
"0x9c15b54878ba618f494b38f0ae7443db6af648ba",
|
||||
"0x27b137a85656544b1ccb5a0f2e561a5703c6a68f",
|
||||
"0x21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241",
|
||||
"0x23b75c2f6791eef49c69684db4c6c1f93bf49a50",
|
||||
"0x1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b",
|
||||
"0xb9637156d330c0d605a791f1c31ba5890582fe1c",
|
||||
"0x6131c42fa982e56929107413a9d526fd99405560",
|
||||
"0x1591fc0f688c81fbeb17f5426a162a7024d430c2",
|
||||
"0x542a9515200d14b68e934e9830d91645a980dd7a",
|
||||
"0xc4bbd073882dd2add2424cf47d35213405b01324",
|
||||
"0x782495b7b3355efb2833d56ecb34dc22ad7dfcc4",
|
||||
"0x58b95c9a9d5d26825e70a82b6adb139d3fd829eb",
|
||||
"0x3ba4d81db016dc2890c81f3acec2454bff5aada5",
|
||||
"0xb52042c8ca3f8aa246fa79c3feaa3d959347c0ab",
|
||||
"0xe4ae1efdfc53b73893af49113d8694a057b9c0d1",
|
||||
"0x3c02a7bc0391e86d91b7d144e61c2c01a25a79c5",
|
||||
"0x0737a6b837f97f46ebade41b9bc3e1c509c85c53",
|
||||
"0x97f43a37f595ab5dd318fb46e7a155eae057317a",
|
||||
"0x52c5317c848ba20c7504cb2c8052abd1fde29d03",
|
||||
"0x4863226780fe7c0356454236d3b1c8792785748d",
|
||||
"0x5d2b2e6fcbe3b11d26b525e085ff818dae332479",
|
||||
"0x5f9f3392e9f62f63b8eac0beb55541fc8627f42c",
|
||||
"0x057b56736d32b86616a10f619859c6cd6f59092a",
|
||||
"0x9aa008f65de0b923a2a4f02012ad034a5e2e2192",
|
||||
"0x304a554a310c7e546dfe434669c62820b7d83490",
|
||||
"0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79",
|
||||
"0x4deb0033bb26bc534b197e61d19e0733e5679784",
|
||||
"0x07f5c1e1bc2c93e0402f23341973a0e043f7bf8a",
|
||||
"0x35a051a0010aba705c9008d7a7eff6fb88f6ea7b",
|
||||
"0x4fa802324e929786dbda3b8820dc7834e9134a2a",
|
||||
"0x9da397b9e80755301a3b32173283a91c0ef6c87e",
|
||||
"0x8d9edb3054ce5c5774a420ac37ebae0ac02343c6",
|
||||
"0x0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9",
|
||||
"0x5dc28b15dffed94048d73806ce4b7a4612a1d48f",
|
||||
"0xbcf899e6c7d9d5a215ab1e3444c86806fa854c76",
|
||||
"0x12e626b0eebfe86a56d633b9864e389b45dcb260",
|
||||
"0xa2f1ccba9395d7fcb155bba8bc92db9bafaeade7",
|
||||
"0xec8e57756626fdc07c63ad2eafbd28d08e7b0ca5",
|
||||
"0xd164b088bd9108b60d0ca3751da4bceb207b0782",
|
||||
"0x6231b6d0d5e77fe001c2a460bd9584fee60d409b",
|
||||
"0x1cba23d343a983e9b5cfd19496b9a9701ada385f",
|
||||
"0xa82f360a8d3455c5c41366975bde739c37bfeb8a",
|
||||
"0x9fcd2deaff372a39cc679d5c5e4de7bafb0b1339",
|
||||
"0x005f5cee7a43331d5a3d3eec71305925a62f34b6",
|
||||
"0x0e0da70933f4c7849fc0d203f5d1d43b9ae4532d",
|
||||
"0xd131637d5275fd1a68a3200f4ad25c71a2a9522e",
|
||||
"0xbc07118b9ac290e4622f5e77a0853539789effbe",
|
||||
"0x47e7aa56d6bdf3f36be34619660de61275420af8",
|
||||
"0xacd87e28b0c9d1254e868b81cba4cc20d9a32225",
|
||||
"0xadf80daec7ba8dcf15392f1ac611fff65d94f880",
|
||||
"0x5524c55fb03cf21f549444ccbecb664d0acad706",
|
||||
"0x40b803a9abce16f50f36a77ba41180eb90023925",
|
||||
"0xfe24cdd8648121a43a7c86d289be4dd2951ed49f",
|
||||
"0x17802f43a0137c506ba92291391a8a8f207f487d",
|
||||
"0x253488078a4edf4d6f42f113d1e62836a942cf1a",
|
||||
"0x86af3e9626fce1957c82e88cbf04ddf3a2ed7915",
|
||||
"0xb136707642a4ea12fb4bae820f03d2562ebff487",
|
||||
"0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940",
|
||||
"0xf14c14075d6c4ed84b86798af0956deef67365b5",
|
||||
"0xca544e5c4687d109611d0f8f928b53a25af72448",
|
||||
"0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c",
|
||||
"0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7",
|
||||
"0x6d87578288b6cb5549d5076a207456a1f6a63dc0",
|
||||
"0xb2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e",
|
||||
"0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6",
|
||||
"0x2b3455ec7fedf16e646268bf88846bd7a2319bb2",
|
||||
"0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a",
|
||||
"0xd343b217de44030afaa275f54d31a9317c7f441e",
|
||||
"0x84ef4b2357079cd7a7c69fd7a37cd0609a679106",
|
||||
"0xda2fef9e4a3230988ff17df2165440f37e8b1708",
|
||||
"0xf4c64518ea10f995918a454158c6b61407ea345c",
|
||||
"0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97",
|
||||
"0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
|
||||
"0x807640a13483f8ac783c557fcdf27be11ea4ac7a"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name": "Frontier (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0x118c30",
|
||||
"daoRescueSoftFork": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"daoRescueSoftFork": false
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": 0,
|
||||
"daoRescueSoftFork": false
|
||||
"frontierCompatibilityModeLimit": "0x0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name": "Homestead (Test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"frontierCompatibilityModeLimit": 0,
|
||||
"daoRescueSoftFork": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x0000000000000042",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x400000000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
|
||||
"gasLimit": "0x1388"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,8 @@
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar": "",
|
||||
"frontierCompatibilityModeLimit": "0x789b0",
|
||||
"daoRescueSoftFork": false
|
||||
"registrar": "0x8e4e9b13d4b45cb0befc93c3061b1408f67316b2",
|
||||
"frontierCompatibilityModeLimit": "0x789b0"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x08",
|
||||
"blockReward": "0x14D1120D7B160000",
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050",
|
||||
"frontierCompatibilityModeLimit": "0xffffffffffffffff",
|
||||
"daoRescueSoftFork": false
|
||||
"registrar": "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Submodule ethcore/res/ethereum/tests updated: 99afe8f5aa...ac5475d676
33
ethcore/res/instant_seal.json
Normal file
33
ethcore/res/instant_seal.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "TestInstantSeal",
|
||||
"engine": {
|
||||
"InstantSeal": null
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
"nonce": "0x00006d6f7264656e",
|
||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty": "0x20000",
|
||||
"author": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2fefd8"
|
||||
},
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
|
||||
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" }
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"Null": null
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
|
||||
@@ -19,9 +19,15 @@
|
||||
use util::*;
|
||||
use pod_account::*;
|
||||
use account_db::*;
|
||||
use lru_cache::LruCache;
|
||||
|
||||
use std::cell::{RefCell, Cell};
|
||||
|
||||
const STORAGE_CACHE_ITEMS: usize = 8192;
|
||||
|
||||
/// Single account in the system.
|
||||
#[derive(Clone)]
|
||||
/// Keeps track of changes to the code and storage.
|
||||
/// The changes are applied in `commit_storage` and `commit_code`
|
||||
pub struct Account {
|
||||
// Balance of the account.
|
||||
balance: U256,
|
||||
@@ -29,12 +35,22 @@ pub struct Account {
|
||||
nonce: U256,
|
||||
// Trie-backed storage.
|
||||
storage_root: H256,
|
||||
// Overlay on trie-backed storage - tuple is (<clean>, <value>).
|
||||
storage_overlay: RefCell<HashMap<H256, (Filth, H256)>>,
|
||||
// Code hash of the account. If None, means that it's a contract whose code has not yet been set.
|
||||
code_hash: Option<H256>,
|
||||
// LRU Cache of the trie-backed storage.
|
||||
// This is limited to `STORAGE_CACHE_ITEMS` recent queries
|
||||
storage_cache: RefCell<LruCache<H256, H256>>,
|
||||
// Modified storage. Accumulates changes to storage made in `set_storage`
|
||||
// Takes precedence over `storage_cache`.
|
||||
storage_changes: HashMap<H256, H256>,
|
||||
// Code hash of the account.
|
||||
code_hash: H256,
|
||||
// Size of the accoun code.
|
||||
code_size: Option<u64>,
|
||||
// Code cache of the account.
|
||||
code_cache: Bytes,
|
||||
code_cache: Arc<Bytes>,
|
||||
// Account code new or has been modified.
|
||||
code_filth: Filth,
|
||||
// Cached address hash.
|
||||
address_hash: Cell<Option<H256>>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
@@ -45,21 +61,33 @@ impl Account {
|
||||
balance: balance,
|
||||
nonce: nonce,
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
storage_overlay: RefCell::new(storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
|
||||
code_hash: Some(code.sha3()),
|
||||
code_cache: code
|
||||
storage_cache: Self::empty_storage_cache(),
|
||||
storage_changes: storage,
|
||||
code_hash: code.sha3(),
|
||||
code_size: Some(code.len() as u64),
|
||||
code_cache: Arc::new(code),
|
||||
code_filth: Filth::Dirty,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_storage_cache() -> RefCell<LruCache<H256, H256>> {
|
||||
RefCell::new(LruCache::new(STORAGE_CACHE_ITEMS))
|
||||
}
|
||||
|
||||
/// General constructor.
|
||||
pub fn from_pod(pod: PodAccount) -> Account {
|
||||
Account {
|
||||
balance: pod.balance,
|
||||
nonce: pod.nonce,
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
storage_overlay: RefCell::new(pod.storage.into_iter().map(|(k, v)| (k, (Filth::Dirty, v))).collect()),
|
||||
code_hash: Some(pod.code.sha3()),
|
||||
code_cache: pod.code
|
||||
storage_cache: Self::empty_storage_cache(),
|
||||
storage_changes: pod.storage.into_iter().collect(),
|
||||
code_hash: pod.code.as_ref().map_or(SHA3_EMPTY, |c| c.sha3()),
|
||||
code_filth: Filth::Dirty,
|
||||
code_size: Some(pod.code.as_ref().map_or(0, |c| c.len() as u64)),
|
||||
code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)),
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,9 +97,13 @@ impl Account {
|
||||
balance: balance,
|
||||
nonce: nonce,
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
storage_overlay: RefCell::new(HashMap::new()),
|
||||
code_hash: Some(SHA3_EMPTY),
|
||||
code_cache: vec![],
|
||||
storage_cache: Self::empty_storage_cache(),
|
||||
storage_changes: HashMap::new(),
|
||||
code_hash: SHA3_EMPTY,
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: Some(0),
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,9 +114,13 @@ impl Account {
|
||||
nonce: r.val_at(0),
|
||||
balance: r.val_at(1),
|
||||
storage_root: r.val_at(2),
|
||||
storage_overlay: RefCell::new(HashMap::new()),
|
||||
code_hash: Some(r.val_at(3)),
|
||||
code_cache: vec![],
|
||||
storage_cache: Self::empty_storage_cache(),
|
||||
storage_changes: HashMap::new(),
|
||||
code_hash: r.val_at(3),
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: None,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,40 +131,65 @@ impl Account {
|
||||
balance: balance,
|
||||
nonce: nonce,
|
||||
storage_root: SHA3_NULL_RLP,
|
||||
storage_overlay: RefCell::new(HashMap::new()),
|
||||
code_hash: None,
|
||||
code_cache: vec![],
|
||||
storage_cache: Self::empty_storage_cache(),
|
||||
storage_changes: HashMap::new(),
|
||||
code_hash: SHA3_EMPTY,
|
||||
code_cache: Arc::new(vec![]),
|
||||
code_size: None,
|
||||
code_filth: Filth::Clean,
|
||||
address_hash: Cell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set this account's code to the given code.
|
||||
/// NOTE: Account should have been created with `new_contract()`
|
||||
pub fn init_code(&mut self, code: Bytes) {
|
||||
assert!(self.code_hash.is_none());
|
||||
self.code_cache = code;
|
||||
self.code_hash = code.sha3();
|
||||
self.code_cache = Arc::new(code);
|
||||
self.code_size = Some(self.code_cache.len() as u64);
|
||||
self.code_filth = Filth::Dirty;
|
||||
}
|
||||
|
||||
/// Reset this account's code to the given code.
|
||||
pub fn reset_code(&mut self, code: Bytes) {
|
||||
self.code_hash = None;
|
||||
self.init_code(code);
|
||||
}
|
||||
|
||||
/// Set (and cache) the contents of the trie's storage at `key` to `value`.
|
||||
pub fn set_storage(&mut self, key: H256, value: H256) {
|
||||
self.storage_overlay.borrow_mut().insert(key, (Filth::Dirty, value));
|
||||
self.storage_changes.insert(key, value);
|
||||
}
|
||||
|
||||
/// Get (and cache) the contents of the trie's storage at `key`.
|
||||
/// Takes modifed storage into account.
|
||||
pub fn storage_at(&self, db: &AccountDB, key: &H256) -> H256 {
|
||||
self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{
|
||||
let db = SecTrieDB::new(db, &self.storage_root)
|
||||
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||
using it will not fail.");
|
||||
if let Some(value) = self.cached_storage_at(key) {
|
||||
return value;
|
||||
}
|
||||
let db = SecTrieDB::new(db, &self.storage_root)
|
||||
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||
using it will not fail.");
|
||||
|
||||
(Filth::Clean, H256::from(db.get(key.bytes()).map_or(U256::zero(), |v| -> U256 {decode(v)})))
|
||||
}).1.clone()
|
||||
let item: U256 = match db.get(key){
|
||||
Ok(x) => x.map_or_else(U256::zero, decode),
|
||||
Err(e) => panic!("Encountered potential DB corruption: {}", e),
|
||||
};
|
||||
let value: H256 = item.into();
|
||||
self.storage_cache.borrow_mut().insert(key.clone(), value.clone());
|
||||
value
|
||||
}
|
||||
|
||||
/// Get cached storage value if any. Returns `None` if the
|
||||
/// key is not in the cache.
|
||||
pub fn cached_storage_at(&self, key: &H256) -> Option<H256> {
|
||||
if let Some(value) = self.storage_changes.get(key) {
|
||||
return Some(value.clone())
|
||||
}
|
||||
if let Some(value) = self.storage_cache.borrow_mut().get_mut(key) {
|
||||
return Some(value.clone())
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// return the balance associated with this account.
|
||||
@@ -137,39 +198,52 @@ impl Account {
|
||||
/// return the nonce associated with this account.
|
||||
pub fn nonce(&self) -> &U256 { &self.nonce }
|
||||
|
||||
#[cfg(test)]
|
||||
/// return the code hash associated with this account.
|
||||
pub fn code_hash(&self) -> H256 {
|
||||
self.code_hash.clone().unwrap_or(SHA3_EMPTY)
|
||||
self.code_hash.clone()
|
||||
}
|
||||
|
||||
/// return the code hash associated with this account.
|
||||
pub fn address_hash(&self, address: &Address) -> H256 {
|
||||
let hash = self.address_hash.get();
|
||||
hash.unwrap_or_else(|| {
|
||||
let hash = address.sha3();
|
||||
self.address_hash.set(Some(hash.clone()));
|
||||
hash
|
||||
})
|
||||
}
|
||||
|
||||
/// returns the account's code. If `None` then the code cache isn't available -
|
||||
/// get someone who knows to call `note_code`.
|
||||
pub fn code(&self) -> Option<&[u8]> {
|
||||
match self.code_hash {
|
||||
Some(c) if c == SHA3_EMPTY && self.code_cache.is_empty() => Some(&self.code_cache),
|
||||
Some(_) if !self.code_cache.is_empty() => Some(&self.code_cache),
|
||||
None => Some(&self.code_cache),
|
||||
_ => None,
|
||||
pub fn code(&self) -> Option<Arc<Bytes>> {
|
||||
if self.code_hash != SHA3_EMPTY && self.code_cache.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(self.code_cache.clone())
|
||||
}
|
||||
|
||||
/// returns the account's code size. If `None` then the code cache or code size cache isn't available -
|
||||
/// get someone who knows to call `note_code`.
|
||||
pub fn code_size(&self) -> Option<u64> {
|
||||
self.code_size.clone()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Provide a byte array which hashes to the `code_hash`. returns the hash as a result.
|
||||
pub fn note_code(&mut self, code: Bytes) -> Result<(), H256> {
|
||||
let h = code.sha3();
|
||||
match self.code_hash {
|
||||
Some(ref i) if h == *i => {
|
||||
self.code_cache = code;
|
||||
Ok(())
|
||||
},
|
||||
_ => Err(h)
|
||||
if self.code_hash == h {
|
||||
self.code_cache = Arc::new(code);
|
||||
self.code_size = Some(self.code_cache.len() as u64);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(h)
|
||||
}
|
||||
}
|
||||
|
||||
/// Is `code_cache` valid; such that code is going to return Some?
|
||||
pub fn is_cached(&self) -> bool {
|
||||
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == Some(SHA3_EMPTY))
|
||||
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == SHA3_EMPTY)
|
||||
}
|
||||
|
||||
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
|
||||
@@ -177,68 +251,100 @@ impl Account {
|
||||
// TODO: fill out self.code_cache;
|
||||
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
||||
self.is_cached() ||
|
||||
match self.code_hash {
|
||||
Some(ref h) => match db.get(h) {
|
||||
Some(x) => { self.code_cache = x.to_vec(); true },
|
||||
match db.get(&self.code_hash) {
|
||||
Some(x) => {
|
||||
self.code_cache = Arc::new(x.to_vec());
|
||||
self.code_size = Some(x.len() as u64);
|
||||
true
|
||||
},
|
||||
_ => {
|
||||
warn!("Failed reverse get of {}", self.code_hash);
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide a database to get `code_size`. Should not be called if it is a contract without code.
|
||||
pub fn cache_code_size(&mut self, db: &AccountDB) -> bool {
|
||||
// TODO: fill out self.code_cache;
|
||||
trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
||||
self.code_size.is_some() ||
|
||||
if self.code_hash != SHA3_EMPTY {
|
||||
match db.get(&self.code_hash) {
|
||||
Some(x) => {
|
||||
self.code_size = Some(x.len() as u64);
|
||||
true
|
||||
},
|
||||
_ => {
|
||||
warn!("Failed reverse get of {}", h);
|
||||
warn!("Failed reverse get of {}", self.code_hash);
|
||||
false
|
||||
},
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Determine whether there are any un-`commit()`-ed storage-setting operations.
|
||||
pub fn storage_is_clean(&self) -> bool { self.storage_overlay.borrow().iter().find(|&(_, &(f, _))| f == Filth::Dirty).is_none() }
|
||||
pub fn storage_is_clean(&self) -> bool { self.storage_changes.is_empty() }
|
||||
|
||||
#[cfg(test)]
|
||||
/// return the storage root associated with this account or None if it has been altered via the overlay.
|
||||
pub fn storage_root(&self) -> Option<&H256> { if self.storage_is_clean() {Some(&self.storage_root)} else {None} }
|
||||
|
||||
/// return the storage overlay.
|
||||
pub fn storage_overlay(&self) -> Ref<HashMap<H256, (Filth, H256)>> { self.storage_overlay.borrow() }
|
||||
pub fn storage_changes(&self) -> &HashMap<H256, H256> { &self.storage_changes }
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
pub fn inc_nonce(&mut self) { self.nonce = self.nonce + U256::from(1u8); }
|
||||
pub fn inc_nonce(&mut self) {
|
||||
self.nonce = self.nonce + U256::from(1u8);
|
||||
}
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
pub fn add_balance(&mut self, x: &U256) { self.balance = self.balance + *x; }
|
||||
/// Increase account balance.
|
||||
pub fn add_balance(&mut self, x: &U256) {
|
||||
self.balance = self.balance + *x;
|
||||
}
|
||||
|
||||
/// Increment the nonce of the account by one.
|
||||
/// Decrease account balance.
|
||||
/// Panics if balance is less than `x`
|
||||
pub fn sub_balance(&mut self, x: &U256) {
|
||||
assert!(self.balance >= *x);
|
||||
self.balance = self.balance - *x;
|
||||
}
|
||||
|
||||
/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
|
||||
pub fn commit_storage(&mut self, db: &mut AccountDBMut) {
|
||||
let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root)
|
||||
/// Commit the `storage_changes` to the backing DB and update `storage_root`.
|
||||
pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) {
|
||||
let mut t = trie_factory.from_existing(db, &mut self.storage_root)
|
||||
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \
|
||||
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
|
||||
using it will not fail.");
|
||||
for (k, &mut (ref mut f, ref mut v)) in self.storage_overlay.borrow_mut().iter_mut() {
|
||||
if f == &Filth::Dirty {
|
||||
// cast key and value to trait type,
|
||||
// so we can call overloaded `to_bytes` method
|
||||
match v.is_zero() {
|
||||
true => { t.remove(k); },
|
||||
false => { t.insert(k, &encode(&U256::from(v.as_slice()))); },
|
||||
}
|
||||
*f = Filth::Clean;
|
||||
for (k, v) in self.storage_changes.drain() {
|
||||
// cast key and value to trait type,
|
||||
// so we can call overloaded `to_bytes` method
|
||||
let res = match v.is_zero() {
|
||||
true => t.remove(k.as_slice()),
|
||||
false => t.insert(k.as_slice(), &encode(&U256::from(v.as_slice()))),
|
||||
};
|
||||
|
||||
if let Err(e) = res {
|
||||
warn!("Encountered potential DB corruption: {}", e);
|
||||
}
|
||||
self.storage_cache.borrow_mut().insert(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this.
|
||||
pub fn commit_code(&mut self, db: &mut AccountDBMut) {
|
||||
trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty());
|
||||
match (self.code_hash.is_none(), self.code_cache.is_empty()) {
|
||||
(true, true) => self.code_hash = Some(SHA3_EMPTY),
|
||||
trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_filth == Filth::Dirty, self.code_cache.is_empty());
|
||||
match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) {
|
||||
(true, true) => {
|
||||
self.code_size = Some(0);
|
||||
self.code_filth = Filth::Clean;
|
||||
},
|
||||
(true, false) => {
|
||||
self.code_hash = Some(db.insert(&self.code_cache));
|
||||
db.emplace(self.code_hash.clone(), (*self.code_cache).clone());
|
||||
self.code_size = Some(self.code_cache.len() as u64);
|
||||
self.code_filth = Filth::Clean;
|
||||
},
|
||||
(false, _) => {},
|
||||
}
|
||||
@@ -250,9 +356,59 @@ impl Account {
|
||||
stream.append(&self.nonce);
|
||||
stream.append(&self.balance);
|
||||
stream.append(&self.storage_root);
|
||||
stream.append(self.code_hash.as_ref().expect("Cannot form RLP of contract account without code."));
|
||||
stream.append(&self.code_hash);
|
||||
stream.out()
|
||||
}
|
||||
|
||||
/// Clone basic account data
|
||||
pub fn clone_basic(&self) -> Account {
|
||||
Account {
|
||||
balance: self.balance.clone(),
|
||||
nonce: self.nonce.clone(),
|
||||
storage_root: self.storage_root.clone(),
|
||||
storage_cache: Self::empty_storage_cache(),
|
||||
storage_changes: HashMap::new(),
|
||||
code_hash: self.code_hash.clone(),
|
||||
code_size: self.code_size.clone(),
|
||||
code_cache: self.code_cache.clone(),
|
||||
code_filth: self.code_filth,
|
||||
address_hash: self.address_hash.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone account data and dirty storage keys
|
||||
pub fn clone_dirty(&self) -> Account {
|
||||
let mut account = self.clone_basic();
|
||||
account.storage_changes = self.storage_changes.clone();
|
||||
account.code_cache = self.code_cache.clone();
|
||||
account
|
||||
}
|
||||
|
||||
/// Clone account data, dirty storage keys and cached storage keys.
|
||||
pub fn clone_all(&self) -> Account {
|
||||
let mut account = self.clone_dirty();
|
||||
account.storage_cache = self.storage_cache.clone();
|
||||
account
|
||||
}
|
||||
|
||||
/// Replace self with the data from other account merging storage cache.
|
||||
/// Basic account data and all modifications are overwritten
|
||||
/// with new values.
|
||||
pub fn overwrite_with(&mut self, other: Account) {
|
||||
self.balance = other.balance;
|
||||
self.nonce = other.nonce;
|
||||
self.storage_root = other.storage_root;
|
||||
self.code_hash = other.code_hash;
|
||||
self.code_filth = other.code_filth;
|
||||
self.code_cache = other.code_cache;
|
||||
self.code_size = other.code_size;
|
||||
self.address_hash = other.address_hash;
|
||||
let mut cache = self.storage_cache.borrow_mut();
|
||||
for (k, v) in other.storage_cache.into_inner().into_iter() {
|
||||
cache.insert(k.clone() , v.clone()); //TODO: cloning should not be required here
|
||||
}
|
||||
self.storage_changes = other.storage_changes;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Account {
|
||||
@@ -268,6 +424,16 @@ mod tests {
|
||||
use super::*;
|
||||
use account_db::*;
|
||||
|
||||
#[test]
|
||||
fn account_compress() {
|
||||
let raw = Account::new_basic(2.into(), 4.into()).rlp();
|
||||
let rlp = UntrustedRlp::new(&raw);
|
||||
let compact_vec = rlp.compress(RlpType::Snapshot).to_vec();
|
||||
assert!(raw.len() > compact_vec.len());
|
||||
let again_raw = UntrustedRlp::new(&compact_vec).decompress(RlpType::Snapshot);
|
||||
assert_eq!(raw, again_raw.to_vec());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_at() {
|
||||
let mut db = MemoryDB::new();
|
||||
@@ -275,7 +441,7 @@ mod tests {
|
||||
let rlp = {
|
||||
let mut a = Account::new_contract(69.into(), 0.into());
|
||||
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
|
||||
a.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
a.init_code(vec![]);
|
||||
a.commit_code(&mut db);
|
||||
a.rlp()
|
||||
@@ -313,7 +479,7 @@ mod tests {
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.set_storage(0.into(), 0x1234.into());
|
||||
assert_eq!(a.storage_root(), None);
|
||||
a.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
|
||||
}
|
||||
|
||||
@@ -323,11 +489,11 @@ mod tests {
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.set_storage(0.into(), 0x1234.into());
|
||||
a.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
a.set_storage(1.into(), 0x1234.into());
|
||||
a.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
a.set_storage(1.into(), 0.into());
|
||||
a.commit_storage(&mut db);
|
||||
a.commit_storage(&Default::default(), &mut db);
|
||||
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
|
||||
}
|
||||
|
||||
@@ -337,7 +503,8 @@ mod tests {
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.init_code(vec![0x55, 0x44, 0xffu8]);
|
||||
assert_eq!(a.code_hash(), SHA3_EMPTY);
|
||||
assert_eq!(a.code_filth, Filth::Dirty);
|
||||
assert_eq!(a.code_size(), Some(3));
|
||||
a.commit_code(&mut db);
|
||||
assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb");
|
||||
}
|
||||
@@ -348,11 +515,12 @@ mod tests {
|
||||
let mut db = MemoryDB::new();
|
||||
let mut db = AccountDBMut::new(&mut db, &Address::new());
|
||||
a.init_code(vec![0x55, 0x44, 0xffu8]);
|
||||
assert_eq!(a.code_hash(), SHA3_EMPTY);
|
||||
assert_eq!(a.code_filth, Filth::Dirty);
|
||||
a.commit_code(&mut db);
|
||||
assert_eq!(a.code_filth, Filth::Clean);
|
||||
assert_eq!(a.code_hash().hex(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb");
|
||||
a.reset_code(vec![0x55]);
|
||||
assert_eq!(a.code_hash(), SHA3_EMPTY);
|
||||
assert_eq!(a.code_filth, Filth::Dirty);
|
||||
a.commit_code(&mut db);
|
||||
assert_eq!(a.code_hash().hex(), "37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be");
|
||||
}
|
||||
|
||||
@@ -1,26 +1,59 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
//! DB backend wrapper for Account trie
|
||||
use util::*;
|
||||
|
||||
static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];
|
||||
|
||||
// combines a key with an address hash to ensure uniqueness.
|
||||
// leaves the first 96 bits untouched in order to support partial key lookup.
|
||||
#[inline]
|
||||
fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 {
|
||||
let mut dst = key.clone();
|
||||
{
|
||||
let last_src: &[u8] = &*address_hash;
|
||||
let last_dst: &mut [u8] = &mut *dst;
|
||||
for (k, a) in last_dst[12..].iter_mut().zip(&last_src[12..]) {
|
||||
*k ^= *a
|
||||
}
|
||||
}
|
||||
|
||||
dst
|
||||
}
|
||||
|
||||
// TODO: introduce HashDBMut?
|
||||
/// DB backend wrapper for Account trie
|
||||
/// Transforms trie node keys for the database
|
||||
pub struct AccountDB<'db> {
|
||||
db: &'db HashDB,
|
||||
address: H256,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn combine_key<'a>(address: &'a H256, key: &'a H256) -> H256 {
|
||||
address ^ key
|
||||
address_hash: H256,
|
||||
}
|
||||
|
||||
impl<'db> AccountDB<'db> {
|
||||
pub fn new(db: &'db HashDB, address: &Address) -> AccountDB<'db> {
|
||||
/// Create a new AccountDB from an address.
|
||||
pub fn new(db: &'db HashDB, address: &Address) -> Self {
|
||||
Self::from_hash(db, address.sha3())
|
||||
}
|
||||
|
||||
/// Create a new AcountDB from an address' hash.
|
||||
pub fn from_hash(db: &'db HashDB, address_hash: H256) -> Self {
|
||||
AccountDB {
|
||||
db: db,
|
||||
address: address.into(),
|
||||
address_hash: address_hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,14 +67,14 @@ impl<'db> HashDB for AccountDB<'db>{
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return Some(&NULL_RLP_STATIC);
|
||||
}
|
||||
self.db.get(&combine_key(&self.address, key))
|
||||
self.db.get(&combine_key(&self.address_hash, key))
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H256) -> bool {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return true;
|
||||
}
|
||||
self.db.contains(&combine_key(&self.address, key))
|
||||
self.db.contains(&combine_key(&self.address_hash, key))
|
||||
}
|
||||
|
||||
fn insert(&mut self, _value: &[u8]) -> H256 {
|
||||
@@ -60,20 +93,26 @@ impl<'db> HashDB for AccountDB<'db>{
|
||||
/// DB backend wrapper for Account trie
|
||||
pub struct AccountDBMut<'db> {
|
||||
db: &'db mut HashDB,
|
||||
address: H256,
|
||||
address_hash: H256,
|
||||
}
|
||||
|
||||
impl<'db> AccountDBMut<'db> {
|
||||
pub fn new(db: &'db mut HashDB, address: &Address) -> AccountDBMut<'db> {
|
||||
/// Create a new AccountDB from an address.
|
||||
pub fn new(db: &'db mut HashDB, address: &Address) -> Self {
|
||||
Self::from_hash(db, address.sha3())
|
||||
}
|
||||
|
||||
/// Create a new AcountDB from an address' hash.
|
||||
pub fn from_hash(db: &'db mut HashDB, address_hash: H256) -> Self {
|
||||
AccountDBMut {
|
||||
db: db,
|
||||
address: address.into(),
|
||||
address_hash: address_hash,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn immutable(&'db self) -> AccountDB<'db> {
|
||||
AccountDB { db: self.db, address: self.address.clone() }
|
||||
AccountDB { db: self.db, address_hash: self.address_hash.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,14 +125,14 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return Some(&NULL_RLP_STATIC);
|
||||
}
|
||||
self.db.get(&combine_key(&self.address, key))
|
||||
self.db.get(&combine_key(&self.address_hash, key))
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H256) -> bool {
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return true;
|
||||
}
|
||||
self.db.contains(&combine_key(&self.address, key))
|
||||
self.db.contains(&combine_key(&self.address_hash, key))
|
||||
}
|
||||
|
||||
fn insert(&mut self, value: &[u8]) -> H256 {
|
||||
@@ -101,7 +140,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
||||
return SHA3_NULL_RLP.clone();
|
||||
}
|
||||
let k = value.sha3();
|
||||
let ak = combine_key(&self.address, &k);
|
||||
let ak = combine_key(&self.address_hash, &k);
|
||||
self.db.emplace(ak, value.to_vec());
|
||||
k
|
||||
}
|
||||
@@ -110,7 +149,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
||||
if key == SHA3_NULL_RLP {
|
||||
return;
|
||||
}
|
||||
let key = combine_key(&self.address, &key);
|
||||
let key = combine_key(&self.address_hash, &key);
|
||||
self.db.emplace(key, value.to_vec())
|
||||
}
|
||||
|
||||
@@ -118,7 +157,7 @@ impl<'db> HashDB for AccountDBMut<'db>{
|
||||
if key == &SHA3_NULL_RLP {
|
||||
return;
|
||||
}
|
||||
let key = combine_key(&self.address, key);
|
||||
let key = combine_key(&self.address_hash, key);
|
||||
self.db.remove(&key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
//! Account management.
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::RwLock;
|
||||
use std::collections::HashMap;
|
||||
use util::{Address as H160, H256, H520};
|
||||
use std::time::{Instant, Duration};
|
||||
use util::{Address as H160, H256, H520, Mutex, RwLock};
|
||||
use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore};
|
||||
use ethstore::dir::{KeyDirectory};
|
||||
use ethstore::ethkey::{Address as SSAddress, Message as SSMessage, Secret as SSSecret, Random, Generator};
|
||||
|
||||
|
||||
/// Type of unlock.
|
||||
#[derive(Clone)]
|
||||
enum Unlock {
|
||||
@@ -32,6 +33,8 @@ enum Unlock {
|
||||
/// Account unlocked permantently can always sign message.
|
||||
/// Use with caution.
|
||||
Perm,
|
||||
/// Account unlocked with a timeout
|
||||
Timed((Instant, u32)),
|
||||
}
|
||||
|
||||
/// Data associated with account.
|
||||
@@ -107,18 +110,23 @@ impl_bridge_type!(Message, 32, H256, SSMessage);
|
||||
impl_bridge_type!(Address, 20, H160, SSAddress);
|
||||
|
||||
|
||||
struct NullDir;
|
||||
#[derive(Default)]
|
||||
struct NullDir {
|
||||
accounts: RwLock<HashMap<SSAddress, SafeAccount>>,
|
||||
}
|
||||
|
||||
impl KeyDirectory for NullDir {
|
||||
fn load(&self) -> Result<Vec<SafeAccount>, SSError> {
|
||||
Ok(vec![])
|
||||
Ok(self.accounts.read().values().cloned().collect())
|
||||
}
|
||||
|
||||
fn insert(&self, _account: SafeAccount) -> Result<(), SSError> {
|
||||
Ok(())
|
||||
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, SSError> {
|
||||
self.accounts.write().insert(account.address.clone(), account.clone());
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
fn remove(&self, _address: &SSAddress) -> Result<(), SSError> {
|
||||
fn remove(&self, address: &SSAddress) -> Result<(), SSError> {
|
||||
self.accounts.write().remove(address);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -126,15 +134,36 @@ impl KeyDirectory for NullDir {
|
||||
/// Account management.
|
||||
/// Responsible for unlocking accounts.
|
||||
pub struct AccountProvider {
|
||||
unlocked: RwLock<HashMap<SSAddress, AccountData>>,
|
||||
unlocked: Mutex<HashMap<SSAddress, AccountData>>,
|
||||
sstore: Box<SecretStore>,
|
||||
}
|
||||
|
||||
/// Collected account metadata
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AccountMeta {
|
||||
/// The name of the account.
|
||||
pub name: String,
|
||||
/// The rest of the metadata of the account.
|
||||
pub meta: String,
|
||||
/// The 128-bit UUID of the account, if it has one (brain-wallets don't).
|
||||
pub uuid: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for AccountMeta {
|
||||
fn default() -> Self {
|
||||
AccountMeta {
|
||||
name: String::new(),
|
||||
meta: "{}".to_owned(),
|
||||
uuid: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AccountProvider {
|
||||
/// Creates new account provider.
|
||||
pub fn new(sstore: Box<SecretStore>) -> Self {
|
||||
AccountProvider {
|
||||
unlocked: RwLock::new(HashMap::new()),
|
||||
unlocked: Mutex::new(HashMap::new()),
|
||||
sstore: sstore,
|
||||
}
|
||||
}
|
||||
@@ -142,8 +171,8 @@ impl AccountProvider {
|
||||
/// Creates not disk backed provider.
|
||||
pub fn transient_provider() -> Self {
|
||||
AccountProvider {
|
||||
unlocked: RwLock::new(HashMap::new()),
|
||||
sstore: Box::new(EthStore::open(Box::new(NullDir)).unwrap())
|
||||
unlocked: Mutex::new(HashMap::new()),
|
||||
sstore: Box::new(EthStore::open(Box::new(NullDir::default())).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,8 +192,42 @@ impl AccountProvider {
|
||||
}
|
||||
|
||||
/// Returns addresses of all accounts.
|
||||
pub fn accounts(&self) -> Vec<H160> {
|
||||
self.sstore.accounts().into_iter().map(|a| H160(a.into())).collect()
|
||||
pub fn accounts(&self) -> Result<Vec<H160>, Error> {
|
||||
let accounts = try!(self.sstore.accounts()).into_iter().map(|a| H160(a.into())).collect();
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn accounts_info(&self) -> Result<HashMap<H160, AccountMeta>, Error> {
|
||||
let r: HashMap<H160, AccountMeta> = try!(self.sstore.accounts())
|
||||
.into_iter()
|
||||
.map(|a| (H160(a.clone().into()), self.account_meta(a).unwrap_or_else(|_| Default::default())))
|
||||
.collect();
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn account_meta<A>(&self, account: A) -> Result<AccountMeta, Error> where Address: From<A> {
|
||||
let account = Address::from(account).into();
|
||||
Ok(AccountMeta {
|
||||
name: try!(self.sstore.name(&account)),
|
||||
meta: try!(self.sstore.meta(&account)),
|
||||
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a UUID
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_name<A>(&self, account: A, name: String) -> Result<(), Error> where Address: From<A> {
|
||||
let account = Address::from(account).into();
|
||||
try!(self.sstore.set_name(&account, name));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns each account along with name and meta.
|
||||
pub fn set_account_meta<A>(&self, account: A, meta: String) -> Result<(), Error> where Address: From<A> {
|
||||
let account = Address::from(account).into();
|
||||
try!(self.sstore.set_meta(&account, meta));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper method used for unlocking accounts.
|
||||
@@ -176,12 +239,10 @@ impl AccountProvider {
|
||||
let _ = try!(self.sstore.sign(&account, &password, &Default::default()));
|
||||
|
||||
// check if account is already unlocked pernamently, if it is, do nothing
|
||||
{
|
||||
let unlocked = self.unlocked.read().unwrap();
|
||||
if let Some(data) = unlocked.get(&account) {
|
||||
if let Unlock::Perm = data.unlock {
|
||||
return Ok(())
|
||||
}
|
||||
let mut unlocked = self.unlocked.lock();
|
||||
if let Some(data) = unlocked.get(&account) {
|
||||
if let Unlock::Perm = data.unlock {
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +251,6 @@ impl AccountProvider {
|
||||
password: password,
|
||||
};
|
||||
|
||||
let mut unlocked = self.unlocked.write().unwrap();
|
||||
unlocked.insert(account, data);
|
||||
Ok(())
|
||||
}
|
||||
@@ -205,10 +265,15 @@ impl AccountProvider {
|
||||
self.unlock_account(account, password, Unlock::Temp)
|
||||
}
|
||||
|
||||
/// Unlocks account temporarily with a timeout.
|
||||
pub fn unlock_account_timed<A>(&self, account: A, password: String, duration_ms: u32) -> Result<(), Error> where Address: From<A> {
|
||||
self.unlock_account(account, password, Unlock::Timed((Instant::now(), duration_ms)))
|
||||
}
|
||||
|
||||
/// Checks if given account is unlocked
|
||||
pub fn is_unlocked<A>(&self, account: A) -> bool where Address: From<A> {
|
||||
let account = Address::from(account).into();
|
||||
let unlocked = self.unlocked.read().unwrap();
|
||||
let unlocked = self.unlocked.lock();
|
||||
unlocked.get(&account).is_some()
|
||||
}
|
||||
|
||||
@@ -218,15 +283,20 @@ impl AccountProvider {
|
||||
let message = Message::from(message).into();
|
||||
|
||||
let data = {
|
||||
let unlocked = self.unlocked.read().unwrap();
|
||||
try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone()
|
||||
let mut unlocked = self.unlocked.lock();
|
||||
let data = try!(unlocked.get(&account).ok_or(Error::NotUnlocked)).clone();
|
||||
if let Unlock::Temp = data.unlock {
|
||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
||||
}
|
||||
if let Unlock::Timed((ref start, ref duration)) = data.unlock {
|
||||
if start.elapsed() > Duration::from_millis(*duration as u64) {
|
||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
||||
return Err(Error::NotUnlocked);
|
||||
}
|
||||
}
|
||||
data
|
||||
};
|
||||
|
||||
if let Unlock::Temp = data.unlock {
|
||||
let mut unlocked = self.unlocked.write().unwrap();
|
||||
unlocked.remove(&account).expect("data exists: so key must exist: qed");
|
||||
}
|
||||
|
||||
let signature = try!(self.sstore.sign(&account, &data.password, &message));
|
||||
Ok(H520(signature.into()))
|
||||
}
|
||||
@@ -244,6 +314,7 @@ impl AccountProvider {
|
||||
mod tests {
|
||||
use super::AccountProvider;
|
||||
use ethstore::ethkey::{Generator, Random};
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn unlock_account_temp() {
|
||||
@@ -269,4 +340,16 @@ mod tests {
|
||||
assert!(ap.sign(kp.address(), [0u8; 32]).is_ok());
|
||||
assert!(ap.sign(kp.address(), [0u8; 32]).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unlock_account_timer() {
|
||||
let kp = Random.generate().unwrap();
|
||||
let ap = AccountProvider::transient_provider();
|
||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 2000).is_err());
|
||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 2000).is_ok());
|
||||
assert!(ap.sign(kp.address(), [0u8; 32]).is_ok());
|
||||
::std::thread::sleep(Duration::from_millis(2000));
|
||||
assert!(ap.sign(kp.address(), [0u8; 32]).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
//! Evm input params.
|
||||
use common::*;
|
||||
use ethjson;
|
||||
use types::executed::CallType;
|
||||
|
||||
/// Transaction value
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -42,6 +43,8 @@ impl ActionValue {
|
||||
pub struct ActionParams {
|
||||
/// Address of currently executed code.
|
||||
pub code_address: Address,
|
||||
/// Hash of currently executed code.
|
||||
pub code_hash: H256,
|
||||
/// Receive address. Usually equal to code_address,
|
||||
/// except when called using CALLCODE.
|
||||
pub address: Address,
|
||||
@@ -56,9 +59,12 @@ pub struct ActionParams {
|
||||
/// Transaction value.
|
||||
pub value: ActionValue,
|
||||
/// Code being executed.
|
||||
pub code: Option<Bytes>,
|
||||
pub code: Option<Arc<Bytes>>,
|
||||
/// Input data.
|
||||
pub data: Option<Bytes>
|
||||
pub data: Option<Bytes>,
|
||||
/// Type of call
|
||||
pub call_type: CallType,
|
||||
|
||||
}
|
||||
|
||||
impl Default for ActionParams {
|
||||
@@ -66,6 +72,7 @@ impl Default for ActionParams {
|
||||
fn default() -> ActionParams {
|
||||
ActionParams {
|
||||
code_address: Address::new(),
|
||||
code_hash: SHA3_EMPTY,
|
||||
address: Address::new(),
|
||||
sender: Address::new(),
|
||||
origin: Address::new(),
|
||||
@@ -73,23 +80,27 @@ impl Default for ActionParams {
|
||||
gas_price: U256::zero(),
|
||||
value: ActionValue::Transfer(U256::zero()),
|
||||
code: None,
|
||||
data: None
|
||||
data: None,
|
||||
call_type: CallType::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ethjson::vm::Transaction> for ActionParams {
|
||||
fn from(t: ethjson::vm::Transaction) -> Self {
|
||||
let address: Address = t.address.into();
|
||||
ActionParams {
|
||||
code_address: Address::new(),
|
||||
address: t.address.into(),
|
||||
code_hash: (&*t.code).sha3(),
|
||||
address: address,
|
||||
sender: t.sender.into(),
|
||||
origin: t.origin.into(),
|
||||
code: Some(t.code.into()),
|
||||
code: Some(Arc::new(t.code.into())),
|
||||
data: Some(t.data.into()),
|
||||
gas: t.gas.into(),
|
||||
gas_price: t.gas_price.into(),
|
||||
value: ActionValue::Transfer(t.value.into()),
|
||||
call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,16 @@
|
||||
|
||||
//! Blockchain block.
|
||||
|
||||
#![cfg_attr(feature="dev", allow(ptr_arg))] // Because of &LastHashes -> &Vec<_>
|
||||
|
||||
use common::*;
|
||||
use engine::*;
|
||||
use engines::Engine;
|
||||
use state::*;
|
||||
use state_db::StateDB;
|
||||
use verification::PreverifiedBlock;
|
||||
use trace::Trace;
|
||||
use trace::FlatTrace;
|
||||
use evm::Factory as EvmFactory;
|
||||
|
||||
/// A block, encoded as it is on the block chain.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone, PartialEq)]
|
||||
pub struct Block {
|
||||
/// The header of this block.
|
||||
pub header: Header,
|
||||
@@ -41,8 +40,18 @@ impl Block {
|
||||
pub fn is_good(b: &[u8]) -> bool {
|
||||
UntrustedRlp::new(b).as_val::<Block>().is_ok()
|
||||
}
|
||||
|
||||
/// Get the RLP-encoding of the block with or without the seal.
|
||||
pub fn rlp_bytes(&self, seal: Seal) -> Bytes {
|
||||
let mut block_rlp = RlpStream::new_list(3);
|
||||
self.header.stream_rlp(&mut block_rlp, seal);
|
||||
block_rlp.append(&self.transactions);
|
||||
block_rlp.append(&self.uncles);
|
||||
block_rlp.out()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Decodable for Block {
|
||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
|
||||
if decoder.as_raw().len() != try!(decoder.as_rlp().payload_info()).total() {
|
||||
@@ -68,7 +77,7 @@ pub struct ExecutedBlock {
|
||||
receipts: Vec<Receipt>,
|
||||
transactions_set: HashSet<H256>,
|
||||
state: State,
|
||||
traces: Option<Vec<Trace>>,
|
||||
traces: Option<Vec<Vec<FlatTrace>>>,
|
||||
}
|
||||
|
||||
/// A set of references to `ExecutedBlock` fields that are publicly accessible.
|
||||
@@ -76,15 +85,15 @@ pub struct BlockRefMut<'a> {
|
||||
/// Block header.
|
||||
pub header: &'a mut Header,
|
||||
/// Block transactions.
|
||||
pub transactions: &'a Vec<SignedTransaction>,
|
||||
pub transactions: &'a [SignedTransaction],
|
||||
/// Block uncles.
|
||||
pub uncles: &'a Vec<Header>,
|
||||
pub uncles: &'a [Header],
|
||||
/// Transaction receipts.
|
||||
pub receipts: &'a Vec<Receipt>,
|
||||
pub receipts: &'a [Receipt],
|
||||
/// State.
|
||||
pub state: &'a mut State,
|
||||
/// Traces.
|
||||
pub traces: &'a Option<Vec<Trace>>,
|
||||
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
|
||||
}
|
||||
|
||||
/// A set of immutable references to `ExecutedBlock` fields that are publicly accessible.
|
||||
@@ -92,15 +101,15 @@ pub struct BlockRef<'a> {
|
||||
/// Block header.
|
||||
pub header: &'a Header,
|
||||
/// Block transactions.
|
||||
pub transactions: &'a Vec<SignedTransaction>,
|
||||
pub transactions: &'a [SignedTransaction],
|
||||
/// Block uncles.
|
||||
pub uncles: &'a Vec<Header>,
|
||||
pub uncles: &'a [Header],
|
||||
/// Transaction receipts.
|
||||
pub receipts: &'a Vec<Receipt>,
|
||||
pub receipts: &'a [Receipt],
|
||||
/// State.
|
||||
pub state: &'a State,
|
||||
/// Traces.
|
||||
pub traces: &'a Option<Vec<Trace>>,
|
||||
pub traces: &'a Option<Vec<Vec<FlatTrace>>>,
|
||||
}
|
||||
|
||||
impl ExecutedBlock {
|
||||
@@ -142,9 +151,12 @@ impl ExecutedBlock {
|
||||
|
||||
/// Trait for a object that is a `ExecutedBlock`.
|
||||
pub trait IsBlock {
|
||||
/// Get the block associated with this object.
|
||||
/// Get the `ExecutedBlock` associated with this object.
|
||||
fn block(&self) -> &ExecutedBlock;
|
||||
|
||||
/// Get the base `Block` object associated with this.
|
||||
fn base(&self) -> &Block { &self.block().base }
|
||||
|
||||
/// Get the header associated with this object's block.
|
||||
fn header(&self) -> &Header { &self.block().base.header }
|
||||
|
||||
@@ -152,22 +164,22 @@ pub trait IsBlock {
|
||||
fn state(&self) -> &State { &self.block().state }
|
||||
|
||||
/// Get all information on transactions in this block.
|
||||
fn transactions(&self) -> &Vec<SignedTransaction> { &self.block().base.transactions }
|
||||
fn transactions(&self) -> &[SignedTransaction] { &self.block().base.transactions }
|
||||
|
||||
/// Get all information on receipts in this block.
|
||||
fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
|
||||
fn receipts(&self) -> &[Receipt] { &self.block().receipts }
|
||||
|
||||
/// Get all information concerning transaction tracing in this block.
|
||||
fn traces(&self) -> &Option<Vec<Trace>> { &self.block().traces }
|
||||
fn traces(&self) -> &Option<Vec<Vec<FlatTrace>>> { &self.block().traces }
|
||||
|
||||
/// Get all uncles in this block.
|
||||
fn uncles(&self) -> &Vec<Header> { &self.block().base.uncles }
|
||||
fn uncles(&self) -> &[Header] { &self.block().base.uncles }
|
||||
}
|
||||
|
||||
/// Trait for a object that has a state database.
|
||||
pub trait Drain {
|
||||
/// Drop this object and return the underlieing database.
|
||||
fn drain(self) -> Box<JournalDB>;
|
||||
fn drain(self) -> StateDB;
|
||||
}
|
||||
|
||||
impl IsBlock for ExecutedBlock {
|
||||
@@ -182,8 +194,7 @@ pub struct OpenBlock<'x> {
|
||||
block: ExecutedBlock,
|
||||
engine: &'x Engine,
|
||||
vm_factory: &'x EvmFactory,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
}
|
||||
|
||||
/// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
|
||||
@@ -194,8 +205,7 @@ pub struct OpenBlock<'x> {
|
||||
pub struct ClosedBlock {
|
||||
block: ExecutedBlock,
|
||||
uncle_bytes: Bytes,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
unclosed_state: State,
|
||||
}
|
||||
|
||||
@@ -222,22 +232,21 @@ impl<'x> OpenBlock<'x> {
|
||||
pub fn new(
|
||||
engine: &'x Engine,
|
||||
vm_factory: &'x EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
tracing: bool,
|
||||
db: Box<JournalDB>,
|
||||
db: StateDB,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
author: Address,
|
||||
gas_range_target: (U256, U256),
|
||||
extra_data: Bytes,
|
||||
) -> Result<Self, Error> {
|
||||
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce()));
|
||||
let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), trie_factory));
|
||||
let mut r = OpenBlock {
|
||||
block: ExecutedBlock::new(state, tracing),
|
||||
engine: engine,
|
||||
vm_factory: vm_factory,
|
||||
last_hashes: last_hashes,
|
||||
dao_rescue_block_gas_limit: dao_rescue_block_gas_limit,
|
||||
};
|
||||
|
||||
r.block.base.header.parent_hash = parent.hash();
|
||||
@@ -267,6 +276,15 @@ impl<'x> OpenBlock<'x> {
|
||||
/// Alter the gas limit for the block.
|
||||
pub fn set_gas_used(&mut self, a: U256) { self.block.base.header.set_gas_used(a); }
|
||||
|
||||
/// Alter the uncles hash the block.
|
||||
pub fn set_uncles_hash(&mut self, h: H256) { self.block.base.header.set_uncles_hash(h); }
|
||||
|
||||
/// Alter transactions root for the block.
|
||||
pub fn set_transactions_root(&mut self, h: H256) { self.block.base.header.set_transactions_root(h); }
|
||||
|
||||
/// Alter the receipts root for the block.
|
||||
pub fn set_receipts_root(&mut self, h: H256) { self.block.base.header.set_receipts_root(h); }
|
||||
|
||||
/// Alter the extra_data for the block.
|
||||
pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> {
|
||||
if extra_data.len() > self.engine.maximum_extra_data_size() {
|
||||
@@ -294,16 +312,14 @@ impl<'x> OpenBlock<'x> {
|
||||
/// Get the environment info concerning this block.
|
||||
pub fn env_info(&self) -> EnvInfo {
|
||||
// TODO: memoise.
|
||||
const SOFT_FORK_BLOCK: u64 = 1_800_000;
|
||||
EnvInfo {
|
||||
number: self.block.base.header.number,
|
||||
author: self.block.base.header.author.clone(),
|
||||
timestamp: self.block.base.header.timestamp,
|
||||
difficulty: self.block.base.header.difficulty.clone(),
|
||||
last_hashes: self.last_hashes.clone(), // TODO: should be a reference.
|
||||
last_hashes: self.last_hashes.clone(),
|
||||
gas_used: self.block.receipts.last().map_or(U256::zero(), |r| r.gas_used),
|
||||
gas_limit: self.block.base.header.gas_limit.clone(),
|
||||
dao_rescue_block_gas_limit: if self.block.base.header.number == SOFT_FORK_BLOCK { Some(self.block.base.header.gas_limit) } else { self.dao_rescue_block_gas_limit },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,9 +338,9 @@ impl<'x> OpenBlock<'x> {
|
||||
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
|
||||
self.block.base.transactions.push(t);
|
||||
let t = outcome.trace;
|
||||
self.block.traces.as_mut().map(|traces| traces.push(t.expect("self.block.traces.is_some(): so we must be tracing: qed")));
|
||||
self.block.traces.as_mut().map(|traces| traces.push(t));
|
||||
self.block.receipts.push(outcome.receipt);
|
||||
Ok(&self.block.receipts.last().unwrap())
|
||||
Ok(self.block.receipts.last().unwrap())
|
||||
}
|
||||
Err(x) => Err(From::from(x))
|
||||
}
|
||||
@@ -350,7 +366,6 @@ impl<'x> OpenBlock<'x> {
|
||||
block: s.block,
|
||||
uncle_bytes: uncle_bytes,
|
||||
last_hashes: s.last_hashes,
|
||||
dao_rescue_block_gas_limit: s.dao_rescue_block_gas_limit,
|
||||
unclosed_state: unclosed_state,
|
||||
}
|
||||
}
|
||||
@@ -360,11 +375,17 @@ impl<'x> OpenBlock<'x> {
|
||||
let mut s = self;
|
||||
|
||||
s.engine.on_close_block(&mut s.block);
|
||||
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|ref e| e.rlp_bytes().to_vec()).collect());
|
||||
if s.block.base.header.transactions_root.is_zero() || s.block.base.header.transactions_root == SHA3_NULL_RLP {
|
||||
s.block.base.header.transactions_root = ordered_trie_root(s.block.base.transactions.iter().map(|ref e| e.rlp_bytes().to_vec()).collect());
|
||||
}
|
||||
let uncle_bytes = s.block.base.uncles.iter().fold(RlpStream::new_list(s.block.base.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out();
|
||||
s.block.base.header.uncles_hash = uncle_bytes.sha3();
|
||||
if s.block.base.header.uncles_hash.is_zero() {
|
||||
s.block.base.header.uncles_hash = uncle_bytes.sha3();
|
||||
}
|
||||
if s.block.base.header.receipts_root.is_zero() || s.block.base.header.receipts_root == SHA3_NULL_RLP {
|
||||
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|ref r| r.rlp_bytes().to_vec()).collect());
|
||||
}
|
||||
s.block.base.header.state_root = s.block.state.root().clone();
|
||||
s.block.base.header.receipts_root = ordered_trie_root(s.block.receipts.iter().map(|ref r| r.rlp_bytes().to_vec()).collect());
|
||||
s.block.base.header.log_bloom = s.block.receipts.iter().fold(LogBloom::zero(), |mut b, r| {b = &b | &r.log_bloom; b}); //TODO: use |= operator
|
||||
s.block.base.header.gas_used = s.block.receipts.last().map_or(U256::zero(), |r| r.gas_used);
|
||||
s.block.base.header.note_dirty();
|
||||
@@ -410,7 +431,6 @@ impl ClosedBlock {
|
||||
engine: engine,
|
||||
vm_factory: vm_factory,
|
||||
last_hashes: self.last_hashes,
|
||||
dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,7 +466,9 @@ impl LockedBlock {
|
||||
|
||||
impl Drain for LockedBlock {
|
||||
/// Drop this object and return the underlieing database.
|
||||
fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
||||
fn drain(self) -> StateDB {
|
||||
self.block.state.drop().1
|
||||
}
|
||||
}
|
||||
|
||||
impl SealedBlock {
|
||||
@@ -462,7 +484,9 @@ impl SealedBlock {
|
||||
|
||||
impl Drain for SealedBlock {
|
||||
/// Drop this object and return the underlieing database.
|
||||
fn drain(self) -> Box<JournalDB> { self.block.state.drop().1 }
|
||||
fn drain(self) -> StateDB {
|
||||
self.block.state.drop().1
|
||||
}
|
||||
}
|
||||
|
||||
impl IsBlock for SealedBlock {
|
||||
@@ -477,23 +501,28 @@ pub fn enact(
|
||||
uncles: &[Header],
|
||||
engine: &Engine,
|
||||
tracing: bool,
|
||||
db: Box<JournalDB>,
|
||||
db: StateDB,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
vm_factory: &EvmFactory
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
{
|
||||
if ::log::max_log_level() >= ::log::LogLevel::Trace {
|
||||
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce()));
|
||||
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
|
||||
let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone()));
|
||||
trace!(target: "enact", "num={}, root={}, author={}, author_balance={}\n", header.number(), s.root(), header.author(), s.balance(&header.author()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut b = try!(OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, header.author().clone(), (3141562.into(), 31415620.into()), header.extra_data().clone()));
|
||||
let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![]));
|
||||
b.set_difficulty(*header.difficulty());
|
||||
b.set_gas_limit(*header.gas_limit());
|
||||
b.set_timestamp(header.timestamp());
|
||||
b.set_author(header.author().clone());
|
||||
b.set_extra_data(header.extra_data().clone()).unwrap_or_else(|e| warn!("Couldn't set extradata: {}. Ignoring.", e));
|
||||
b.set_uncles_hash(header.uncles_hash().clone());
|
||||
b.set_transactions_root(header.transactions_root().clone());
|
||||
b.set_receipts_root(header.receipts_root().clone());
|
||||
for t in transactions { try!(b.push_transaction(t.clone(), None)); }
|
||||
for u in uncles { try!(b.push_uncle(u.clone())); }
|
||||
Ok(b.close_and_lock())
|
||||
@@ -505,15 +534,15 @@ pub fn enact_bytes(
|
||||
block_bytes: &[u8],
|
||||
engine: &Engine,
|
||||
tracing: bool,
|
||||
db: Box<JournalDB>,
|
||||
db: StateDB,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
vm_factory: &EvmFactory
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
let block = BlockView::new(block_bytes);
|
||||
let header = block.header();
|
||||
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)
|
||||
enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
|
||||
}
|
||||
|
||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header
|
||||
@@ -522,14 +551,14 @@ pub fn enact_verified(
|
||||
block: &PreverifiedBlock,
|
||||
engine: &Engine,
|
||||
tracing: bool,
|
||||
db: Box<JournalDB>,
|
||||
db: StateDB,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
vm_factory: &EvmFactory
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
let view = BlockView::new(&block.bytes);
|
||||
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)
|
||||
enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)
|
||||
}
|
||||
|
||||
/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards
|
||||
@@ -538,14 +567,14 @@ pub fn enact_and_seal(
|
||||
block_bytes: &[u8],
|
||||
engine: &Engine,
|
||||
tracing: bool,
|
||||
db: Box<JournalDB>,
|
||||
db: StateDB,
|
||||
parent: &Header,
|
||||
last_hashes: LastHashes,
|
||||
dao_rescue_block_gas_limit: Option<U256>,
|
||||
vm_factory: &EvmFactory
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
) -> Result<SealedBlock, Error> {
|
||||
let header = BlockView::new(block_bytes).header_view();
|
||||
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, dao_rescue_block_gas_limit, vm_factory)).seal(engine, header.seal())))
|
||||
Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)).seal(engine, header.seal())))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -558,73 +587,74 @@ mod tests {
|
||||
fn open_block() {
|
||||
use spec::*;
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(&*spec.engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
let _ = b.seal(engine.deref(), vec![]);
|
||||
let _ = b.seal(&*spec.engine, vec![]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enact_block() {
|
||||
use spec::*;
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
|
||||
.close_and_lock().seal(engine.deref(), vec![]).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap()
|
||||
.close_and_lock().seal(engine, vec![]).unwrap();
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default()).unwrap();
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
|
||||
|
||||
assert_eq!(e.rlp_bytes(), orig_bytes);
|
||||
|
||||
let db = e.drain();
|
||||
assert_eq!(orig_db.keys(), db.keys());
|
||||
assert!(orig_db.keys().iter().filter(|k| orig_db.get(k.0) != db.get(k.0)).next() == None);
|
||||
assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys());
|
||||
assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enact_block_with_uncle() {
|
||||
use spec::*;
|
||||
let spec = Spec::new_test();
|
||||
let engine = &spec.engine;
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let vm_factory = Default::default();
|
||||
let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let mut open_block = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let mut uncle1_header = Header::new();
|
||||
uncle1_header.extra_data = b"uncle1".to_vec();
|
||||
let mut uncle2_header = Header::new();
|
||||
uncle2_header.extra_data = b"uncle2".to_vec();
|
||||
open_block.push_uncle(uncle1_header).unwrap();
|
||||
open_block.push_uncle(uncle2_header).unwrap();
|
||||
let b = open_block.close_and_lock().seal(engine.deref(), vec![]).unwrap();
|
||||
let b = open_block.close_and_lock().seal(engine, vec![]).unwrap();
|
||||
|
||||
let orig_bytes = b.rlp_bytes();
|
||||
let orig_db = b.drain();
|
||||
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let e = enact_and_seal(&orig_bytes, engine.deref(), false, db, &genesis_header, vec![genesis_header.hash()], None, &Default::default()).unwrap();
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap();
|
||||
|
||||
let bytes = e.rlp_bytes();
|
||||
assert_eq!(bytes, orig_bytes);
|
||||
@@ -632,7 +662,7 @@ mod tests {
|
||||
assert_eq!(uncles[1].extra_data, b"uncle2");
|
||||
|
||||
let db = e.drain();
|
||||
assert_eq!(orig_db.keys(), db.keys());
|
||||
assert!(orig_db.keys().iter().filter(|k| orig_db.get(k.0) != db.get(k.0)).next() == None);
|
||||
assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys());
|
||||
assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,18 @@
|
||||
//! Sorts them ready for blockchain insertion.
|
||||
use std::thread::{JoinHandle, self};
|
||||
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
|
||||
use std::sync::{Condvar as SCondvar, Mutex as SMutex};
|
||||
use util::*;
|
||||
use io::*;
|
||||
use verification::*;
|
||||
use error::*;
|
||||
use engine::Engine;
|
||||
use engines::Engine;
|
||||
use views::*;
|
||||
use header::*;
|
||||
use service::*;
|
||||
use client::BlockStatus;
|
||||
use util::panics::*;
|
||||
|
||||
pub use types::block_queue_info::BlockQueueInfo;
|
||||
|
||||
known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock);
|
||||
|
||||
@@ -34,7 +37,7 @@ const MIN_MEM_LIMIT: usize = 16384;
|
||||
const MIN_QUEUE_LIMIT: usize = 512;
|
||||
|
||||
/// Block queue configuration
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BlockQueueConfig {
|
||||
/// Maximum number of blocks to keep in unverified queue.
|
||||
/// When the limit is reached, is_full returns true.
|
||||
@@ -53,22 +56,6 @@ impl Default for BlockQueueConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Block queue status
|
||||
#[derive(Debug)]
|
||||
pub struct BlockQueueInfo {
|
||||
/// Number of queued blocks pending verification
|
||||
pub unverified_queue_size: usize,
|
||||
/// Number of verified queued blocks pending import
|
||||
pub verified_queue_size: usize,
|
||||
/// Number of blocks being verified
|
||||
pub verifying_queue_size: usize,
|
||||
/// Configured maximum number of blocks in the queue
|
||||
pub max_queue_size: usize,
|
||||
/// Configured maximum number of bytes to use
|
||||
pub max_mem_use: usize,
|
||||
/// Heap memory used in bytes
|
||||
pub mem_used: usize,
|
||||
}
|
||||
|
||||
impl BlockQueueInfo {
|
||||
/// The total size of the queues.
|
||||
@@ -93,13 +80,13 @@ impl BlockQueueInfo {
|
||||
/// Sorts them ready for blockchain insertion.
|
||||
pub struct BlockQueue {
|
||||
panic_handler: Arc<PanicHandler>,
|
||||
engine: Arc<Box<Engine>>,
|
||||
more_to_verify: Arc<Condvar>,
|
||||
engine: Arc<Engine>,
|
||||
more_to_verify: Arc<SCondvar>,
|
||||
verification: Arc<Verification>,
|
||||
verifiers: Vec<JoinHandle<()>>,
|
||||
deleting: Arc<AtomicBool>,
|
||||
ready_signal: Arc<QueueSignal>,
|
||||
empty: Arc<Condvar>,
|
||||
empty: Arc<SCondvar>,
|
||||
processing: RwLock<HashSet<H256>>,
|
||||
max_queue_size: usize,
|
||||
max_mem_use: usize,
|
||||
@@ -118,7 +105,7 @@ struct VerifyingBlock {
|
||||
struct QueueSignal {
|
||||
deleting: Arc<AtomicBool>,
|
||||
signalled: AtomicBool,
|
||||
message_channel: IoChannel<NetSyncMessage>,
|
||||
message_channel: IoChannel<ClientIoMessage>,
|
||||
}
|
||||
|
||||
impl QueueSignal {
|
||||
@@ -130,7 +117,7 @@ impl QueueSignal {
|
||||
}
|
||||
|
||||
if self.signalled.compare_and_swap(false, true, AtomicOrdering::Relaxed) == false {
|
||||
if let Err(e) = self.message_channel.send(UserMessage(SyncMessage::BlockVerified)) {
|
||||
if let Err(e) = self.message_channel.send(ClientIoMessage::BlockVerified) {
|
||||
debug!("Error sending BlockVerified message: {:?}", e);
|
||||
}
|
||||
}
|
||||
@@ -147,25 +134,30 @@ struct Verification {
|
||||
verified: Mutex<VecDeque<PreverifiedBlock>>,
|
||||
verifying: Mutex<VecDeque<VerifyingBlock>>,
|
||||
bad: Mutex<HashSet<H256>>,
|
||||
more_to_verify: SMutex<()>,
|
||||
empty: SMutex<()>,
|
||||
}
|
||||
|
||||
impl BlockQueue {
|
||||
/// Creates a new queue instance.
|
||||
pub fn new(config: BlockQueueConfig, engine: Arc<Box<Engine>>, message_channel: IoChannel<NetSyncMessage>) -> BlockQueue {
|
||||
pub fn new(config: BlockQueueConfig, engine: Arc<Engine>, message_channel: IoChannel<ClientIoMessage>) -> BlockQueue {
|
||||
let verification = Arc::new(Verification {
|
||||
unverified: Mutex::new(VecDeque::new()),
|
||||
verified: Mutex::new(VecDeque::new()),
|
||||
verifying: Mutex::new(VecDeque::new()),
|
||||
bad: Mutex::new(HashSet::new()),
|
||||
more_to_verify: SMutex::new(()),
|
||||
empty: SMutex::new(()),
|
||||
|
||||
});
|
||||
let more_to_verify = Arc::new(Condvar::new());
|
||||
let more_to_verify = Arc::new(SCondvar::new());
|
||||
let deleting = Arc::new(AtomicBool::new(false));
|
||||
let ready_signal = Arc::new(QueueSignal {
|
||||
deleting: deleting.clone(),
|
||||
signalled: AtomicBool::new(false),
|
||||
message_channel: message_channel
|
||||
});
|
||||
let empty = Arc::new(Condvar::new());
|
||||
let empty = Arc::new(SCondvar::new());
|
||||
let panic_handler = PanicHandler::new_in_arc();
|
||||
|
||||
let mut verifiers: Vec<JoinHandle<()>> = Vec::new();
|
||||
@@ -204,17 +196,17 @@ impl BlockQueue {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify(verification: Arc<Verification>, engine: Arc<Box<Engine>>, wait: Arc<Condvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<Condvar>) {
|
||||
fn verify(verification: Arc<Verification>, engine: Arc<Engine>, wait: Arc<SCondvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<SCondvar>) {
|
||||
while !deleting.load(AtomicOrdering::Acquire) {
|
||||
{
|
||||
let mut unverified = verification.unverified.lock().unwrap();
|
||||
let mut more_to_verify = verification.more_to_verify.lock().unwrap();
|
||||
|
||||
if unverified.is_empty() && verification.verifying.lock().unwrap().is_empty() {
|
||||
if verification.unverified.lock().is_empty() && verification.verifying.lock().is_empty() {
|
||||
empty.notify_all();
|
||||
}
|
||||
|
||||
while unverified.is_empty() && !deleting.load(AtomicOrdering::Acquire) {
|
||||
unverified = wait.wait(unverified).unwrap();
|
||||
while verification.unverified.lock().is_empty() && !deleting.load(AtomicOrdering::Acquire) {
|
||||
more_to_verify = wait.wait(more_to_verify).unwrap();
|
||||
}
|
||||
|
||||
if deleting.load(AtomicOrdering::Acquire) {
|
||||
@@ -223,20 +215,20 @@ impl BlockQueue {
|
||||
}
|
||||
|
||||
let block = {
|
||||
let mut unverified = verification.unverified.lock().unwrap();
|
||||
let mut unverified = verification.unverified.lock();
|
||||
if unverified.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut verifying = verification.verifying.lock().unwrap();
|
||||
let mut verifying = verification.verifying.lock();
|
||||
let block = unverified.pop_front().unwrap();
|
||||
verifying.push_back(VerifyingBlock{ hash: block.header.hash(), block: None });
|
||||
block
|
||||
};
|
||||
|
||||
let block_hash = block.header.hash();
|
||||
match verify_block_unordered(block.header, block.bytes, engine.deref().deref()) {
|
||||
match verify_block_unordered(block.header, block.bytes, &*engine) {
|
||||
Ok(verified) => {
|
||||
let mut verifying = verification.verifying.lock().unwrap();
|
||||
let mut verifying = verification.verifying.lock();
|
||||
for e in verifying.iter_mut() {
|
||||
if e.hash == block_hash {
|
||||
e.block = Some(verified);
|
||||
@@ -245,16 +237,16 @@ impl BlockQueue {
|
||||
}
|
||||
if !verifying.is_empty() && verifying.front().unwrap().hash == block_hash {
|
||||
// we're next!
|
||||
let mut verified = verification.verified.lock().unwrap();
|
||||
let mut bad = verification.bad.lock().unwrap();
|
||||
let mut verified = verification.verified.lock();
|
||||
let mut bad = verification.bad.lock();
|
||||
BlockQueue::drain_verifying(&mut verifying, &mut verified, &mut bad);
|
||||
ready.set();
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
let mut verifying = verification.verifying.lock().unwrap();
|
||||
let mut verified = verification.verified.lock().unwrap();
|
||||
let mut bad = verification.bad.lock().unwrap();
|
||||
let mut verifying = verification.verifying.lock();
|
||||
let mut verified = verification.verified.lock();
|
||||
let mut bad = verification.bad.lock();
|
||||
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
|
||||
bad.insert(block_hash.clone());
|
||||
verifying.retain(|e| e.hash != block_hash);
|
||||
@@ -279,29 +271,29 @@ impl BlockQueue {
|
||||
|
||||
/// Clear the queue and stop verification activity.
|
||||
pub fn clear(&self) {
|
||||
let mut unverified = self.verification.unverified.lock().unwrap();
|
||||
let mut verifying = self.verification.verifying.lock().unwrap();
|
||||
let mut verified = self.verification.verified.lock().unwrap();
|
||||
let mut unverified = self.verification.unverified.lock();
|
||||
let mut verifying = self.verification.verifying.lock();
|
||||
let mut verified = self.verification.verified.lock();
|
||||
unverified.clear();
|
||||
verifying.clear();
|
||||
verified.clear();
|
||||
self.processing.write().unwrap().clear();
|
||||
self.processing.write().clear();
|
||||
}
|
||||
|
||||
/// Wait for unverified queue to be empty
|
||||
pub fn flush(&self) {
|
||||
let mut unverified = self.verification.unverified.lock().unwrap();
|
||||
while !unverified.is_empty() || !self.verification.verifying.lock().unwrap().is_empty() {
|
||||
unverified = self.empty.wait(unverified).unwrap();
|
||||
let mut lock = self.verification.empty.lock().unwrap();
|
||||
while !self.verification.unverified.lock().is_empty() || !self.verification.verifying.lock().is_empty() {
|
||||
lock = self.empty.wait(lock).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the block is currently in the queue
|
||||
pub fn block_status(&self, hash: &H256) -> BlockStatus {
|
||||
if self.processing.read().unwrap().contains(&hash) {
|
||||
if self.processing.read().contains(hash) {
|
||||
return BlockStatus::Queued;
|
||||
}
|
||||
if self.verification.bad.lock().unwrap().contains(&hash) {
|
||||
if self.verification.bad.lock().contains(hash) {
|
||||
return BlockStatus::Bad;
|
||||
}
|
||||
BlockStatus::Unknown
|
||||
@@ -312,11 +304,11 @@ impl BlockQueue {
|
||||
let header = BlockView::new(&bytes).header();
|
||||
let h = header.hash();
|
||||
{
|
||||
if self.processing.read().unwrap().contains(&h) {
|
||||
if self.processing.read().contains(&h) {
|
||||
return Err(ImportError::AlreadyQueued.into());
|
||||
}
|
||||
|
||||
let mut bad = self.verification.bad.lock().unwrap();
|
||||
let mut bad = self.verification.bad.lock();
|
||||
if bad.contains(&h) {
|
||||
return Err(ImportError::KnownBad.into());
|
||||
}
|
||||
@@ -327,16 +319,16 @@ impl BlockQueue {
|
||||
}
|
||||
}
|
||||
|
||||
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
|
||||
match verify_block_basic(&header, &bytes, &*self.engine) {
|
||||
Ok(()) => {
|
||||
self.processing.write().unwrap().insert(h.clone());
|
||||
self.verification.unverified.lock().unwrap().push_back(UnverifiedBlock { header: header, bytes: bytes });
|
||||
self.processing.write().insert(h.clone());
|
||||
self.verification.unverified.lock().push_back(UnverifiedBlock { header: header, bytes: bytes });
|
||||
self.more_to_verify.notify_all();
|
||||
Ok(h)
|
||||
},
|
||||
Err(err) => {
|
||||
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
|
||||
self.verification.bad.lock().unwrap().insert(h.clone());
|
||||
self.verification.bad.lock().insert(h.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
@@ -347,14 +339,14 @@ impl BlockQueue {
|
||||
if block_hashes.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut verified_lock = self.verification.verified.lock().unwrap();
|
||||
let mut verified = verified_lock.deref_mut();
|
||||
let mut bad = self.verification.bad.lock().unwrap();
|
||||
let mut processing = self.processing.write().unwrap();
|
||||
let mut verified_lock = self.verification.verified.lock();
|
||||
let mut verified = &mut *verified_lock;
|
||||
let mut bad = self.verification.bad.lock();
|
||||
let mut processing = self.processing.write();
|
||||
bad.reserve(block_hashes.len());
|
||||
for hash in block_hashes {
|
||||
bad.insert(hash.clone());
|
||||
processing.remove(&hash);
|
||||
processing.remove(hash);
|
||||
}
|
||||
|
||||
let mut new_verified = VecDeque::new();
|
||||
@@ -374,15 +366,15 @@ impl BlockQueue {
|
||||
if block_hashes.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut processing = self.processing.write().unwrap();
|
||||
let mut processing = self.processing.write();
|
||||
for hash in block_hashes {
|
||||
processing.remove(&hash);
|
||||
processing.remove(hash);
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes up to `max` verified blocks from the queue
|
||||
pub fn drain(&self, max: usize) -> Vec<PreverifiedBlock> {
|
||||
let mut verified = self.verification.verified.lock().unwrap();
|
||||
let mut verified = self.verification.verified.lock();
|
||||
let count = min(max, verified.len());
|
||||
let mut result = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
@@ -399,15 +391,15 @@ impl BlockQueue {
|
||||
/// Get queue status.
|
||||
pub fn queue_info(&self) -> BlockQueueInfo {
|
||||
let (unverified_len, unverified_bytes) = {
|
||||
let v = self.verification.unverified.lock().unwrap();
|
||||
let v = self.verification.unverified.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
};
|
||||
let (verifying_len, verifying_bytes) = {
|
||||
let v = self.verification.verifying.lock().unwrap();
|
||||
let v = self.verification.verifying.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
};
|
||||
let (verified_len, verified_bytes) = {
|
||||
let v = self.verification.verified.lock().unwrap();
|
||||
let v = self.verification.verified.lock();
|
||||
(v.len(), v.heap_size_of_children())
|
||||
};
|
||||
BlockQueueInfo {
|
||||
@@ -421,18 +413,18 @@ impl BlockQueue {
|
||||
+ verifying_bytes
|
||||
+ verified_bytes
|
||||
// TODO: https://github.com/servo/heapsize/pull/50
|
||||
//+ self.processing.read().unwrap().heap_size_of_children(),
|
||||
//+ self.processing.read().heap_size_of_children(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Optimise memory footprint of the heap fields.
|
||||
pub fn collect_garbage(&self) {
|
||||
{
|
||||
self.verification.unverified.lock().unwrap().shrink_to_fit();
|
||||
self.verification.verifying.lock().unwrap().shrink_to_fit();
|
||||
self.verification.verified.lock().unwrap().shrink_to_fit();
|
||||
self.verification.unverified.lock().shrink_to_fit();
|
||||
self.verification.verifying.lock().shrink_to_fit();
|
||||
self.verification.verified.lock().shrink_to_fit();
|
||||
}
|
||||
self.processing.write().unwrap().shrink_to_fit();
|
||||
self.processing.write().shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,6 +450,7 @@ impl Drop for BlockQueue {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use util::*;
|
||||
use io::*;
|
||||
use spec::*;
|
||||
use block_queue::*;
|
||||
use tests::helpers::*;
|
||||
@@ -467,7 +460,7 @@ mod tests {
|
||||
fn get_test_queue() -> BlockQueue {
|
||||
let spec = get_test_spec();
|
||||
let engine = spec.engine;
|
||||
BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected())
|
||||
BlockQueue::new(BlockQueueConfig::default(), engine, IoChannel::disconnected())
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -475,7 +468,7 @@ mod tests {
|
||||
// TODO better test
|
||||
let spec = Spec::new_test();
|
||||
let engine = spec.engine;
|
||||
let _ = BlockQueue::new(BlockQueueConfig::default(), Arc::new(engine), IoChannel::disconnected());
|
||||
let _ = BlockQueue::new(BlockQueueConfig::default(), engine, IoChannel::disconnected());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -538,7 +531,7 @@ mod tests {
|
||||
let engine = spec.engine;
|
||||
let mut config = BlockQueueConfig::default();
|
||||
config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000
|
||||
let queue = BlockQueue::new(config, Arc::new(engine), IoChannel::disconnected());
|
||||
let queue = BlockQueue::new(config, engine, IoChannel::disconnected());
|
||||
assert!(!queue.queue_info().is_full());
|
||||
let mut blocks = get_good_dummy_block_seq(50);
|
||||
for b in blocks.drain(..) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::bytes::Bytes;
|
||||
use util::numbers::{U256,H256};
|
||||
use header::BlockNumber;
|
||||
|
||||
@@ -25,5 +26,7 @@ pub struct BestBlock {
|
||||
/// Best block number.
|
||||
pub number: BlockNumber,
|
||||
/// Best block total difficulty.
|
||||
pub total_difficulty: U256
|
||||
pub total_difficulty: U256,
|
||||
/// Best block uncompressed bytes
|
||||
pub block: Bytes,
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
use util::numbers::{U256,H256};
|
||||
use header::BlockNumber;
|
||||
|
||||
use util::bytes::{FromRawBytesVariable, FromBytesError, ToBytesWithMap};
|
||||
|
||||
/// Brief info about inserted block.
|
||||
#[derive(Clone)]
|
||||
pub struct BlockInfo {
|
||||
@@ -33,19 +31,19 @@ pub struct BlockInfo {
|
||||
}
|
||||
|
||||
/// Describes location of newly inserted block.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BlockLocation {
|
||||
/// It's part of the canon chain.
|
||||
CanonChain,
|
||||
/// It's not a part of the canon chain.
|
||||
Branch,
|
||||
/// It's part of the fork which should become canon chain,
|
||||
/// because it's total difficulty is higher than current
|
||||
/// because its total difficulty is higher than current
|
||||
/// canon chain difficulty.
|
||||
BranchBecomingCanonChain(BranchBecomingCanonChainData),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BranchBecomingCanonChainData {
|
||||
/// Hash of the newest common ancestor with old canon chain.
|
||||
pub ancestor: H256,
|
||||
@@ -54,43 +52,3 @@ pub struct BranchBecomingCanonChainData {
|
||||
/// Hashes of the blocks which were invalidated.
|
||||
pub retracted: Vec<H256>,
|
||||
}
|
||||
|
||||
impl FromRawBytesVariable for BranchBecomingCanonChainData {
|
||||
fn from_bytes_variable(bytes: &[u8]) -> Result<BranchBecomingCanonChainData, FromBytesError> {
|
||||
type Tuple = (Vec<H256>, Vec<H256>, H256);
|
||||
let (enacted, retracted, ancestor) = try!(Tuple::from_bytes_variable(bytes));
|
||||
Ok(BranchBecomingCanonChainData { ancestor: ancestor, enacted: enacted, retracted: retracted })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawBytesVariable for BlockLocation {
|
||||
fn from_bytes_variable(bytes: &[u8]) -> Result<BlockLocation, FromBytesError> {
|
||||
match bytes[0] {
|
||||
0 => Ok(BlockLocation::CanonChain),
|
||||
1 => Ok(BlockLocation::Branch),
|
||||
2 => Ok(BlockLocation::BranchBecomingCanonChain(
|
||||
try!(BranchBecomingCanonChainData::from_bytes_variable(&bytes[1..bytes.len()])))),
|
||||
_ => Err(FromBytesError::UnknownMarker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBytesWithMap for BranchBecomingCanonChainData {
|
||||
fn to_bytes_map(&self) -> Vec<u8> {
|
||||
(&self.enacted, &self.retracted, &self.ancestor).to_bytes_map()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBytesWithMap for BlockLocation {
|
||||
fn to_bytes_map(&self) -> Vec<u8> {
|
||||
match *self {
|
||||
BlockLocation::CanonChain => vec![0u8],
|
||||
BlockLocation::Branch => vec![1u8],
|
||||
BlockLocation::BranchBecomingCanonChain(ref data) => {
|
||||
let mut bytes = (&data.enacted, &data.retracted, &data.ancestor).to_bytes_map();
|
||||
bytes.insert(0, 2u8);
|
||||
bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@
|
||||
//! Blockchain configuration.
|
||||
|
||||
/// Blockchain configuration.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Config {
|
||||
/// Preferred cache size in bytes.
|
||||
pub pref_cache_size: usize,
|
||||
|
||||
@@ -41,7 +41,7 @@ pub enum ExtrasIndex {
|
||||
fn with_index(hash: &H256, i: ExtrasIndex) -> H264 {
|
||||
let mut result = H264::default();
|
||||
result[0] = i as u8;
|
||||
result.deref_mut()[1..].clone_from_slice(hash);
|
||||
(*result)[1..].clone_from_slice(hash);
|
||||
result
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ impl Encodable for BlockDetails {
|
||||
}
|
||||
|
||||
/// Represents address of certain transaction within block
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TransactionAddress {
|
||||
/// Block hash
|
||||
pub block_hash: H256,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
use util::rlp::*;
|
||||
use util::{H256, H2048};
|
||||
use util::U256;
|
||||
use util::bytes::Bytes;
|
||||
use header::Header;
|
||||
use transaction::SignedTransaction;
|
||||
@@ -24,6 +23,7 @@ use transaction::SignedTransaction;
|
||||
use super::fork::Forkable;
|
||||
use super::bloom::WithBloom;
|
||||
use super::complete::CompleteBlock;
|
||||
use super::transaction::WithTransaction;
|
||||
|
||||
/// Helper structure, used for encoding blocks.
|
||||
#[derive(Default)]
|
||||
@@ -44,7 +44,8 @@ impl Encodable for Block {
|
||||
|
||||
impl Forkable for Block {
|
||||
fn fork(mut self, fork_number: usize) -> Self where Self: Sized {
|
||||
self.header.difficulty = self.header.difficulty - U256::from(fork_number);
|
||||
let difficulty = self.header.difficulty().clone() - fork_number.into();
|
||||
self.header.difficulty = difficulty;
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -56,6 +57,13 @@ impl WithBloom for Block {
|
||||
}
|
||||
}
|
||||
|
||||
impl WithTransaction for Block {
|
||||
fn with_transaction(mut self, transaction: SignedTransaction) -> Self where Self: Sized {
|
||||
self.transactions.push(transaction);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl CompleteBlock for Block {
|
||||
fn complete(mut self, parent_hash: H256) -> Bytes {
|
||||
self.header.parent_hash = parent_hash;
|
||||
|
||||
@@ -18,10 +18,12 @@ use util::hash::H2048;
|
||||
use util::numbers::U256;
|
||||
use util::bytes::Bytes;
|
||||
use header::BlockNumber;
|
||||
use transaction::SignedTransaction;
|
||||
use super::fork::Fork;
|
||||
use super::bloom::Bloom;
|
||||
use super::complete::{BlockFinalizer, CompleteBlock, Complete};
|
||||
use super::block::Block;
|
||||
use super::transaction::Transaction;
|
||||
|
||||
/// Chain iterator interface.
|
||||
pub trait ChainIterator: Iterator + Sized {
|
||||
@@ -30,6 +32,8 @@ pub trait ChainIterator: Iterator + Sized {
|
||||
fn fork(&self, fork_number: usize) -> Fork<Self> where Self: Clone;
|
||||
/// Should be called to make every consecutive block have given bloom.
|
||||
fn with_bloom(&mut self, bloom: H2048) -> Bloom<Self>;
|
||||
/// Should be called to make every consecutive block have given transaction.
|
||||
fn with_transaction(&mut self, transaction: SignedTransaction) -> Transaction<Self>;
|
||||
/// Should be called to complete block. Without complete, block may have incorrect hash.
|
||||
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>;
|
||||
/// Completes and generates block.
|
||||
@@ -51,6 +55,13 @@ impl<I> ChainIterator for I where I: Iterator + Sized {
|
||||
}
|
||||
}
|
||||
|
||||
fn with_transaction(&mut self, transaction: SignedTransaction) -> Transaction<Self> {
|
||||
Transaction {
|
||||
iter: self,
|
||||
transaction: transaction,
|
||||
}
|
||||
}
|
||||
|
||||
fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self> {
|
||||
Complete {
|
||||
iter: self,
|
||||
@@ -85,7 +96,7 @@ impl Default for ChainGenerator {
|
||||
fn default() -> Self {
|
||||
ChainGenerator {
|
||||
number: 0,
|
||||
difficulty: U256::from(1000),
|
||||
difficulty: 1000.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Blockchain generator for tests.
|
||||
|
||||
mod bloom;
|
||||
mod block;
|
||||
mod complete;
|
||||
mod fork;
|
||||
pub mod generator;
|
||||
mod transaction;
|
||||
|
||||
pub use self::complete::BlockFinalizer;
|
||||
pub use self::generator::{ChainIterator, ChainGenerator};
|
||||
|
||||
35
ethcore/src/blockchain/generator/transaction.rs
Normal file
35
ethcore/src/blockchain/generator/transaction.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 transaction::SignedTransaction;
|
||||
|
||||
pub trait WithTransaction {
|
||||
fn with_transaction(self, transaction: SignedTransaction) -> Self where Self: Sized;
|
||||
}
|
||||
|
||||
pub struct Transaction<'a, I> where I: 'a {
|
||||
pub iter: &'a mut I,
|
||||
pub transaction: SignedTransaction,
|
||||
}
|
||||
|
||||
impl <'a, I> Iterator for Transaction<'a, I> where I: Iterator, <I as Iterator>::Item: WithTransaction {
|
||||
type Item = <I as Iterator>::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|item| item.with_transaction(self.transaction.clone()))
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ mod import_route;
|
||||
mod update;
|
||||
|
||||
#[cfg(test)]
|
||||
mod generator;
|
||||
pub mod generator;
|
||||
|
||||
pub use self::blockchain::{BlockProvider, BlockChain};
|
||||
pub use self::cache::CacheSize;
|
||||
|
||||
@@ -6,17 +6,19 @@ use blooms::BloomGroup;
|
||||
use super::extras::{BlockDetails, BlockReceipts, TransactionAddress, LogGroupPosition};
|
||||
|
||||
/// Block extras update info.
|
||||
pub struct ExtrasUpdate {
|
||||
pub struct ExtrasUpdate<'a> {
|
||||
/// Block info.
|
||||
pub info: BlockInfo,
|
||||
/// Current block uncompressed rlp bytes
|
||||
pub block: &'a [u8],
|
||||
/// Modified block hashes.
|
||||
pub block_hashes: HashMap<BlockNumber, H256>,
|
||||
/// Modified block details.
|
||||
pub block_details: HashMap<H256, BlockDetails>,
|
||||
/// Modified block receipts.
|
||||
pub block_receipts: HashMap<H256, BlockReceipts>,
|
||||
/// Modified transaction addresses.
|
||||
pub transactions_addresses: HashMap<H256, TransactionAddress>,
|
||||
/// Modified blocks blooms.
|
||||
pub blocks_blooms: HashMap<LogGroupPosition, BloomGroup>,
|
||||
/// Modified transaction addresses (None signifies removed transactions).
|
||||
pub transactions_addresses: HashMap<H256, Option<TransactionAddress>>,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ use util::HeapSizeOf;
|
||||
use basic_types::LogBloom;
|
||||
|
||||
/// Helper structure representing bloom of the trace.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bloom(LogBloom);
|
||||
|
||||
impl From<LogBloom> for Bloom {
|
||||
|
||||
@@ -20,7 +20,7 @@ use util::HeapSizeOf;
|
||||
use super::Bloom;
|
||||
|
||||
/// Represents group of X consecutive blooms.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BloomGroup {
|
||||
blooms: Vec<Bloom>,
|
||||
}
|
||||
|
||||
72
ethcore/src/cache_manager.rs
Normal file
72
ethcore/src/cache_manager.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{VecDeque, HashSet};
|
||||
use std::hash::Hash;
|
||||
|
||||
const COLLECTION_QUEUE_SIZE: usize = 8;
|
||||
|
||||
pub struct CacheManager<T> where T: Eq + Hash {
|
||||
pref_cache_size: usize,
|
||||
max_cache_size: usize,
|
||||
bytes_per_cache_entry: usize,
|
||||
cache_usage: VecDeque<HashSet<T>>
|
||||
}
|
||||
|
||||
impl<T> CacheManager<T> where T: Eq + Hash {
|
||||
pub fn new(pref_cache_size: usize, max_cache_size: usize, bytes_per_cache_entry: usize) -> Self {
|
||||
CacheManager {
|
||||
pref_cache_size: pref_cache_size,
|
||||
max_cache_size: max_cache_size,
|
||||
bytes_per_cache_entry: bytes_per_cache_entry,
|
||||
cache_usage: (0..COLLECTION_QUEUE_SIZE).into_iter().map(|_| Default::default()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn note_used(&mut self, id: T) {
|
||||
if !self.cache_usage[0].contains(&id) {
|
||||
if let Some(c) = self.cache_usage.iter_mut().skip(1).find(|e| e.contains(&id)) {
|
||||
c.remove(&id);
|
||||
}
|
||||
self.cache_usage[0].insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects unused objects from cache.
|
||||
/// First params is the current size of the cache.
|
||||
/// Second one is an with objects to remove. It should also return new size of the cache.
|
||||
pub fn collect_garbage<F>(&mut self, current_size: usize, mut notify_unused: F) where F: FnMut(HashSet<T>) -> usize {
|
||||
if current_size < self.pref_cache_size {
|
||||
self.rotate_cache_if_needed();
|
||||
return;
|
||||
}
|
||||
|
||||
for _ in 0..COLLECTION_QUEUE_SIZE {
|
||||
let current_size = notify_unused(self.cache_usage.pop_back().unwrap());
|
||||
self.cache_usage.push_front(Default::default());
|
||||
if current_size < self.max_cache_size {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate_cache_if_needed(&mut self) {
|
||||
if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE {
|
||||
let cache = self.cache_usage.pop_back().unwrap();
|
||||
self.cache_usage.push_front(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
ethcore/src/client/chain_notify.rs
Normal file
47
ethcore/src/client/chain_notify.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use util::numbers::*;
|
||||
use ipc::{IpcConfig, BinaryConvertError};
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
|
||||
/// Represents what has to be handled by actor listening to chain events
|
||||
#[derive(Ipc)]
|
||||
pub trait ChainNotify : Send + Sync {
|
||||
/// fires when chain has new blocks
|
||||
fn new_blocks(&self,
|
||||
_imported: Vec<H256>,
|
||||
_invalid: Vec<H256>,
|
||||
_enacted: Vec<H256>,
|
||||
_retracted: Vec<H256>,
|
||||
_sealed: Vec<H256>,
|
||||
_duration: u64) {
|
||||
// does nothing by default
|
||||
}
|
||||
|
||||
/// fires when chain achieves active mode
|
||||
fn start(&self) {
|
||||
// does nothing by default
|
||||
}
|
||||
|
||||
/// fires when chain achieves passive mode
|
||||
fn stop(&self) {
|
||||
// does nothing by default
|
||||
}
|
||||
}
|
||||
|
||||
impl IpcConfig for ChainNotify { }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,12 +14,15 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::str::FromStr;
|
||||
pub use std::time::Duration;
|
||||
pub use block_queue::BlockQueueConfig;
|
||||
pub use blockchain::Config as BlockChainConfig;
|
||||
pub use trace::{Config as TraceConfig, Switch};
|
||||
pub use evm::VMType;
|
||||
pub use verification::VerifierType;
|
||||
use util::journaldb;
|
||||
use util::{journaldb, CompactionProfile};
|
||||
use util::trie::TrieSpec;
|
||||
|
||||
/// Client state db compaction profile
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -31,11 +34,54 @@ pub enum DatabaseCompactionProfile {
|
||||
}
|
||||
|
||||
impl Default for DatabaseCompactionProfile {
|
||||
fn default() -> Self { DatabaseCompactionProfile::Default }
|
||||
fn default() -> Self {
|
||||
DatabaseCompactionProfile::Default
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseCompactionProfile {
|
||||
/// Returns corresponding compaction profile.
|
||||
pub fn compaction_profile(&self) -> CompactionProfile {
|
||||
match *self {
|
||||
DatabaseCompactionProfile::Default => Default::default(),
|
||||
DatabaseCompactionProfile::HDD => CompactionProfile::hdd(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for DatabaseCompactionProfile {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"ssd" | "default" => Ok(DatabaseCompactionProfile::Default),
|
||||
"hdd" => Ok(DatabaseCompactionProfile::HDD),
|
||||
_ => Err("Invalid compaction profile given. Expected hdd/ssd (default).".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Operating mode for the client.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum Mode {
|
||||
/// Always on.
|
||||
Active,
|
||||
/// Goes offline after RLP is inactive for some (given) time, but
|
||||
/// comes back online after a while of inactivity.
|
||||
Passive(Duration, Duration),
|
||||
/// Goes offline after RLP is inactive for some (given) time and
|
||||
/// stays inactive.
|
||||
Dark(Duration),
|
||||
}
|
||||
|
||||
impl Default for Mode {
|
||||
fn default() -> Self {
|
||||
Mode::Active
|
||||
}
|
||||
}
|
||||
|
||||
/// Client configuration. Includes configs for all sub-systems.
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
pub struct ClientConfig {
|
||||
/// Block queue configuration.
|
||||
pub queue: BlockQueueConfig,
|
||||
@@ -45,6 +91,8 @@ pub struct ClientConfig {
|
||||
pub tracing: TraceConfig,
|
||||
/// VM type.
|
||||
pub vm_type: VMType,
|
||||
/// Trie type.
|
||||
pub trie_spec: TrieSpec,
|
||||
/// The JournalDB ("pruning") algorithm to use.
|
||||
pub pruning: journaldb::Algorithm,
|
||||
/// The name of the client instance.
|
||||
@@ -53,6 +101,32 @@ pub struct ClientConfig {
|
||||
pub db_cache_size: Option<usize>,
|
||||
/// State db compaction profile
|
||||
pub db_compaction: DatabaseCompactionProfile,
|
||||
/// Should db have WAL enabled?
|
||||
pub db_wal: bool,
|
||||
/// Operating mode
|
||||
pub mode: Mode,
|
||||
/// Type of block verifier used by client.
|
||||
pub verifier_type: VerifierType,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{DatabaseCompactionProfile, Mode};
|
||||
|
||||
#[test]
|
||||
fn test_default_compaction_profile() {
|
||||
assert_eq!(DatabaseCompactionProfile::default(), DatabaseCompactionProfile::Default);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parsing_compaction_profile() {
|
||||
assert_eq!(DatabaseCompactionProfile::Default, "ssd".parse().unwrap());
|
||||
assert_eq!(DatabaseCompactionProfile::Default, "default".parse().unwrap());
|
||||
assert_eq!(DatabaseCompactionProfile::HDD, "hdd".parse().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mode_default() {
|
||||
assert_eq!(Mode::default(), Mode::Active);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
use trace::Error as TraceError;
|
||||
use util::UtilError;
|
||||
use std::fmt::{Display, Formatter, Error as FmtError};
|
||||
|
||||
use util::trie::TrieError;
|
||||
|
||||
/// Client configuration errors.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// TraceDB configuration error.
|
||||
Trace(TraceError),
|
||||
/// TrieDB-related error.
|
||||
Trie(TrieError),
|
||||
/// Database error
|
||||
Database(String),
|
||||
/// Util error
|
||||
Util(UtilError),
|
||||
}
|
||||
|
||||
impl From<TraceError> for Error {
|
||||
@@ -14,10 +23,31 @@ impl From<TraceError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TrieError> for Error {
|
||||
fn from(err: TrieError) -> Self {
|
||||
Error::Trie(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UtilError> for Error {
|
||||
fn from(err: UtilError) -> Self {
|
||||
Error::Util(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<Box<E>> for Error where Error: From<E> {
|
||||
fn from(err: Box<E>) -> Self {
|
||||
Error::from(*err)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||
match *self {
|
||||
Error::Trace(ref err) => write!(f, "{}", err)
|
||||
Error::Trace(ref err) => write!(f, "{}", err),
|
||||
Error::Trie(ref err) => write!(f, "{}", err),
|
||||
Error::Util(ref err) => write!(f, "{}", err),
|
||||
Error::Database(ref s) => write!(f, "Database error: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,244 +16,36 @@
|
||||
|
||||
//! Blockchain database client.
|
||||
|
||||
mod client;
|
||||
mod config;
|
||||
mod error;
|
||||
mod test_client;
|
||||
mod trace;
|
||||
mod client;
|
||||
|
||||
pub use self::client::*;
|
||||
pub use self::config::{ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
|
||||
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
|
||||
pub use self::error::Error;
|
||||
pub use types::ids::*;
|
||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||
pub use self::trace::Filter as TraceFilter;
|
||||
pub use types::trace_filter::Filter as TraceFilter;
|
||||
pub use executive::{Executed, Executive, TransactOptions};
|
||||
pub use env_info::{LastHashes, EnvInfo};
|
||||
pub use self::chain_notify::{ChainNotify, ChainNotifyClient};
|
||||
|
||||
use util::bytes::Bytes;
|
||||
use util::hash::{Address, H256, H2048};
|
||||
use util::numbers::U256;
|
||||
use util::Itertools;
|
||||
use blockchain::TreeRoute;
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
use header::{BlockNumber, Header};
|
||||
use transaction::{LocalizedTransaction, SignedTransaction};
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use filter::Filter;
|
||||
use views::{HeaderView, BlockView};
|
||||
use error::{ImportResult, ExecutionError};
|
||||
use receipt::LocalizedReceipt;
|
||||
use trace::LocalizedTrace;
|
||||
use evm::Factory as EvmFactory;
|
||||
use miner::{TransactionImportResult};
|
||||
use error::Error as EthError;
|
||||
pub use types::call_analytics::CallAnalytics;
|
||||
pub use block_import_error::BlockImportError;
|
||||
pub use transaction_import::TransactionImportResult;
|
||||
pub use transaction_import::TransactionImportError;
|
||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient, RemoteClient};
|
||||
|
||||
/// Options concerning what analytics we run on the call.
|
||||
#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)]
|
||||
pub struct CallAnalytics {
|
||||
/// Make a transaction trace.
|
||||
pub transaction_tracing: bool,
|
||||
/// Make a VM trace.
|
||||
pub vm_tracing: bool,
|
||||
/// Make a diff.
|
||||
pub state_diffing: bool,
|
||||
mod traits {
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/traits.rs"));
|
||||
}
|
||||
|
||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||
pub trait BlockChainClient : Sync + Send {
|
||||
/// Get raw block header data by block id.
|
||||
fn block_header(&self, id: BlockID) -> Option<Bytes>;
|
||||
|
||||
/// Get raw block body data by block id.
|
||||
/// Block body is an RLP list of two items: uncles and transactions.
|
||||
fn block_body(&self, id: BlockID) -> Option<Bytes>;
|
||||
|
||||
/// Get raw block data by block header hash.
|
||||
fn block(&self, id: BlockID) -> Option<Bytes>;
|
||||
|
||||
/// Get block status by block header hash.
|
||||
fn block_status(&self, id: BlockID) -> BlockStatus;
|
||||
|
||||
/// Get block total difficulty.
|
||||
fn block_total_difficulty(&self, id: BlockID) -> Option<U256>;
|
||||
|
||||
/// Attempt to get address nonce at given block.
|
||||
/// May not fail on BlockID::Latest.
|
||||
fn nonce(&self, address: &Address, id: BlockID) -> Option<U256>;
|
||||
|
||||
/// Get address nonce at the latest block's state.
|
||||
fn latest_nonce(&self, address: &Address) -> U256 {
|
||||
self.nonce(address, BlockID::Latest)
|
||||
.expect("nonce will return Some when given BlockID::Latest. nonce was given BlockID::Latest. \
|
||||
Therefore nonce has returned Some; qed")
|
||||
}
|
||||
|
||||
/// Get block hash.
|
||||
fn block_hash(&self, id: BlockID) -> Option<H256>;
|
||||
|
||||
/// Get address code.
|
||||
fn code(&self, address: &Address) -> Option<Bytes>;
|
||||
|
||||
/// Get address balance at the given block's state.
|
||||
///
|
||||
/// May not return None if given BlockID::Latest.
|
||||
/// Returns None if and only if the block's root hash has been pruned from the DB.
|
||||
fn balance(&self, address: &Address, id: BlockID) -> Option<U256>;
|
||||
|
||||
/// Get address balance at the latest block's state.
|
||||
fn latest_balance(&self, address: &Address) -> U256 {
|
||||
self.balance(address, BlockID::Latest)
|
||||
.expect("balance will return Some if given BlockID::Latest. balance was given BlockID::Latest \
|
||||
Therefore balance has returned Some; qed")
|
||||
}
|
||||
|
||||
/// Get value of the storage at given position at the given block's state.
|
||||
///
|
||||
/// May not return None if given BlockID::Latest.
|
||||
/// Returns None if and only if the block's root hash has been pruned from the DB.
|
||||
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256>;
|
||||
|
||||
/// Get value of the storage at given position at the latest block's state.
|
||||
fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 {
|
||||
self.storage_at(address, position, BlockID::Latest)
|
||||
.expect("storage_at will return Some if given BlockID::Latest. storage_at was given BlockID::Latest. \
|
||||
Therefore storage_at has returned Some; qed")
|
||||
}
|
||||
|
||||
/// Get transaction with given hash.
|
||||
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
|
||||
|
||||
/// Get uncle with given id.
|
||||
fn uncle(&self, id: UncleID) -> Option<Header>;
|
||||
|
||||
/// Get transaction receipt with given hash.
|
||||
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>;
|
||||
|
||||
/// Get a tree route between `from` and `to`.
|
||||
/// See `BlockChain::tree_route`.
|
||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
|
||||
|
||||
/// Get all possible uncle hashes for a block.
|
||||
fn find_uncles(&self, hash: &H256) -> Option<Vec<H256>>;
|
||||
|
||||
/// Get latest state node
|
||||
fn state_data(&self, hash: &H256) -> Option<Bytes>;
|
||||
|
||||
/// Get raw block receipts data by block header hash.
|
||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
|
||||
|
||||
/// Import a block into the blockchain.
|
||||
fn import_block(&self, bytes: Bytes) -> ImportResult;
|
||||
|
||||
/// Get block queue information.
|
||||
fn queue_info(&self) -> BlockQueueInfo;
|
||||
|
||||
/// Clear block queue and abort all import activity.
|
||||
fn clear_queue(&self);
|
||||
|
||||
/// Get blockchain information.
|
||||
fn chain_info(&self) -> BlockChainInfo;
|
||||
|
||||
/// Get the best block header.
|
||||
fn best_block_header(&self) -> Bytes {
|
||||
// TODO: lock blockchain only once
|
||||
self.block_header(BlockID::Hash(self.chain_info().best_block_hash)).unwrap()
|
||||
}
|
||||
|
||||
/// Returns numbers of blocks containing given bloom.
|
||||
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>>;
|
||||
|
||||
/// Returns logs matching given filter.
|
||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
||||
|
||||
/// Makes a non-persistent transaction call.
|
||||
// TODO: should be able to accept blockchain location for call.
|
||||
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>;
|
||||
|
||||
/// Returns traces matching given filter.
|
||||
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
/// Returns trace with given id.
|
||||
fn trace(&self, trace: TraceId) -> Option<LocalizedTrace>;
|
||||
|
||||
/// Returns traces created by transaction.
|
||||
fn transaction_traces(&self, trace: TransactionID) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
/// Returns traces created by transaction from block.
|
||||
fn block_traces(&self, trace: BlockID) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
/// Get last hashes starting from best block.
|
||||
fn last_hashes(&self) -> LastHashes;
|
||||
|
||||
/// import transactions from network/other 3rd party
|
||||
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, EthError>>;
|
||||
|
||||
/// Queue transactions for importing.
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>);
|
||||
|
||||
/// list all transactions
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get the gas price distribution.
|
||||
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
|
||||
let mut h = self.chain_info().best_block_hash;
|
||||
let mut corpus = Vec::new();
|
||||
for _ in 0..sample_size {
|
||||
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
||||
let block = BlockView::new(&block_bytes);
|
||||
let header = block.header_view();
|
||||
if header.number() == 0 {
|
||||
break;
|
||||
}
|
||||
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
|
||||
h = header.parent_hash().clone();
|
||||
}
|
||||
corpus.sort();
|
||||
let n = corpus.len();
|
||||
if n > 0 {
|
||||
Ok((0..(distribution_size + 1))
|
||||
.map(|i| corpus[i * (n - 1) / distribution_size])
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get `Some` gas limit of SOFT_FORK_BLOCK, or `None` if chain is not yet that long.
|
||||
fn dao_rescue_block_gas_limit(&self, chain_hash: H256) -> Option<U256> {
|
||||
const SOFT_FORK_BLOCK: u64 = 1800000;
|
||||
// shortcut if the canon chain is already known.
|
||||
if self.chain_info().best_block_number > SOFT_FORK_BLOCK + 1000 {
|
||||
return self.block_header(BlockID::Number(SOFT_FORK_BLOCK)).map(|header| HeaderView::new(&header).gas_limit());
|
||||
}
|
||||
// otherwise check according to `chain_hash`.
|
||||
if let Some(mut header) = self.block_header(BlockID::Hash(chain_hash)) {
|
||||
if HeaderView::new(&header).number() < SOFT_FORK_BLOCK {
|
||||
None
|
||||
} else {
|
||||
while HeaderView::new(&header).number() != SOFT_FORK_BLOCK {
|
||||
header = self.block_header(BlockID::Hash(HeaderView::new(&header).parent_hash())).expect("chain is complete; parent of chain entry must be in chain; qed");
|
||||
}
|
||||
Some(HeaderView::new(&header).gas_limit())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub mod chain_notify {
|
||||
//! Chain notify interface
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/chain_notify.rs"));
|
||||
}
|
||||
|
||||
/// Extended client interface used for mining
|
||||
pub trait MiningBlockChainClient : BlockChainClient {
|
||||
/// Returns OpenBlock prepared for closing.
|
||||
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes)
|
||||
-> OpenBlock;
|
||||
|
||||
/// Returns EvmFactory.
|
||||
fn vm_factory(&self) -> &EvmFactory;
|
||||
|
||||
/// Import sealed block. Skips all verifications.
|
||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
|
||||
}
|
||||
|
||||
@@ -18,27 +18,30 @@
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
|
||||
use util::*;
|
||||
use devtools::*;
|
||||
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
|
||||
use blockchain::TreeRoute;
|
||||
use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics};
|
||||
use client::{
|
||||
BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
|
||||
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
|
||||
DB_NO_OF_COLUMNS, DB_COL_STATE,
|
||||
};
|
||||
use header::{Header as BlockHeader, BlockNumber};
|
||||
use filter::Filter;
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use blockchain::extras::BlockReceipts;
|
||||
use error::{ImportResult};
|
||||
use evm::Factory as EvmFactory;
|
||||
use evm::{Factory as EvmFactory, VMType};
|
||||
use miner::{Miner, MinerService};
|
||||
use spec::Spec;
|
||||
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
use executive::Executed;
|
||||
use error::{ExecutionError};
|
||||
use error::CallError;
|
||||
use trace::LocalizedTrace;
|
||||
|
||||
use miner::{TransactionImportResult, AccountDetails};
|
||||
use error::Error as EthError;
|
||||
use state_db::StateDB;
|
||||
|
||||
/// Test client.
|
||||
pub struct TestBlockChainClient {
|
||||
@@ -61,13 +64,17 @@ pub struct TestBlockChainClient {
|
||||
/// Code.
|
||||
pub code: RwLock<HashMap<Address, Bytes>>,
|
||||
/// Execution result.
|
||||
pub execution_result: RwLock<Option<Executed>>,
|
||||
pub execution_result: RwLock<Option<Result<Executed, CallError>>>,
|
||||
/// Transaction receipts.
|
||||
pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>,
|
||||
/// Block queue size.
|
||||
pub queue_size: AtomicUsize,
|
||||
/// Miner
|
||||
pub miner: Arc<Miner>,
|
||||
/// Spec
|
||||
pub spec: Spec,
|
||||
/// VM Factory
|
||||
pub vm_factory: EvmFactory,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -92,7 +99,7 @@ impl Default for TestBlockChainClient {
|
||||
impl TestBlockChainClient {
|
||||
/// Creates new test client.
|
||||
pub fn new() -> Self {
|
||||
|
||||
let spec = Spec::new_test();
|
||||
let mut client = TestBlockChainClient {
|
||||
blocks: RwLock::new(HashMap::new()),
|
||||
numbers: RwLock::new(HashMap::new()),
|
||||
@@ -106,41 +113,43 @@ impl TestBlockChainClient {
|
||||
execution_result: RwLock::new(None),
|
||||
receipts: RwLock::new(HashMap::new()),
|
||||
queue_size: AtomicUsize::new(0),
|
||||
miner: Arc::new(Miner::with_spec(Spec::new_test())),
|
||||
miner: Arc::new(Miner::with_spec(&spec)),
|
||||
spec: spec,
|
||||
vm_factory: EvmFactory::new(VMType::Interpreter),
|
||||
};
|
||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||
client.genesis_hash = client.last_hash.read().unwrap().clone();
|
||||
client.genesis_hash = client.last_hash.read().clone();
|
||||
client
|
||||
}
|
||||
|
||||
/// Set the transaction receipt result
|
||||
pub fn set_transaction_receipt(&self, id: TransactionID, receipt: LocalizedReceipt) {
|
||||
self.receipts.write().unwrap().insert(id, receipt);
|
||||
self.receipts.write().insert(id, receipt);
|
||||
}
|
||||
|
||||
/// Set the execution result.
|
||||
pub fn set_execution_result(&self, result: Executed) {
|
||||
*self.execution_result.write().unwrap() = Some(result);
|
||||
pub fn set_execution_result(&self, result: Result<Executed, CallError>) {
|
||||
*self.execution_result.write() = Some(result);
|
||||
}
|
||||
|
||||
/// Set the balance of account `address` to `balance`.
|
||||
pub fn set_balance(&self, address: Address, balance: U256) {
|
||||
self.balances.write().unwrap().insert(address, balance);
|
||||
self.balances.write().insert(address, balance);
|
||||
}
|
||||
|
||||
/// Set nonce of account `address` to `nonce`.
|
||||
pub fn set_nonce(&self, address: Address, nonce: U256) {
|
||||
self.nonces.write().unwrap().insert(address, nonce);
|
||||
self.nonces.write().insert(address, nonce);
|
||||
}
|
||||
|
||||
/// Set `code` at `address`.
|
||||
pub fn set_code(&self, address: Address, code: Bytes) {
|
||||
self.code.write().unwrap().insert(address, code);
|
||||
self.code.write().insert(address, code);
|
||||
}
|
||||
|
||||
/// Set storage `position` to `value` for account `address`.
|
||||
pub fn set_storage(&self, address: Address, position: H256, value: H256) {
|
||||
self.storage.write().unwrap().insert((address, position), value);
|
||||
self.storage.write().insert((address, position), value);
|
||||
}
|
||||
|
||||
/// Set block queue size for testing
|
||||
@@ -150,11 +159,11 @@ impl TestBlockChainClient {
|
||||
|
||||
/// Add blocks to test client.
|
||||
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
|
||||
let len = self.numbers.read().unwrap().len();
|
||||
let len = self.numbers.read().len();
|
||||
for n in len..(len + count) {
|
||||
let mut header = BlockHeader::new();
|
||||
header.difficulty = From::from(n);
|
||||
header.parent_hash = self.last_hash.read().unwrap().clone();
|
||||
header.parent_hash = self.last_hash.read().clone();
|
||||
header.number = n as BlockNumber;
|
||||
header.gas_limit = U256::from(1_000_000);
|
||||
let uncles = match with {
|
||||
@@ -162,7 +171,7 @@ impl TestBlockChainClient {
|
||||
let mut uncles = RlpStream::new_list(1);
|
||||
let mut uncle_header = BlockHeader::new();
|
||||
uncle_header.difficulty = From::from(n);
|
||||
uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
|
||||
uncle_header.parent_hash = self.last_hash.read().clone();
|
||||
uncle_header.number = n as BlockNumber;
|
||||
uncles.append(&uncle_header);
|
||||
header.uncles_hash = uncles.as_raw().sha3();
|
||||
@@ -175,7 +184,7 @@ impl TestBlockChainClient {
|
||||
let mut txs = RlpStream::new_list(1);
|
||||
let keypair = KeyPair::create().unwrap();
|
||||
// Update nonces value
|
||||
self.nonces.write().unwrap().insert(keypair.address(), U256::one());
|
||||
self.nonces.write().insert(keypair.address(), U256::one());
|
||||
let tx = Transaction {
|
||||
action: Action::Create,
|
||||
value: U256::from(100),
|
||||
@@ -184,7 +193,7 @@ impl TestBlockChainClient {
|
||||
gas_price: U256::one(),
|
||||
nonce: U256::zero()
|
||||
};
|
||||
let signed_tx = tx.sign(&keypair.secret());
|
||||
let signed_tx = tx.sign(keypair.secret());
|
||||
txs.append(&signed_tx);
|
||||
txs.out()
|
||||
},
|
||||
@@ -208,7 +217,7 @@ impl TestBlockChainClient {
|
||||
rlp.append(&header);
|
||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
||||
self.blocks.write().unwrap().insert(hash, rlp.out());
|
||||
self.blocks.write().insert(hash, rlp.out());
|
||||
}
|
||||
|
||||
/// Make a bad block by setting invalid parent hash.
|
||||
@@ -220,12 +229,12 @@ impl TestBlockChainClient {
|
||||
rlp.append(&header);
|
||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
||||
rlp.append_raw(&rlp::NULL_RLP, 1);
|
||||
self.blocks.write().unwrap().insert(hash, rlp.out());
|
||||
self.blocks.write().insert(hash, rlp.out());
|
||||
}
|
||||
|
||||
/// TODO:
|
||||
pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 {
|
||||
let blocks_read = self.numbers.read().unwrap();
|
||||
let blocks_read = self.numbers.read();
|
||||
let index = blocks_read.len() - delta;
|
||||
blocks_read[&index].clone()
|
||||
}
|
||||
@@ -233,30 +242,66 @@ impl TestBlockChainClient {
|
||||
fn block_hash(&self, id: BlockID) -> Option<H256> {
|
||||
match id {
|
||||
BlockID::Hash(hash) => Some(hash),
|
||||
BlockID::Number(n) => self.numbers.read().unwrap().get(&(n as usize)).cloned(),
|
||||
BlockID::Earliest => self.numbers.read().unwrap().get(&0).cloned(),
|
||||
BlockID::Latest => self.numbers.read().unwrap().get(&(self.numbers.read().unwrap().len() - 1)).cloned()
|
||||
BlockID::Number(n) => self.numbers.read().get(&(n as usize)).cloned(),
|
||||
BlockID::Earliest => self.numbers.read().get(&0).cloned(),
|
||||
BlockID::Latest | BlockID::Pending => self.numbers.read().get(&(self.numbers.read().len() - 1)).cloned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MiningBlockChainClient for TestBlockChainClient {
|
||||
fn prepare_open_block(&self, _author: Address, _gas_range_target: (U256, U256), _extra_data: Bytes) -> OpenBlock {
|
||||
unimplemented!();
|
||||
pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
|
||||
let temp = RandomTempPath::new();
|
||||
let db = Database::open(&DatabaseConfig::with_columns(DB_NO_OF_COLUMNS), temp.as_str()).unwrap();
|
||||
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, DB_COL_STATE);
|
||||
let state_db = StateDB::new(journal_db);
|
||||
GuardedTempResult {
|
||||
_temp: temp,
|
||||
result: Some(state_db)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl MiningBlockChainClient for TestBlockChainClient {
|
||||
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
|
||||
let engine = &*self.spec.engine;
|
||||
let genesis_header = self.spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
self.spec.ensure_db_good(&mut db).unwrap();
|
||||
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
let mut open_block = OpenBlock::new(
|
||||
engine,
|
||||
self.vm_factory(),
|
||||
Default::default(),
|
||||
false,
|
||||
db,
|
||||
&genesis_header,
|
||||
Arc::new(last_hashes),
|
||||
author,
|
||||
gas_range_target,
|
||||
extra_data
|
||||
).expect("Opening block for tests will not fail.");
|
||||
// TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks)
|
||||
open_block.set_timestamp(10_000_000);
|
||||
open_block
|
||||
}
|
||||
|
||||
fn vm_factory(&self) -> &EvmFactory {
|
||||
unimplemented!();
|
||||
&self.vm_factory
|
||||
}
|
||||
|
||||
fn import_sealed_block(&self, _block: SealedBlock) -> ImportResult {
|
||||
unimplemented!();
|
||||
Ok(H256::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockChainClient for TestBlockChainClient {
|
||||
fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
|
||||
Ok(self.execution_result.read().unwrap().clone().unwrap())
|
||||
fn call(&self, _t: &SignedTransaction, _block: BlockID, _analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
self.execution_result.read().clone().unwrap()
|
||||
}
|
||||
|
||||
fn replay(&self, _id: TransactionID, _analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
self.execution_result.read().clone().unwrap()
|
||||
}
|
||||
|
||||
fn block_total_difficulty(&self, _id: BlockID) -> Option<U256> {
|
||||
@@ -269,41 +314,52 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
|
||||
fn nonce(&self, address: &Address, id: BlockID) -> Option<U256> {
|
||||
match id {
|
||||
BlockID::Latest => Some(self.nonces.read().unwrap().get(address).cloned().unwrap_or_else(U256::zero)),
|
||||
BlockID::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params.account_start_nonce)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn code(&self, address: &Address) -> Option<Bytes> {
|
||||
self.code.read().unwrap().get(address).cloned()
|
||||
fn latest_nonce(&self, address: &Address) -> U256 {
|
||||
self.nonce(address, BlockID::Latest).unwrap()
|
||||
}
|
||||
|
||||
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
|
||||
match id {
|
||||
BlockID::Latest => Some(self.code.read().get(address).cloned()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {
|
||||
if let BlockID::Latest = id {
|
||||
Some(self.balances.read().unwrap().get(address).cloned().unwrap_or_else(U256::zero))
|
||||
Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn latest_balance(&self, address: &Address) -> U256 {
|
||||
self.balance(address, BlockID::Latest).unwrap()
|
||||
}
|
||||
|
||||
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> {
|
||||
if let BlockID::Latest = id {
|
||||
Some(self.storage.read().unwrap().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new))
|
||||
Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> {
|
||||
unimplemented!();
|
||||
None
|
||||
}
|
||||
|
||||
fn uncle(&self, _id: UncleID) -> Option<BlockHeader> {
|
||||
unimplemented!();
|
||||
fn uncle(&self, _id: UncleID) -> Option<Bytes> {
|
||||
None
|
||||
}
|
||||
|
||||
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
|
||||
self.receipts.read().unwrap().get(&id).cloned()
|
||||
self.receipts.read().get(&id).cloned()
|
||||
}
|
||||
|
||||
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockID, _to_block: BlockID) -> Option<Vec<BlockNumber>> {
|
||||
@@ -318,27 +374,31 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn best_block_header(&self) -> Bytes {
|
||||
self.block_header(BlockID::Hash(self.chain_info().best_block_hash)).expect("Best block always have header.")
|
||||
}
|
||||
|
||||
fn block_header(&self, id: BlockID) -> Option<Bytes> {
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
|
||||
}
|
||||
|
||||
fn block_body(&self, id: BlockID) -> Option<Bytes> {
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| {
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| {
|
||||
let mut stream = RlpStream::new_list(2);
|
||||
stream.append_raw(Rlp::new(&r).at(1).as_raw(), 1);
|
||||
stream.append_raw(Rlp::new(&r).at(2).as_raw(), 1);
|
||||
stream.append_raw(Rlp::new(r).at(1).as_raw(), 1);
|
||||
stream.append_raw(Rlp::new(r).at(2).as_raw(), 1);
|
||||
stream.out()
|
||||
}))
|
||||
}
|
||||
|
||||
fn block(&self, id: BlockID) -> Option<Bytes> {
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).cloned())
|
||||
self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).cloned())
|
||||
}
|
||||
|
||||
fn block_status(&self, id: BlockID) -> BlockStatus {
|
||||
match id {
|
||||
BlockID::Number(number) if (number as usize) < self.blocks.read().unwrap().len() => BlockStatus::InChain,
|
||||
BlockID::Hash(ref hash) if self.blocks.read().unwrap().get(hash).is_some() => BlockStatus::InChain,
|
||||
BlockID::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain,
|
||||
BlockID::Hash(ref hash) if self.blocks.read().get(hash).is_some() => BlockStatus::InChain,
|
||||
_ => BlockStatus::Unknown
|
||||
}
|
||||
}
|
||||
@@ -349,7 +409,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
ancestor: H256::new(),
|
||||
index: 0,
|
||||
blocks: {
|
||||
let numbers_read = self.numbers.read().unwrap();
|
||||
let numbers_read = self.numbers.read();
|
||||
let mut adding = false;
|
||||
|
||||
let mut blocks = Vec::new();
|
||||
@@ -402,15 +462,15 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
None
|
||||
}
|
||||
|
||||
fn import_block(&self, b: Bytes) -> ImportResult {
|
||||
fn import_block(&self, b: Bytes) -> Result<H256, BlockImportError> {
|
||||
let header = Rlp::new(&b).val_at::<BlockHeader>(0);
|
||||
let h = header.hash();
|
||||
let number: usize = header.number as usize;
|
||||
if number > self.blocks.read().unwrap().len() {
|
||||
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().unwrap().len(), number);
|
||||
if number > self.blocks.read().len() {
|
||||
panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().len(), number);
|
||||
}
|
||||
if number > 0 {
|
||||
match self.blocks.read().unwrap().get(&header.parent_hash) {
|
||||
match self.blocks.read().get(&header.parent_hash) {
|
||||
Some(parent) => {
|
||||
let parent = Rlp::new(parent).val_at::<BlockHeader>(0);
|
||||
if parent.number != (header.number - 1) {
|
||||
@@ -422,27 +482,27 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
let len = self.numbers.read().unwrap().len();
|
||||
let len = self.numbers.read().len();
|
||||
if number == len {
|
||||
{
|
||||
let mut difficulty = self.difficulty.write().unwrap();
|
||||
*difficulty.deref_mut() = *difficulty.deref() + header.difficulty;
|
||||
let mut difficulty = self.difficulty.write();
|
||||
*difficulty = *difficulty + header.difficulty;
|
||||
}
|
||||
mem::replace(self.last_hash.write().unwrap().deref_mut(), h.clone());
|
||||
self.blocks.write().unwrap().insert(h.clone(), b);
|
||||
self.numbers.write().unwrap().insert(number, h.clone());
|
||||
mem::replace(&mut *self.last_hash.write(), h.clone());
|
||||
self.blocks.write().insert(h.clone(), b);
|
||||
self.numbers.write().insert(number, h.clone());
|
||||
let mut parent_hash = header.parent_hash;
|
||||
if number > 0 {
|
||||
let mut n = number - 1;
|
||||
while n > 0 && self.numbers.read().unwrap()[&n] != parent_hash {
|
||||
*self.numbers.write().unwrap().get_mut(&n).unwrap() = parent_hash.clone();
|
||||
while n > 0 && self.numbers.read()[&n] != parent_hash {
|
||||
*self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone();
|
||||
n -= 1;
|
||||
parent_hash = Rlp::new(&self.blocks.read().unwrap()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash;
|
||||
parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::<BlockHeader>(0).parent_hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.blocks.write().unwrap().insert(h.clone(), b.to_vec());
|
||||
self.blocks.write().insert(h.clone(), b.to_vec());
|
||||
}
|
||||
Ok(h)
|
||||
}
|
||||
@@ -461,13 +521,17 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
fn clear_queue(&self) {
|
||||
}
|
||||
|
||||
fn additional_params(&self) -> BTreeMap<String, String> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn chain_info(&self) -> BlockChainInfo {
|
||||
BlockChainInfo {
|
||||
total_difficulty: *self.difficulty.read().unwrap(),
|
||||
pending_total_difficulty: *self.difficulty.read().unwrap(),
|
||||
total_difficulty: *self.difficulty.read(),
|
||||
pending_total_difficulty: *self.difficulty.read(),
|
||||
genesis_hash: self.genesis_hash.clone(),
|
||||
best_block_hash: self.last_hash.read().unwrap().clone(),
|
||||
best_block_number: self.blocks.read().unwrap().len() as BlockNumber - 1,
|
||||
best_block_hash: self.last_hash.read().clone(),
|
||||
best_block_number: self.blocks.read().len() as BlockNumber - 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,24 +551,13 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn import_transactions(&self, transactions: Vec<SignedTransaction>) -> Vec<Result<TransactionImportResult, EthError>> {
|
||||
let nonces = self.nonces.read().unwrap();
|
||||
let balances = self.balances.read().unwrap();
|
||||
let fetch_account = |a: &Address| AccountDetails {
|
||||
nonce: nonces[a],
|
||||
balance: balances[a],
|
||||
};
|
||||
|
||||
self.miner.import_transactions(self, transactions, &fetch_account)
|
||||
}
|
||||
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>) {
|
||||
// import right here
|
||||
let tx = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
|
||||
self.import_transactions(tx);
|
||||
let txs = transactions.into_iter().filter_map(|bytes| UntrustedRlp::new(&bytes).as_val().ok()).collect();
|
||||
self.miner.import_external_transactions(self, txs);
|
||||
}
|
||||
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction> {
|
||||
self.miner.pending_transactions()
|
||||
self.miner.pending_transactions(self.chain_info().best_block_number)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
|
||||
//! Bridge between Tracedb and Blockchain.
|
||||
|
||||
use std::ops::Range;
|
||||
use util::{Address, H256};
|
||||
use util::{H256};
|
||||
use header::BlockNumber;
|
||||
use trace::DatabaseExtras as TraceDatabaseExtras;
|
||||
use blockchain::{BlockChain, BlockProvider};
|
||||
use blockchain::extras::TransactionAddress;
|
||||
use super::BlockID;
|
||||
pub use types::trace_filter::Filter;
|
||||
|
||||
impl TraceDatabaseExtras for BlockChain {
|
||||
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
|
||||
@@ -26,13 +25,3 @@ impl TraceDatabaseExtras for BlockChain {
|
||||
.map(|tx| tx.hash())
|
||||
}
|
||||
}
|
||||
|
||||
/// Easy to use trace filter.
|
||||
pub struct Filter {
|
||||
/// Range of filtering.
|
||||
pub range: Range<BlockID>,
|
||||
/// From address.
|
||||
pub from_address: Vec<Address>,
|
||||
/// To address.
|
||||
pub to_address: Vec<Address>,
|
||||
}
|
||||
|
||||
233
ethcore/src/client/traits.rs
Normal file
233
ethcore/src/client/traits.rs
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{BTreeMap};
|
||||
use util::bytes::Bytes;
|
||||
use util::hash::{Address, H256, H2048};
|
||||
use util::numbers::U256;
|
||||
use util::Itertools;
|
||||
use blockchain::TreeRoute;
|
||||
use block_queue::BlockQueueInfo;
|
||||
use block::{OpenBlock, SealedBlock};
|
||||
use header::{BlockNumber};
|
||||
use transaction::{LocalizedTransaction, SignedTransaction};
|
||||
use log_entry::LocalizedLogEntry;
|
||||
use filter::Filter;
|
||||
use views::{BlockView};
|
||||
use error::{ImportResult, CallError};
|
||||
use receipt::LocalizedReceipt;
|
||||
use trace::LocalizedTrace;
|
||||
use evm::Factory as EvmFactory;
|
||||
use types::ids::*;
|
||||
use types::trace_filter::Filter as TraceFilter;
|
||||
use executive::Executed;
|
||||
use env_info::LastHashes;
|
||||
use types::call_analytics::CallAnalytics;
|
||||
use block_import_error::BlockImportError;
|
||||
use std::mem;
|
||||
use std::collections::VecDeque;
|
||||
use ipc::{IpcConfig, BinaryConvertError};
|
||||
use types::blockchain_info::BlockChainInfo;
|
||||
use types::block_status::BlockStatus;
|
||||
|
||||
#[derive(Ipc)]
|
||||
#[ipc(client_ident="RemoteClient")]
|
||||
/// Blockchain database client. Owns and manages a blockchain and a block queue.
|
||||
pub trait BlockChainClient : Sync + Send {
|
||||
|
||||
/// Should be called by any external-facing interface when actively using the client.
|
||||
/// To minimise chatter, there's no need to call more than once every 30s.
|
||||
fn keep_alive(&self) {}
|
||||
|
||||
/// Get raw block header data by block id.
|
||||
fn block_header(&self, id: BlockID) -> Option<Bytes>;
|
||||
|
||||
/// Get raw block body data by block id.
|
||||
/// Block body is an RLP list of two items: uncles and transactions.
|
||||
fn block_body(&self, id: BlockID) -> Option<Bytes>;
|
||||
|
||||
/// Get raw block data by block header hash.
|
||||
fn block(&self, id: BlockID) -> Option<Bytes>;
|
||||
|
||||
/// Get block status by block header hash.
|
||||
fn block_status(&self, id: BlockID) -> BlockStatus;
|
||||
|
||||
/// Get block total difficulty.
|
||||
fn block_total_difficulty(&self, id: BlockID) -> Option<U256>;
|
||||
|
||||
/// Attempt to get address nonce at given block.
|
||||
/// May not fail on BlockID::Latest.
|
||||
fn nonce(&self, address: &Address, id: BlockID) -> Option<U256>;
|
||||
|
||||
/// Get address nonce at the latest block's state.
|
||||
fn latest_nonce(&self, address: &Address) -> U256 {
|
||||
self.nonce(address, BlockID::Latest)
|
||||
.expect("nonce will return Some when given BlockID::Latest. nonce was given BlockID::Latest. \
|
||||
Therefore nonce has returned Some; qed")
|
||||
}
|
||||
|
||||
/// Get block hash.
|
||||
fn block_hash(&self, id: BlockID) -> Option<H256>;
|
||||
|
||||
/// Get address code at given block's state.
|
||||
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>>;
|
||||
|
||||
/// Get address code at the latest block's state.
|
||||
fn latest_code(&self, address: &Address) -> Option<Bytes> {
|
||||
self.code(address, BlockID::Latest)
|
||||
.expect("code will return Some if given BlockID::Latest; qed")
|
||||
}
|
||||
|
||||
/// Get address balance at the given block's state.
|
||||
///
|
||||
/// May not return None if given BlockID::Latest.
|
||||
/// Returns None if and only if the block's root hash has been pruned from the DB.
|
||||
fn balance(&self, address: &Address, id: BlockID) -> Option<U256>;
|
||||
|
||||
/// Get address balance at the latest block's state.
|
||||
fn latest_balance(&self, address: &Address) -> U256 {
|
||||
self.balance(address, BlockID::Latest)
|
||||
.expect("balance will return Some if given BlockID::Latest. balance was given BlockID::Latest \
|
||||
Therefore balance has returned Some; qed")
|
||||
}
|
||||
|
||||
/// Get value of the storage at given position at the given block's state.
|
||||
///
|
||||
/// May not return None if given BlockID::Latest.
|
||||
/// Returns None if and only if the block's root hash has been pruned from the DB.
|
||||
fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256>;
|
||||
|
||||
/// Get value of the storage at given position at the latest block's state.
|
||||
fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 {
|
||||
self.storage_at(address, position, BlockID::Latest)
|
||||
.expect("storage_at will return Some if given BlockID::Latest. storage_at was given BlockID::Latest. \
|
||||
Therefore storage_at has returned Some; qed")
|
||||
}
|
||||
|
||||
/// Get transaction with given hash.
|
||||
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;
|
||||
|
||||
/// Get uncle with given id.
|
||||
fn uncle(&self, id: UncleID) -> Option<Bytes>;
|
||||
|
||||
/// Get transaction receipt with given hash.
|
||||
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>;
|
||||
|
||||
/// Get a tree route between `from` and `to`.
|
||||
/// See `BlockChain::tree_route`.
|
||||
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
|
||||
|
||||
/// Get all possible uncle hashes for a block.
|
||||
fn find_uncles(&self, hash: &H256) -> Option<Vec<H256>>;
|
||||
|
||||
/// Get latest state node
|
||||
fn state_data(&self, hash: &H256) -> Option<Bytes>;
|
||||
|
||||
/// Get raw block receipts data by block header hash.
|
||||
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
|
||||
|
||||
/// Import a block into the blockchain.
|
||||
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError>;
|
||||
|
||||
/// Get block queue information.
|
||||
fn queue_info(&self) -> BlockQueueInfo;
|
||||
|
||||
/// Clear block queue and abort all import activity.
|
||||
fn clear_queue(&self);
|
||||
|
||||
/// Get blockchain information.
|
||||
fn chain_info(&self) -> BlockChainInfo;
|
||||
|
||||
/// Get the registrar address, if it exists.
|
||||
fn additional_params(&self) -> BTreeMap<String, String>;
|
||||
|
||||
/// Get the best block header.
|
||||
fn best_block_header(&self) -> Bytes;
|
||||
|
||||
/// Returns numbers of blocks containing given bloom.
|
||||
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>>;
|
||||
|
||||
/// Returns logs matching given filter.
|
||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
||||
|
||||
/// Makes a non-persistent transaction call.
|
||||
fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError>;
|
||||
|
||||
/// Replays a given transaction for inspection.
|
||||
fn replay(&self, t: TransactionID, analytics: CallAnalytics) -> Result<Executed, CallError>;
|
||||
|
||||
/// Returns traces matching given filter.
|
||||
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
/// Returns trace with given id.
|
||||
fn trace(&self, trace: TraceId) -> Option<LocalizedTrace>;
|
||||
|
||||
/// Returns traces created by transaction.
|
||||
fn transaction_traces(&self, trace: TransactionID) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
/// Returns traces created by transaction from block.
|
||||
fn block_traces(&self, trace: BlockID) -> Option<Vec<LocalizedTrace>>;
|
||||
|
||||
/// Get last hashes starting from best block.
|
||||
fn last_hashes(&self) -> LastHashes;
|
||||
|
||||
/// Queue transactions for importing.
|
||||
fn queue_transactions(&self, transactions: Vec<Bytes>);
|
||||
|
||||
/// list all transactions
|
||||
fn pending_transactions(&self) -> Vec<SignedTransaction>;
|
||||
|
||||
/// Get the gas price distribution.
|
||||
fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result<Vec<U256>, ()> {
|
||||
let mut h = self.chain_info().best_block_hash;
|
||||
let mut corpus = Vec::new();
|
||||
for _ in 0..sample_size {
|
||||
let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed");
|
||||
let block = BlockView::new(&block_bytes);
|
||||
let header = block.header_view();
|
||||
if header.number() == 0 {
|
||||
break;
|
||||
}
|
||||
block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price()));
|
||||
h = header.parent_hash().clone();
|
||||
}
|
||||
corpus.sort();
|
||||
let n = corpus.len();
|
||||
if n > 0 {
|
||||
Ok((0..(distribution_size + 1))
|
||||
.map(|i| corpus[i * (n - 1) / distribution_size])
|
||||
.collect::<Vec<_>>()
|
||||
)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended client interface used for mining
|
||||
pub trait MiningBlockChainClient : BlockChainClient {
|
||||
/// Returns OpenBlock prepared for closing.
|
||||
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes)
|
||||
-> OpenBlock;
|
||||
|
||||
/// Returns EvmFactory.
|
||||
fn vm_factory(&self) -> &EvmFactory;
|
||||
|
||||
/// Import sealed block. Skips all verifications.
|
||||
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult;
|
||||
}
|
||||
|
||||
impl IpcConfig for BlockChainClient { }
|
||||
@@ -18,11 +18,11 @@
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::hash::Hash;
|
||||
use std::sync::RwLock;
|
||||
use std::collections::HashMap;
|
||||
use util::{DBTransaction, Database};
|
||||
use util::{DBTransaction, Database, RwLock};
|
||||
use util::rlp::{encode, Encodable, decode, Decodable};
|
||||
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CacheUpdatePolicy {
|
||||
Overwrite,
|
||||
@@ -62,14 +62,17 @@ pub trait Key<T> {
|
||||
/// Should be used to write value into database.
|
||||
pub trait Writable {
|
||||
/// Writes the value into the database.
|
||||
fn write<T, R>(&self, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]>;
|
||||
fn write<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]>;
|
||||
|
||||
/// Deletes key from the databse.
|
||||
fn delete<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) where T: Encodable, R: Deref<Target = [u8]>;
|
||||
|
||||
/// Writes the value into the database and updates the cache.
|
||||
fn write_with_cache<K, T, R>(&self, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
||||
fn write_with_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: Encodable,
|
||||
R: Deref<Target = [u8]> {
|
||||
self.write(&key, &value);
|
||||
self.write(col, &key, &value);
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
cache.insert(key, value);
|
||||
@@ -81,84 +84,119 @@ pub trait Writable {
|
||||
}
|
||||
|
||||
/// Writes the values into the database and updates the cache.
|
||||
fn extend_with_cache<K, T, R>(&self, cache: &mut Cache<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy) where
|
||||
fn extend_with_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: Encodable,
|
||||
R: Deref<Target = [u8]> {
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
for (key, value) in values.into_iter() {
|
||||
self.write(&key, &value);
|
||||
self.write(col, &key, &value);
|
||||
cache.insert(key, value);
|
||||
}
|
||||
},
|
||||
CacheUpdatePolicy::Remove => {
|
||||
for (key, value) in &values {
|
||||
self.write(key, value);
|
||||
self.write(col, key, value);
|
||||
cache.remove(key);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes and removes the values into the database and updates the cache.
|
||||
fn extend_with_option_cache<K, T, R>(&self, col: Option<u32>, cache: &mut Cache<K, Option<T>>, values: HashMap<K, Option<T>>, policy: CacheUpdatePolicy) where
|
||||
K: Key<T, Target = R> + Hash + Eq,
|
||||
T: Encodable,
|
||||
R: Deref<Target = [u8]> {
|
||||
match policy {
|
||||
CacheUpdatePolicy::Overwrite => {
|
||||
for (key, value) in values.into_iter() {
|
||||
match value {
|
||||
Some(ref v) => self.write(col, &key, v),
|
||||
None => self.delete(col, &key),
|
||||
}
|
||||
cache.insert(key, value);
|
||||
}
|
||||
},
|
||||
CacheUpdatePolicy::Remove => {
|
||||
for (key, value) in values.into_iter() {
|
||||
match value {
|
||||
Some(v) => self.write(col, &key, &v),
|
||||
None => self.delete(col, &key),
|
||||
}
|
||||
cache.remove(&key);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Should be used to read values from database.
|
||||
pub trait Readable {
|
||||
/// Returns value for given key.
|
||||
fn read<T, R>(&self, key: &Key<T, Target = R>) -> Option<T> where
|
||||
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T> where
|
||||
T: Decodable,
|
||||
R: Deref<Target = [u8]>;
|
||||
|
||||
/// Returns value for given key either in cache or in database.
|
||||
fn read_with_cache<K, T, C>(&self, cache: &RwLock<C>, key: &K) -> Option<T> where
|
||||
fn read_with_cache<K, T, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> Option<T> where
|
||||
K: Key<T> + Eq + Hash + Clone,
|
||||
T: Clone + Decodable,
|
||||
C: Cache<K, T> {
|
||||
{
|
||||
let read = cache.read().unwrap();
|
||||
let read = cache.read();
|
||||
if let Some(v) = read.get(key) {
|
||||
return Some(v.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.read(key).map(|value: T|{
|
||||
let mut write = cache.write().unwrap();
|
||||
self.read(col, key).map(|value: T|{
|
||||
let mut write = cache.write();
|
||||
write.insert(key.clone(), value.clone());
|
||||
value
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if given value exists.
|
||||
fn exists<T, R>(&self, key: &Key<T, Target = R>) -> bool where R: Deref<Target= [u8]>;
|
||||
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: Deref<Target= [u8]>;
|
||||
|
||||
/// Returns true if given value exists either in cache or in database.
|
||||
fn exists_with_cache<K, T, R, C>(&self, cache: &RwLock<C>, key: &K) -> bool where
|
||||
fn exists_with_cache<K, T, R, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> bool where
|
||||
K: Eq + Hash + Key<T, Target = R>,
|
||||
R: Deref<Target = [u8]>,
|
||||
C: Cache<K, T> {
|
||||
{
|
||||
let read = cache.read().unwrap();
|
||||
let read = cache.read();
|
||||
if read.get(key).is_some() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
self.exists::<T, R>(key)
|
||||
self.exists::<T, R>(col, key)
|
||||
}
|
||||
}
|
||||
|
||||
impl Writable for DBTransaction {
|
||||
fn write<T, R>(&self, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]> {
|
||||
let result = self.put(&key.key(), &encode(value));
|
||||
fn write<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: Encodable, R: Deref<Target = [u8]> {
|
||||
let result = self.put(col, &key.key(), &encode(value));
|
||||
if let Err(err) = result {
|
||||
panic!("db put failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
||||
}
|
||||
}
|
||||
|
||||
fn delete<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) where T: Encodable, R: Deref<Target = [u8]> {
|
||||
let result = DBTransaction::delete(self, col, &key.key());
|
||||
if let Err(err) = result {
|
||||
panic!("db delete failed, key: {:?}, err: {:?}", &key.key() as &[u8], err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for Database {
|
||||
fn read<T, R>(&self, key: &Key<T, Target = R>) -> Option<T> where T: Decodable, R: Deref<Target = [u8]> {
|
||||
let result = self.get(&key.key());
|
||||
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T> where T: Decodable, R: Deref<Target = [u8]> {
|
||||
let result = self.get(col, &key.key());
|
||||
|
||||
match result {
|
||||
Ok(option) => option.map(|v| decode(&v)),
|
||||
@@ -168,8 +206,8 @@ impl Readable for Database {
|
||||
}
|
||||
}
|
||||
|
||||
fn exists<T, R>(&self, key: &Key<T, Target = R>) -> bool where R: Deref<Target = [u8]> {
|
||||
let result = self.get(&key.key());
|
||||
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: Deref<Target = [u8]> {
|
||||
let result = self.get(col, &key.key());
|
||||
|
||||
match result {
|
||||
Ok(v) => v.is_some(),
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
use common::*;
|
||||
use account_provider::AccountProvider;
|
||||
use block::*;
|
||||
use spec::{CommonParams, Spec};
|
||||
use engine::*;
|
||||
use spec::CommonParams;
|
||||
use engines::Engine;
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
|
||||
@@ -176,16 +176,16 @@ impl Header {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new test chain spec with `BasicAuthority` consensus engine.
|
||||
pub fn new_test_authority() -> Spec { Spec::load(include_bytes!("../res/test_authority.json")) }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::*;
|
||||
use block::*;
|
||||
use tests::helpers::*;
|
||||
use account_provider::AccountProvider;
|
||||
use spec::Spec;
|
||||
|
||||
/// Create a new test chain spec with `BasicAuthority` consensus engine.
|
||||
fn new_test_authority() -> Spec { Spec::load(include_bytes!("../../res/test_authority.json")) }
|
||||
|
||||
#[test]
|
||||
fn has_valid_metadata() {
|
||||
@@ -202,8 +202,7 @@ mod tests {
|
||||
author: 0.into(),
|
||||
timestamp: 0,
|
||||
difficulty: 0.into(),
|
||||
last_hashes: vec![],
|
||||
dao_rescue_block_gas_limit: None,
|
||||
last_hashes: Arc::new(vec![]),
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
});
|
||||
@@ -247,16 +246,16 @@ mod tests {
|
||||
tap.unlock_account_permanently(addr, "".into()).unwrap();
|
||||
|
||||
let spec = new_test_authority();
|
||||
let engine = &spec.engine;
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||
assert!(b.try_seal(engine.deref(), seal).is_ok());
|
||||
assert!(b.try_seal(engine, seal).is_ok());
|
||||
}
|
||||
}
|
||||
108
ethcore/src/engines/instant_seal.rs
Normal file
108
ethcore/src/engines/instant_seal.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::hash::Address;
|
||||
use builtin::Builtin;
|
||||
use engines::Engine;
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use env_info::EnvInfo;
|
||||
use block::ExecutedBlock;
|
||||
use common::Bytes;
|
||||
use account_provider::AccountProvider;
|
||||
|
||||
/// An engine which does not provide any consensus mechanism, just seals blocks internally.
|
||||
pub struct InstantSeal {
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
}
|
||||
|
||||
impl InstantSeal {
|
||||
/// Returns new instance of InstantSeal with default VM Factory
|
||||
pub fn new(params: CommonParams, builtins: BTreeMap<Address, Builtin>) -> Self {
|
||||
InstantSeal {
|
||||
params: params,
|
||||
builtins: builtins,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine for InstantSeal {
|
||||
fn name(&self) -> &str {
|
||||
"InstantSeal"
|
||||
}
|
||||
|
||||
fn params(&self) -> &CommonParams {
|
||||
&self.params
|
||||
}
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
&self.builtins
|
||||
}
|
||||
|
||||
fn schedule(&self, _env_info: &EnvInfo) -> Schedule {
|
||||
Schedule::new_homestead()
|
||||
}
|
||||
|
||||
fn generate_seal(&self, _block: &ExecutedBlock, _accounts: Option<&AccountProvider>) -> Option<Vec<Bytes>> {
|
||||
Some(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::*;
|
||||
use tests::helpers::*;
|
||||
use account_provider::AccountProvider;
|
||||
use spec::Spec;
|
||||
use block::*;
|
||||
|
||||
/// Create a new test chain spec with `BasicAuthority` consensus engine.
|
||||
fn new_test_instant() -> Spec { Spec::load(include_bytes!("../../res/instant_seal.json")) }
|
||||
|
||||
#[test]
|
||||
fn instant_can_seal() {
|
||||
let tap = AccountProvider::transient_provider();
|
||||
let addr = tap.insert_account("".sha3(), "").unwrap();
|
||||
|
||||
let spec = new_test_instant();
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close_and_lock();
|
||||
// Seal with empty AccountProvider.
|
||||
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
|
||||
assert!(b.try_seal(engine, seal).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instant_cant_verify() {
|
||||
let engine = new_test_instant().engine;
|
||||
let mut header: Header = Header::default();
|
||||
|
||||
assert!(engine.verify_block_basic(&header, None).is_ok());
|
||||
|
||||
header.set_seal(vec![rlp::encode(&Signature::zero()).to_vec()]);
|
||||
|
||||
assert!(engine.verify_block_unordered(&header, None).is_ok());
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,15 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Consensus engine specification
|
||||
//! Consensus engine specification and basic implementations.
|
||||
|
||||
mod null_engine;
|
||||
mod instant_seal;
|
||||
mod basic_authority;
|
||||
|
||||
pub use self::null_engine::NullEngine;
|
||||
pub use self::instant_seal::InstantSeal;
|
||||
pub use self::basic_authority::BasicAuthority;
|
||||
|
||||
use common::*;
|
||||
use account_provider::AccountProvider;
|
||||
@@ -36,6 +44,9 @@ pub trait Engine : Sync + Send {
|
||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||
fn extra_info(&self, _header: &Header) -> HashMap<String, String> { HashMap::new() }
|
||||
|
||||
/// Additional information.
|
||||
fn additional_params(&self) -> HashMap<String, String> { HashMap::new() }
|
||||
|
||||
/// Get the general parameters of the chain.
|
||||
fn params(&self) -> &CommonParams;
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
use std::collections::BTreeMap;
|
||||
use util::hash::Address;
|
||||
use builtin::Builtin;
|
||||
use engine::Engine;
|
||||
use engines::Engine;
|
||||
use spec::CommonParams;
|
||||
use evm::Schedule;
|
||||
use env_info::EnvInfo;
|
||||
|
||||
/// An engine which does not provide any consensus mechanism.
|
||||
/// An engine which does not provide any consensus mechanism and does not seal blocks.
|
||||
pub struct NullEngine {
|
||||
params: CommonParams,
|
||||
builtins: BTreeMap<Address, Builtin>,
|
||||
@@ -36,12 +36,9 @@ pub struct EnvInfo {
|
||||
/// The block gas limit.
|
||||
pub gas_limit: U256,
|
||||
/// The last 256 block hashes.
|
||||
pub last_hashes: LastHashes,
|
||||
pub last_hashes: Arc<LastHashes>,
|
||||
/// The gas used.
|
||||
pub gas_used: U256,
|
||||
|
||||
/// Block gas limit at DAO rescue block SOFT_FORK_BLOCK or None if not yet there.
|
||||
pub dao_rescue_block_gas_limit: Option<U256>,
|
||||
}
|
||||
|
||||
impl Default for EnvInfo {
|
||||
@@ -52,9 +49,8 @@ impl Default for EnvInfo {
|
||||
timestamp: 0,
|
||||
difficulty: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
last_hashes: vec![],
|
||||
last_hashes: Arc::new(vec![]),
|
||||
gas_used: 0.into(),
|
||||
dao_rescue_block_gas_limit: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,9 +64,8 @@ impl From<ethjson::vm::Env> for EnvInfo {
|
||||
difficulty: e.difficulty.into(),
|
||||
gas_limit: e.gas_limit.into(),
|
||||
timestamp: e.timestamp.into(),
|
||||
last_hashes: (1..cmp::min(number + 1, 257)).map(|i| format!("{}", number - i).as_bytes().sha3()).collect(),
|
||||
last_hashes: Arc::new((1..cmp::min(number + 1, 257)).map(|i| format!("{}", number - i).as_bytes().sha3()).collect()),
|
||||
gas_used: U256::zero(),
|
||||
dao_rescue_block_gas_limit: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,17 @@
|
||||
//! General error types for use in ethcore.
|
||||
|
||||
use util::*;
|
||||
use io::*;
|
||||
use header::BlockNumber;
|
||||
use basic_types::LogBloom;
|
||||
use client::Error as ClientError;
|
||||
use ipc::binary::{BinaryConvertError, BinaryConvertable};
|
||||
use types::block_import_error::BlockImportError;
|
||||
use snapshot::Error as SnapshotError;
|
||||
|
||||
pub use types::executed::ExecutionError;
|
||||
pub use types::executed::{ExecutionError, CallError};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
/// Errors concerning transaction processing.
|
||||
pub enum TransactionError {
|
||||
/// Transaction is already imported to the queue
|
||||
@@ -58,8 +62,6 @@ pub enum TransactionError {
|
||||
},
|
||||
/// Transaction's gas limit (aka gas) is invalid.
|
||||
InvalidGasLimit(OutOfBounds<U256>),
|
||||
/// Transaction is invalid for some other reason.
|
||||
DAORescue,
|
||||
}
|
||||
|
||||
impl fmt::Display for TransactionError {
|
||||
@@ -78,7 +80,6 @@ impl fmt::Display for TransactionError {
|
||||
GasLimitExceeded { limit, got } =>
|
||||
format!("Gas limit exceeded. Limit={}, Given={}", limit, got),
|
||||
InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
|
||||
DAORescue => "Transaction is invalid due to the DAO rescue.".into(),
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("Transaction error ({})", msg))
|
||||
@@ -229,22 +230,34 @@ pub enum Error {
|
||||
PowInvalid,
|
||||
/// Error concerning TrieDBs
|
||||
Trie(TrieError),
|
||||
/// Io crate error.
|
||||
Io(IoError),
|
||||
/// Standard io error.
|
||||
StdIo(::std::io::Error),
|
||||
/// Snappy error.
|
||||
Snappy(::util::snappy::InvalidInput),
|
||||
/// Snapshot error.
|
||||
Snapshot(SnapshotError),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::Client(ref err) => f.write_fmt(format_args!("{}", err)),
|
||||
Error::Util(ref err) => f.write_fmt(format_args!("{}", err)),
|
||||
Error::Block(ref err) => f.write_fmt(format_args!("{}", err)),
|
||||
Error::Execution(ref err) => f.write_fmt(format_args!("{}", err)),
|
||||
Error::Transaction(ref err) => f.write_fmt(format_args!("{}", err)),
|
||||
Error::Import(ref err) => f.write_fmt(format_args!("{}", err)),
|
||||
Error::Client(ref err) => err.fmt(f),
|
||||
Error::Util(ref err) => err.fmt(f),
|
||||
Error::Io(ref err) => err.fmt(f),
|
||||
Error::Block(ref err) => err.fmt(f),
|
||||
Error::Execution(ref err) => err.fmt(f),
|
||||
Error::Transaction(ref err) => err.fmt(f),
|
||||
Error::Import(ref err) => err.fmt(f),
|
||||
Error::UnknownEngineName(ref name) =>
|
||||
f.write_fmt(format_args!("Unknown engine name ({})", name)),
|
||||
Error::PowHashInvalid => f.write_str("Invalid or out of date PoW hash."),
|
||||
Error::PowInvalid => f.write_str("Invalid nonce or mishash"),
|
||||
Error::Trie(ref err) => f.write_fmt(format_args!("{}", err)),
|
||||
Error::Trie(ref err) => err.fmt(f),
|
||||
Error::StdIo(ref err) => err.fmt(f),
|
||||
Error::Snappy(ref err) => err.fmt(f),
|
||||
Error::Snapshot(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,7 +267,10 @@ pub type ImportResult = Result<H256, Error>;
|
||||
|
||||
impl From<ClientError> for Error {
|
||||
fn from(err: ClientError) -> Error {
|
||||
Error::Client(err)
|
||||
match err {
|
||||
ClientError::Trie(err) => Error::Trie(err),
|
||||
_ => Error::Client(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +318,7 @@ impl From<UtilError> for Error {
|
||||
|
||||
impl From<IoError> for Error {
|
||||
fn from(err: IoError) -> Error {
|
||||
Error::Util(From::from(err))
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,6 +328,49 @@ impl From<TrieError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::io::Error> for Error {
|
||||
fn from(err: ::std::io::Error) -> Error {
|
||||
Error::StdIo(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockImportError> for Error {
|
||||
fn from(err: BlockImportError) -> Error {
|
||||
match err {
|
||||
BlockImportError::Block(e) => Error::Block(e),
|
||||
BlockImportError::Import(e) => Error::Import(e),
|
||||
BlockImportError::Other(s) => Error::Util(UtilError::SimpleString(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<snappy::InvalidInput> for Error {
|
||||
fn from(err: snappy::InvalidInput) -> Error {
|
||||
Error::Snappy(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SnapshotError> for Error {
|
||||
fn from(err: SnapshotError) -> Error {
|
||||
match err {
|
||||
SnapshotError::Io(err) => Error::StdIo(err),
|
||||
SnapshotError::Trie(err) => Error::Trie(err),
|
||||
SnapshotError::Decoder(err) => err.into(),
|
||||
other => Error::Snapshot(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<Box<E>> for Error where Error: From<E> {
|
||||
fn from(err: Box<E>) -> Error {
|
||||
Error::from(*err)
|
||||
}
|
||||
}
|
||||
|
||||
binary_fixed_size!(BlockError);
|
||||
binary_fixed_size!(ImportError);
|
||||
binary_fixed_size!(TransactionError);
|
||||
|
||||
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
|
||||
/*#![feature(concat_idents)]
|
||||
macro_rules! assimilate {
|
||||
|
||||
@@ -18,7 +18,7 @@ use ethash::{quick_get_difficulty, EthashManager, H256 as EH256};
|
||||
use common::*;
|
||||
use block::*;
|
||||
use spec::CommonParams;
|
||||
use engine::*;
|
||||
use engines::Engine;
|
||||
use evm::Schedule;
|
||||
use ethjson;
|
||||
|
||||
@@ -39,8 +39,12 @@ pub struct EthashParams {
|
||||
pub registrar: Address,
|
||||
/// Homestead transition block number.
|
||||
pub frontier_compatibility_mode_limit: u64,
|
||||
/// Enable the soft-fork logic.
|
||||
pub dao_rescue_soft_fork: bool,
|
||||
/// DAO hard-fork transition block (X).
|
||||
pub dao_hardfork_transition: u64,
|
||||
/// DAO hard-fork refund contract address (C).
|
||||
pub dao_hardfork_beneficiary: Address,
|
||||
/// DAO hard-fork DAO accounts list (L)
|
||||
pub dao_hardfork_accounts: Vec<Address>,
|
||||
}
|
||||
|
||||
impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
@@ -51,9 +55,11 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
|
||||
duration_limit: p.duration_limit.into(),
|
||||
block_reward: p.block_reward.into(),
|
||||
registrar: p.registrar.into(),
|
||||
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.into(),
|
||||
dao_rescue_soft_fork: p.dao_rescue_soft_fork.into(),
|
||||
registrar: p.registrar.map_or_else(Address::new, Into::into),
|
||||
frontier_compatibility_mode_limit: p.frontier_compatibility_mode_limit.map_or(0, Into::into),
|
||||
dao_hardfork_transition: p.dao_hardfork_transition.map_or(0x7fffffffffffffff, Into::into),
|
||||
dao_hardfork_beneficiary: p.dao_hardfork_beneficiary.map_or_else(Address::new, Into::into),
|
||||
dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +92,7 @@ impl Engine for Ethash {
|
||||
fn seal_fields(&self) -> usize { 2 }
|
||||
|
||||
fn params(&self) -> &CommonParams { &self.params }
|
||||
fn additional_params(&self) -> HashMap<String, String> { hash_map!["registrar".to_owned() => self.ethash_params.registrar.hex()] }
|
||||
|
||||
fn builtins(&self) -> &BTreeMap<Address, Builtin> {
|
||||
&self.builtins
|
||||
@@ -102,11 +109,7 @@ impl Engine for Ethash {
|
||||
if env_info.number < self.ethash_params.frontier_compatibility_mode_limit {
|
||||
Schedule::new_frontier()
|
||||
} else {
|
||||
let mut s = Schedule::new_homestead();
|
||||
if self.ethash_params.dao_rescue_soft_fork {
|
||||
s.reject_dao_transactions = env_info.dao_rescue_block_gas_limit.map_or(false, |x| x <= 4_000_000.into());
|
||||
}
|
||||
s
|
||||
Schedule::new_homestead()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,10 +129,27 @@ impl Engine for Ethash {
|
||||
(header.gas_used * 6.into() / 5.into()) / bound_divisor))
|
||||
}
|
||||
};
|
||||
if header.number >= self.ethash_params.dao_hardfork_transition &&
|
||||
header.number <= self.ethash_params.dao_hardfork_transition + 9 {
|
||||
header.extra_data = b"dao-hard-fork"[..].to_owned();
|
||||
}
|
||||
header.note_dirty();
|
||||
// info!("ethash: populate_from_parent #{}: difficulty={} and gas_limit={}", header.number, header.difficulty, header.gas_limit);
|
||||
}
|
||||
|
||||
fn on_new_block(&self, block: &mut ExecutedBlock) {
|
||||
if block.fields().header.number == self.ethash_params.dao_hardfork_transition {
|
||||
// TODO: enable trigger function maybe?
|
||||
// if block.fields().header.gas_limit <= 4_000_000.into() {
|
||||
let mut state = block.fields_mut().state;
|
||||
for child in &self.ethash_params.dao_hardfork_accounts {
|
||||
let b = state.balance(child);
|
||||
state.transfer_balance(child, &self.ethash_params.dao_hardfork_beneficiary, &b);
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply the block reward on finalisation of the block.
|
||||
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
|
||||
fn on_close_block(&self, block: &mut ExecutedBlock) {
|
||||
@@ -144,7 +164,9 @@ impl Engine for Ethash {
|
||||
for u in fields.uncles.iter() {
|
||||
fields.state.add_balance(u.author(), &(reward * U256::from(8 + u.number() - current_number) / U256::from(8)));
|
||||
}
|
||||
fields.state.commit();
|
||||
if let Err(e) = fields.state.commit() {
|
||||
warn!("Encountered error on state commit: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
|
||||
@@ -171,6 +193,17 @@ impl Engine for Ethash {
|
||||
if difficulty < header.difficulty {
|
||||
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty), max: None, found: difficulty })));
|
||||
}
|
||||
|
||||
if header.number >= self.ethash_params.dao_hardfork_transition &&
|
||||
header.number <= self.ethash_params.dao_hardfork_transition + 9 &&
|
||||
header.extra_data[..] != b"dao-hard-fork"[..] {
|
||||
return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 })));
|
||||
}
|
||||
|
||||
if header.gas_limit > 0x7fffffffffffffffu64.into() {
|
||||
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(0x7fffffffffffffffu64.into()), found: header.gas_limit })));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -318,14 +351,14 @@ mod tests {
|
||||
#[test]
|
||||
fn on_close_block() {
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let b = b.close();
|
||||
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
|
||||
}
|
||||
@@ -333,14 +366,14 @@ mod tests {
|
||||
#[test]
|
||||
fn on_close_block_with_uncle() {
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let last_hashes = vec![genesis_header.hash()];
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let vm_factory = Default::default();
|
||||
let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, None, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let mut b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
|
||||
let mut uncle = Header::new();
|
||||
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
|
||||
uncle.author = uncle_author.clone();
|
||||
@@ -366,10 +399,9 @@ mod tests {
|
||||
author: 0.into(),
|
||||
timestamp: 0,
|
||||
difficulty: 0.into(),
|
||||
last_hashes: vec![],
|
||||
last_hashes: Arc::new(vec![]),
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
dao_rescue_block_gas_limit: None,
|
||||
});
|
||||
|
||||
assert!(schedule.stack_limit > 0);
|
||||
@@ -379,10 +411,9 @@ mod tests {
|
||||
author: 0.into(),
|
||||
timestamp: 0,
|
||||
difficulty: 0.into(),
|
||||
last_hashes: vec![],
|
||||
last_hashes: Arc::new(vec![]),
|
||||
gas_used: 0.into(),
|
||||
gas_limit: 0.into(),
|
||||
dao_rescue_block_gas_limit: None,
|
||||
});
|
||||
|
||||
assert!(!schedule.have_delegate_call);
|
||||
|
||||
@@ -33,12 +33,10 @@ use super::spec::*;
|
||||
pub fn new_olympic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/olympic.json")) }
|
||||
|
||||
/// Create a new Frontier mainnet chain spec.
|
||||
pub fn new_frontier(dao_rescue: bool) -> Spec {
|
||||
Spec::load(match dao_rescue {
|
||||
true => include_bytes!("../../res/ethereum/frontier_dao_rescue.json"),
|
||||
false => include_bytes!("../../res/ethereum/frontier.json"),
|
||||
})
|
||||
}
|
||||
pub fn new_frontier() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier.json")) }
|
||||
|
||||
/// Create a new Frontier mainnet chain spec without the DAO hardfork.
|
||||
pub fn new_classic() -> Spec { Spec::load(include_bytes!("../../res/ethereum/classic.json")) }
|
||||
|
||||
/// Create a new Frontier chain spec as though it never changes to Homestead.
|
||||
pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_test.json")) }
|
||||
@@ -46,6 +44,9 @@ pub fn new_frontier_test() -> Spec { Spec::load(include_bytes!("../../res/ethere
|
||||
/// Create a new Homestead chain spec as though it never changed from Frontier.
|
||||
pub fn new_homestead_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/homestead_test.json")) }
|
||||
|
||||
/// Create a new Frontier/Homestead/DAO chain spec with transition points at #5 and #8.
|
||||
pub fn new_daohardfork_test() -> Spec { Spec::load(include_bytes!("../../res/ethereum/daohardfork_test.json")) }
|
||||
|
||||
/// Create a new Frontier main net chain spec without genesis accounts.
|
||||
pub fn new_mainnet_like() -> Spec { Spec::load(include_bytes!("../../res/ethereum/frontier_like_test.json")) }
|
||||
|
||||
@@ -64,10 +65,10 @@ mod tests {
|
||||
let spec = new_morden();
|
||||
let engine = &spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let mut db_result = get_temp_journal_db();
|
||||
let mut db_result = get_temp_state_db();
|
||||
let mut db = db_result.take();
|
||||
spec.ensure_db_good(db.as_hashdb_mut());
|
||||
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce()).unwrap();
|
||||
spec.ensure_db_good(&mut db).unwrap();
|
||||
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce(), Default::default()).unwrap();
|
||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
|
||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
|
||||
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64));
|
||||
@@ -89,7 +90,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn frontier() {
|
||||
let frontier = new_frontier(true);
|
||||
let frontier = new_frontier();
|
||||
|
||||
assert_eq!(frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap());
|
||||
let genesis = frontier.genesis_block();
|
||||
|
||||
126
ethcore/src/evm/benches/mod.rs
Normal file
126
ethcore/src/evm/benches/mod.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! benchmarking for EVM
|
||||
//! should be started with:
|
||||
//! ```bash
|
||||
//! multirust run nightly cargo bench
|
||||
//! ```
|
||||
|
||||
extern crate test;
|
||||
|
||||
use self::test::{Bencher, black_box};
|
||||
|
||||
use common::*;
|
||||
use evm::{self, Factory, VMType};
|
||||
use evm::tests::FakeExt;
|
||||
|
||||
#[bench]
|
||||
fn simple_loop_log0_usize(b: &mut Bencher) {
|
||||
simple_loop_log0(U256::from(::std::usize::MAX), b)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn simple_loop_log0_u256(b: &mut Bencher) {
|
||||
simple_loop_log0(!U256::zero(), b)
|
||||
}
|
||||
|
||||
fn simple_loop_log0(gas: U256, b: &mut Bencher) {
|
||||
let mut vm = Factory::new(VMType::Interpreter).create(gas);
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||
let code = black_box(
|
||||
"62ffffff5b600190036000600fa0600357".from_hex().unwrap()
|
||||
);
|
||||
|
||||
b.iter(|| {
|
||||
let mut params = ActionParams::default();
|
||||
params.address = address.clone();
|
||||
params.gas = gas;
|
||||
params.code = Some(code.clone());
|
||||
|
||||
result(vm.exec(params, &mut ext))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn mem_gas_calculation_same_usize(b: &mut Bencher) {
|
||||
mem_gas_calculation_same(U256::from(::std::usize::MAX), b)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn mem_gas_calculation_same_u256(b: &mut Bencher) {
|
||||
mem_gas_calculation_same(!U256::zero(), b)
|
||||
}
|
||||
|
||||
fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) {
|
||||
let mut vm = Factory::new(VMType::Interpreter).create(gas);
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let code = black_box(
|
||||
"6110006001556001546000555b610fff805560016000540380600055600c57".from_hex().unwrap()
|
||||
);
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.address = address.clone();
|
||||
params.gas = gas;
|
||||
params.code = Some(code.clone());
|
||||
|
||||
result(vm.exec(params, &mut ext))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn mem_gas_calculation_increasing_usize(b: &mut Bencher) {
|
||||
mem_gas_calculation_increasing(U256::from(::std::usize::MAX), b)
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn mem_gas_calculation_increasing_u256(b: &mut Bencher) {
|
||||
mem_gas_calculation_increasing(!U256::zero(), b)
|
||||
}
|
||||
|
||||
fn mem_gas_calculation_increasing(gas: U256, b: &mut Bencher) {
|
||||
let mut vm = Factory::new(VMType::Interpreter).create(gas);
|
||||
let mut ext = FakeExt::new();
|
||||
|
||||
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let code = black_box(
|
||||
"6110006001556001546000555b610fff60005401805560016000540380600055600c57".from_hex().unwrap()
|
||||
);
|
||||
|
||||
let mut params = ActionParams::default();
|
||||
params.address = address.clone();
|
||||
params.gas = gas;
|
||||
params.code = Some(code.clone());
|
||||
|
||||
result(vm.exec(params, &mut ext))
|
||||
});
|
||||
}
|
||||
|
||||
fn result(r: evm::Result<evm::GasLeft>) -> U256 {
|
||||
match r {
|
||||
Ok(evm::GasLeft::Known(v)) => v,
|
||||
Ok(evm::GasLeft::NeedsReturn(v, _)) => v,
|
||||
_ => U256::zero(),
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,95 @@ impl<'a> Finalize for Result<GasLeft<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
|
||||
pub trait CostType: ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> + ops::Sub<Output=Self> + ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self> + cmp::Ord + Sized + From<usize> + Copy {
|
||||
/// Converts this cost into `U256`
|
||||
fn as_u256(&self) -> U256;
|
||||
/// Tries to fit `U256` into this `Cost` type
|
||||
fn from_u256(val: U256) -> Result<Self>;
|
||||
/// Convert to usize (may panic)
|
||||
fn as_usize(&self) -> usize;
|
||||
/// Add with overflow
|
||||
fn overflow_add(self, other: Self) -> (Self, bool);
|
||||
/// Multiple with overflow
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool);
|
||||
/// Single-step full multiplication and shift: `(self*other) >> shr`
|
||||
/// Should not overflow on intermediate steps
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool);
|
||||
}
|
||||
|
||||
impl CostType for U256 {
|
||||
fn as_u256(&self) -> U256 {
|
||||
*self
|
||||
}
|
||||
|
||||
fn from_u256(val: U256) -> Result<Self> {
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn as_usize(&self) -> usize {
|
||||
self.as_u64() as usize
|
||||
}
|
||||
|
||||
fn overflow_add(self, other: Self) -> (Self, bool) {
|
||||
Uint::overflowing_add(self, other)
|
||||
}
|
||||
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||
Uint::overflowing_mul(self, other)
|
||||
}
|
||||
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
|
||||
let x = self.full_mul(other);
|
||||
let U512(parts) = x;
|
||||
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
|
||||
let U512(parts) = x >> shr;
|
||||
(
|
||||
U256([parts[0], parts[1], parts[2], parts[3]]),
|
||||
overflow
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CostType for usize {
|
||||
fn as_u256(&self) -> U256 {
|
||||
U256::from(*self)
|
||||
}
|
||||
|
||||
fn from_u256(val: U256) -> Result<Self> {
|
||||
let res = val.low_u64() as usize;
|
||||
|
||||
// validate if value fits into usize
|
||||
if U256::from(res) != val {
|
||||
return Err(Error::OutOfGas);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn as_usize(&self) -> usize {
|
||||
*self
|
||||
}
|
||||
|
||||
fn overflow_add(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_add(other)
|
||||
}
|
||||
|
||||
fn overflow_mul(self, other: Self) -> (Self, bool) {
|
||||
self.overflowing_mul(other)
|
||||
}
|
||||
|
||||
fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
|
||||
let (c, o) = U128::from(self).overflowing_mul(U128::from(other));
|
||||
let U128(parts) = c;
|
||||
let overflow = o | (parts[1] > 0);
|
||||
let U128(parts) = c >> shr;
|
||||
let result = parts[0] as usize;
|
||||
let overflow = overflow | (parts[0] > result as u64);
|
||||
(result, overflow)
|
||||
}
|
||||
}
|
||||
|
||||
/// Evm interface
|
||||
pub trait Evm {
|
||||
/// This function should be used to execute transaction.
|
||||
@@ -103,3 +192,54 @@ pub trait Evm {
|
||||
/// to compute the final gas left.
|
||||
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn should_calculate_overflow_mul_shr_without_overflow() {
|
||||
// given
|
||||
let num = 1048576;
|
||||
|
||||
// when
|
||||
let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
|
||||
let (res2, o2) = num.overflow_mul_shr(num, 20);
|
||||
|
||||
// then
|
||||
assert_eq!(res1, U256::from(num));
|
||||
assert!(!o1);
|
||||
assert_eq!(res2, num);
|
||||
assert!(!o2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn should_calculate_overflow_mul_shr_with_overflow() {
|
||||
// given
|
||||
let max = ::std::u64::MAX;
|
||||
let num1 = U256([max, max, max, max]);
|
||||
let num2 = ::std::usize::MAX;
|
||||
|
||||
// when
|
||||
let (res1, o1) = num1.overflow_mul_shr(num1, 256);
|
||||
let (res2, o2) = num2.overflow_mul_shr(num2, 64);
|
||||
|
||||
// then
|
||||
assert_eq!(res2, num2 - 1);
|
||||
assert!(o2);
|
||||
|
||||
assert_eq!(res1, !U256::zero() - U256::one());
|
||||
assert!(o1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(test)]
|
||||
fn should_validate_u256_to_usize_conversion() {
|
||||
// given
|
||||
let v = U256::from(::std::usize::MAX) + U256::from(1);
|
||||
|
||||
// when
|
||||
let res = usize::from_u256(v);
|
||||
|
||||
// then
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
use util::common::*;
|
||||
use evm::{self, Schedule};
|
||||
use env_info::*;
|
||||
use types::executed::CallType;
|
||||
|
||||
/// Result of externalities create function.
|
||||
pub enum ContractCreateResult {
|
||||
@@ -69,16 +70,21 @@ pub trait Ext {
|
||||
/// and true if subcall was successfull.
|
||||
#[cfg_attr(feature="dev", allow(too_many_arguments))]
|
||||
fn call(&mut self,
|
||||
gas: &U256,
|
||||
sender_address: &Address,
|
||||
receive_address: &Address,
|
||||
value: Option<U256>,
|
||||
data: &[u8],
|
||||
code_address: &Address,
|
||||
output: &mut [u8]) -> MessageCallResult;
|
||||
gas: &U256,
|
||||
sender_address: &Address,
|
||||
receive_address: &Address,
|
||||
value: Option<U256>,
|
||||
data: &[u8],
|
||||
code_address: &Address,
|
||||
output: &mut [u8],
|
||||
call_type: CallType
|
||||
) -> MessageCallResult;
|
||||
|
||||
/// Returns code at given address
|
||||
fn extcode(&self, address: &Address) -> Bytes;
|
||||
fn extcode(&self, address: &Address) -> Arc<Bytes>;
|
||||
|
||||
/// Returns code length in bytes at given address
|
||||
fn extcode_len(&self, address: &Address) -> u64;
|
||||
|
||||
/// Creates log entry with given topics and data
|
||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]);
|
||||
|
||||
@@ -18,9 +18,12 @@
|
||||
//!
|
||||
//! TODO: consider spliting it into two separate files.
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use evm::Evm;
|
||||
use util::{U256, Uint};
|
||||
use super::interpreter::SharedCache;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
/// Type of EVM to use.
|
||||
pub enum VMType {
|
||||
/// JIT EVM
|
||||
@@ -80,29 +83,36 @@ impl VMType {
|
||||
|
||||
/// Evm factory. Creates appropriate Evm.
|
||||
pub struct Factory {
|
||||
evm: VMType
|
||||
evm: VMType,
|
||||
evm_cache: Arc<SharedCache>,
|
||||
}
|
||||
|
||||
impl Factory {
|
||||
/// Create fresh instance of VM
|
||||
/// Might choose implementation depending on supplied gas.
|
||||
#[cfg(feature = "jit")]
|
||||
pub fn create(&self) -> Box<Evm> {
|
||||
pub fn create(&self, gas: U256) -> Box<Evm> {
|
||||
match self.evm {
|
||||
VMType::Jit => {
|
||||
Box::new(super::jit::JitEvm::default())
|
||||
},
|
||||
VMType::Interpreter => {
|
||||
Box::new(super::interpreter::Interpreter::default())
|
||||
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
|
||||
Box::new(super::interpreter::Interpreter::<usize>::new(self.evm_cache.clone()))
|
||||
} else {
|
||||
Box::new(super::interpreter::Interpreter::<U256>::new(self.evm_cache.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create fresh instance of VM
|
||||
/// Might choose implementation depending on supplied gas.
|
||||
#[cfg(not(feature = "jit"))]
|
||||
pub fn create(&self) -> Box<Evm> {
|
||||
pub fn create(&self, gas: U256) -> Box<Evm> {
|
||||
match self.evm {
|
||||
VMType::Interpreter => {
|
||||
Box::new(super::interpreter::Interpreter::default())
|
||||
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
|
||||
Box::new(super::interpreter::Interpreter::<usize>::new(self.evm_cache.clone()))
|
||||
} else {
|
||||
Box::new(super::interpreter::Interpreter::<U256>::new(self.evm_cache.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,9 +120,14 @@ impl Factory {
|
||||
/// Create new instance of specific `VMType` factory
|
||||
pub fn new(evm: VMType) -> Self {
|
||||
Factory {
|
||||
evm: evm
|
||||
evm: evm,
|
||||
evm_cache: Arc::new(SharedCache::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn can_fit_in_usize(gas: U256) -> bool {
|
||||
gas == U256::from(gas.low_u64() as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Factory {
|
||||
@@ -120,7 +135,8 @@ impl Default for Factory {
|
||||
#[cfg(feature = "jit")]
|
||||
fn default() -> Factory {
|
||||
Factory {
|
||||
evm: VMType::Jit
|
||||
evm: VMType::Jit,
|
||||
evm_cache: Arc::new(SharedCache::default()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,14 +144,15 @@ impl Default for Factory {
|
||||
#[cfg(not(feature = "jit"))]
|
||||
fn default() -> Factory {
|
||||
Factory {
|
||||
evm: VMType::Interpreter
|
||||
evm: VMType::Interpreter,
|
||||
evm_cache: Arc::new(SharedCache::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_vm() {
|
||||
let _vm = Factory::default().create();
|
||||
let _vm = Factory::default().create(U256::zero());
|
||||
}
|
||||
|
||||
/// Create tests by injecting different VM factories
|
||||
|
||||
@@ -79,7 +79,7 @@ fn test_get_log_topics() {
|
||||
assert_eq!(get_log_topics(LOG4), 4);
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum GasPriceTier {
|
||||
/// 0 Zero
|
||||
Zero,
|
||||
@@ -101,6 +101,12 @@ pub enum GasPriceTier {
|
||||
Invalid
|
||||
}
|
||||
|
||||
impl Default for GasPriceTier {
|
||||
fn default() -> Self {
|
||||
GasPriceTier::Invalid
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index in schedule for specific `GasPriceTier`
|
||||
pub fn get_tier_idx (tier: GasPriceTier) -> usize {
|
||||
match tier {
|
||||
@@ -116,6 +122,7 @@ pub fn get_tier_idx (tier: GasPriceTier) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct InstructionInfo {
|
||||
pub name: &'static str,
|
||||
pub additional: usize,
|
||||
@@ -126,7 +133,7 @@ pub struct InstructionInfo {
|
||||
}
|
||||
|
||||
impl InstructionInfo {
|
||||
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> InstructionInfo {
|
||||
pub fn new(name: &'static str, additional: usize, args: usize, ret: usize, side_effects: bool, tier: GasPriceTier) -> Self {
|
||||
InstructionInfo {
|
||||
name: name,
|
||||
additional: additional,
|
||||
@@ -138,142 +145,141 @@ impl InstructionInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
/// Return details about specific instruction
|
||||
pub fn get_info(instruction: Instruction) -> InstructionInfo {
|
||||
match instruction {
|
||||
STOP => InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero),
|
||||
ADD => InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
SUB => InstructionInfo::new("SUB", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
MUL => InstructionInfo::new("MUL", 0, 2, 1, false, GasPriceTier::Low),
|
||||
DIV => InstructionInfo::new("DIV", 0, 2, 1, false, GasPriceTier::Low),
|
||||
SDIV => InstructionInfo::new("SDIV", 0, 2, 1, false, GasPriceTier::Low),
|
||||
MOD => InstructionInfo::new("MOD", 0, 2, 1, false, GasPriceTier::Low),
|
||||
SMOD => InstructionInfo::new("SMOD", 0, 2, 1, false, GasPriceTier::Low),
|
||||
EXP => InstructionInfo::new("EXP", 0, 2, 1, false, GasPriceTier::Special),
|
||||
NOT => InstructionInfo::new("NOT", 0, 1, 1, false, GasPriceTier::VeryLow),
|
||||
LT => InstructionInfo::new("LT", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
GT => InstructionInfo::new("GT", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
SLT => InstructionInfo::new("SLT", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
SGT => InstructionInfo::new("SGT", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
EQ => InstructionInfo::new("EQ", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
ISZERO => InstructionInfo::new("ISZERO", 0, 1, 1, false, GasPriceTier::VeryLow),
|
||||
AND => InstructionInfo::new("AND", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
OR => InstructionInfo::new("OR", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
XOR => InstructionInfo::new("XOR", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
BYTE => InstructionInfo::new("BYTE", 0, 2, 1, false, GasPriceTier::VeryLow),
|
||||
ADDMOD => InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid),
|
||||
MULMOD => InstructionInfo::new("MULMOD", 0, 3, 1, false, GasPriceTier::Mid),
|
||||
SIGNEXTEND => InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low),
|
||||
SHA3 => InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special),
|
||||
ADDRESS => InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base),
|
||||
BALANCE => InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Ext),
|
||||
ORIGIN => InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base),
|
||||
CALLER => InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base),
|
||||
CALLVALUE => InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base),
|
||||
CALLDATALOAD => InstructionInfo::new("CALLDATALOAD", 0, 1, 1, false, GasPriceTier::VeryLow),
|
||||
CALLDATASIZE => InstructionInfo::new("CALLDATASIZE", 0, 0, 1, false, GasPriceTier::Base),
|
||||
CALLDATACOPY => InstructionInfo::new("CALLDATACOPY", 0, 3, 0, true, GasPriceTier::VeryLow),
|
||||
CODESIZE => InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base),
|
||||
CODECOPY => InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow),
|
||||
GASPRICE => InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base),
|
||||
EXTCODESIZE => InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Ext),
|
||||
EXTCODECOPY => InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Ext),
|
||||
BLOCKHASH => InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext),
|
||||
COINBASE => InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base),
|
||||
TIMESTAMP => InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base),
|
||||
NUMBER => InstructionInfo::new("NUMBER", 0, 0, 1, false, GasPriceTier::Base),
|
||||
DIFFICULTY => InstructionInfo::new("DIFFICULTY", 0, 0, 1, false, GasPriceTier::Base),
|
||||
GASLIMIT => InstructionInfo::new("GASLIMIT", 0, 0, 1, false, GasPriceTier::Base),
|
||||
POP => InstructionInfo::new("POP", 0, 1, 0, false, GasPriceTier::Base),
|
||||
MLOAD => InstructionInfo::new("MLOAD", 0, 1, 1, false, GasPriceTier::VeryLow),
|
||||
MSTORE => InstructionInfo::new("MSTORE", 0, 2, 0, true, GasPriceTier::VeryLow),
|
||||
MSTORE8 => InstructionInfo::new("MSTORE8", 0, 2, 0, true, GasPriceTier::VeryLow),
|
||||
SLOAD => InstructionInfo::new("SLOAD", 0, 1, 1, false, GasPriceTier::Special),
|
||||
SSTORE => InstructionInfo::new("SSTORE", 0, 2, 0, true, GasPriceTier::Special),
|
||||
JUMP => InstructionInfo::new("JUMP", 0, 1, 0, true, GasPriceTier::Mid),
|
||||
JUMPI => InstructionInfo::new("JUMPI", 0, 2, 0, true, GasPriceTier::High),
|
||||
PC => InstructionInfo::new("PC", 0, 0, 1, false, GasPriceTier::Base),
|
||||
MSIZE => InstructionInfo::new("MSIZE", 0, 0, 1, false, GasPriceTier::Base),
|
||||
GAS => InstructionInfo::new("GAS", 0, 0, 1, false, GasPriceTier::Base),
|
||||
JUMPDEST => InstructionInfo::new("JUMPDEST", 0, 0, 0, true, GasPriceTier::Special),
|
||||
PUSH1 => InstructionInfo::new("PUSH1", 1, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH2 => InstructionInfo::new("PUSH2", 2, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH3 => InstructionInfo::new("PUSH3", 3, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH4 => InstructionInfo::new("PUSH4", 4, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH5 => InstructionInfo::new("PUSH5", 5, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH6 => InstructionInfo::new("PUSH6", 6, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH7 => InstructionInfo::new("PUSH7", 7, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH8 => InstructionInfo::new("PUSH8", 8, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH9 => InstructionInfo::new("PUSH9", 9, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH10 => InstructionInfo::new("PUSH10", 10, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH11 => InstructionInfo::new("PUSH11", 11, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH12 => InstructionInfo::new("PUSH12", 12, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH13 => InstructionInfo::new("PUSH13", 13, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH14 => InstructionInfo::new("PUSH14", 14, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH15 => InstructionInfo::new("PUSH15", 15, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH16 => InstructionInfo::new("PUSH16", 16, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH17 => InstructionInfo::new("PUSH17", 17, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH18 => InstructionInfo::new("PUSH18", 18, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH19 => InstructionInfo::new("PUSH19", 19, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH20 => InstructionInfo::new("PUSH20", 20, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH21 => InstructionInfo::new("PUSH21", 21, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH22 => InstructionInfo::new("PUSH22", 22, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH23 => InstructionInfo::new("PUSH23", 23, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH24 => InstructionInfo::new("PUSH24", 24, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH25 => InstructionInfo::new("PUSH25", 25, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH26 => InstructionInfo::new("PUSH26", 26, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH27 => InstructionInfo::new("PUSH27", 27, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH28 => InstructionInfo::new("PUSH28", 28, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH29 => InstructionInfo::new("PUSH29", 29, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH30 => InstructionInfo::new("PUSH30", 30, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH31 => InstructionInfo::new("PUSH31", 31, 0, 1, false, GasPriceTier::VeryLow),
|
||||
PUSH32 => InstructionInfo::new("PUSH32", 32, 0, 1, false, GasPriceTier::VeryLow),
|
||||
DUP1 => InstructionInfo::new("DUP1", 0, 1, 2, false, GasPriceTier::VeryLow),
|
||||
DUP2 => InstructionInfo::new("DUP2", 0, 2, 3, false, GasPriceTier::VeryLow),
|
||||
DUP3 => InstructionInfo::new("DUP3", 0, 3, 4, false, GasPriceTier::VeryLow),
|
||||
DUP4 => InstructionInfo::new("DUP4", 0, 4, 5, false, GasPriceTier::VeryLow),
|
||||
DUP5 => InstructionInfo::new("DUP5", 0, 5, 6, false, GasPriceTier::VeryLow),
|
||||
DUP6 => InstructionInfo::new("DUP6", 0, 6, 7, false, GasPriceTier::VeryLow),
|
||||
DUP7 => InstructionInfo::new("DUP7", 0, 7, 8, false, GasPriceTier::VeryLow),
|
||||
DUP8 => InstructionInfo::new("DUP8", 0, 8, 9, false, GasPriceTier::VeryLow),
|
||||
DUP9 => InstructionInfo::new("DUP9", 0, 9, 10, false, GasPriceTier::VeryLow),
|
||||
DUP10 => InstructionInfo::new("DUP10", 0, 10, 11, false, GasPriceTier::VeryLow),
|
||||
DUP11 => InstructionInfo::new("DUP11", 0, 11, 12, false, GasPriceTier::VeryLow),
|
||||
DUP12 => InstructionInfo::new("DUP12", 0, 12, 13, false, GasPriceTier::VeryLow),
|
||||
DUP13 => InstructionInfo::new("DUP13", 0, 13, 14, false, GasPriceTier::VeryLow),
|
||||
DUP14 => InstructionInfo::new("DUP14", 0, 14, 15, false, GasPriceTier::VeryLow),
|
||||
DUP15 => InstructionInfo::new("DUP15", 0, 15, 16, false, GasPriceTier::VeryLow),
|
||||
DUP16 => InstructionInfo::new("DUP16", 0, 16, 17, false, GasPriceTier::VeryLow),
|
||||
SWAP1 => InstructionInfo::new("SWAP1", 0, 2, 2, false, GasPriceTier::VeryLow),
|
||||
SWAP2 => InstructionInfo::new("SWAP2", 0, 3, 3, false, GasPriceTier::VeryLow),
|
||||
SWAP3 => InstructionInfo::new("SWAP3", 0, 4, 4, false, GasPriceTier::VeryLow),
|
||||
SWAP4 => InstructionInfo::new("SWAP4", 0, 5, 5, false, GasPriceTier::VeryLow),
|
||||
SWAP5 => InstructionInfo::new("SWAP5", 0, 6, 6, false, GasPriceTier::VeryLow),
|
||||
SWAP6 => InstructionInfo::new("SWAP6", 0, 7, 7, false, GasPriceTier::VeryLow),
|
||||
SWAP7 => InstructionInfo::new("SWAP7", 0, 8, 8, false, GasPriceTier::VeryLow),
|
||||
SWAP8 => InstructionInfo::new("SWAP8", 0, 9, 9, false, GasPriceTier::VeryLow),
|
||||
SWAP9 => InstructionInfo::new("SWAP9", 0, 10, 10, false, GasPriceTier::VeryLow),
|
||||
SWAP10 => InstructionInfo::new("SWAP10", 0, 11, 11, false, GasPriceTier::VeryLow),
|
||||
SWAP11 => InstructionInfo::new("SWAP11", 0, 12, 12, false, GasPriceTier::VeryLow),
|
||||
SWAP12 => InstructionInfo::new("SWAP12", 0, 13, 13, false, GasPriceTier::VeryLow),
|
||||
SWAP13 => InstructionInfo::new("SWAP13", 0, 14, 14, false, GasPriceTier::VeryLow),
|
||||
SWAP14 => InstructionInfo::new("SWAP14", 0, 15, 15, false, GasPriceTier::VeryLow),
|
||||
SWAP15 => InstructionInfo::new("SWAP15", 0, 16, 16, false, GasPriceTier::VeryLow),
|
||||
SWAP16 => InstructionInfo::new("SWAP16", 0, 17, 17, false, GasPriceTier::VeryLow),
|
||||
LOG0 => InstructionInfo::new("LOG0", 0, 2, 0, true, GasPriceTier::Special),
|
||||
LOG1 => InstructionInfo::new("LOG1", 0, 3, 0, true, GasPriceTier::Special),
|
||||
LOG2 => InstructionInfo::new("LOG2", 0, 4, 0, true, GasPriceTier::Special),
|
||||
LOG3 => InstructionInfo::new("LOG3", 0, 5, 0, true, GasPriceTier::Special),
|
||||
LOG4 => InstructionInfo::new("LOG4", 0, 6, 0, true, GasPriceTier::Special),
|
||||
CREATE => InstructionInfo::new("CREATE", 0, 3, 1, true, GasPriceTier::Special),
|
||||
CALL => InstructionInfo::new("CALL", 0, 7, 1, true, GasPriceTier::Special),
|
||||
CALLCODE => InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special),
|
||||
RETURN => InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero),
|
||||
DELEGATECALL => InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special),
|
||||
SUICIDE => InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Zero),
|
||||
_ => InstructionInfo::new("INVALID_INSTRUCTION", 0, 0, 0, false, GasPriceTier::Invalid)
|
||||
}
|
||||
lazy_static! {
|
||||
pub static ref INSTRUCTIONS: [InstructionInfo; 0x100] = {
|
||||
let mut arr = [InstructionInfo::default(); 0x100];
|
||||
arr[STOP as usize] = InstructionInfo::new("STOP", 0, 0, 0, true, GasPriceTier::Zero);
|
||||
arr[ADD as usize] = InstructionInfo::new("ADD", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[SUB as usize] = InstructionInfo::new("SUB", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[MUL as usize] = InstructionInfo::new("MUL", 0, 2, 1, false, GasPriceTier::Low);
|
||||
arr[DIV as usize] = InstructionInfo::new("DIV", 0, 2, 1, false, GasPriceTier::Low);
|
||||
arr[SDIV as usize] = InstructionInfo::new("SDIV", 0, 2, 1, false, GasPriceTier::Low);
|
||||
arr[MOD as usize] = InstructionInfo::new("MOD", 0, 2, 1, false, GasPriceTier::Low);
|
||||
arr[SMOD as usize] = InstructionInfo::new("SMOD", 0, 2, 1, false, GasPriceTier::Low);
|
||||
arr[EXP as usize] = InstructionInfo::new("EXP", 0, 2, 1, false, GasPriceTier::Special);
|
||||
arr[NOT as usize] = InstructionInfo::new("NOT", 0, 1, 1, false, GasPriceTier::VeryLow);
|
||||
arr[LT as usize] = InstructionInfo::new("LT", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[GT as usize] = InstructionInfo::new("GT", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[SLT as usize] = InstructionInfo::new("SLT", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[SGT as usize] = InstructionInfo::new("SGT", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[EQ as usize] = InstructionInfo::new("EQ", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[ISZERO as usize] = InstructionInfo::new("ISZERO", 0, 1, 1, false, GasPriceTier::VeryLow);
|
||||
arr[AND as usize] = InstructionInfo::new("AND", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[OR as usize] = InstructionInfo::new("OR", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[XOR as usize] = InstructionInfo::new("XOR", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[BYTE as usize] = InstructionInfo::new("BYTE", 0, 2, 1, false, GasPriceTier::VeryLow);
|
||||
arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 0, 3, 1, false, GasPriceTier::Mid);
|
||||
arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 0, 3, 1, false, GasPriceTier::Mid);
|
||||
arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 0, 2, 1, false, GasPriceTier::Low);
|
||||
arr[SHA3 as usize] = InstructionInfo::new("SHA3", 0, 2, 1, false, GasPriceTier::Special);
|
||||
arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 0, 1, 1, false, GasPriceTier::Ext);
|
||||
arr[ORIGIN as usize] = InstructionInfo::new("ORIGIN", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[CALLER as usize] = InstructionInfo::new("CALLER", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[CALLVALUE as usize] = InstructionInfo::new("CALLVALUE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[CALLDATALOAD as usize] = InstructionInfo::new("CALLDATALOAD", 0, 1, 1, false, GasPriceTier::VeryLow);
|
||||
arr[CALLDATASIZE as usize] = InstructionInfo::new("CALLDATASIZE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[CALLDATACOPY as usize] = InstructionInfo::new("CALLDATACOPY", 0, 3, 0, true, GasPriceTier::VeryLow);
|
||||
arr[CODESIZE as usize] = InstructionInfo::new("CODESIZE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[CODECOPY as usize] = InstructionInfo::new("CODECOPY", 0, 3, 0, true, GasPriceTier::VeryLow);
|
||||
arr[GASPRICE as usize] = InstructionInfo::new("GASPRICE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 0, 1, 1, false, GasPriceTier::Ext);
|
||||
arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 0, 4, 0, true, GasPriceTier::Ext);
|
||||
arr[BLOCKHASH as usize] = InstructionInfo::new("BLOCKHASH", 0, 1, 1, false, GasPriceTier::Ext);
|
||||
arr[COINBASE as usize] = InstructionInfo::new("COINBASE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[TIMESTAMP as usize] = InstructionInfo::new("TIMESTAMP", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[NUMBER as usize] = InstructionInfo::new("NUMBER", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[DIFFICULTY as usize] = InstructionInfo::new("DIFFICULTY", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[GASLIMIT as usize] = InstructionInfo::new("GASLIMIT", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[POP as usize] = InstructionInfo::new("POP", 0, 1, 0, false, GasPriceTier::Base);
|
||||
arr[MLOAD as usize] = InstructionInfo::new("MLOAD", 0, 1, 1, false, GasPriceTier::VeryLow);
|
||||
arr[MSTORE as usize] = InstructionInfo::new("MSTORE", 0, 2, 0, true, GasPriceTier::VeryLow);
|
||||
arr[MSTORE8 as usize] = InstructionInfo::new("MSTORE8", 0, 2, 0, true, GasPriceTier::VeryLow);
|
||||
arr[SLOAD as usize] = InstructionInfo::new("SLOAD", 0, 1, 1, false, GasPriceTier::Special);
|
||||
arr[SSTORE as usize] = InstructionInfo::new("SSTORE", 0, 2, 0, true, GasPriceTier::Special);
|
||||
arr[JUMP as usize] = InstructionInfo::new("JUMP", 0, 1, 0, true, GasPriceTier::Mid);
|
||||
arr[JUMPI as usize] = InstructionInfo::new("JUMPI", 0, 2, 0, true, GasPriceTier::High);
|
||||
arr[PC as usize] = InstructionInfo::new("PC", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[MSIZE as usize] = InstructionInfo::new("MSIZE", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[GAS as usize] = InstructionInfo::new("GAS", 0, 0, 1, false, GasPriceTier::Base);
|
||||
arr[JUMPDEST as usize] = InstructionInfo::new("JUMPDEST", 0, 0, 0, true, GasPriceTier::Special);
|
||||
arr[PUSH1 as usize] = InstructionInfo::new("PUSH1", 1, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH2 as usize] = InstructionInfo::new("PUSH2", 2, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH3 as usize] = InstructionInfo::new("PUSH3", 3, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH4 as usize] = InstructionInfo::new("PUSH4", 4, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH5 as usize] = InstructionInfo::new("PUSH5", 5, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH6 as usize] = InstructionInfo::new("PUSH6", 6, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH7 as usize] = InstructionInfo::new("PUSH7", 7, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH8 as usize] = InstructionInfo::new("PUSH8", 8, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH9 as usize] = InstructionInfo::new("PUSH9", 9, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH10 as usize] = InstructionInfo::new("PUSH10", 10, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH11 as usize] = InstructionInfo::new("PUSH11", 11, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH12 as usize] = InstructionInfo::new("PUSH12", 12, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH13 as usize] = InstructionInfo::new("PUSH13", 13, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH14 as usize] = InstructionInfo::new("PUSH14", 14, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH15 as usize] = InstructionInfo::new("PUSH15", 15, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH16 as usize] = InstructionInfo::new("PUSH16", 16, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH17 as usize] = InstructionInfo::new("PUSH17", 17, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH18 as usize] = InstructionInfo::new("PUSH18", 18, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH19 as usize] = InstructionInfo::new("PUSH19", 19, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH20 as usize] = InstructionInfo::new("PUSH20", 20, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH21 as usize] = InstructionInfo::new("PUSH21", 21, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH22 as usize] = InstructionInfo::new("PUSH22", 22, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH23 as usize] = InstructionInfo::new("PUSH23", 23, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH24 as usize] = InstructionInfo::new("PUSH24", 24, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH25 as usize] = InstructionInfo::new("PUSH25", 25, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH26 as usize] = InstructionInfo::new("PUSH26", 26, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH27 as usize] = InstructionInfo::new("PUSH27", 27, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH28 as usize] = InstructionInfo::new("PUSH28", 28, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH29 as usize] = InstructionInfo::new("PUSH29", 29, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH30 as usize] = InstructionInfo::new("PUSH30", 30, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH31 as usize] = InstructionInfo::new("PUSH31", 31, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[PUSH32 as usize] = InstructionInfo::new("PUSH32", 32, 0, 1, false, GasPriceTier::VeryLow);
|
||||
arr[DUP1 as usize] = InstructionInfo::new("DUP1", 0, 1, 2, false, GasPriceTier::VeryLow);
|
||||
arr[DUP2 as usize] = InstructionInfo::new("DUP2", 0, 2, 3, false, GasPriceTier::VeryLow);
|
||||
arr[DUP3 as usize] = InstructionInfo::new("DUP3", 0, 3, 4, false, GasPriceTier::VeryLow);
|
||||
arr[DUP4 as usize] = InstructionInfo::new("DUP4", 0, 4, 5, false, GasPriceTier::VeryLow);
|
||||
arr[DUP5 as usize] = InstructionInfo::new("DUP5", 0, 5, 6, false, GasPriceTier::VeryLow);
|
||||
arr[DUP6 as usize] = InstructionInfo::new("DUP6", 0, 6, 7, false, GasPriceTier::VeryLow);
|
||||
arr[DUP7 as usize] = InstructionInfo::new("DUP7", 0, 7, 8, false, GasPriceTier::VeryLow);
|
||||
arr[DUP8 as usize] = InstructionInfo::new("DUP8", 0, 8, 9, false, GasPriceTier::VeryLow);
|
||||
arr[DUP9 as usize] = InstructionInfo::new("DUP9", 0, 9, 10, false, GasPriceTier::VeryLow);
|
||||
arr[DUP10 as usize] = InstructionInfo::new("DUP10", 0, 10, 11, false, GasPriceTier::VeryLow);
|
||||
arr[DUP11 as usize] = InstructionInfo::new("DUP11", 0, 11, 12, false, GasPriceTier::VeryLow);
|
||||
arr[DUP12 as usize] = InstructionInfo::new("DUP12", 0, 12, 13, false, GasPriceTier::VeryLow);
|
||||
arr[DUP13 as usize] = InstructionInfo::new("DUP13", 0, 13, 14, false, GasPriceTier::VeryLow);
|
||||
arr[DUP14 as usize] = InstructionInfo::new("DUP14", 0, 14, 15, false, GasPriceTier::VeryLow);
|
||||
arr[DUP15 as usize] = InstructionInfo::new("DUP15", 0, 15, 16, false, GasPriceTier::VeryLow);
|
||||
arr[DUP16 as usize] = InstructionInfo::new("DUP16", 0, 16, 17, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP1 as usize] = InstructionInfo::new("SWAP1", 0, 2, 2, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP2 as usize] = InstructionInfo::new("SWAP2", 0, 3, 3, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP3 as usize] = InstructionInfo::new("SWAP3", 0, 4, 4, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP4 as usize] = InstructionInfo::new("SWAP4", 0, 5, 5, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP5 as usize] = InstructionInfo::new("SWAP5", 0, 6, 6, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP6 as usize] = InstructionInfo::new("SWAP6", 0, 7, 7, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP7 as usize] = InstructionInfo::new("SWAP7", 0, 8, 8, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP8 as usize] = InstructionInfo::new("SWAP8", 0, 9, 9, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP9 as usize] = InstructionInfo::new("SWAP9", 0, 10, 10, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP10 as usize] = InstructionInfo::new("SWAP10", 0, 11, 11, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP11 as usize] = InstructionInfo::new("SWAP11", 0, 12, 12, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP12 as usize] = InstructionInfo::new("SWAP12", 0, 13, 13, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP13 as usize] = InstructionInfo::new("SWAP13", 0, 14, 14, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP14 as usize] = InstructionInfo::new("SWAP14", 0, 15, 15, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP15 as usize] = InstructionInfo::new("SWAP15", 0, 16, 16, false, GasPriceTier::VeryLow);
|
||||
arr[SWAP16 as usize] = InstructionInfo::new("SWAP16", 0, 17, 17, false, GasPriceTier::VeryLow);
|
||||
arr[LOG0 as usize] = InstructionInfo::new("LOG0", 0, 2, 0, true, GasPriceTier::Special);
|
||||
arr[LOG1 as usize] = InstructionInfo::new("LOG1", 0, 3, 0, true, GasPriceTier::Special);
|
||||
arr[LOG2 as usize] = InstructionInfo::new("LOG2", 0, 4, 0, true, GasPriceTier::Special);
|
||||
arr[LOG3 as usize] = InstructionInfo::new("LOG3", 0, 5, 0, true, GasPriceTier::Special);
|
||||
arr[LOG4 as usize] = InstructionInfo::new("LOG4", 0, 6, 0, true, GasPriceTier::Special);
|
||||
arr[CREATE as usize] = InstructionInfo::new("CREATE", 0, 3, 1, true, GasPriceTier::Special);
|
||||
arr[CALL as usize] = InstructionInfo::new("CALL", 0, 7, 1, true, GasPriceTier::Special);
|
||||
arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 0, 7, 1, true, GasPriceTier::Special);
|
||||
arr[RETURN as usize] = InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero);
|
||||
arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special);
|
||||
arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Zero);
|
||||
arr
|
||||
};
|
||||
}
|
||||
|
||||
/// Virtual machine bytecode instruction.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user