Compare commits
1543 Commits
v0.6.3
...
v0.7.10-br
Author | SHA1 | Date | |
---|---|---|---|
3983dd2428 | |||
0a9dc1536c | |||
1508a23a6f | |||
f5b8f3d41b | |||
0e93b98533 | |||
5da5db5a0a | |||
88af879f7a | |||
12671c82ea | |||
59ef6e3693 | |||
a7f4ade711 | |||
1e985f9865 | |||
4789084998 | |||
3325683794 | |||
f7287c626e | |||
198cc69357 | |||
5ad473d758 | |||
db494170dc | |||
9e286e1c33 | |||
2d09e67713 | |||
49e0267fe7 | |||
590aace88d | |||
4b2f1f7628 | |||
df3366d910 | |||
4dbdcaecb1 | |||
b1c58b76a9 | |||
ef4135eabe | |||
c3ba4ace64 | |||
15e46b97b4 | |||
da900f9435 | |||
f0f6727778 | |||
65e39bf20e | |||
f8061fcba8 | |||
4c84db85c5 | |||
0122989808 | |||
ccb4fc8d03 | |||
aec0c0efcb | |||
bf566a657c | |||
8577e4171a | |||
06e76422b5 | |||
1c7fd62e57 | |||
a17a1f9208 | |||
ed1538248f | |||
9e1689df9c | |||
ca7f385294 | |||
a92ccfc5c6 | |||
25cf0c440c | |||
05fd1dafe2 | |||
e28c60caf9 | |||
9423401d73 | |||
0d57ca486a | |||
2b5fcb4642 | |||
5553e5aaed | |||
af6afbaa56 | |||
1b98cbbfa4 | |||
4082c8b61d | |||
dda778eda7 | |||
0f5c6c5e2d | |||
186948da22 | |||
4f1ef89cb2 | |||
1fb84d3c5f | |||
3308491c92 | |||
9ff7be68fb | |||
ce0c4e5d86 | |||
984c7e6689 | |||
87adff7e18 | |||
c24018e273 | |||
6cf4e0329c | |||
df5157c0b0 | |||
acf4b5753f | |||
829730dec4 | |||
c084a7daa5 | |||
93e858f88e | |||
4f12f0697e | |||
76842b0df8 | |||
e3a8412df3 | |||
ebe2d9d872 | |||
f06543fd06 | |||
195b2d2ebd | |||
384b8c75f0 | |||
9925916851 | |||
095cb97675 | |||
d80f8bda94 | |||
3cf0477c21 | |||
8dbca75d85 | |||
710360bab6 | |||
3db9c80070 | |||
085f604b27 | |||
a5b27bbc10 | |||
8c7e4b290f | |||
5653898150 | |||
73123bf257 | |||
73c4ca3a6f | |||
296112848c | |||
008e91db6a | |||
3043b233ea | |||
bff5999efa | |||
f298ffdbb8 | |||
3664cd58e3 | |||
491edc16e7 | |||
83663ed4b0 | |||
9008b155d3 | |||
b6cb5272de | |||
6d99c03d91 | |||
99853ac3ce | |||
8240550187 | |||
709eff4ea7 | |||
6212175be3 | |||
6095edac58 | |||
3d9a4e7084 | |||
f778922086 | |||
edc52bdcbf | |||
0a22dc2ce5 | |||
cb4d168ecc | |||
5ebae82eeb | |||
64f35ba8d1 | |||
99481a245a | |||
9f7a8ea5e6 | |||
b4eeffa8f1 | |||
2df8ad6307 | |||
a052357872 | |||
a22056db59 | |||
beb7d35c40 | |||
d33987cb44 | |||
6dc46d3341 | |||
616066a598 | |||
1bce02eff7 | |||
a3559c5e1b | |||
c8d0f8adc5 | |||
6ba8328077 | |||
61556ef01d | |||
ef7961b7d2 | |||
c17a3cb0ce | |||
cfd7e74c25 | |||
3a09459c4c | |||
9b85002b70 | |||
f816fdcb69 | |||
6049fcd52a | |||
c1fca72552 | |||
205af02a1f | |||
5a5560f105 | |||
59b63caf5e | |||
e4a601c644 | |||
7149191dd9 | |||
f38052c499 | |||
4cd9d57bad | |||
8cf9ed0ea5 | |||
28b7dcc204 | |||
eba3fca7bc | |||
6c098ee779 | |||
c0bbb7e69a | |||
d5ac24907e | |||
0f47129113 | |||
9b8a12b4b5 | |||
12f1aea38d | |||
b05e63c34d | |||
cab7e6000e | |||
0f460ad26e | |||
e70529a977 | |||
14e2e488fd | |||
437d79f094 | |||
93e693be72 | |||
675ba4d7eb | |||
f8d0cd9906 | |||
a1b6a9ac29 | |||
62cd9946ee | |||
e08aba5dd9 | |||
34f29d57b6 | |||
3220a32ff0 | |||
f7417d3552 | |||
5c958ec5f6 | |||
2a5af8fac7 | |||
559a81da6f | |||
a19d2c2278 | |||
cad770c734 | |||
ca74bcc4cd | |||
b4e47a094c | |||
bd0a50fdc3 | |||
74266d5bbd | |||
9095b37343 | |||
ffab36c7d8 | |||
f82fb889b7 | |||
ddca18638f | |||
3588c4a9fe | |||
71b338e8ac | |||
58812fad42 | |||
790ded0f47 | |||
6dbd7a9a80 | |||
e1e50f4afd | |||
3a656c07f1 | |||
ec75119341 | |||
0b64906a72 | |||
1beb9a2b8b | |||
242667ebe5 | |||
5211fb0c79 | |||
81e7f8679e | |||
6f7f570670 | |||
8f9a354682 | |||
bc5d924b83 | |||
9481f86c37 | |||
dd9e99a418 | |||
33afb10b0d | |||
1ecb3b4cf3 | |||
dcd8cc8341 | |||
e6e468ef59 | |||
2a9fc7baa9 | |||
56aa24002d | |||
711be7009e | |||
f6e55962a8 | |||
20d518ee95 | |||
313cfba7d4 | |||
461324a801 | |||
fa59db7595 | |||
bd9bd4abed | |||
6eacc8eab9 | |||
00878e5b6e | |||
60cdb1148c | |||
9bb1ac7564 | |||
6c9e503eb8 | |||
1d866b5e57 | |||
5c5df21e3d | |||
12e8404f8f | |||
0d1cdd26d6 | |||
cff0d93105 | |||
c24d143c9e | |||
bfd1fe977e | |||
d9ccbf04db | |||
cd94b5ffb3 | |||
3c619baec5 | |||
8f3a03c0cc | |||
75ee3b3f08 | |||
9509322ecd | |||
bed659f9b3 | |||
ad0acb5762 | |||
ebb7db263f | |||
d30571a7a8 | |||
cbeebcd47d | |||
f538ea25e4 | |||
429dd2a100 | |||
0a3a148ed4 | |||
48a3f0986c | |||
a1d62abca4 | |||
be96da179a | |||
8f94f731a5 | |||
ab6b9c44aa | |||
4e15adac6d | |||
b100546c9d | |||
834f8a1dc8 | |||
ada684e054 | |||
92b30cc452 | |||
cb32f52b9c | |||
4f00929084 | |||
94b0ce84da | |||
e76c58d175 | |||
92299b7c24 | |||
48488017e4 | |||
b4da085a9f | |||
7db44b56b0 | |||
99d5769c2b | |||
1b1fa049fa | |||
8cfbf1836d | |||
78fb2af6f1 | |||
b96a59eb28 | |||
64b6172089 | |||
e7d9bcd36b | |||
123282e045 | |||
e22e8b7fef | |||
a9db1ee8d4 | |||
699dcaf65c | |||
f4b717cb9d | |||
f59a3b67f6 | |||
1025d097fd | |||
d56d0c64aa | |||
0c4adeceae | |||
f76f953f0c | |||
9c2b878678 | |||
a82b89e2d5 | |||
c8302882c8 | |||
d87aa24885 | |||
92c7aca817 | |||
76c9c8d653 | |||
bd4f51ff3c | |||
5dcf59bdf4 | |||
9f8bcf3abc | |||
1bb398f4e2 | |||
3f90f7c898 | |||
c8438979a9 | |||
141d3caabb | |||
394e0f60c2 | |||
b95d9e005d | |||
f3473312ba | |||
5af4ff985d | |||
4914a78c8c | |||
af34749a6b | |||
af8f5f0b69 | |||
0ed1a8b50a | |||
8826e9694c | |||
b1c247231b | |||
fd9da72536 | |||
3ee0461cb5 | |||
8e0a39f33f | |||
df5603de0a | |||
fa890c8c01 | |||
ce2ec1980b | |||
f05d422a97 | |||
2ef044edfc | |||
cc67a84e94 | |||
81ec564ef6 | |||
4cf69d7cd3 | |||
38034c3066 | |||
56014844a9 | |||
d9bccdde23 | |||
051af604e2 | |||
9e2f071d26 | |||
fb4113dab4 | |||
665a44646e | |||
6b3f5fb82b | |||
e252dbf989 | |||
8170f96ded | |||
48125a25eb | |||
7849b7e978 | |||
5920aa7be6 | |||
1e55fe3aa2 | |||
9d386033ef | |||
2b02d81253 | |||
11b3f97520 | |||
10782c5aae | |||
cbc6709937 | |||
ca18b3f1e9 | |||
69e745c537 | |||
003280888d | |||
797b93c98c | |||
b095bd3237 | |||
1d3f43f060 | |||
d586dd902d | |||
272d58662c | |||
6623500c6b | |||
08c26ab8b0 | |||
f62b6742f2 | |||
e73aad959e | |||
771fbcc02e | |||
119c5b40a7 | |||
184055b3e2 | |||
bd2862aec6 | |||
69baa465ea | |||
feef194829 | |||
91c876831a | |||
06aa74e7df | |||
50fd469249 | |||
7daa8c2f6e | |||
bb03276c19 | |||
411b9800ae | |||
29b8a0bc5f | |||
2e45e4d015 | |||
5662176f8c | |||
1880c6b3dc | |||
6247a99f41 | |||
53d5c490be | |||
51ecab6967 | |||
037039cace | |||
b5beb1aac1 | |||
ce05634f38 | |||
27cb0750c1 | |||
6b92d541da | |||
6f08e30109 | |||
520fdfe346 | |||
10b252dd05 | |||
6ea44c466a | |||
097ba56df5 | |||
33ca8d7b8f | |||
3ad1daa70d | |||
3bc64b6b6e | |||
73c1c2c4af | |||
350b0b1f66 | |||
3ea0e904ad | |||
20c742e474 | |||
82a2e4fe28 | |||
6956b5104f | |||
a02dc4ccc3 | |||
77badc8c46 | |||
d2bb83833f | |||
e57989c0a6 | |||
b94dfc8d3a | |||
70a00d602e | |||
df238dc7ec | |||
abdf5e76f3 | |||
03fd832ee8 | |||
fc308b842e | |||
35f339e942 | |||
fd9b03a431 | |||
c090a77f1c | |||
50f5ba5b0c | |||
cbd785cfe8 | |||
0165c18330 | |||
e0f93c74c5 | |||
a6265cb49a | |||
793baf060a | |||
ec132749aa | |||
45d1052229 | |||
d5a7ba1626 | |||
3b1296077b | |||
dac128a029 | |||
4e95cecfb9 | |||
3976b52ed7 | |||
e183880d8b | |||
d7736a7bbb | |||
df2b70853f | |||
9e6411db31 | |||
3b70985284 | |||
fa84e50ddb | |||
ade980912d | |||
28570ef109 | |||
20cdb73862 | |||
36cdab2068 | |||
6906904896 | |||
10bbf265b2 | |||
dac4a8f113 | |||
93fcabd251 | |||
f5b8775bed | |||
bb5038699e | |||
65cdb3436e | |||
86f789333a | |||
febec5ca4a | |||
70f7a0be11 | |||
0a99719a39 | |||
311c6f8a3f | |||
7227552f42 | |||
266d212094 | |||
3d177be73e | |||
0aea5fc4a3 | |||
7ca7938d8e | |||
f55e39cf1a | |||
393a92811b | |||
294b437414 | |||
c5bd32b0ad | |||
03e082d4ac | |||
db6f4ab1cc | |||
1644b1e53e | |||
cfd1f0907f | |||
2e894b668a | |||
8128190fb2 | |||
cb845b9bc8 | |||
56843ca0fc | |||
e02c0fa808 | |||
9b494c6869 | |||
3db6a8e92d | |||
6fec5bd32e | |||
6877660fe2 | |||
c1f8a640d3 | |||
e83a999039 | |||
44674cb96c | |||
d451269977 | |||
7c9508ed71 | |||
a38dafcc57 | |||
bd7aca76e1 | |||
4de3ad1712 | |||
83a4b8b49b | |||
b93d6ca5d2 | |||
9d86a49a73 | |||
f3196c915a | |||
9b60cf267a | |||
11ace54307 | |||
6de726f16c | |||
b417766b36 | |||
b4fa28a3e6 | |||
d1a6084bf7 | |||
a9b857769d | |||
0015ce1e35 | |||
4ed94d3d51 | |||
e100aa3c6c | |||
677836cbee | |||
a75c92000f | |||
b55e017e62 | |||
82be305496 | |||
5053ec2190 | |||
a34a971b50 | |||
5fa0173c41 | |||
3af211dd65 | |||
6db40ecb22 | |||
ab6ede51d7 | |||
ea0357bf02 | |||
44d50bc8d2 | |||
e20b113053 | |||
0acdeca3d6 | |||
a45e293267 | |||
41ae6f298e | |||
bd95fd770b | |||
b8354124be | |||
2b8eae9810 | |||
b2dc19155f | |||
7a5b279459 | |||
cf999c4622 | |||
68119d0929 | |||
9ed8dc7384 | |||
dc944f7518 | |||
d5d1e50365 | |||
2458697dad | |||
206672db5e | |||
5c2aa1d4f5 | |||
5cb4120ef5 | |||
496df5c29a | |||
3aeba50c38 | |||
29f5dd38e3 | |||
b8b1453392 | |||
ecdda01841 | |||
bb4495722f | |||
1118aaf840 | |||
1cb12296f6 | |||
96fd1ce270 | |||
544b7fba7f | |||
6ecbbe4006 | |||
cba2f6c2c4 | |||
1fe2d0d0e0 | |||
54558dd8ae | |||
60a8c9527c | |||
84690bfbbe | |||
d3a0bb4f35 | |||
ad13b402d7 | |||
b66fcf85df | |||
ba43364f36 | |||
57dc435f9b | |||
c582890590 | |||
615d20598a | |||
46a496428f | |||
acfb5b85fb | |||
70db149494 | |||
a4007f3b68 | |||
bc1a173d2f | |||
5c5db7d931 | |||
6800c3665a | |||
b73c07dd80 | |||
ea67d853a8 | |||
ac5e86b7ae | |||
353b558536 | |||
8516e748ca | |||
8780deece9 | |||
29ca238a7a | |||
1b66e1c93a | |||
0705bb3fe5 | |||
48fd23dc10 | |||
6e4818d742 | |||
df30588eb6 | |||
ce149d2733 | |||
9ac4e23b66 | |||
154ca03228 | |||
c7d666ad61 | |||
7d08e4f7d1 | |||
2ae3bda029 | |||
d5262a3350 | |||
67dc3be54a | |||
c35950de47 | |||
761af68df4 | |||
1bc815e0b1 | |||
16871ae2b4 | |||
9ed5b4d90e | |||
430a489446 | |||
b4bd70c402 | |||
b65f29f8fa | |||
65a802c678 | |||
151f9c7f82 | |||
8585e59718 | |||
ae1de6593c | |||
723074e71b | |||
80261c803a | |||
e429e2614f | |||
2478f49c50 | |||
0a82e3b75b | |||
9689a2012b | |||
02ea68f1f3 | |||
d953415d91 | |||
06a9ee74bc | |||
a96c5986c5 | |||
863785a520 | |||
e077cad333 | |||
7280057228 | |||
b27100c8fc | |||
f3a93b046e | |||
b3834d8272 | |||
01863ebff0 | |||
e4cc365e89 | |||
a26c479182 | |||
9559b53228 | |||
eb32fe20c8 | |||
fd041d91ee | |||
15ded0bea9 | |||
1549a29c9d | |||
66e309c5c4 | |||
fb528c47c0 | |||
74de0f1f2a | |||
b89d9f6e90 | |||
d02024929a | |||
d22db77248 | |||
399256b384 | |||
33a0dec8a1 | |||
2f614900e8 | |||
4db4ec1621 | |||
74ef22d824 | |||
86d1a26b13 | |||
742a9b00bc | |||
9e89dacc64 | |||
f63cb27803 | |||
18bf586d55 | |||
3dfda15ef3 | |||
954ba211bf | |||
ddefa11695 | |||
91ca5d724e | |||
4b4830692a | |||
2fb57b2ea7 | |||
96cf6fc147 | |||
8ef17c2fb1 | |||
10564723b9 | |||
369e8c4150 | |||
c0187930dc | |||
2f362509b8 | |||
8ac1b85a0b | |||
7dacd7eb78 | |||
2949990016 | |||
893e9256a0 | |||
d91357d00c | |||
0fea62ec6d | |||
a63b74e345 | |||
0b6b6b52fe | |||
250d40bca0 | |||
adabd71a4a | |||
ff27df78fc | |||
627b7c9fd7 | |||
4e6defd657 | |||
1cdf0a2c51 | |||
e68c502f7a | |||
6afc16399f | |||
3f904bf3ac | |||
cdbc3ecc2a | |||
67af300383 | |||
ccea5fa948 | |||
fcc6f183a4 | |||
997e92191d | |||
4be75b1858 | |||
444c9effdb | |||
962255b373 | |||
ded013b7a7 | |||
d9d4f63cd4 | |||
56103f0751 | |||
be9bfb5536 | |||
5ac875b097 | |||
842f2cc8a0 | |||
77fd361c62 | |||
09c7d158d1 | |||
836ed9d6b7 | |||
a9f9a59416 | |||
42d43147ca | |||
93008e279d | |||
740081e2f7 | |||
088bbc6bb7 | |||
2d00027fd9 | |||
d03ab3c976 | |||
fb90ecc8bc | |||
9131a7c65e | |||
7855a233a7 | |||
b849547116 | |||
5196f9a340 | |||
6d171ff511 | |||
732573ba51 | |||
854d6d4e5c | |||
48a99d23cd | |||
a289a77d5d | |||
3def9258be | |||
0eb08693e9 | |||
cc6ad034f1 | |||
0af0f0d890 | |||
b3c975269e | |||
c173e9f4ab | |||
15ef3388c1 | |||
d90ae4d45b | |||
b368549fd5 | |||
c44f4f32fe | |||
eaa2e8900d | |||
79c64f6bca | |||
89c442cadb | |||
55a2f35a64 | |||
b97ea0e447 | |||
b0ae61c652 | |||
793e666f36 | |||
4008ff32c9 | |||
d79387c27e | |||
4bd6003b7f | |||
7d95e8624a | |||
2b9f71c6ca | |||
d701b23230 | |||
79bc628858 | |||
ace551030f | |||
c7ee9844bd | |||
0fcc606569 | |||
07cfb7b64a | |||
0d733aa071 | |||
0283498140 | |||
fa881220ae | |||
e85f3972b9 | |||
a760ce05b9 | |||
2e5d28c73f | |||
42d2bc28af | |||
024be32f06 | |||
27290e1277 | |||
c51db4c940 | |||
3fc24013ef | |||
d6b0ab3028 | |||
7272577fe6 | |||
da50c75148 | |||
4edf7cfb05 | |||
3c78e418fb | |||
e71b198e3d | |||
4f0bda403e | |||
c215bbadf1 | |||
0f84b9c30d | |||
342cc122b4 | |||
03ce15df4c | |||
3debeb7236 | |||
2e7cf83522 | |||
8bed47a2d4 | |||
5ede1224e4 | |||
9831619881 | |||
5a2d62e4d9 | |||
42d47ecfb0 | |||
1f9894c084 | |||
27f8922653 | |||
74d7012025 | |||
5ca2938117 | |||
6fd2401cdf | |||
6e94c024e4 | |||
d1d2b660dc | |||
8e7c4f91e3 | |||
41bd38147c | |||
92ffc1cc4c | |||
d761af84c8 | |||
54f9ea14e1 | |||
7ee49c32b7 | |||
702cf5a3e1 | |||
dcf4fad971 | |||
6d69ca36a7 | |||
a45c08f9fe | |||
3c3292d505 | |||
306b5bcff3 | |||
cbd71ef8f5 | |||
32d125131f | |||
958b482ada | |||
92b16618ed | |||
a3771e7d7b | |||
06ec80f394 | |||
5d57b78471 | |||
490ca410c0 | |||
9c86e99968 | |||
1e8b54abfb | |||
20ee1ae65e | |||
eab0b2a90a | |||
194c58858c | |||
67528cf970 | |||
6774ddaba2 | |||
13cc220c0d | |||
1e4af85a38 | |||
8f91d47bf3 | |||
5d2669dbd3 | |||
93261b98c2 | |||
9f00aeae29 | |||
2762ec22d0 | |||
cd9b344506 | |||
61cc2ba7d9 | |||
dad29bcaa1 | |||
65650a5c66 | |||
db8170def3 | |||
449b9a9d68 | |||
28a146d438 | |||
6a19b62db6 | |||
a626b7ebe1 | |||
3331bb29ea | |||
16f6100500 | |||
90f63657cb | |||
0415e4a637 | |||
ed3424ff75 | |||
14c4f06100 | |||
8820d4e5ac | |||
7c0a27a0f4 | |||
7a410643ac | |||
34da3b4fa8 | |||
4a2dd306c7 | |||
09bade6466 | |||
9a93169898 | |||
017d36e6b2 | |||
1735ec0362 | |||
0ecc5c815e | |||
5ec62a5153 | |||
2784e256f1 | |||
50bc838047 | |||
69acda2c25 | |||
98f21d8973 | |||
767d24ea5d | |||
d1c89727dc | |||
3d5db7288f | |||
dc11b5c55e | |||
5c03adbded | |||
8845fb7eae | |||
5a0c4ce295 | |||
04f8c455e2 | |||
2c46bfde8b | |||
5b2e5d180f | |||
6426f3635e | |||
ee3ba0b1d6 | |||
54715586ab | |||
9010857677 | |||
ff151f9fbc | |||
5c7e96d895 | |||
04561c4ddc | |||
9688ebef52 | |||
d6753b2883 | |||
b7ff773ecf | |||
e504088b79 | |||
d52e5f7130 | |||
bea468f1e5 | |||
639f1fd339 | |||
67e5689f87 | |||
dcbd97d29c | |||
794e65b60e | |||
6cb35836a2 | |||
e4e704f480 | |||
14d13167a7 | |||
78aad9a192 | |||
b01cb2406f | |||
42bb3d8aae | |||
96ac061e68 | |||
890745e846 | |||
4d77b7face | |||
68fba4b781 | |||
239a5d3230 | |||
6fe9b4ab5e | |||
9dab7dcc3c | |||
d40cba3042 | |||
1199941475 | |||
ec040908e9 | |||
b958179263 | |||
5a2afc5754 | |||
d4300c406c | |||
4fb2905b1e | |||
b232acd04e | |||
329887df99 | |||
44d0d6abd2 | |||
0c6f1c9c3a | |||
584d1c61ec | |||
af27264140 | |||
90eb4f1939 | |||
23b5b5fa36 | |||
d5bcc01eae | |||
633027d980 | |||
d3d043dba0 | |||
db60ebbbd7 | |||
cb7ebdf821 | |||
70e1a1a4df | |||
7c41e413e4 | |||
f02602d02d | |||
90c2064640 | |||
c833c3fec8 | |||
c64629964f | |||
de2da4fd19 | |||
198e5eeab9 | |||
8baa0f84e7 | |||
5d67139206 | |||
5b8dde9602 | |||
81bc721c5c | |||
35ae9e3aa8 | |||
2f9bc2ab75 | |||
8de099ae75 | |||
40e3d2ab55 | |||
6748158ab4 | |||
e4d2d00d41 | |||
315d65280b | |||
d15952c867 | |||
1954ef47e6 | |||
fd1d0bbde7 | |||
d7e396a98c | |||
00d3935aac | |||
bb2433ca1a | |||
7238a77327 | |||
dd869064a6 | |||
7a2a63f32b | |||
28ef7d228c | |||
6151ae7db5 | |||
3889785017 | |||
7a9ff4f8d4 | |||
29f613ef84 | |||
ff5703fd9b | |||
89630d2826 | |||
550407b0ec | |||
d0959063d5 | |||
d294dedfad | |||
e798294a4b | |||
114e715e3c | |||
253c23240b | |||
0ce9003ba7 | |||
2bbc204328 | |||
92693e4459 | |||
39263b674c | |||
5f5910c603 | |||
ed276cd7c2 | |||
82272ee08a | |||
9ed0d389b2 | |||
00bb68d7d2 | |||
c23074d5ad | |||
8ddd4c4c52 | |||
5a86892ecb | |||
12972b4b65 | |||
2920795168 | |||
25314313f8 | |||
a8be0d9f48 | |||
dabaa4cce0 | |||
707d413761 | |||
4be3521727 | |||
e3b911652d | |||
5e50b50dc3 | |||
772e7e8c8d | |||
e1ea41ee9c | |||
d87857ffdb | |||
1db3e2241e | |||
d085133c6d | |||
5c1e0a6dc4 | |||
7489fb784e | |||
2eae52ebd1 | |||
79009ca074 | |||
8151858e70 | |||
6c7e58643c | |||
423beddf57 | |||
30f5642c62 | |||
e3ad2a3969 | |||
8119d77a21 | |||
d551a75c35 | |||
8935a93cf8 | |||
9d5a3f0131 | |||
a98e6a262a | |||
491925b71e | |||
853053a3b2 | |||
21e389bec5 | |||
da38faa8f7 | |||
098136b681 | |||
0ed19d9f20 | |||
39cb34850a | |||
782f780476 | |||
a243e3b858 | |||
98f1ee9442 | |||
a02edf7a93 | |||
6465e4c3fd | |||
e75f7ae330 | |||
b0dc50c2a0 | |||
6d32bef65a | |||
4141cc39d0 | |||
d8c675afbf | |||
8fe8175c78 | |||
589d27386a | |||
e58ba2fcfa | |||
9dae1a1732 | |||
0c55a11318 | |||
16e8fc7427 | |||
f58c7ac5a6 | |||
63157c798d | |||
b9e8a3e024 | |||
8e9cc36979 | |||
614624754d | |||
d890258af6 | |||
803e4807ed | |||
a4e26bf7c2 | |||
9350ecd36f | |||
8c96c5662f | |||
842d52db7b | |||
bb1641e4ec | |||
299b50a0d4 | |||
931ae0f116 | |||
7fb5e993e3 | |||
0251fae5cc | |||
0c6b41f4c9 | |||
f63f3853bb | |||
3f1f8438ed | |||
7ad073fb30 | |||
09f37bd023 | |||
8f29f6a4d4 | |||
6fcc6a2f7c | |||
0b8ba1d55b | |||
933aa63b7d | |||
80ffe1610c | |||
9104dcc29c | |||
7e6684d926 | |||
1b431f29e5 | |||
5ea7598408 | |||
731f55a05d | |||
509389be97 | |||
f911087eab | |||
c4af1340fa | |||
3bc57fe5b5 | |||
7f94bd09ae | |||
d179f31d4f | |||
fba6de834e | |||
1f79175898 | |||
2fbcfd8824 | |||
22e16f15a6 | |||
dc9c9369e0 | |||
2565a79575 | |||
8a885c2606 | |||
ca79360fd7 | |||
34c8045d5b | |||
a90ffe1af1 | |||
01e6f63e98 | |||
3621988e15 | |||
53e30f750d | |||
6656f99c54 | |||
5a0e751736 | |||
ff0f15f763 | |||
887debb055 | |||
0d77632839 | |||
8b15732c1e | |||
48bca30e61 | |||
9f62d441a7 | |||
b836267401 | |||
7b55bcf484 | |||
15d1f753f7 | |||
1d76e433f7 | |||
02d8ad030f | |||
58a0e8e3e2 | |||
dccef70728 | |||
1fbea2e438 | |||
8198fd7913 | |||
d80f999a21 | |||
5871dbaf5a | |||
b0e023e432 | |||
6d52da58d9 | |||
63883bf27d | |||
8124547348 | |||
c734dde982 | |||
cebf4e3697 | |||
5e2bf12a31 | |||
d078e9b8c9 | |||
b855e5f7df | |||
6593c69424 | |||
3a9d7d318a | |||
8a2e50ab2a | |||
9ee6295c75 | |||
1bf6f8b4a6 | |||
4d3209ad1d | |||
9ff97a98a7 | |||
1938bfcddf | |||
71ab5d52b6 | |||
e090d131c3 | |||
97cc762143 | |||
bdc206885a | |||
69044fe577 | |||
753f749423 | |||
2e6cf42011 | |||
2995d6c281 | |||
1b40f69ce5 | |||
771f64397f | |||
d8339fa29a | |||
006ac772e6 | |||
a51dfe89c0 | |||
c7d1924c34 | |||
1153fd9a0c | |||
a56f78af67 | |||
d7b882977c | |||
2010fea088 | |||
fb6ff61730 | |||
ff8a834ccc | |||
9e8127accb | |||
6b7dfa1fb5 | |||
17c825f53a | |||
b15e03acd7 | |||
e0b6a31613 | |||
f382221b28 | |||
6c91ffcfbe | |||
15e0093e13 | |||
99797858a6 | |||
8fcba0eb1e | |||
9bb7633254 | |||
e7097641e3 | |||
a2d01d6af8 | |||
2ef3a98929 | |||
4d98762486 | |||
73a42d34a5 | |||
95adac7522 | |||
76a59274bf | |||
a453b5514d | |||
8601604a09 | |||
b695c82520 | |||
a98e35d7a0 | |||
65722aeeca | |||
8278ba5e45 | |||
6e98e5709a | |||
2fec1af275 | |||
cfb979b5e3 | |||
9988b1a047 | |||
73761f7af6 | |||
1c01e9c095 | |||
98d4b51120 | |||
ab8c7252da | |||
818cbcbdd4 | |||
a428533160 | |||
006c757a03 | |||
4eb1771e67 | |||
aba3066658 | |||
c1b09d639c | |||
817def000b | |||
6e24d603a1 | |||
5cdfee5143 | |||
2232974cda | |||
aefcb83419 | |||
24a6d87c3f | |||
4c7bd75c1a | |||
b1463b2dc2 | |||
1f3f76cb09 | |||
3ebd7f1166 | |||
81ef40010f | |||
99fa9afaf1 | |||
281559d427 | |||
cc8464ce80 | |||
230aafbf66 | |||
14787ac148 | |||
4e1c6a8a22 | |||
f8f84ef095 | |||
e1b7bd51ee | |||
56c2f651fe | |||
c371f9a162 | |||
d6e398aba4 | |||
7fe73deb2d | |||
8f5eddd0ba | |||
f5852b47d1 | |||
1275e5bdc9 | |||
86cf69648e | |||
d658a7f4ab | |||
0e9c8568fd | |||
2667cb3ab6 | |||
0c27c5eb7f | |||
05e4e97276 | |||
07fe00c466 | |||
cbf221f6b7 | |||
734b2e4cf7 | |||
85793a4706 | |||
3c35ba7c31 | |||
5ceb1620e9 | |||
e8b4585295 | |||
6ef2832083 | |||
6ba534af92 | |||
47fd4833ee | |||
64701e388c | |||
27aae60381 | |||
7dae955c1a | |||
3b38df085e | |||
de87e436ab | |||
e837c9ab3f | |||
ad51c85e5d | |||
9562aba86a | |||
4b13f93a3e | |||
c37b3cef7d | |||
0c4040d191 | |||
2bd377a3de | |||
31e44c2ab9 | |||
f5d4414734 | |||
7d3e99a2ab | |||
b4e156e1d7 | |||
6a31d55b2e | |||
45b810450f | |||
f292e93e0e | |||
378815ee62 | |||
38b4dc2cdf | |||
530ab6b8fc | |||
fafdd21e4f | |||
faa57ecaef | |||
2450398862 | |||
36c9c93720 | |||
12f30e6220 | |||
fd19142c0d | |||
a2fb265563 | |||
b8034f4d9e | |||
bd48690f63 | |||
8730dfdcc2 | |||
88686cbed2 | |||
f95993e326 | |||
7bf2ae0b11 | |||
2734fc4079 | |||
6efdd21633 | |||
ad4ffdc947 | |||
65f570271c | |||
a6b9ea05e8 | |||
166853aed9 | |||
98a631b556 | |||
ff2cf2dacd | |||
3ac74b1e78 | |||
f4fa0d48cb | |||
0512113bdd | |||
7c0df348f8 | |||
0c1f732c64 | |||
283f4d8eb3 | |||
a4883a029f | |||
b2181ac814 | |||
b0798e0a72 | |||
9831ba20b2 | |||
485e04d9df | |||
86d6aba012 | |||
c9ac5b0f74 | |||
28357d657b | |||
8ec1bb382a | |||
cac9562c05 | |||
d31303a592 | |||
a9d5656a46 | |||
3647cc5b07 | |||
cc341b8734 | |||
52b664b0ae | |||
8fe0864680 | |||
5fcbaefd0b | |||
7f9fd08792 | |||
8b4ed8c505 | |||
4eb3ad192e | |||
36572f03a7 | |||
a32dffb0e8 | |||
e22e83b19a | |||
d3d9ed62e2 | |||
be7da48dad | |||
3af35d922e | |||
dbf8645aaf | |||
c03bf14e02 | |||
afe83af219 | |||
5a0bae1dae | |||
e8fb965ccb | |||
d709815106 | |||
f0440e85dc | |||
554f4f6f7d | |||
6c66cb3fa9 | |||
45ce820b11 | |||
76d7bad722 | |||
a0af7de58e | |||
050684450b | |||
e18b96b486 | |||
af6875f4b2 | |||
66af749023 | |||
2096b3a9ed | |||
fedd4c906f | |||
78cb04cca3 | |||
14a6e6a9ce | |||
3e5a7b34b1 | |||
a4ca9927ab | |||
9b1f11695d | |||
87a5e94525 | |||
b98cc2fb4e | |||
fde3e01f80 | |||
c496aad20b | |||
8adad0654a | |||
5757f5df2a | |||
39b8c83ba6 | |||
7c91159449 | |||
4f20e8f649 | |||
1f6df0cd52 | |||
69d83b1da5 | |||
e798f221dd | |||
c54788338a | |||
ebdf339a61 | |||
70c8656640 | |||
17674fb888 | |||
91aa189ef3 | |||
e6a68f0c3a | |||
d2ab322267 | |||
c3293641e7 | |||
21724f7ef9 | |||
38d6b67b5c | |||
5516efdfa0 | |||
21f8806eed | |||
bf850974f3 | |||
338b698091 | |||
16e52327a4 | |||
05d2d8f27d | |||
ca6e3f6def | |||
d3a159ad3d | |||
0f93da400a | |||
f3818478e2 | |||
ee7c16a8d9 | |||
1c85d8c66b | |||
0651af9dfd | |||
c81804444f | |||
ef7f3f36e2 | |||
f7d4e3cd6b | |||
6b08efabf8 | |||
3a9a252f6e | |||
61cd1594b5 | |||
11c26e3211 | |||
6930260962 | |||
a96c8c8af9 | |||
c5729d7ecc | |||
d811920d8b | |||
9c6aca7893 | |||
ca13e3b105 | |||
086acd122b | |||
116516158d | |||
ca747f2688 | |||
7d6ba88d2b | |||
d927c154e7 | |||
25dd46061f | |||
afc92fb7d7 | |||
891f725909 | |||
969e748dce | |||
6a530ea371 | |||
0fccbeabcc | |||
720521ed4a | |||
e09f0a5f2c | |||
4f2e9c2640 | |||
5714a82778 | |||
272b135b74 | |||
335dc9e687 | |||
03e139d23b | |||
6d28bf534f | |||
c0cad0b534 | |||
a83db489df | |||
035f0ffb8a | |||
b66a99e32d | |||
527a3bbc2a | |||
c0a030ef0a | |||
35a82f8f4a | |||
12643c7c57 | |||
90bb512f42 | |||
782910eaa7 | |||
3558dd5ed4 | |||
0a88010826 | |||
7d0348e4ba | |||
5f49a659c3 | |||
7277c42047 | |||
7cc28c8b46 | |||
205e33bc83 | |||
6625b6ffbd | |||
b888652201 | |||
60fd2f3521 | |||
75e6406c1f | |||
3c3431d111 | |||
56a58ad70d | |||
00c5f9b9a6 | |||
7660e1ed90 | |||
43cad69016 | |||
308c59320c | |||
6253d10938 | |||
e0b6091d7e | |||
ec6ec62dd4 | |||
97786d03d5 | |||
274d5cc91c | |||
6a86c517c4 | |||
01c1bce9c5 | |||
9a9e252cab | |||
b52b1fca89 | |||
2ea4c632d1 | |||
fa1db8d2dc | |||
9cf8ce9ef8 | |||
f567f89b99 | |||
7705b23f24 | |||
f3d27bf5d8 | |||
c68ff9886b | |||
f21eb88ad1 | |||
c642094cac | |||
3520771d68 | |||
c17381b853 | |||
59d8dc3950 | |||
38ea6a6d5d | |||
82d0f65dab | |||
c135b389fe | |||
bdc0d1b7ad | |||
ae837c4719 | |||
344e827061 | |||
2be2fc7974 | |||
826c827e6b | |||
2b9b02812e | |||
07578fe25f | |||
095d5baaed | |||
8ea7e21f64 | |||
3274e0a249 | |||
b15a4985e8 | |||
d5efeab8f9 | |||
54bcee512d | |||
685ea3e9a9 | |||
694ef47041 | |||
9d887234ea | |||
e2e338929f | |||
8162aff8cf | |||
ea873304ca | |||
f80984491a | |||
e7770b2332 | |||
be543a6d17 | |||
6c6e8b0fd7 | |||
79320e2868 | |||
92f2abdf76 | |||
c1de7bcec5 | |||
8577e9116e | |||
5b1613d65b | |||
c1d0ea7366 | |||
9d492b0509 | |||
bfed1c7cac | |||
d2bc57cd34 | |||
d65b4cd0dd | |||
f1b354e6aa | |||
d7c5936ac4 | |||
ceada15290 | |||
b462ca4aad | |||
601340bd46 | |||
8ee8ec8a36 | |||
7de2c7f176 | |||
839bd73fbb | |||
3f7ec1a83f | |||
8fa19664e6 | |||
9d06f9e6fb | |||
c0fcefa3a0 | |||
c9f3d1c00b | |||
179255c3fe | |||
4fad5958d0 | |||
e98b53bbef | |||
4b8c50e2cd | |||
1e7b3cbb13 | |||
ce07d9bb4c | |||
507fc7b9d1 | |||
4f4175a3e2 | |||
c7e73ba12d | |||
b30b9ab8cb | |||
0afdedb01a | |||
6c7255cde2 | |||
f2234264ed | |||
95a8ebc249 | |||
88a9c62fcc | |||
b29c1eecd1 | |||
d69db6e617 | |||
d8b5bbd48c | |||
22e2c3429b | |||
4cc5b03137 | |||
a3fb7008b2 | |||
55c1c220d0 | |||
5971c82094 | |||
1a98bbf1c8 | |||
377c995103 | |||
8ecb24f114 | |||
3a45cdeaf9 | |||
a4a4ffbeff | |||
f5737b929a | |||
c66cf95b40 | |||
73b9ae9579 | |||
4bfd717ba2 | |||
cca8585554 | |||
18cc35338a | |||
681eacaa7f | |||
b20c0b1d59 | |||
f2a1260294 | |||
06ea7fc830 | |||
ed05779adb | |||
8f69c2ac45 | |||
504d356232 | |||
059ad35215 | |||
9bc5c4a0c5 | |||
4afb624c45 | |||
39b6eaf51c | |||
d4cc125456 | |||
dc994b3518 | |||
8e7daec886 | |||
b3da104e56 | |||
0936e5ccf5 | |||
531b3a96eb | |||
3dae732f36 | |||
1e241e3a61 | |||
24f2b2afc3 | |||
357b4bc14c | |||
5adbd39946 | |||
6dac014978 | |||
4d405f6654 | |||
bbcf304b99 | |||
c866fcc5b3 | |||
d7eca7bcc1 | |||
68028f492f | |||
8629d9a418 | |||
c7623c3165 | |||
ba95849097 | |||
bb3e28310e | |||
a5b7279cb5 | |||
c5b009ba6f | |||
e72a782bf0 | |||
7413552a28 | |||
b7a636b894 | |||
e5b97fe03e | |||
2ea05292c0 | |||
e4a6ee3d7f | |||
7264044122 | |||
f1d6f1bd17 | |||
c95a27e394 | |||
066940f134 | |||
07c12f0b92 | |||
5883446b21 | |||
9bcb3d2216 | |||
424b0b8bac | |||
30ee32a725 | |||
f5fbbb147f | |||
147a74dff2 | |||
60afd02e59 | |||
fd1aa869e1 | |||
334c41d088 | |||
4a656eff7b | |||
73fd358d94 | |||
f247f0c518 | |||
f6d1bfe45b | |||
c2fb9f06ad | |||
67de76c217 | |||
02acef23d5 | |||
5a83114efd | |||
42123b4396 | |||
0ae6a38825 | |||
8ab6c53231 | |||
1d26ae2dea | |||
d2edc2bbf4 | |||
8db7d791f0 | |||
a50b4f6b11 | |||
156495732b | |||
3c4fb01da3 | |||
c00b1dd508 | |||
0de31a3898 | |||
24349bc431 | |||
1f7b13ff4e | |||
04c00f40f0 | |||
9e9b7a520e | |||
f995f5763b | |||
6292c5ad5a | |||
a9a564c226 | |||
04b6e413d9 | |||
aa9341570b | |||
f4a96ca588 | |||
48b41862ef | |||
3f503ffc7f | |||
ae0d4eb7aa | |||
cb8a7d979d | |||
b70fe3a9be | |||
dfa778fed6 | |||
8c4746a3df | |||
ce69334988 | |||
8c09602a8b | |||
da66eddfcc | |||
dfa38b3f91 | |||
36f221dbe7 | |||
7f100e9610 | |||
7ccf51fd30 | |||
3e400739a7 | |||
4a82230de5 | |||
884f792871 | |||
7931c6624c | |||
1b7cba1878 | |||
233f5200ef | |||
878e796c0a | |||
2b32f47d2c |
4
.gitignore
vendored
@ -9,6 +9,4 @@
|
|||||||
*un~
|
*un~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*/**/.DS_Store
|
*/**/.DS_Store
|
||||||
ethereum/ethereum
|
.ethtest
|
||||||
ethereal/ethereal
|
|
||||||
|
|
||||||
|
25
.travis.yml
@ -1,4 +1,25 @@
|
|||||||
before_install: sudo apt-get install libgmp3-dev
|
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.2
|
- 1.3
|
||||||
|
before_install:
|
||||||
|
- sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y
|
||||||
|
- sudo apt-get update -qq
|
||||||
|
- sudo apt-get install -yqq libgmp3-dev qtbase5-private-dev qtdeclarative5-private-dev libqt5opengl5-dev libreadline6-dev
|
||||||
|
install:
|
||||||
|
- go get code.google.com/p/go.tools/cmd/goimports
|
||||||
|
- go get github.com/golang/lint/golint
|
||||||
|
# - go get code.google.com/p/go.tools/cmd/vet
|
||||||
|
- go get code.google.com/p/go.tools/cmd/cover
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- ./install_deps.sh
|
||||||
|
before_script:
|
||||||
|
- gofmt -l -w .
|
||||||
|
- goimports -l -w .
|
||||||
|
- golint .
|
||||||
|
# - go vet ./...
|
||||||
|
# - go test -race ./...
|
||||||
|
script:
|
||||||
|
- ./gocoverage.sh && goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN
|
||||||
|
env:
|
||||||
|
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
|
||||||
|
|
||||||
|
41
Dockerfile
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
FROM ubuntu:14.04
|
||||||
|
|
||||||
|
## Environment setup
|
||||||
|
ENV HOME /root
|
||||||
|
ENV GOPATH /root/go
|
||||||
|
ENV PATH /go/bin:/root/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
|
||||||
|
|
||||||
|
RUN mkdir -p /root/go
|
||||||
|
ENV DEBIAN_FRONTEND noninteractive
|
||||||
|
|
||||||
|
## Install base dependencies
|
||||||
|
RUN apt-get update && apt-get upgrade -y
|
||||||
|
RUN apt-get install -y git mercurial build-essential software-properties-common pkg-config libgmp3-dev libreadline6-dev libpcre3-dev libpcre++-dev
|
||||||
|
|
||||||
|
## Build and install Go
|
||||||
|
RUN hg clone -u release https://code.google.com/p/go
|
||||||
|
RUN cd go && hg update go1.4
|
||||||
|
RUN cd go/src && ./all.bash && go version
|
||||||
|
|
||||||
|
## Install GUI dependencies
|
||||||
|
RUN add-apt-repository ppa:ubuntu-sdk-team/ppa -y
|
||||||
|
RUN apt-get update -y
|
||||||
|
RUN apt-get install -y qtbase5-private-dev qtdeclarative5-private-dev libqt5opengl5-dev
|
||||||
|
|
||||||
|
## Fetch and install serpent-go
|
||||||
|
RUN go get -v -d github.com/ethereum/serpent-go
|
||||||
|
WORKDIR $GOPATH/src/github.com/ethereum/serpent-go
|
||||||
|
RUN git checkout master
|
||||||
|
RUN git submodule update --init
|
||||||
|
RUN go install -v
|
||||||
|
|
||||||
|
# Fetch and install go-ethereum
|
||||||
|
RUN go get -v -d github.com/ethereum/go-ethereum/...
|
||||||
|
WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum
|
||||||
|
RUN git checkout poc8
|
||||||
|
RUN ETH_DEPS=$(go list -f '{{.Imports}} {{.TestImports}} {{.XTestImports}}' github.com/ethereum/go-ethereum/... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g'); if [ "$ETH_DEPS" ]; then go get $ETH_DEPS; fi
|
||||||
|
RUN go install -v ./cmd/ethereum
|
||||||
|
|
||||||
|
# Run JSON RPC
|
||||||
|
ENTRYPOINT ["ethereum", "-rpc=true", "-rpcport=8080"]
|
||||||
|
EXPOSE 8080
|
8
LICENSE
@ -1,16 +1,16 @@
|
|||||||
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU General Public
|
modify it under the terms of the GNU Lesser General Public
|
||||||
License as published by the Free Software Foundation; either
|
License as published by the Free Software Foundation; either
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
This library is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
General Public License for more details.
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU Lesser General Public
|
||||||
along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
MA 02110-1301 USA
|
MA 02110-1301 USA
|
||||||
|
95
README.md
@ -1,44 +1,75 @@
|
|||||||
|
[](https://waffle.io/ethereum/go-ethereum)
|
||||||
|
[](https://waffle.io/ethereum/go-ethereum)
|
||||||
|
[](http://waffle.io/ethereum/go-ethereum)
|
||||||
|
|
||||||
Ethereum
|
Ethereum
|
||||||
========
|
========
|
||||||
|
|
||||||
Master [](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-master-docker/builds/-1) Develop [](http://build.ethdev.com:8010/builders/Linux%20Go%20master%20branch/builds/-1) master [](http://cpt-obvious.ethercasts.com:8010/builders/go-ethereum-develop-docker/builds/-1)
|
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20develop%20branch/builds/-1) develop
|
||||||
|
|
||||||
Ethereum Go Client © 2014 Jeffrey Wilcke.
|
Ethereum Go Client © 2014 Jeffrey Wilcke.
|
||||||
|
|
||||||
Current state: Proof of Concept 0.6.3.
|
Current state: Proof of Concept 0.7
|
||||||
|
|
||||||
For the development package please see the [eth-go package](https://github.com/ethereum/eth-go).
|
Ethereum is currently in its testing phase.
|
||||||
|
|
||||||
Build
|
Build
|
||||||
=======
|
=====
|
||||||
|
|
||||||
To build Ethereal (GUI):
|
To build Mist (GUI):
|
||||||
|
|
||||||
`go get github.com/ethereum/go-ethereum/ethereal`
|
`go get github.com/ethereum/go-ethereum/cmd/mist`
|
||||||
|
|
||||||
To build the node (CLI):
|
To build the node (CLI):
|
||||||
|
|
||||||
`go get github.com/ethereum/go-ethereum/ethereum`
|
`go get github.com/ethereum/go-ethereum/cmd/ethereum`
|
||||||
|
|
||||||
For further, detailed, build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go))
|
For further, detailed, build instruction please see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go))
|
||||||
|
|
||||||
|
Automated (dev) builds
|
||||||
|
======================
|
||||||
|
|
||||||
|
* [[OS X](http://build.ethdev.com/builds/OSX%20Go%20develop%20branch/latest/app/)]
|
||||||
|
* [Windows] Coming soon™
|
||||||
|
* [Linux] Coming soon™
|
||||||
|
|
||||||
|
Binaries
|
||||||
|
========
|
||||||
|
|
||||||
|
Go Ethereum comes with several binaries found in
|
||||||
|
[cmd](https://github.com/ethereum/go-ethereum/tree/master/cmd):
|
||||||
|
|
||||||
|
* `mist` Official Ethereum Browser
|
||||||
|
* `ethereum` Ethereum CLI
|
||||||
|
* `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suit:
|
||||||
|
`ethtest "`cat myfile.json`"`.
|
||||||
|
* `evm` is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas
|
||||||
|
10000 -price 0 -dump`. See `-h` for a detailed description.
|
||||||
|
|
||||||
General command line options
|
General command line options
|
||||||
====================
|
============================
|
||||||
|
|
||||||
```
|
```
|
||||||
Shared between ethereum and ethereal
|
== Shared between ethereum and Mist ==
|
||||||
|
|
||||||
|
= Settings
|
||||||
-id Set the custom identifier of the client (shows up on other clients)
|
-id Set the custom identifier of the client (shows up on other clients)
|
||||||
-port Port on which the server will accept incomming connections
|
-port Port on which the server will accept incomming connections
|
||||||
-upnp Enable UPnP
|
-upnp Enable UPnP
|
||||||
-maxpeer Desired amount of peers
|
-maxpeer Desired amount of peers
|
||||||
-rpc Start JSON RPC
|
-rpc Start JSON RPC
|
||||||
|
|
||||||
-dir Data directory used to store configs and databases
|
-dir Data directory used to store configs and databases
|
||||||
-import Import a private key
|
|
||||||
-genaddr Generates a new address and private key (destructive action)
|
= Utility
|
||||||
-h This
|
-h This
|
||||||
|
-import Import a private key
|
||||||
|
-genaddr Generates a new address and private key (destructive action)
|
||||||
|
-dump Dump a specific state of a block to stdout given the -number or -hash
|
||||||
|
-difftool Supress all output and prints VM output to stdout
|
||||||
|
-diff vm=only vm output, all=all output including state storage
|
||||||
|
|
||||||
Ethereum only
|
Ethereum only
|
||||||
ethereum [options] [filename]
|
ethereum [options] [filename]
|
||||||
@ -46,31 +77,34 @@ ethereum [options] [filename]
|
|||||||
filename Load the given file and interpret as JavaScript
|
filename Load the given file and interpret as JavaScript
|
||||||
-m Start mining blocks
|
-m Start mining blocks
|
||||||
|
|
||||||
Etheral only
|
== Mist only ==
|
||||||
|
|
||||||
-asset_path absolute path to GUI assets directory
|
-asset_path absolute path to GUI assets directory
|
||||||
```
|
```
|
||||||
|
|
||||||
Contribution
|
Contribution
|
||||||
============
|
============
|
||||||
|
|
||||||
If you would like to contribute to Ethereum Go, please fork, fix, commit and
|
If you'd like to contribute to Ethereum please fork, fix, commit and
|
||||||
send a pull request to the main repository. Commits which do not comply with the coding standards explained below
|
send a pull request. Commits who do not comply with the coding standards
|
||||||
will be ignored. If you send a pull request, make sure that you
|
are ignored (use gofmt!). If you send pull requests make absolute sure that you
|
||||||
commit to the `develop` branch and that you do not merge to `master`.
|
commit on the `develop` branch and that you do not merge to master.
|
||||||
Commits that are directly based off of the `master` branch instead of the `develop` branch will be ignored.
|
Commits that are directly based on master are simply ignored.
|
||||||
|
|
||||||
To make this process simpler try following the [git flow](http://nvie.com/posts/a-successful-git-branching-model/) branching model, as it sets this process up and streamlines work flow.
|
To make life easier try [git flow](http://nvie.com/posts/a-successful-git-branching-model/) it sets
|
||||||
|
this all up and streamlines your work flow.
|
||||||
|
|
||||||
Coding standards
|
Coding standards
|
||||||
================
|
================
|
||||||
|
|
||||||
Code should be formatted according to the [Go Formatting
|
Sources should be formatted according to the [Go Formatting
|
||||||
Style](http://golang.org/doc/effective_go.html#formatting).
|
Style](http://golang.org/doc/effective_go.html#formatting).
|
||||||
|
|
||||||
Unless struct fields are supposed to be directly accessible, provide
|
Unless structs fields are supposed to be directly accesible, provide
|
||||||
getters and hide the fields through Go's exporting facility.
|
Getters and hide the fields through Go's exporting facility.
|
||||||
|
|
||||||
Make comments in your code meaningful and only use them when necessary. Describe in detail what your code is trying to achieve. For example, this would be redundant and unnecessary commenting:
|
When you comment put meaningfull comments. Describe in detail what you
|
||||||
|
want to achieve.
|
||||||
|
|
||||||
*wrong*
|
*wrong*
|
||||||
|
|
||||||
@ -81,7 +115,12 @@ if x > y {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Everyone reading the source code should know what this code snippet was meant to achieve, and so those are **not** meaningful comments.
|
Everyone reading the source probably know what you wanted to achieve
|
||||||
|
with above code. Those are **not** meaningful comments.
|
||||||
|
|
||||||
While this project is constantly tested and run, code tests should be written regardless. There is not time to evaluate every person's code specifically, so it is expected of you to write tests for the code so that it does not have to be tested manually. In fact, contributing by simply writing tests is perfectly fine!
|
While the project isn't 100% tested I want you to write tests non the
|
||||||
|
less. I haven't got time to evaluate everyone's code in detail so I
|
||||||
|
expect you to write tests for me so I don't have to test your code
|
||||||
|
manually. (If you want to contribute by just writing tests that's fine
|
||||||
|
too!)
|
||||||
|
|
||||||
|
BIN
_data/chain1
Executable file
BIN
_data/chain2
Executable file
351
block_pool.go
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
package eth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"container/list"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var poollogger = logger.NewLogger("BPOOL")
|
||||||
|
|
||||||
|
type block struct {
|
||||||
|
from *Peer
|
||||||
|
peer *Peer
|
||||||
|
block *types.Block
|
||||||
|
reqAt time.Time
|
||||||
|
requested int
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockPool struct {
|
||||||
|
mut sync.Mutex
|
||||||
|
|
||||||
|
eth *Ethereum
|
||||||
|
|
||||||
|
hashes [][]byte
|
||||||
|
pool map[string]*block
|
||||||
|
|
||||||
|
td *big.Int
|
||||||
|
quit chan bool
|
||||||
|
|
||||||
|
fetchingHashes bool
|
||||||
|
downloadStartedAt time.Time
|
||||||
|
|
||||||
|
ChainLength, BlocksProcessed int
|
||||||
|
|
||||||
|
peer *Peer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockPool(eth *Ethereum) *BlockPool {
|
||||||
|
return &BlockPool{
|
||||||
|
eth: eth,
|
||||||
|
pool: make(map[string]*block),
|
||||||
|
td: ethutil.Big0,
|
||||||
|
quit: make(chan bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) Len() int {
|
||||||
|
return len(self.hashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) Reset() {
|
||||||
|
self.pool = make(map[string]*block)
|
||||||
|
self.hashes = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) HasLatestHash() bool {
|
||||||
|
self.mut.Lock()
|
||||||
|
defer self.mut.Unlock()
|
||||||
|
|
||||||
|
return self.pool[string(self.eth.ChainManager().CurrentBlock().Hash())] != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) HasCommonHash(hash []byte) bool {
|
||||||
|
return self.eth.ChainManager().GetBlock(hash) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) Blocks() (blocks types.Blocks) {
|
||||||
|
for _, item := range self.pool {
|
||||||
|
if item.block != nil {
|
||||||
|
blocks = append(blocks, item.block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) FetchHashes(peer *Peer) bool {
|
||||||
|
highestTd := self.eth.HighestTDPeer()
|
||||||
|
|
||||||
|
if (self.peer == nil && peer.td.Cmp(highestTd) >= 0) || (self.peer != nil && peer.td.Cmp(self.peer.td) > 0) || self.peer == peer {
|
||||||
|
if self.peer != peer {
|
||||||
|
poollogger.Infof("Found better suitable peer (%v vs %v)\n", self.td, peer.td)
|
||||||
|
|
||||||
|
if self.peer != nil {
|
||||||
|
self.peer.doneFetchingHashes = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.peer = peer
|
||||||
|
self.td = peer.td
|
||||||
|
|
||||||
|
if !self.HasLatestHash() {
|
||||||
|
self.fetchHashes()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) fetchHashes() {
|
||||||
|
peer := self.peer
|
||||||
|
|
||||||
|
peer.doneFetchingHashes = false
|
||||||
|
|
||||||
|
const amount = 256
|
||||||
|
peerlogger.Debugf("Fetching hashes (%d) %x...\n", amount, peer.lastReceivedHash[0:4])
|
||||||
|
peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{peer.lastReceivedHash, uint32(amount)}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) AddHash(hash []byte, peer *Peer) {
|
||||||
|
self.mut.Lock()
|
||||||
|
defer self.mut.Unlock()
|
||||||
|
|
||||||
|
if self.pool[string(hash)] == nil {
|
||||||
|
self.pool[string(hash)] = &block{peer, nil, nil, time.Now(), 0}
|
||||||
|
|
||||||
|
self.hashes = append([][]byte{hash}, self.hashes...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) Add(b *types.Block, peer *Peer) {
|
||||||
|
self.addBlock(b, peer, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) AddNew(b *types.Block, peer *Peer) {
|
||||||
|
self.addBlock(b, peer, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) addBlock(b *types.Block, peer *Peer, newBlock bool) {
|
||||||
|
self.mut.Lock()
|
||||||
|
defer self.mut.Unlock()
|
||||||
|
|
||||||
|
hash := string(b.Hash())
|
||||||
|
|
||||||
|
if self.pool[hash] == nil && !self.eth.ChainManager().HasBlock(b.Hash()) {
|
||||||
|
poollogger.Infof("Got unrequested block (%x...)\n", hash[0:4])
|
||||||
|
|
||||||
|
self.hashes = append(self.hashes, b.Hash())
|
||||||
|
self.pool[hash] = &block{peer, peer, b, time.Now(), 0}
|
||||||
|
|
||||||
|
// The following is only performed on an unrequested new block
|
||||||
|
if newBlock {
|
||||||
|
fmt.Println("1.", !self.eth.ChainManager().HasBlock(b.PrevHash), ethutil.Bytes2Hex(b.Hash()[0:4]), ethutil.Bytes2Hex(b.PrevHash[0:4]))
|
||||||
|
fmt.Println("2.", self.pool[string(b.PrevHash)] == nil)
|
||||||
|
fmt.Println("3.", !self.fetchingHashes)
|
||||||
|
if !self.eth.ChainManager().HasBlock(b.PrevHash) /*&& self.pool[string(b.PrevHash)] == nil*/ && !self.fetchingHashes {
|
||||||
|
poollogger.Infof("Unknown chain, requesting (%x...)\n", b.PrevHash[0:4])
|
||||||
|
peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{b.Hash(), uint32(256)}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if self.pool[hash] != nil {
|
||||||
|
self.pool[hash].block = b
|
||||||
|
}
|
||||||
|
|
||||||
|
self.BlocksProcessed++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) Remove(hash []byte) {
|
||||||
|
self.mut.Lock()
|
||||||
|
defer self.mut.Unlock()
|
||||||
|
|
||||||
|
self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash)
|
||||||
|
delete(self.pool, string(hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) DistributeHashes() {
|
||||||
|
self.mut.Lock()
|
||||||
|
defer self.mut.Unlock()
|
||||||
|
|
||||||
|
var (
|
||||||
|
peerLen = self.eth.peers.Len()
|
||||||
|
amount = 256 * peerLen
|
||||||
|
dist = make(map[*Peer][][]byte)
|
||||||
|
)
|
||||||
|
|
||||||
|
num := int(math.Min(float64(amount), float64(len(self.pool))))
|
||||||
|
for i, j := 0, 0; i < len(self.hashes) && j < num; i++ {
|
||||||
|
hash := self.hashes[i]
|
||||||
|
item := self.pool[string(hash)]
|
||||||
|
|
||||||
|
if item != nil && item.block == nil {
|
||||||
|
var peer *Peer
|
||||||
|
lastFetchFailed := time.Since(item.reqAt) > 5*time.Second
|
||||||
|
|
||||||
|
// Handle failed requests
|
||||||
|
if lastFetchFailed && item.requested > 5 && item.peer != nil {
|
||||||
|
if item.requested < 100 {
|
||||||
|
// Select peer the hash was retrieved off
|
||||||
|
peer = item.from
|
||||||
|
} else {
|
||||||
|
// Remove it
|
||||||
|
self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash)
|
||||||
|
delete(self.pool, string(hash))
|
||||||
|
}
|
||||||
|
} else if lastFetchFailed || item.peer == nil {
|
||||||
|
// Find a suitable, available peer
|
||||||
|
eachPeer(self.eth.peers, func(p *Peer, v *list.Element) {
|
||||||
|
if peer == nil && len(dist[p]) < amount/peerLen && p.statusKnown {
|
||||||
|
peer = p
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if peer != nil {
|
||||||
|
item.reqAt = time.Now()
|
||||||
|
item.peer = peer
|
||||||
|
item.requested++
|
||||||
|
|
||||||
|
dist[peer] = append(dist[peer], hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for peer, hashes := range dist {
|
||||||
|
peer.FetchBlocks(hashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dist) > 0 {
|
||||||
|
self.downloadStartedAt = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) Start() {
|
||||||
|
go self.downloadThread()
|
||||||
|
go self.chainThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) Stop() {
|
||||||
|
close(self.quit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) downloadThread() {
|
||||||
|
serviceTimer := time.NewTicker(100 * time.Millisecond)
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-self.quit:
|
||||||
|
break out
|
||||||
|
case <-serviceTimer.C:
|
||||||
|
// Check if we're catching up. If not distribute the hashes to
|
||||||
|
// the peers and download the blockchain
|
||||||
|
self.fetchingHashes = false
|
||||||
|
eachPeer(self.eth.peers, func(p *Peer, v *list.Element) {
|
||||||
|
if p.statusKnown && p.FetchingHashes() {
|
||||||
|
self.fetchingHashes = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(self.hashes) > 0 {
|
||||||
|
self.DistributeHashes()
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ChainLength < len(self.hashes) {
|
||||||
|
self.ChainLength = len(self.hashes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.peer != nil &&
|
||||||
|
!self.peer.doneFetchingHashes &&
|
||||||
|
time.Since(self.peer.lastHashAt) > 10*time.Second &&
|
||||||
|
time.Since(self.peer.lastHashRequestedAt) > 5*time.Second {
|
||||||
|
self.fetchHashes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if !self.fetchingHashes {
|
||||||
|
blocks := self.Blocks()
|
||||||
|
chain.BlockBy(chain.Number).Sort(blocks)
|
||||||
|
|
||||||
|
if len(blocks) > 0 {
|
||||||
|
if !self.eth.ChainManager().HasBlock(b.PrevHash) && self.pool[string(b.PrevHash)] == nil && !self.fetchingHashes {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockPool) chainThread() {
|
||||||
|
procTimer := time.NewTicker(500 * time.Millisecond)
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-self.quit:
|
||||||
|
break out
|
||||||
|
case <-procTimer.C:
|
||||||
|
blocks := self.Blocks()
|
||||||
|
types.BlockBy(types.Number).Sort(blocks)
|
||||||
|
|
||||||
|
// Find common block
|
||||||
|
for i, block := range blocks {
|
||||||
|
if self.eth.ChainManager().HasBlock(block.PrevHash) {
|
||||||
|
blocks = blocks[i:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(blocks) > 0 {
|
||||||
|
if self.eth.ChainManager().HasBlock(blocks[0].PrevHash) {
|
||||||
|
for i, block := range blocks[1:] {
|
||||||
|
// NOTE: The Ith element in this loop refers to the previous block in
|
||||||
|
// outer "blocks"
|
||||||
|
if bytes.Compare(block.PrevHash, blocks[i].Hash()) != 0 {
|
||||||
|
blocks = blocks[:i]
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blocks = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(blocks) > 0 {
|
||||||
|
chainman := self.eth.ChainManager()
|
||||||
|
|
||||||
|
err := chainman.InsertChain(blocks)
|
||||||
|
if err != nil {
|
||||||
|
poollogger.Debugln(err)
|
||||||
|
|
||||||
|
self.Reset()
|
||||||
|
|
||||||
|
if self.peer != nil && self.peer.conn != nil {
|
||||||
|
poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr())
|
||||||
|
}
|
||||||
|
|
||||||
|
// This peer gave us bad hashes and made us fetch a bad chain, therefor he shall be punished.
|
||||||
|
self.eth.BlacklistPeer(self.peer)
|
||||||
|
self.peer.StopWithReason(DiscBadPeer)
|
||||||
|
self.td = ethutil.Big0
|
||||||
|
self.peer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, block := range blocks {
|
||||||
|
self.Remove(block.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
cmd/LICENSE
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
|
||||||
|
This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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 this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
MA 02110-1301 USA
|
52
cmd/ethereum/cmd.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/ethereum/repl"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/javascript"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitJsConsole(ethereum *eth.Ethereum) {
|
||||||
|
repl := ethrepl.NewJSRepl(ethereum)
|
||||||
|
go repl.Start()
|
||||||
|
utils.RegisterInterrupt(func(os.Signal) {
|
||||||
|
repl.Stop()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecJsFile(ethereum *eth.Ethereum, InputFile string) {
|
||||||
|
file, err := os.Open(InputFile)
|
||||||
|
if err != nil {
|
||||||
|
clilogger.Fatalln(err)
|
||||||
|
}
|
||||||
|
content, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
clilogger.Fatalln(err)
|
||||||
|
}
|
||||||
|
re := javascript.NewJSRE(ethereum)
|
||||||
|
utils.RegisterInterrupt(func(os.Signal) {
|
||||||
|
re.Stop()
|
||||||
|
})
|
||||||
|
re.Run(string(content))
|
||||||
|
}
|
@ -1,45 +1,70 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Identifier string
|
var (
|
||||||
var KeyRing string
|
Identifier string
|
||||||
var DiffTool bool
|
KeyRing string
|
||||||
var DiffType string
|
DiffTool bool
|
||||||
var KeyStore string
|
DiffType string
|
||||||
var StartRpc bool
|
KeyStore string
|
||||||
var RpcPort int
|
StartRpc bool
|
||||||
var UseUPnP bool
|
StartWebSockets bool
|
||||||
var OutboundPort string
|
RpcPort int
|
||||||
var ShowGenesis bool
|
UseUPnP bool
|
||||||
var AddPeer string
|
OutboundPort string
|
||||||
var MaxPeer int
|
ShowGenesis bool
|
||||||
var GenAddr bool
|
AddPeer string
|
||||||
var UseSeed bool
|
MaxPeer int
|
||||||
var SecretFile string
|
GenAddr bool
|
||||||
var ExportDir string
|
UseSeed bool
|
||||||
var NonInteractive bool
|
SecretFile string
|
||||||
var Datadir string
|
ExportDir string
|
||||||
var LogFile string
|
NonInteractive bool
|
||||||
var ConfigFile string
|
Datadir string
|
||||||
var DebugFile string
|
LogFile string
|
||||||
var LogLevel int
|
ConfigFile string
|
||||||
var Dump bool
|
DebugFile string
|
||||||
var DumpHash string
|
LogLevel int
|
||||||
var DumpNumber int
|
Dump bool
|
||||||
|
DumpHash string
|
||||||
|
DumpNumber int
|
||||||
|
VmType int
|
||||||
|
)
|
||||||
|
|
||||||
// flags specific to cli client
|
// flags specific to cli client
|
||||||
var StartMining bool
|
var (
|
||||||
var StartJsConsole bool
|
StartMining bool
|
||||||
var InputFile string
|
StartJsConsole bool
|
||||||
|
InputFile string
|
||||||
|
)
|
||||||
|
|
||||||
func defaultDataDir() string {
|
func defaultDataDir() string {
|
||||||
usr, _ := user.Current()
|
usr, _ := user.Current()
|
||||||
@ -54,14 +79,16 @@ func Init() {
|
|||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flag.IntVar(&VmType, "vm", 0, "Virtual Machine type: 0-1: standard, debug")
|
||||||
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
|
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
|
||||||
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
|
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
|
||||||
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
||||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
||||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||||
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||||
|
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
||||||
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
||||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
||||||
@ -71,9 +98,10 @@ func Init() {
|
|||||||
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
|
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
|
||||||
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
|
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
|
||||||
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
|
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
|
||||||
flag.IntVar(&LogLevel, "loglevel", int(ethlog.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
|
flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
|
||||||
flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
|
flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
|
||||||
flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
|
flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
|
||||||
|
flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block")
|
||||||
|
|
||||||
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
|
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
|
||||||
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
|
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
|
||||||
@ -84,5 +112,9 @@ func Init() {
|
|||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if VmType >= int(vm.MaxVmTy) {
|
||||||
|
log.Fatal("Invalid VM type ", VmType)
|
||||||
|
}
|
||||||
|
|
||||||
InputFile = flag.Arg(0)
|
InputFile = flag.Arg(0)
|
||||||
}
|
}
|
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -5,18 +22,18 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/utils"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ClientIdentifier = "Ethereum(G)"
|
ClientIdentifier = "Ethereum(G)"
|
||||||
Version = "0.6.3"
|
Version = "0.7.10"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger = ethlog.NewLogger("CLI")
|
var clilogger = logger.NewLogger("CLI")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
@ -31,7 +48,7 @@ func main() {
|
|||||||
LogLevel = 0
|
LogLevel = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.InitConfig(ConfigFile, Datadir, "ETH")
|
utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
|
||||||
ethutil.Config.Diff = DiffTool
|
ethutil.Config.Diff = DiffTool
|
||||||
ethutil.Config.DiffType = DiffType
|
ethutil.Config.DiffType = DiffType
|
||||||
|
|
||||||
@ -40,6 +57,12 @@ func main() {
|
|||||||
utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
|
utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
|
||||||
|
|
||||||
db := utils.NewDatabase()
|
db := utils.NewDatabase()
|
||||||
|
err := utils.DBSanityCheck(db)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
|
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
|
||||||
|
|
||||||
@ -51,14 +74,14 @@ func main() {
|
|||||||
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
|
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
|
||||||
|
|
||||||
if Dump {
|
if Dump {
|
||||||
var block *ethchain.Block
|
var block *types.Block
|
||||||
|
|
||||||
if len(DumpHash) == 0 && DumpNumber == -1 {
|
if len(DumpHash) == 0 && DumpNumber == -1 {
|
||||||
block = ethereum.BlockChain().CurrentBlock
|
block = ethereum.ChainManager().CurrentBlock()
|
||||||
} else if len(DumpHash) > 0 {
|
} else if len(DumpHash) > 0 {
|
||||||
block = ethereum.BlockChain().GetBlock(ethutil.Hex2Bytes(DumpHash))
|
block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash))
|
||||||
} else {
|
} else {
|
||||||
block = ethereum.BlockChain().GetBlockByNumber(uint64(DumpNumber))
|
block = ethereum.ChainManager().GetBlockByNumber(uint64(DumpNumber))
|
||||||
}
|
}
|
||||||
|
|
||||||
if block == nil {
|
if block == nil {
|
||||||
@ -70,9 +93,14 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// block.GetRoot() does not exist
|
||||||
|
//fmt.Printf("RLP: %x\nstate: %x\nhash: %x\n", ethutil.Rlp(block), block.GetRoot(), block.Hash())
|
||||||
|
|
||||||
// Leave the Println. This needs clean output for piping
|
// Leave the Println. This needs clean output for piping
|
||||||
fmt.Printf("%s\n", block.State().Dump())
|
fmt.Printf("%s\n", block.State().Dump())
|
||||||
|
|
||||||
|
fmt.Println(block)
|
||||||
|
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,9 +123,13 @@ func main() {
|
|||||||
utils.StartRpc(ethereum, RpcPort)
|
utils.StartRpc(ethereum, RpcPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if StartWebSockets {
|
||||||
|
utils.StartWebSockets(ethereum)
|
||||||
|
}
|
||||||
|
|
||||||
utils.StartEthereum(ethereum, UseSeed)
|
utils.StartEthereum(ethereum, UseSeed)
|
||||||
|
|
||||||
// this blocks the thread
|
// this blocks the thread
|
||||||
ethereum.WaitForShutdown()
|
ethereum.WaitForShutdown()
|
||||||
ethlog.Flush()
|
logger.Flush()
|
||||||
}
|
}
|
97
cmd/ethereum/repl/console_colors_windows.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
/* Inspired by https://github.com/xuyu/logging/blob/master/colorful_win.go */
|
||||||
|
|
||||||
|
package ethrepl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type color uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
green = color(0x0002)
|
||||||
|
red = color(0x0004)
|
||||||
|
yellow = color(0x000E)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mask = uint16(yellow | green | red)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
procGetStdHandle = kernel32.NewProc("GetStdHandle")
|
||||||
|
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||||
|
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||||
|
hStdout uintptr
|
||||||
|
initScreenInfo *consoleScreenBufferInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
|
||||||
|
ret, _, _ := procSetConsoleTextAttribute.Call(hConsoleOutput, uintptr(wAttributes))
|
||||||
|
return ret != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type coord struct {
|
||||||
|
X, Y int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type smallRect struct {
|
||||||
|
Left, Top, Right, Bottom int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type consoleScreenBufferInfo struct {
|
||||||
|
DwSize coord
|
||||||
|
DwCursorPosition coord
|
||||||
|
WAttributes uint16
|
||||||
|
SrWindow smallRect
|
||||||
|
DwMaximumWindowSize coord
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
|
||||||
|
var csbi consoleScreenBufferInfo
|
||||||
|
ret, _, _ := procGetConsoleScreenBufferInfo.Call(hConsoleOutput, uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
if ret == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &csbi
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
stdOutputHandle = uint32(-11 & 0xFFFFFFFF)
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
hStdout, _, _ = procGetStdHandle.Call(uintptr(stdOutputHandle))
|
||||||
|
initScreenInfo = getConsoleScreenBufferInfo(hStdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetColorful() {
|
||||||
|
if initScreenInfo == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setConsoleTextAttribute(hStdout, initScreenInfo.WAttributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeColor(c color) {
|
||||||
|
attr := uint16(0) & ^mask | uint16(c)
|
||||||
|
setConsoleTextAttribute(hStdout, attr)
|
||||||
|
}
|
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
package ethrepl
|
package ethrepl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -7,13 +24,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
|
||||||
"github.com/ethereum/go-ethereum/javascript"
|
"github.com/ethereum/go-ethereum/javascript"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger = ethlog.NewLogger("REPL")
|
var repllogger = logger.NewLogger("REPL")
|
||||||
|
|
||||||
type Repl interface {
|
type Repl interface {
|
||||||
Start()
|
Start()
|
||||||
@ -42,7 +59,7 @@ func NewJSRepl(ethereum *eth.Ethereum) *JSRepl {
|
|||||||
func (self *JSRepl) Start() {
|
func (self *JSRepl) Start() {
|
||||||
if !self.running {
|
if !self.running {
|
||||||
self.running = true
|
self.running = true
|
||||||
logger.Infoln("init JS Console")
|
repllogger.Infoln("init JS Console")
|
||||||
reader := bufio.NewReader(self.history)
|
reader := bufio.NewReader(self.history)
|
||||||
for {
|
for {
|
||||||
line, err := reader.ReadString('\n')
|
line, err := reader.ReadString('\n')
|
||||||
@ -63,7 +80,7 @@ func (self *JSRepl) Stop() {
|
|||||||
if self.running {
|
if self.running {
|
||||||
self.running = false
|
self.running = false
|
||||||
self.re.Stop()
|
self.re.Stop()
|
||||||
logger.Infoln("exit JS Console")
|
repllogger.Infoln("exit JS Console")
|
||||||
self.history.Close()
|
self.history.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
package ethrepl
|
package ethrepl
|
||||||
|
|
||||||
// #cgo darwin CFLAGS: -I/usr/local/opt/readline/include
|
// #cgo darwin CFLAGS: -I/usr/local/opt/readline/include
|
||||||
@ -9,6 +26,7 @@ package ethrepl
|
|||||||
// #include <readline/history.h>
|
// #include <readline/history.h>
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
@ -118,6 +136,9 @@ func (self *JSRepl) PrintValue(v interface{}) {
|
|||||||
method, _ := self.re.Vm.Get("prettyPrint")
|
method, _ := self.re.Vm.Get("prettyPrint")
|
||||||
v, err := self.re.Vm.ToValue(v)
|
v, err := self.re.Vm.ToValue(v)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
method.Call(method, v)
|
val, err := method.Call(method, v)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("%v", val)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
92
cmd/ethereum/repl/repl_windows.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
package ethrepl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *JSRepl) read() {
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
for {
|
||||||
|
fmt.Printf(self.prompt)
|
||||||
|
str, _, err := reader.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading input", err)
|
||||||
|
} else {
|
||||||
|
if string(str) == "exit" {
|
||||||
|
self.Stop()
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
self.parseInput(string(str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addHistory(s string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func printColored(outputVal string) {
|
||||||
|
for outputVal != "" {
|
||||||
|
codePart := ""
|
||||||
|
if strings.HasPrefix(outputVal, "\033[32m") {
|
||||||
|
codePart = "\033[32m"
|
||||||
|
changeColor(2)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(outputVal, "\033[1m\033[30m") {
|
||||||
|
codePart = "\033[1m\033[30m"
|
||||||
|
changeColor(8)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(outputVal, "\033[31m") {
|
||||||
|
codePart = "\033[31m"
|
||||||
|
changeColor(red)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(outputVal, "\033[35m") {
|
||||||
|
codePart = "\033[35m"
|
||||||
|
changeColor(5)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(outputVal, "\033[0m") {
|
||||||
|
codePart = "\033[0m"
|
||||||
|
resetColorful()
|
||||||
|
}
|
||||||
|
textPart := outputVal[len(codePart):len(outputVal)]
|
||||||
|
index := strings.Index(textPart, "\033")
|
||||||
|
if index == -1 {
|
||||||
|
outputVal = ""
|
||||||
|
} else {
|
||||||
|
outputVal = textPart[index:len(textPart)]
|
||||||
|
textPart = textPart[0:index]
|
||||||
|
}
|
||||||
|
fmt.Printf("%v", textPart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *JSRepl) PrintValue(v interface{}) {
|
||||||
|
method, _ := self.re.Vm.Get("prettyPrint")
|
||||||
|
v, err := self.re.Vm.ToValue(v)
|
||||||
|
if err == nil {
|
||||||
|
val, err := method.Call(method, v)
|
||||||
|
if err == nil {
|
||||||
|
printColored(fmt.Sprintf("%v", val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
cmd/ethtest/main.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
This file is part of go-ethereum
|
||||||
|
|
||||||
|
go-ethereum 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.
|
||||||
|
|
||||||
|
go-ethereum 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 go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @authors:
|
||||||
|
* Jeffrey Wilcke <i@jev.io>
|
||||||
|
* @date 2014
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/tests/helper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
Balance string
|
||||||
|
Code string
|
||||||
|
Nonce string
|
||||||
|
Storage map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func StateObjectFromAccount(addr string, account Account) *state.StateObject {
|
||||||
|
obj := state.NewStateObject(ethutil.Hex2Bytes(addr))
|
||||||
|
obj.SetBalance(ethutil.Big(account.Balance))
|
||||||
|
|
||||||
|
if ethutil.IsHex(account.Code) {
|
||||||
|
account.Code = account.Code[2:]
|
||||||
|
}
|
||||||
|
obj.Code = ethutil.Hex2Bytes(account.Code)
|
||||||
|
obj.Nonce = ethutil.Big(account.Nonce).Uint64()
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
type VmTest struct {
|
||||||
|
Callcreates interface{}
|
||||||
|
Env map[string]string
|
||||||
|
Exec map[string]string
|
||||||
|
Gas string
|
||||||
|
Out string
|
||||||
|
Post map[string]Account
|
||||||
|
Pre map[string]Account
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunVmTest(js string) (failed int) {
|
||||||
|
tests := make(map[string]VmTest)
|
||||||
|
|
||||||
|
data, _ := ioutil.ReadAll(strings.NewReader(js))
|
||||||
|
err := json.Unmarshal(data, &tests)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
state := state.New(helper.NewTrie())
|
||||||
|
for addr, account := range test.Pre {
|
||||||
|
obj := StateObjectFromAccount(addr, account)
|
||||||
|
state.SetStateObject(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, _, gas, err := helper.RunVm(state, test.Env, test.Exec)
|
||||||
|
// When an error is returned it doesn't always mean the tests fails.
|
||||||
|
// Have to come up with some conditional failing mechanism.
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rexp := helper.FromHex(test.Out)
|
||||||
|
if bytes.Compare(rexp, ret) != 0 {
|
||||||
|
log.Printf("%s's return failed. Expected %x, got %x\n", name, rexp, ret)
|
||||||
|
failed = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
gexp := ethutil.Big(test.Gas)
|
||||||
|
if gexp.Cmp(gas) != 0 {
|
||||||
|
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
|
||||||
|
failed = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr, account := range test.Post {
|
||||||
|
obj := state.GetStateObject(helper.FromHex(addr))
|
||||||
|
for addr, value := range account.Storage {
|
||||||
|
v := obj.GetState(helper.FromHex(addr)).Bytes()
|
||||||
|
vexp := helper.FromHex(value)
|
||||||
|
|
||||||
|
if bytes.Compare(v, vexp) != 0 {
|
||||||
|
log.Printf("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address()[0:4], addr, vexp, v, ethutil.BigD(vexp), ethutil.BigD(v))
|
||||||
|
failed = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
helper.Logger.SetLogLevel(5)
|
||||||
|
if len(os.Args) == 1 {
|
||||||
|
log.Fatalln("no json supplied")
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(RunVmTest(os.Args[1]))
|
||||||
|
}
|
1
cmd/evm/code.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
60006102ff5360003560001a60008114156103395760013560405260216040516020025990590160009052606052604051602002816060513760405160200281019050506002604051121561005957604051602002606051f35b604051602002599059016000905260a052600060c052604051602002599059016000905260e0526000610100526001610120525b604051610120511215610109576060515161012051602002606051015112156100d8576101205160200260605101516101005160200260e051015260016101005101610100526100f9565b61012051602002606051015160c05160200260a0510152600160c0510160c0525b600161012051016101205261008d565b60216020599059016000905260c051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260a0518260005b8381101561016657808301518186015260208101905061014b565b50505050825160200281019050604059905901600090526102405281610240515283602061024051015261024051905090509050905060c05160200280599059016000905281816020850151855160003060195a03f1508090509050905060a05260216020599059016000905261010051808252806020028301925050602082015990590160009052600081538151600182015260218101825160200260e0518260005b8381101561022557808301518186015260208101905061020a565b50505050825160200281019050604059905901600090526102c052816102c051528360206102c05101526102c05190509050905090506101005160200280599059016000905281816020850151855160003060195a03f1508090509050905060e05260405160200259905901600090526102e0526000610120525b610100516101205112156102d7576101205160200260e0510151610120516020026102e051015260016101205101610120526102a0565b60605151610100516020026102e05101526000610120525b60c05161012051121561032d576101205160200260a05101516101205160016101005101016020026102e051015260016101205101610120526102ef565b6040516020026102e051f35b50
|
1
cmd/evm/input.txt
Normal file
164
cmd/evm/main.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
This file is part of go-ethereum
|
||||||
|
|
||||||
|
go-ethereum 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.
|
||||||
|
|
||||||
|
go-ethereum 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 go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @authors
|
||||||
|
* Jeffrey Wilcke <i@jev.io>
|
||||||
|
* @date 2014
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
code = flag.String("code", "", "evm code")
|
||||||
|
loglevel = flag.Int("log", 4, "log level")
|
||||||
|
gas = flag.String("gas", "1000000000", "gas amount")
|
||||||
|
price = flag.String("price", "0", "gas price")
|
||||||
|
value = flag.String("value", "0", "tx value")
|
||||||
|
dump = flag.Bool("dump", false, "dump state after run")
|
||||||
|
data = flag.String("data", "", "data")
|
||||||
|
)
|
||||||
|
|
||||||
|
func perr(v ...interface{}) {
|
||||||
|
fmt.Println(v...)
|
||||||
|
//os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel)))
|
||||||
|
|
||||||
|
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
|
||||||
|
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
statedb := state.New(trie.New(db, ""))
|
||||||
|
sender := statedb.NewStateObject([]byte("sender"))
|
||||||
|
receiver := statedb.NewStateObject([]byte("receiver"))
|
||||||
|
//receiver.SetCode([]byte(*code))
|
||||||
|
receiver.SetCode(ethutil.Hex2Bytes(*code))
|
||||||
|
|
||||||
|
vmenv := NewEnv(statedb, []byte("evmuser"), ethutil.Big(*value))
|
||||||
|
|
||||||
|
tstart := time.Now()
|
||||||
|
|
||||||
|
ret, e := vmenv.Call(sender, receiver.Address(), ethutil.Hex2Bytes(*data), ethutil.Big(*gas), ethutil.Big(*price), ethutil.Big(*value))
|
||||||
|
|
||||||
|
logger.Flush()
|
||||||
|
if e != nil {
|
||||||
|
perr(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *dump {
|
||||||
|
fmt.Println(string(statedb.Dump()))
|
||||||
|
}
|
||||||
|
|
||||||
|
var mem runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&mem)
|
||||||
|
fmt.Printf("vm took %v\n", time.Since(tstart))
|
||||||
|
fmt.Printf(`alloc: %d
|
||||||
|
tot alloc: %d
|
||||||
|
no. malloc: %d
|
||||||
|
heap alloc: %d
|
||||||
|
heap objs: %d
|
||||||
|
num gc: %d
|
||||||
|
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
|
||||||
|
|
||||||
|
fmt.Printf("%x\n", ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
type VMEnv struct {
|
||||||
|
state *state.StateDB
|
||||||
|
block *types.Block
|
||||||
|
|
||||||
|
transactor []byte
|
||||||
|
value *big.Int
|
||||||
|
|
||||||
|
depth int
|
||||||
|
Gas *big.Int
|
||||||
|
time int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnv(state *state.StateDB, transactor []byte, value *big.Int) *VMEnv {
|
||||||
|
return &VMEnv{
|
||||||
|
state: state,
|
||||||
|
transactor: transactor,
|
||||||
|
value: value,
|
||||||
|
time: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *VMEnv) State() *state.StateDB { return self.state }
|
||||||
|
func (self *VMEnv) Origin() []byte { return self.transactor }
|
||||||
|
func (self *VMEnv) BlockNumber() *big.Int { return ethutil.Big0 }
|
||||||
|
func (self *VMEnv) PrevHash() []byte { return make([]byte, 32) }
|
||||||
|
func (self *VMEnv) Coinbase() []byte { return self.transactor }
|
||||||
|
func (self *VMEnv) Time() int64 { return self.time }
|
||||||
|
func (self *VMEnv) Difficulty() *big.Int { return ethutil.Big1 }
|
||||||
|
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
||||||
|
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||||
|
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||||
|
func (self *VMEnv) Depth() int { return 0 }
|
||||||
|
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||||
|
func (self *VMEnv) AddLog(log state.Log) {
|
||||||
|
self.state.AddLog(log)
|
||||||
|
}
|
||||||
|
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
||||||
|
return vm.Transfer(from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
|
||||||
|
evm := vm.New(self, vm.DebugVmTy)
|
||||||
|
|
||||||
|
return core.NewExecution(evm, addr, data, gas, price, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||||
|
exe := self.vm(addr, data, gas, price, value)
|
||||||
|
ret, err := exe.Call(addr, caller)
|
||||||
|
self.Gas = exe.Gas
|
||||||
|
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
func (self *VMEnv) CallCode(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||||
|
exe := self.vm(caller.Address(), data, gas, price, value)
|
||||||
|
return exe.Call(addr, caller)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *VMEnv) Create(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) {
|
||||||
|
exe := self.vm(addr, data, gas, price, value)
|
||||||
|
return exe.Create(caller)
|
||||||
|
}
|
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 1004 B |
BIN
cmd/mist/assets/browser.png
Normal file
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 905 B |
435
cmd/mist/assets/debugger/debugger.qml
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Dialogs 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
import Ethereum 1.0
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: win
|
||||||
|
visible: false
|
||||||
|
title: "IceCREAM"
|
||||||
|
minimumWidth: 1280
|
||||||
|
minimumHeight: 700
|
||||||
|
width: 1290
|
||||||
|
height: 750
|
||||||
|
|
||||||
|
property alias codeText: codeEditor.text
|
||||||
|
property alias dataText: rawDataField.text
|
||||||
|
|
||||||
|
onClosing: {
|
||||||
|
dbg.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
menuBar: MenuBar {
|
||||||
|
Menu {
|
||||||
|
title: "Edit"
|
||||||
|
MenuItem {
|
||||||
|
text: "Focus code"
|
||||||
|
shortcut: "Ctrl+1"
|
||||||
|
onTriggered: {
|
||||||
|
codeEditor.focus = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: "Focus data"
|
||||||
|
shortcut: "Ctrl+2"
|
||||||
|
onTriggered: {
|
||||||
|
rawDataField.focus = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Command"
|
||||||
|
shortcut: "Ctrl+l"
|
||||||
|
onTriggered: {
|
||||||
|
dbgCommand.focus = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
title: "Debugger"
|
||||||
|
MenuItem {
|
||||||
|
text: "Run"
|
||||||
|
shortcut: "Ctrl+r"
|
||||||
|
onTriggered: debugCurrent()
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Stop"
|
||||||
|
onTriggered: dbp.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuSeparator {}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Next"
|
||||||
|
shortcut: "Ctrl+n"
|
||||||
|
onTriggered: dbg.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Continue"
|
||||||
|
shortcut: "Ctrl+g"
|
||||||
|
onTriggered: dbg.continue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
anchors.fill: parent
|
||||||
|
property var asmModel: ListModel {
|
||||||
|
id: asmModel
|
||||||
|
}
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: asmTableView
|
||||||
|
width: 200
|
||||||
|
headerVisible: false
|
||||||
|
TableViewColumn{ role: "value" ; title: "" ; width: asmTableView.width - 2 }
|
||||||
|
model: asmModel
|
||||||
|
/*
|
||||||
|
alternatingRowColors: false
|
||||||
|
itemDelegate: Item {
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "#DDD"
|
||||||
|
Text {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 10
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
color: "#333"
|
||||||
|
elide: styleData.elideMode
|
||||||
|
text: styleData.value
|
||||||
|
font.pixelSize: 11
|
||||||
|
MouseArea {
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
mouse.accepted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#00000000"
|
||||||
|
anchors.left: asmTableView.right
|
||||||
|
anchors.right: parent.right
|
||||||
|
SplitView {
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#00000000"
|
||||||
|
height: 330
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
TextArea {
|
||||||
|
id: codeEditor
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: settings.left
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
/*
|
||||||
|
Timer {
|
||||||
|
id: compileTimer
|
||||||
|
interval: 500 ; running: true ; repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
dbg.autoComp(codeEditor.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: settings
|
||||||
|
spacing: 5
|
||||||
|
width: 300
|
||||||
|
height: parent.height
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Arbitrary data"
|
||||||
|
}
|
||||||
|
TextArea {
|
||||||
|
id: rawDataField
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: 150
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Amount"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: txValue
|
||||||
|
width: 200
|
||||||
|
placeholderText: "Amount"
|
||||||
|
validator: RegExpValidator { regExp: /\d*/ }
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Amount of gas"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: txGas
|
||||||
|
width: 200
|
||||||
|
validator: RegExpValidator { regExp: /\d*/ }
|
||||||
|
text: "10000"
|
||||||
|
placeholderText: "Gas"
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: "Gas price"
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: txGasPrice
|
||||||
|
width: 200
|
||||||
|
placeholderText: "Gas price"
|
||||||
|
text: "1000000000000"
|
||||||
|
validator: RegExpValidator { regExp: /\d*/ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
orientation: Qt.Vertical
|
||||||
|
id: inspectorPane
|
||||||
|
height: 500
|
||||||
|
|
||||||
|
SplitView {
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
height: 150
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: stackTableView
|
||||||
|
property var stackModel: ListModel {
|
||||||
|
id: stackModel
|
||||||
|
}
|
||||||
|
height: parent.height
|
||||||
|
width: 300
|
||||||
|
TableViewColumn{ role: "value" ; title: "Local VM stack" ; width: stackTableView.width - 2 }
|
||||||
|
model: stackModel
|
||||||
|
}
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: memoryTableView
|
||||||
|
property var memModel: ListModel {
|
||||||
|
id: memModel
|
||||||
|
}
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width - stackTableView.width
|
||||||
|
TableViewColumn{ id:mnumColmn ; role: "num" ; title: "#" ; width: 50 }
|
||||||
|
TableViewColumn{ role: "value" ; title: "Memory" ; width: 650 }
|
||||||
|
model: memModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 100
|
||||||
|
width: parent.width
|
||||||
|
TableView {
|
||||||
|
id: storageTableView
|
||||||
|
property var memModel: ListModel {
|
||||||
|
id: storageModel
|
||||||
|
}
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
TableViewColumn{ id: key ; role: "key" ; title: "#" ; width: storageTableView.width / 2 - 1}
|
||||||
|
TableViewColumn{ role: "value" ; title: "Storage" ; width: storageTableView.width / 2 - 1}
|
||||||
|
model: storageModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 200
|
||||||
|
width: parent.width * 0.66
|
||||||
|
TableView {
|
||||||
|
id: logTableView
|
||||||
|
property var logModel: ListModel {
|
||||||
|
id: logModel
|
||||||
|
}
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width
|
||||||
|
TableViewColumn{ id: message ; role: "message" ; title: "log" ; width: logTableView.width - 2 }
|
||||||
|
model: logModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec() {
|
||||||
|
dbg.execCommand(dbgCommand.text);
|
||||||
|
dbgCommand.text = "";
|
||||||
|
}
|
||||||
|
statusBar: StatusBar {
|
||||||
|
height: 30
|
||||||
|
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: dbgCommand
|
||||||
|
y: 1
|
||||||
|
x: asmTableView.width
|
||||||
|
width: 500
|
||||||
|
placeholderText: "Debugger (type 'help')"
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
exec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.left: dbgCommand.right
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
spacing: 5
|
||||||
|
y: parent.height / 2 - this.height / 2
|
||||||
|
|
||||||
|
Text {
|
||||||
|
objectName: "stackFrame"
|
||||||
|
font.pixelSize: 10
|
||||||
|
text: "<b>stack ptr</b>: 0"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
objectName: "stackSize"
|
||||||
|
font.pixelSize: 10
|
||||||
|
text: "<b>stack size</b>: 0"
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
objectName: "memSize"
|
||||||
|
font.pixelSize: 10
|
||||||
|
text: "<b>mem size</b>: 0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toolBar: ToolBar {
|
||||||
|
height: 30
|
||||||
|
RowLayout {
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Button {
|
||||||
|
property var enabled: true
|
||||||
|
id: debugStart
|
||||||
|
onClicked: {
|
||||||
|
debugCurrent()
|
||||||
|
}
|
||||||
|
text: "Debug"
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
property var enabled: true
|
||||||
|
id: debugNextButton
|
||||||
|
onClicked: {
|
||||||
|
dbg.next()
|
||||||
|
}
|
||||||
|
text: "Next"
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: debugContinueButton
|
||||||
|
onClicked: {
|
||||||
|
dbg.continue()
|
||||||
|
}
|
||||||
|
text: "Continue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
id: snippets
|
||||||
|
anchors.right: parent.right
|
||||||
|
model: ListModel {
|
||||||
|
ListElement { text: "Snippets" ; value: "" }
|
||||||
|
ListElement { text: "Call Contract" ; value: "var[2] in = { \"arg1\", 0xdeadbeef };\nvar ret;\n\nvar success = call(0x0c542ddea93dae0c2fcb2cf175f03ad80d6be9a0, 0, 7000, in, ret)\n\nreturn ret" }
|
||||||
|
}
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
if(currentIndex != 0) {
|
||||||
|
var code = snippets.model.get(currentIndex).value;
|
||||||
|
codeEditor.insert(codeEditor.cursorPosition, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugCurrent() {
|
||||||
|
dbg.debug(txValue.text, txGas.text, txGasPrice.text, codeEditor.text, rawDataField.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAsm(asm) {
|
||||||
|
asmModel.append({asm: asm})
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAsm() {
|
||||||
|
asmModel.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInstruction(num) {
|
||||||
|
asmTableView.selection.clear()
|
||||||
|
asmTableView.selection.select(num)
|
||||||
|
asmTableView.positionViewAtRow(num, ListView.Center)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMem(mem) {
|
||||||
|
memModel.append({num: mem.num, value: mem.value})
|
||||||
|
}
|
||||||
|
function clearMem(){
|
||||||
|
memModel.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStack(stack) {
|
||||||
|
stackModel.append({value: stack})
|
||||||
|
}
|
||||||
|
function addDebugMessage(message){
|
||||||
|
debuggerLog.append({value: message})
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearStack() {
|
||||||
|
stackModel.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearStorage() {
|
||||||
|
storageModel.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStorage(storage) {
|
||||||
|
storageModel.append({key: storage.key, value: storage.value})
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLog(msg) {
|
||||||
|
// Remove first item once we've reached max log items
|
||||||
|
if(logModel.count > 250) {
|
||||||
|
logModel.remove(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(msg.len != 0) {
|
||||||
|
if(logTableView.flickableItem.atYEnd) {
|
||||||
|
logModel.append({message: msg})
|
||||||
|
logTableView.positionViewAtRow(logTableView.rowCount - 1, ListView.Contain)
|
||||||
|
} else {
|
||||||
|
logModel.append({message: msg})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearLog() {
|
||||||
|
logModel.clear()
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
var bigInt = (function () {
|
var bigInt = (function () {
|
||||||
var base = 10000000, logBase = 7;
|
var base = 10000000, logBase = 7;
|
||||||
var sign = {
|
var sign = {
|
14
cmd/mist/assets/ext/eth.js/.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
#
|
||||||
|
# If you find yourself ignoring temporary files generated by your text editor
|
||||||
|
# or operating system, you probably want to add a global ignore instead:
|
||||||
|
# git config --global core.excludesfile ~/.gitignore_global
|
||||||
|
|
||||||
|
/tmp
|
||||||
|
*/**/*un~
|
||||||
|
*un~
|
||||||
|
.DS_Store
|
||||||
|
*/**/.DS_Store
|
||||||
|
ethereum/ethereum
|
||||||
|
ethereal/ethereal
|
||||||
|
|
18
cmd/mist/assets/ext/eth.js/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Ethereum JavaScript API
|
||||||
|
|
||||||
|
This is the Ethereum compatible JavaScript API using `Promise`s
|
||||||
|
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec.
|
||||||
|
|
||||||
|
For an example see `index.html`.
|
||||||
|
|
||||||
|
**Please note this repo is in it's early stage.**
|
||||||
|
|
||||||
|
If you'd like to run a WebSocket ethereum node check out
|
||||||
|
[go-ethereum](https://github.com/ethereum/go-ethereum).
|
||||||
|
|
||||||
|
To install ethereum and spawn a node:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get github.com/ethereum/go-ethereum/ethereum
|
||||||
|
ethereum -ws -loglevel=4
|
||||||
|
```
|
70
cmd/mist/assets/ext/eth.js/httprpc.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
(function () {
|
||||||
|
var HttpRpcProvider = function (host) {
|
||||||
|
this.handlers = [];
|
||||||
|
this.host = host;
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatJsonRpcObject(object) {
|
||||||
|
return {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
method: object.call,
|
||||||
|
params: object.args,
|
||||||
|
id: object._id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatJsonRpcMessage(message) {
|
||||||
|
var object = JSON.parse(message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: object.id,
|
||||||
|
data: object.result
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
|
||||||
|
var data = formatJsonRpcObject(payload);
|
||||||
|
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.open("POST", this.host, true);
|
||||||
|
request.send(JSON.stringify(data));
|
||||||
|
request.onreadystatechange = function () {
|
||||||
|
if (request.readyState === 4 && cb) {
|
||||||
|
cb(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpRpcProvider.prototype.send = function (payload) {
|
||||||
|
var self = this;
|
||||||
|
this.sendRequest(payload, function (request) {
|
||||||
|
self.handlers.forEach(function (handler) {
|
||||||
|
handler.call(self, formatJsonRpcMessage(request.responseText));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpRpcProvider.prototype.poll = function (payload, id) {
|
||||||
|
var self = this;
|
||||||
|
this.sendRequest(payload, function (request) {
|
||||||
|
var parsed = JSON.parse(request.responseText);
|
||||||
|
if (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.handlers.forEach(function (handler) {
|
||||||
|
handler.call(self, {_event: payload.call, _id: id, data: parsed.result});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
|
||||||
|
set: function (handler) {
|
||||||
|
this.handlers.push(handler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof(web3) !== "undefined" && web3.providers !== undefined) {
|
||||||
|
web3.providers.HttpRpcProvider = HttpRpcProvider;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
33
cmd/mist/assets/ext/eth.js/index.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!doctype>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script type="text/javascript" src="main.js"></script>
|
||||||
|
<script type="text/javascript" src="websocket.js"></script>
|
||||||
|
<script type="text/javascript" src="qt.js"></script>
|
||||||
|
<script type="text/javascript" src="httprpc.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function registerName() {
|
||||||
|
var name = document.querySelector("#name").value;
|
||||||
|
name = web3.fromAscii(name);
|
||||||
|
|
||||||
|
var eth = web3.eth;
|
||||||
|
eth.transact({to: "NameReg", gas: "10000", gasPrice: eth.gasPrice, data: [web3.fromAscii("register"), name]}).then(function(tx) {
|
||||||
|
document.querySelector("#result").innerHTML = "Registered name. Please wait for the next block to come through.";
|
||||||
|
}, function(err) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>std::name_reg</h1>
|
||||||
|
<input type="text" id="name"></input>
|
||||||
|
<input type="submit" onClick="registerName();"></input>
|
||||||
|
<div id="result"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
432
cmd/mist/assets/ext/eth.js/main.js
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
(function(window) {
|
||||||
|
function isPromise(o) {
|
||||||
|
return o instanceof Promise
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenPromise (obj) {
|
||||||
|
if (obj instanceof Promise) {
|
||||||
|
return Promise.resolve(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj instanceof Array) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
var promises = obj.map(function (o) {
|
||||||
|
return flattenPromise(o);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises).then(function (res) {
|
||||||
|
for (var i = 0; i < obj.length; i++) {
|
||||||
|
obj[i] = res[i];
|
||||||
|
}
|
||||||
|
resolve(obj);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj instanceof Object) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
var keys = Object.keys(obj);
|
||||||
|
var promises = keys.map(function (key) {
|
||||||
|
return flattenPromise(obj[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises).then(function (res) {
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
obj[keys[i]] = res[i];
|
||||||
|
}
|
||||||
|
resolve(obj);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
var ethMethods = function () {
|
||||||
|
var blockCall = function (args) {
|
||||||
|
return typeof args[0] === "string" ? "blockByHash" : "blockByNumber";
|
||||||
|
};
|
||||||
|
|
||||||
|
var transactionCall = function (args) {
|
||||||
|
return typeof args[0] === "string" ? 'transactionByHash' : 'transactionByNumber';
|
||||||
|
};
|
||||||
|
|
||||||
|
var uncleCall = function (args) {
|
||||||
|
return typeof args[0] === "string" ? 'uncleByHash' : 'uncleByNumber';
|
||||||
|
};
|
||||||
|
|
||||||
|
var methods = [
|
||||||
|
{ name: 'balanceAt', call: 'balanceAt' },
|
||||||
|
{ name: 'stateAt', call: 'stateAt' },
|
||||||
|
{ name: 'countAt', call: 'countAt'},
|
||||||
|
{ name: 'codeAt', call: 'codeAt' },
|
||||||
|
{ name: 'transact', call: 'transact' },
|
||||||
|
{ name: 'call', call: 'call' },
|
||||||
|
{ name: 'block', call: blockCall },
|
||||||
|
{ name: 'transaction', call: transactionCall },
|
||||||
|
{ name: 'uncle', call: uncleCall },
|
||||||
|
{ name: 'compile', call: 'compile' }
|
||||||
|
];
|
||||||
|
return methods;
|
||||||
|
};
|
||||||
|
|
||||||
|
var ethProperties = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'coinbase', getter: 'coinbase', setter: 'setCoinbase' },
|
||||||
|
{ name: 'listening', getter: 'listening', setter: 'setListening' },
|
||||||
|
{ name: 'mining', getter: 'mining', setter: 'setMining' },
|
||||||
|
{ name: 'gasPrice', getter: 'gasPrice' },
|
||||||
|
{ name: 'account', getter: 'account' },
|
||||||
|
{ name: 'accounts', getter: 'accounts' },
|
||||||
|
{ name: 'peerCount', getter: 'peerCount' },
|
||||||
|
{ name: 'defaultBlock', getter: 'defaultBlock', setter: 'setDefaultBlock' },
|
||||||
|
{ name: 'number', getter: 'number'}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
var dbMethods = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'put', call: 'put' },
|
||||||
|
{ name: 'get', call: 'get' },
|
||||||
|
{ name: 'putString', call: 'putString' },
|
||||||
|
{ name: 'getString', call: 'getString' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
var shhMethods = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'post', call: 'post' },
|
||||||
|
{ name: 'newIdentity', call: 'newIdentity' },
|
||||||
|
{ name: 'haveIdentity', call: 'haveIdentity' },
|
||||||
|
{ name: 'newGroup', call: 'newGroup' },
|
||||||
|
{ name: 'addToGroup', call: 'addToGroup' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
var ethWatchMethods = function () {
|
||||||
|
var newFilter = function (args) {
|
||||||
|
return typeof args[0] === 'string' ? 'newFilterString' : 'newFilter';
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ name: 'newFilter', call: newFilter },
|
||||||
|
{ name: 'uninstallFilter', call: 'uninstallFilter' },
|
||||||
|
{ name: 'getMessages', call: 'getMessages' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
var shhWatchMethods = function () {
|
||||||
|
return [
|
||||||
|
{ name: 'newFilter', call: 'shhNewFilter' },
|
||||||
|
{ name: 'uninstallFilter', call: 'shhUninstallFilter' },
|
||||||
|
{ name: 'getMessage', call: 'shhGetMessages' }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
var setupMethods = function (obj, methods) {
|
||||||
|
methods.forEach(function (method) {
|
||||||
|
obj[method.name] = function () {
|
||||||
|
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {
|
||||||
|
var call = typeof method.call === "function" ? method.call(args) : method.call;
|
||||||
|
return {call: call, args: args};
|
||||||
|
}).then(function (request) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
web3.provider.send(request, function (result) {
|
||||||
|
//if (result || typeof result === "boolean") {
|
||||||
|
resolve(result);
|
||||||
|
return;
|
||||||
|
//}
|
||||||
|
//reject(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).catch(function( err) {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var setupProperties = function (obj, properties) {
|
||||||
|
properties.forEach(function (property) {
|
||||||
|
var proto = {};
|
||||||
|
proto.get = function () {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
web3.provider.send({call: property.getter}, function(result) {
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (property.setter) {
|
||||||
|
proto.set = function (val) {
|
||||||
|
return flattenPromise([val]).then(function (args) {
|
||||||
|
return new Promise(function (resolve) {
|
||||||
|
web3.provider.send({call: property.setter, args: args}, function (result) {
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).catch(function (err) {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object.defineProperty(obj, property.name, proto);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var web3 = {
|
||||||
|
_callbacks: {},
|
||||||
|
_events: {},
|
||||||
|
providers: {},
|
||||||
|
toHex: function(str) {
|
||||||
|
var hex = "";
|
||||||
|
for(var i = 0; i < str.length; i++) {
|
||||||
|
var n = str.charCodeAt(i).toString(16);
|
||||||
|
hex += n.length < 2 ? '0' + n : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
},
|
||||||
|
|
||||||
|
toAscii: function(hex) {
|
||||||
|
// Find termination
|
||||||
|
var str = "";
|
||||||
|
var i = 0, l = hex.length;
|
||||||
|
for(; i < l; i+=2) {
|
||||||
|
var code = hex.charCodeAt(i)
|
||||||
|
if(code == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
|
||||||
|
toDecimal: function (val) {
|
||||||
|
return parseInt(val, 16);
|
||||||
|
},
|
||||||
|
|
||||||
|
fromAscii: function(str, pad) {
|
||||||
|
pad = pad === undefined ? 32 : pad;
|
||||||
|
var hex = this.toHex(str);
|
||||||
|
while(hex.length < pad*2)
|
||||||
|
hex += "00";
|
||||||
|
return hex
|
||||||
|
},
|
||||||
|
|
||||||
|
eth: {
|
||||||
|
prototype: Object(),
|
||||||
|
watch: function (params) {
|
||||||
|
return new Filter(params, ethWatch);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
db: {
|
||||||
|
prototype: Object()
|
||||||
|
},
|
||||||
|
|
||||||
|
shh: {
|
||||||
|
prototype: Object(),
|
||||||
|
watch: function (params) {
|
||||||
|
return new Filter(params, shhWatch);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
on: function(event, id, cb) {
|
||||||
|
if(web3._events[event] === undefined) {
|
||||||
|
web3._events[event] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
web3._events[event][id] = cb;
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
off: function(event, id) {
|
||||||
|
if(web3._events[event] !== undefined) {
|
||||||
|
delete web3._events[event][id];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
trigger: function(event, id, data) {
|
||||||
|
var callbacks = web3._events[event];
|
||||||
|
if (!callbacks || !callbacks[id]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var cb = callbacks[id];
|
||||||
|
cb(data);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var eth = web3.eth;
|
||||||
|
setupMethods(eth, ethMethods());
|
||||||
|
setupProperties(eth, ethProperties());
|
||||||
|
setupMethods(web3.db, dbMethods());
|
||||||
|
setupMethods(web3.shh, shhMethods());
|
||||||
|
|
||||||
|
var ethWatch = {
|
||||||
|
changed: 'changed'
|
||||||
|
};
|
||||||
|
setupMethods(ethWatch, ethWatchMethods());
|
||||||
|
var shhWatch = {
|
||||||
|
changed: 'shhChanged'
|
||||||
|
};
|
||||||
|
setupMethods(shhWatch, shhWatchMethods());
|
||||||
|
|
||||||
|
var ProviderManager = function() {
|
||||||
|
this.queued = [];
|
||||||
|
this.polls = [];
|
||||||
|
this.ready = false;
|
||||||
|
this.provider = undefined;
|
||||||
|
this.id = 1;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var poll = function () {
|
||||||
|
if (self.provider && self.provider.poll) {
|
||||||
|
self.polls.forEach(function (data) {
|
||||||
|
data.data._id = self.id;
|
||||||
|
self.id++;
|
||||||
|
self.provider.poll(data.data, data.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setTimeout(poll, 12000);
|
||||||
|
};
|
||||||
|
poll();
|
||||||
|
};
|
||||||
|
|
||||||
|
ProviderManager.prototype.send = function(data, cb) {
|
||||||
|
data._id = this.id;
|
||||||
|
if (cb) {
|
||||||
|
web3._callbacks[data._id] = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.args = data.args || [];
|
||||||
|
this.id++;
|
||||||
|
|
||||||
|
if(this.provider !== undefined) {
|
||||||
|
this.provider.send(data);
|
||||||
|
} else {
|
||||||
|
console.warn("provider is not set");
|
||||||
|
this.queued.push(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ProviderManager.prototype.set = function(provider) {
|
||||||
|
if(this.provider !== undefined && this.provider.unload !== undefined) {
|
||||||
|
this.provider.unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.provider = provider;
|
||||||
|
this.ready = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
ProviderManager.prototype.sendQueued = function() {
|
||||||
|
for(var i = 0; this.queued.length; i++) {
|
||||||
|
// Resend
|
||||||
|
this.send(this.queued[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ProviderManager.prototype.installed = function() {
|
||||||
|
return this.provider !== undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
ProviderManager.prototype.startPolling = function (data, pollId) {
|
||||||
|
if (!this.provider || !this.provider.poll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.polls.push({data: data, id: pollId});
|
||||||
|
};
|
||||||
|
|
||||||
|
ProviderManager.prototype.stopPolling = function (pollId) {
|
||||||
|
for (var i = this.polls.length; i--;) {
|
||||||
|
var poll = this.polls[i];
|
||||||
|
if (poll.id === pollId) {
|
||||||
|
this.polls.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
web3.provider = new ProviderManager();
|
||||||
|
|
||||||
|
web3.setProvider = function(provider) {
|
||||||
|
provider.onmessage = messageHandler;
|
||||||
|
web3.provider.set(provider);
|
||||||
|
web3.provider.sendQueued();
|
||||||
|
};
|
||||||
|
|
||||||
|
var Filter = function(options, impl) {
|
||||||
|
this.impl = impl;
|
||||||
|
this.callbacks = [];
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this.promise = impl.newFilter(options);
|
||||||
|
this.promise.then(function (id) {
|
||||||
|
self.id = id;
|
||||||
|
web3.on(impl.changed, id, self.trigger.bind(self));
|
||||||
|
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.arrived = function(callback) {
|
||||||
|
this.changed(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter.prototype.changed = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
this.promise.then(function(id) {
|
||||||
|
self.callbacks.push(callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.trigger = function(messages) {
|
||||||
|
for(var i = 0; i < this.callbacks.length; i++) {
|
||||||
|
this.callbacks[i].call(this, messages);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.uninstall = function() {
|
||||||
|
var self = this;
|
||||||
|
this.promise.then(function (id) {
|
||||||
|
self.impl.uninstallFilter(id);
|
||||||
|
web3.provider.stopPolling(id);
|
||||||
|
web3.off(impl.changed, id);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.messages = function() {
|
||||||
|
var self = this;
|
||||||
|
return this.promise.then(function (id) {
|
||||||
|
return self.impl.getMessages(id);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function messageHandler(data) {
|
||||||
|
if(data._event !== undefined) {
|
||||||
|
web3.trigger(data._event, data._id, data.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data._id) {
|
||||||
|
var cb = web3._callbacks[data._id];
|
||||||
|
if (cb) {
|
||||||
|
cb.call(this, data.data)
|
||||||
|
delete web3._callbacks[data._id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Install default provider
|
||||||
|
if(!web3.provider.installed()) {
|
||||||
|
var sock = new web3.WebSocket("ws://localhost:40404/eth");
|
||||||
|
|
||||||
|
web3.setProvider(sock);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
window.web3 = web3;
|
||||||
|
|
||||||
|
})(this);
|
27
cmd/mist/assets/ext/eth.js/qt.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
(function() {
|
||||||
|
var QtProvider = function() {
|
||||||
|
this.handlers = [];
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
navigator.qt.onmessage = function (message) {
|
||||||
|
self.handlers.forEach(function (handler) {
|
||||||
|
handler.call(self, JSON.parse(message.data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QtProvider.prototype.send = function(payload) {
|
||||||
|
navigator.qt.postMessage(JSON.stringify(payload));
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperty(QtProvider.prototype, "onmessage", {
|
||||||
|
set: function(handler) {
|
||||||
|
this.handlers.push(handler);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if(typeof(web3) !== "undefined" && web3.providers !== undefined) {
|
||||||
|
web3.providers.QtProvider = QtProvider;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
51
cmd/mist/assets/ext/eth.js/websocket.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
(function() {
|
||||||
|
var WebSocketProvider = function(host) {
|
||||||
|
// onmessage handlers
|
||||||
|
this.handlers = [];
|
||||||
|
// queue will be filled with messages if send is invoked before the ws is ready
|
||||||
|
this.queued = [];
|
||||||
|
this.ready = false;
|
||||||
|
|
||||||
|
this.ws = new WebSocket(host);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this.ws.onmessage = function(event) {
|
||||||
|
for(var i = 0; i < self.handlers.length; i++) {
|
||||||
|
self.handlers[i].call(self, JSON.parse(event.data), event)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onopen = function() {
|
||||||
|
self.ready = true;
|
||||||
|
|
||||||
|
for(var i = 0; i < self.queued.length; i++) {
|
||||||
|
// Resend
|
||||||
|
self.send(self.queued[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
WebSocketProvider.prototype.send = function(payload) {
|
||||||
|
if(this.ready) {
|
||||||
|
var data = JSON.stringify(payload);
|
||||||
|
|
||||||
|
this.ws.send(data);
|
||||||
|
} else {
|
||||||
|
this.queued.push(payload);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebSocketProvider.prototype.onMessage = function(handler) {
|
||||||
|
this.handlers.push(handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
WebSocketProvider.prototype.unload = function() {
|
||||||
|
this.ws.close();
|
||||||
|
};
|
||||||
|
Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
|
||||||
|
set: function(provider) { this.onMessage(provider); }
|
||||||
|
});
|
||||||
|
|
||||||
|
if(typeof(web3) !== "undefined" && web3.providers !== undefined) {
|
||||||
|
web3.providers.WebSocketProvider = WebSocketProvider;
|
||||||
|
}
|
||||||
|
})();
|
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
// Main Ethereum library
|
// Main Ethereum library
|
||||||
window.eth = {
|
window.eth = {
|
||||||
prototype: Object(),
|
prototype: Object(),
|
66
cmd/mist/assets/ext/filter.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
var ethx = {
|
||||||
|
prototype: Object,
|
||||||
|
|
||||||
|
watch: function(options) {
|
||||||
|
return new Filter(options);
|
||||||
|
},
|
||||||
|
|
||||||
|
note: function() {
|
||||||
|
var args = Array.prototype.slice.call(arguments, 0);
|
||||||
|
var o = []
|
||||||
|
for(var i = 0; i < args.length; i++) {
|
||||||
|
o.push(args[i].toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
eth.notef(o);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var Filter = function(options) {
|
||||||
|
this.callbacks = [];
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
if(options === "chain") {
|
||||||
|
this.id = eth.newFilterString(options);
|
||||||
|
} else if(typeof options === "object") {
|
||||||
|
this.id = eth.newFilter(options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.changed = function(callback) {
|
||||||
|
this.callbacks.push(callback);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
messages.connect(function(messages, id) {
|
||||||
|
if(id == self.id) {
|
||||||
|
for(var i = 0; i < self.callbacks.length; i++) {
|
||||||
|
self.callbacks[i].call(self, messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.uninstall = function() {
|
||||||
|
eth.uninstallFilter(this.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
Filter.prototype.messages = function() {
|
||||||
|
return eth.messages(this.id)
|
||||||
|
}
|
481
cmd/mist/assets/ext/html_messaging.js
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
// The magic return variable. The magic return variable will be set during the execution of the QML call.
|
||||||
|
(function(window) {
|
||||||
|
var Promise = window.Promise;
|
||||||
|
if(typeof(Promise) === "undefined") {
|
||||||
|
var Promise = Q.Promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPromise(o) {
|
||||||
|
return typeof o === "object" && o.then
|
||||||
|
}
|
||||||
|
|
||||||
|
window.eth = {
|
||||||
|
_callbacks: {},
|
||||||
|
_events: {},
|
||||||
|
prototype: Object(),
|
||||||
|
|
||||||
|
toHex: function(str) {
|
||||||
|
var hex = "";
|
||||||
|
for(var i = 0; i < str.length; i++) {
|
||||||
|
var n = str.charCodeAt(i).toString(16);
|
||||||
|
hex += n.length < 2 ? '0' + n : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
},
|
||||||
|
|
||||||
|
toAscii: function(hex) {
|
||||||
|
// Find termination
|
||||||
|
var str = "";
|
||||||
|
var i = 0, l = hex.length;
|
||||||
|
for(; i < l; i+=2) {
|
||||||
|
var code = hex.charCodeAt(i)
|
||||||
|
if(code == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromAscii: function(str, pad) {
|
||||||
|
if(pad === undefined) {
|
||||||
|
pad = 32
|
||||||
|
}
|
||||||
|
|
||||||
|
var hex = this.toHex(str);
|
||||||
|
|
||||||
|
while(hex.length < pad*2)
|
||||||
|
hex += "00";
|
||||||
|
|
||||||
|
return hex
|
||||||
|
},
|
||||||
|
|
||||||
|
block: function(numberOrHash) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var func;
|
||||||
|
if(typeof numberOrHash == "string") {
|
||||||
|
func = "getBlockByHash";
|
||||||
|
} else {
|
||||||
|
func = "getBlockByNumber";
|
||||||
|
}
|
||||||
|
|
||||||
|
postData({call: func, args: [numberOrHash]}, function(block) {
|
||||||
|
if(block)
|
||||||
|
resolve(block);
|
||||||
|
else
|
||||||
|
reject("not found");
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
transact: function(params) {
|
||||||
|
if(params === undefined) {
|
||||||
|
params = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(params.endowment !== undefined)
|
||||||
|
params.value = params.endowment;
|
||||||
|
if(params.code !== undefined)
|
||||||
|
params.data = params.code;
|
||||||
|
|
||||||
|
|
||||||
|
var promises = []
|
||||||
|
if(isPromise(params.to)) {
|
||||||
|
promises.push(params.to.then(function(_to) { params.to = _to; }));
|
||||||
|
}
|
||||||
|
if(isPromise(params.from)) {
|
||||||
|
promises.push(params.from.then(function(_from) { params.from = _from; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof params.data !== "object" || isPromise(params.data)) {
|
||||||
|
params.data = [params.data]
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = params.data;
|
||||||
|
for(var i = 0; i < params.data.length; i++) {
|
||||||
|
if(isPromise(params.data[i])) {
|
||||||
|
var promise = params.data[i];
|
||||||
|
var _i = i;
|
||||||
|
promises.push(promise.then(function(_arg) { params.data[_i] = _arg; }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure everything is string
|
||||||
|
var fields = ["value", "gas", "gasPrice"];
|
||||||
|
for(var i = 0; i < fields.length; i++) {
|
||||||
|
if(params[fields[i]] === undefined) {
|
||||||
|
params[fields[i]] = "";
|
||||||
|
}
|
||||||
|
params[fields[i]] = params[fields[i]].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load promises then call the last "transact".
|
||||||
|
return Q.all(promises).then(function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
params.data = params.data.join("");
|
||||||
|
postData({call: "transact", args: params}, function(data) {
|
||||||
|
if(data[1])
|
||||||
|
reject(data[0]);
|
||||||
|
else
|
||||||
|
resolve(data[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
compile: function(code) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "compile", args: [code]}, function(data) {
|
||||||
|
if(data[1])
|
||||||
|
reject(data[0]);
|
||||||
|
else
|
||||||
|
resolve(data[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
balanceAt: function(address) {
|
||||||
|
var promises = [];
|
||||||
|
|
||||||
|
if(isPromise(address)) {
|
||||||
|
promises.push(address.then(function(_address) { address = _address; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q.all(promises).then(function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getBalanceAt", args: [address]}, function(balance) {
|
||||||
|
resolve(balance);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
countAt: function(address) {
|
||||||
|
var promises = [];
|
||||||
|
|
||||||
|
if(isPromise(address)) {
|
||||||
|
promises.push(address.then(function(_address) { address = _address; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q.all(promises).then(function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getCountAt", args: [address]}, function(count) {
|
||||||
|
resolve(count);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
codeAt: function(address) {
|
||||||
|
var promises = [];
|
||||||
|
|
||||||
|
if(isPromise(address)) {
|
||||||
|
promises.push(address.then(function(_address) { address = _address; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q.all(promises).then(function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getCodeAt", args: [address]}, function(code) {
|
||||||
|
resolve(code);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
storageAt: function(address, storageAddress) {
|
||||||
|
var promises = [];
|
||||||
|
|
||||||
|
if(isPromise(address)) {
|
||||||
|
promises.push(address.then(function(_address) { address = _address; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isPromise(storageAddress)) {
|
||||||
|
promises.push(storageAddress.then(function(_sa) { storageAddress = _sa; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q.all(promises).then(function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getStorageAt", args: [address, storageAddress]}, function(entry) {
|
||||||
|
resolve(entry);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
stateAt: function(address, storageAddress) {
|
||||||
|
return this.storageAt(address, storageAddress);
|
||||||
|
},
|
||||||
|
|
||||||
|
call: function(params) {
|
||||||
|
if(params === undefined) {
|
||||||
|
params = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(params.endowment !== undefined)
|
||||||
|
params.value = params.endowment;
|
||||||
|
if(params.code !== undefined)
|
||||||
|
params.data = params.code;
|
||||||
|
|
||||||
|
|
||||||
|
var promises = []
|
||||||
|
if(isPromise(params.to)) {
|
||||||
|
promises.push(params.to.then(function(_to) { params.to = _to; }));
|
||||||
|
}
|
||||||
|
if(isPromise(params.from)) {
|
||||||
|
promises.push(params.from.then(function(_from) { params.from = _from; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isPromise(params.data)) {
|
||||||
|
promises.push(params.data.then(function(_code) { params.data = _code; }));
|
||||||
|
} else {
|
||||||
|
if(typeof params.data === "object") {
|
||||||
|
data = "";
|
||||||
|
for(var i = 0; i < params.data.length; i++) {
|
||||||
|
data += params.data[i]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = params.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure everything is string
|
||||||
|
var fields = ["value", "gas", "gasPrice"];
|
||||||
|
for(var i = 0; i < fields.length; i++) {
|
||||||
|
if(params[fields[i]] === undefined) {
|
||||||
|
params[fields[i]] = "";
|
||||||
|
}
|
||||||
|
params[fields[i]] = params[fields[i]].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load promises then call the last "transact".
|
||||||
|
return Q.all(promises).then(function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "call", args: params}, function(data) {
|
||||||
|
if(data[1])
|
||||||
|
reject(data[0]);
|
||||||
|
else
|
||||||
|
resolve(data[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: function(params) {
|
||||||
|
return new Filter(params);
|
||||||
|
},
|
||||||
|
|
||||||
|
secretToAddress: function(key) {
|
||||||
|
var promises = [];
|
||||||
|
if(isPromise(key)) {
|
||||||
|
promises.push(key.then(function(_key) { key = _key; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q.all(promises).then(function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getSecretToAddress", args: [key]}, function(address) {
|
||||||
|
resolve(address);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
on: function(event, cb) {
|
||||||
|
if(eth._events[event] === undefined) {
|
||||||
|
eth._events[event] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
eth._events[event].push(cb);
|
||||||
|
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
off: function(event, cb) {
|
||||||
|
if(eth._events[event] !== undefined) {
|
||||||
|
var callbacks = eth._events[event];
|
||||||
|
for(var i = 0; i < callbacks.length; i++) {
|
||||||
|
if(callbacks[i] === cb) {
|
||||||
|
delete callbacks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
|
||||||
|
trigger: function(event, data) {
|
||||||
|
var callbacks = eth._events[event];
|
||||||
|
if(callbacks !== undefined) {
|
||||||
|
for(var i = 0; i < callbacks.length; i++) {
|
||||||
|
// Figure out whether the returned data was an array
|
||||||
|
// array means multiple return arguments (multiple params)
|
||||||
|
if(data instanceof Array) {
|
||||||
|
callbacks[i].apply(this, data);
|
||||||
|
} else {
|
||||||
|
callbacks[i].call(this, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Eth object properties
|
||||||
|
Object.defineProperty(eth, "key", {
|
||||||
|
get: function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getKey"}, function(k) {
|
||||||
|
resolve(k);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(eth, "gasPrice", {
|
||||||
|
get: function() {
|
||||||
|
return "10000000000000"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(eth, "coinbase", {
|
||||||
|
get: function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getCoinBase"}, function(coinbase) {
|
||||||
|
resolve(coinbase);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(eth, "listening", {
|
||||||
|
get: function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getIsListening"}, function(listening) {
|
||||||
|
resolve(listening);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Object.defineProperty(eth, "mining", {
|
||||||
|
get: function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getIsMining"}, function(mining) {
|
||||||
|
resolve(mining);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(eth, "peerCount", {
|
||||||
|
get: function() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getPeerCount"}, function(peerCount) {
|
||||||
|
resolve(peerCount);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var filters = [];
|
||||||
|
var Filter = function(options) {
|
||||||
|
filters.push(this);
|
||||||
|
|
||||||
|
this.callbacks = [];
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
var call;
|
||||||
|
if(options === "chain") {
|
||||||
|
call = "newFilterString"
|
||||||
|
} else if(typeof options === "object") {
|
||||||
|
call = "newFilter"
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this; // Cheaper than binding
|
||||||
|
this.promise = new Promise(function(resolve, reject) {
|
||||||
|
postData({call: call, args: [options]}, function(id) {
|
||||||
|
self.id = id;
|
||||||
|
|
||||||
|
resolve(id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.changed = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
this.promise.then(function(id) {
|
||||||
|
self.callbacks.push(callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.trigger = function(messages, id) {
|
||||||
|
if(id == this.id) {
|
||||||
|
for(var i = 0; i < this.callbacks.length; i++) {
|
||||||
|
this.callbacks[i].call(this, messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.uninstall = function() {
|
||||||
|
this.promise.then(function(id) {
|
||||||
|
postData({call: "uninstallFilter", args:[id]});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.messages = function() {
|
||||||
|
var self=this;
|
||||||
|
return Q.all([this.promise]).then(function() {
|
||||||
|
var id = self.id
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
postData({call: "getMessages", args: [id]}, function(messages) {
|
||||||
|
resolve(messages);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register to the messages callback. "messages" will be emitted when new messages
|
||||||
|
// from the client have been created.
|
||||||
|
eth.on("messages", function(messages, id) {
|
||||||
|
for(var i = 0; i < filters.length; i++) {
|
||||||
|
filters[i].trigger(messages, id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var g_seed = 1;
|
||||||
|
function postData(data, cb) {
|
||||||
|
data._seed = g_seed;
|
||||||
|
if(cb) {
|
||||||
|
eth._callbacks[data._seed] = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data.args === undefined) {
|
||||||
|
data.args = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
g_seed++;
|
||||||
|
|
||||||
|
window._messagingAdapter.call(this, JSON.stringify(data))
|
||||||
|
}
|
||||||
|
})(this);
|
30
cmd/mist/assets/ext/http.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
// this function is included locally, but you can also include separately via a header definition
|
||||||
|
function request(url, callback) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.onreadystatechange = (function(req) {
|
||||||
|
return function() {
|
||||||
|
if(req.readyState === 4) {
|
||||||
|
callback(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(xhr);
|
||||||
|
xhr.open('GET', url, true);
|
||||||
|
xhr.send('');
|
||||||
|
}
|
1909
cmd/mist/assets/ext/q.js
Normal file
30
cmd/mist/assets/ext/qml_messaging.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
function HandleMessage(data) {
|
||||||
|
var message;
|
||||||
|
try { message = JSON.parse(data) } catch(e) {};
|
||||||
|
|
||||||
|
if(message) {
|
||||||
|
switch(message.type) {
|
||||||
|
case "coinbase":
|
||||||
|
return eth.coinBase();
|
||||||
|
case "block":
|
||||||
|
return eth.blockByNumber(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
cmd/mist/assets/ext/qt_messaging_adapter.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
window._messagingAdapter = function(data) {
|
||||||
|
navigator.qt.postMessage(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
navigator.qt.onmessage = function(ev) {
|
||||||
|
var data = JSON.parse(ev.data)
|
||||||
|
|
||||||
|
if(data._event !== undefined) {
|
||||||
|
eth.trigger(data._event, data.data);
|
||||||
|
} else {
|
||||||
|
if(data._seed) {
|
||||||
|
var cb = eth._callbacks[data._seed];
|
||||||
|
if(cb) {
|
||||||
|
cb.call(this, data.data)
|
||||||
|
|
||||||
|
// Remove the "trigger" callback
|
||||||
|
delete eth._callbacks[ev._seed];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
cmd/mist/assets/ext/setup.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
(function() {
|
||||||
|
if (typeof(Promise) === "undefined")
|
||||||
|
window.Promise = Q.Promise;
|
||||||
|
|
||||||
|
var eth = web3.eth;
|
||||||
|
|
||||||
|
web3.setProvider(new web3.providers.QtProvider());
|
||||||
|
})()
|
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
String.prototype.pad = function(l, r) {
|
String.prototype.pad = function(l, r) {
|
||||||
if (r === undefined) {
|
if (r === undefined) {
|
||||||
r = l
|
r = l
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
cmd/mist/assets/icecream.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
cmd/mist/assets/miner.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
// Helper function for generating pseudo callbacks and sending data to the QML part of the application
|
// Helper function for generating pseudo callbacks and sending data to the QML part of the application
|
||||||
function postData(data, cb) {
|
function postData(data, cb) {
|
||||||
data._seed = Math.floor(Math.random() * 1000000)
|
data._seed = Math.floor(Math.random() * 1000000)
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 932 B After Width: | Height: | Size: 932 B |
@ -7,26 +7,28 @@ import QtQuick.Controls.Styles 1.1
|
|||||||
import Ethereum 1.0
|
import Ethereum 1.0
|
||||||
|
|
||||||
import "../ext/filter.js" as Eth
|
import "../ext/filter.js" as Eth
|
||||||
|
import "../ext/http.js" as Http
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias miningButtonText: miningButton.text
|
property var ethx : Eth.ethx
|
||||||
|
property var browser
|
||||||
|
|
||||||
|
width: 1200
|
||||||
width: 900
|
height: 820
|
||||||
height: 600
|
|
||||||
minimumHeight: 300
|
minimumHeight: 300
|
||||||
|
|
||||||
title: "Ether browser"
|
title: "Mist"
|
||||||
|
|
||||||
// This signal is used by the filter API. The filter API connects using this signal handler from
|
// This signal is used by the filter API. The filter API connects using this signal handler from
|
||||||
// the different QML files and plugins.
|
// the different QML files and plugins.
|
||||||
signal message(var callback, int seed, int seedCallback);
|
signal messages(var messages, int id);
|
||||||
function invokeFilterCallback(data, receiverSeed, callbackSeed) {
|
function invokeFilterCallback(data, receiverSeed) {
|
||||||
var messages = JSON.parse(data)
|
//var messages = JSON.parse(data)
|
||||||
// Signal handler
|
// Signal handler
|
||||||
message(messages, receiverSeed, callbackSeed);
|
messages(data, receiverSeed);
|
||||||
|
root.browser.view.messages(data, receiverSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
@ -42,30 +44,26 @@ ApplicationWindow {
|
|||||||
|
|
||||||
// Takes care of loading all default plugins
|
// Takes care of loading all default plugins
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
var walletView = addPlugin("./views/wallet.qml", {noAdd: true, section: "ethereum", active: true})
|
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||||
var historyView = addPlugin("./views/history.qml", {noAdd: true, section: "legacy"})
|
var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||||
var newTxView = addPlugin("./views/transaction.qml", {noAdd: true, section: "legacy"})
|
root.browser = browser;
|
||||||
var chainView = addPlugin("./views/chain.qml", {noAdd: true, section: "legacy"})
|
addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||||
var infoView = addPlugin("./views/info.qml", {noAdd: true, section: "legacy"})
|
|
||||||
var pendingTxView = addPlugin("./views/pending_tx.qml", {noAdd: true, section: "legacy"})
|
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
|
||||||
var pendingTxView = addPlugin("./views/javascript.qml", {noAdd: true, section: "legacy"})
|
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
|
||||||
|
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
|
||||||
|
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
|
||||||
|
|
||||||
|
addPlugin("./views/jeffcoin/jeffcoin.qml", {noAdd: true, close: false, section: "apps"})
|
||||||
|
|
||||||
|
mainSplit.setView(wallet.view, wallet.menuItem);
|
||||||
|
|
||||||
// Call the ready handler
|
// Call the ready handler
|
||||||
gui.done()
|
gui.done();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPlugin(path, options) {
|
function addViews(view, path, options) {
|
||||||
var component = Qt.createComponent(path);
|
var views = mainSplit.addComponent(view, options)
|
||||||
if(component.status != Component.Ready) {
|
|
||||||
if(component.status == Component.Error) {
|
|
||||||
console.debug("Error:"+ component.errorString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var views = mainSplit.addComponent(component, options)
|
|
||||||
views.menuItem.path = path
|
views.menuItem.path = path
|
||||||
|
|
||||||
mainSplit.views.push(views);
|
mainSplit.views.push(views);
|
||||||
@ -74,10 +72,42 @@ ApplicationWindow {
|
|||||||
gui.addPlugin(path)
|
gui.addPlugin(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return views.view
|
return views
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuBar {
|
function addPlugin(path, options) {
|
||||||
|
try {
|
||||||
|
if(typeof(path) === "string" && /^https?/.test(path)) {
|
||||||
|
console.log('load http')
|
||||||
|
Http.request(path, function(o) {
|
||||||
|
if(o.status === 200) {
|
||||||
|
var view = Qt.createQmlObject(o.responseText, mainView, path)
|
||||||
|
addViews(view, path, options)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var component = Qt.createComponent(path);
|
||||||
|
if(component.status != Component.Ready) {
|
||||||
|
if(component.status == Component.Error) {
|
||||||
|
ethx.note("error: ", component.errorString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var view = mainView.createView(component, options)
|
||||||
|
var views = addViews(view, path, options)
|
||||||
|
|
||||||
|
return views
|
||||||
|
} catch(e) {
|
||||||
|
ethx.note(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menuBar: MenuBar {
|
||||||
Menu {
|
Menu {
|
||||||
title: "File"
|
title: "File"
|
||||||
MenuItem {
|
MenuItem {
|
||||||
@ -88,16 +118,18 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
/*
|
||||||
text: "Browser"
|
MenuItem {
|
||||||
onTriggered: eth.openBrowser()
|
text: "Browser"
|
||||||
}
|
onTriggered: eth.openBrowser()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Add plugin"
|
text: "Add plugin"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
generalFileDialog.show(true, function(path) {
|
generalFileDialog.show(true, function(path) {
|
||||||
addPlugin(path, {canClose: true, section: "apps"})
|
addPlugin(path, {close: true, section: "apps"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,11 +154,13 @@ ApplicationWindow {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
title: "Developer"
|
title: "Developer"
|
||||||
MenuItem {
|
MenuItem {
|
||||||
|
iconSource: "../icecream.png"
|
||||||
text: "Debugger"
|
text: "Debugger"
|
||||||
shortcut: "Ctrl+d"
|
shortcut: "Ctrl+d"
|
||||||
onTriggered: eth.startDebugger()
|
onTriggered: eth.startDebugger()
|
||||||
@ -157,6 +191,23 @@ ApplicationWindow {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MenuSeparator {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
MenuItem {
|
||||||
|
id: miningSpeed
|
||||||
|
text: "Mining: Turbo"
|
||||||
|
onTriggered: {
|
||||||
|
gui.toggleTurboMining()
|
||||||
|
if(text == "Mining: Turbo") {
|
||||||
|
text = "Mining: Normal";
|
||||||
|
} else {
|
||||||
|
text = "Mining: Turbo";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
@ -187,39 +238,41 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Menu {
|
||||||
|
title: "GLOBAL SHORTCUTS"
|
||||||
statusBar: StatusBar {
|
visible: false
|
||||||
height: 32
|
MenuItem {
|
||||||
RowLayout {
|
visible: false
|
||||||
Button {
|
shortcut: "Ctrl+l"
|
||||||
id: miningButton
|
onTriggered: {
|
||||||
text: "Start Mining"
|
url.focus = true
|
||||||
onClicked: {
|
|
||||||
gui.toggleMining()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: importAppButton
|
|
||||||
text: "Browser"
|
|
||||||
onClicked: {
|
|
||||||
eth.openBrowser()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Label {
|
|
||||||
id: walletValueLabel
|
|
||||||
|
|
||||||
font.pixelSize: 10
|
|
||||||
styleColor: "#797979"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statusBar: StatusBar {
|
||||||
|
//height: 32
|
||||||
|
id: statusBar
|
||||||
|
Label {
|
||||||
|
//y: 6
|
||||||
|
id: walletValueLabel
|
||||||
|
|
||||||
|
font.pixelSize: 10
|
||||||
|
styleColor: "#797979"
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
y: 6
|
//y: 6
|
||||||
|
objectName: "miningLabel"
|
||||||
|
visible: true
|
||||||
|
font.pixelSize: 10
|
||||||
|
anchors.right: lastBlockLabel.left
|
||||||
|
anchors.rightMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
//y: 6
|
||||||
id: lastBlockLabel
|
id: lastBlockLabel
|
||||||
objectName: "lastBlockLabel"
|
objectName: "lastBlockLabel"
|
||||||
visible: true
|
visible: true
|
||||||
@ -230,19 +283,27 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProgressBar {
|
ProgressBar {
|
||||||
id: syncProgressIndicator
|
id: downloadIndicator
|
||||||
visible: false
|
value: 0
|
||||||
objectName: "syncProgressIndicator"
|
objectName: "downloadIndicator"
|
||||||
y: 3
|
y: -4
|
||||||
width: 140
|
x: statusBar.width / 2 - this.width / 2
|
||||||
indeterminate: true
|
width: 160
|
||||||
anchors.right: peerGroup.left
|
|
||||||
anchors.rightMargin: 5
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
objectName: "downloadLabel"
|
||||||
|
//y: 7
|
||||||
|
anchors.left: downloadIndicator.right
|
||||||
|
anchors.leftMargin: 5
|
||||||
|
font.pixelSize: 10
|
||||||
|
text: "0 / 0"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: peerGroup
|
id: peerGroup
|
||||||
y: 7
|
//y: 7
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
MouseArea {
|
MouseArea {
|
||||||
onDoubleClicked: peerWindow.visible = true
|
onDoubleClicked: peerWindow.visible = true
|
||||||
@ -251,14 +312,9 @@ ApplicationWindow {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: peerLabel
|
id: peerLabel
|
||||||
font.pixelSize: 8
|
font.pixelSize: 10
|
||||||
text: "0 / 0"
|
text: "0 / 0"
|
||||||
}
|
}
|
||||||
Image {
|
|
||||||
id: peerImage
|
|
||||||
width: 10; height: 10
|
|
||||||
source: "../network.png"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,27 +333,20 @@ ApplicationWindow {
|
|||||||
function setView(view, menu) {
|
function setView(view, menu) {
|
||||||
for(var i = 0; i < views.length; i++) {
|
for(var i = 0; i < views.length; i++) {
|
||||||
views[i].view.visible = false
|
views[i].view.visible = false
|
||||||
|
views[i].menuItem.setSelection(false)
|
||||||
views[i].menuItem.border.color = "#00000000"
|
|
||||||
views[i].menuItem.color = "#00000000"
|
|
||||||
}
|
}
|
||||||
view.visible = true
|
view.visible = true
|
||||||
|
|
||||||
menu.border.color = "#CCCCCC"
|
//menu.border.color = "#CCCCCC"
|
||||||
menu.color = "#FFFFFFFF"
|
//menu.color = "#FFFFFFFF"
|
||||||
|
menu.setSelection(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
function addComponent(component, options) {
|
function addComponent(view, options) {
|
||||||
var view = mainView.createView(component, options)
|
|
||||||
view.visible = false
|
view.visible = false
|
||||||
view.anchors.fill = mainView
|
view.anchors.fill = mainView
|
||||||
|
|
||||||
if( !view.hasOwnProperty("iconSource") ) {
|
var menuItem = menu.createMenuItem(view, options);
|
||||||
console.log("Could not load plugin. Property 'iconSourc' not found on view.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var menuItem = menu.createMenuItem(view.iconSource, view, options);
|
|
||||||
if( view.hasOwnProperty("menuItem") ) {
|
if( view.hasOwnProperty("menuItem") ) {
|
||||||
view.menuItem = menuItem;
|
view.menuItem = menuItem;
|
||||||
}
|
}
|
||||||
@ -319,8 +368,8 @@ ApplicationWindow {
|
|||||||
********************/
|
********************/
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: menu
|
id: menu
|
||||||
Layout.minimumWidth: 180
|
Layout.minimumWidth: 210
|
||||||
Layout.maximumWidth: 180
|
Layout.maximumWidth: 210
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
color: "#ececec"
|
color: "#ececec"
|
||||||
|
|
||||||
@ -330,16 +379,17 @@ ApplicationWindow {
|
|||||||
id: menuItem
|
id: menuItem
|
||||||
property var view;
|
property var view;
|
||||||
property var path;
|
property var path;
|
||||||
|
property var closable;
|
||||||
|
|
||||||
property alias title: label.text
|
property alias title: label.text
|
||||||
property alias icon: icon.source
|
property alias icon: icon.source
|
||||||
property alias secondaryTitle: secondary.text
|
property alias secondaryTitle: secondary.text
|
||||||
|
function setSelection(on) {
|
||||||
|
sel.visible = on
|
||||||
|
}
|
||||||
|
|
||||||
width: 180
|
width: 206
|
||||||
height: 28
|
height: 28
|
||||||
border.color: "#00000000"
|
|
||||||
border.width: 1
|
|
||||||
radius: 5
|
|
||||||
color: "#00000000"
|
color: "#00000000"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@ -347,6 +397,50 @@ ApplicationWindow {
|
|||||||
leftMargin: 4
|
leftMargin: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: sel
|
||||||
|
visible: false
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "#00000000"
|
||||||
|
Rectangle {
|
||||||
|
id: r
|
||||||
|
anchors.fill: parent
|
||||||
|
border.color: "#CCCCCC"
|
||||||
|
border.width: 1
|
||||||
|
radius: 5
|
||||||
|
color: "#FFFFFFFF"
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
top: r.top
|
||||||
|
bottom: r.bottom
|
||||||
|
right: r.right
|
||||||
|
}
|
||||||
|
width: 10
|
||||||
|
color: "#FFFFFFFF"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
height: 1
|
||||||
|
color: "#CCCCCC"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
height: 1
|
||||||
|
color: "#CCCCCC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
@ -396,6 +490,8 @@ ApplicationWindow {
|
|||||||
|
|
||||||
|
|
||||||
function closeApp() {
|
function closeApp() {
|
||||||
|
if(!this.closable) { return; }
|
||||||
|
|
||||||
if(this.view.hasOwnProperty("onDestroy")) {
|
if(this.view.hasOwnProperty("onDestroy")) {
|
||||||
this.view.onDestroy.call(this.view)
|
this.view.onDestroy.call(this.view)
|
||||||
}
|
}
|
||||||
@ -407,7 +503,7 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMenuItem(icon, view, options) {
|
function createMenuItem(view, options) {
|
||||||
if(options === undefined) {
|
if(options === undefined) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
@ -429,20 +525,13 @@ ApplicationWindow {
|
|||||||
|
|
||||||
comp.view = view
|
comp.view = view
|
||||||
comp.title = view.title
|
comp.title = view.title
|
||||||
comp.icon = view.iconSource
|
|
||||||
/*
|
if(view.hasOwnProperty("iconSource")) {
|
||||||
if(view.secondary !== undefined) {
|
comp.icon = view.iconSource;
|
||||||
comp.secondary = view.secondary
|
}
|
||||||
}
|
comp.closable = options.close;
|
||||||
*/
|
|
||||||
|
|
||||||
return comp
|
return comp
|
||||||
|
|
||||||
/*
|
|
||||||
if(options.canClose) {
|
|
||||||
//comp.closeButton.visible = options.canClose
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@ -517,22 +606,77 @@ ApplicationWindow {
|
|||||||
* Main view
|
* Main view
|
||||||
********************/
|
********************/
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: mainView
|
|
||||||
color: "#00000000"
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.left: menu.right
|
anchors.left: menu.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
color: "#00000000"
|
||||||
|
|
||||||
function createView(component) {
|
Rectangle {
|
||||||
var view = component.createObject(mainView)
|
id: urlPane
|
||||||
|
height: 40
|
||||||
|
color: "#00000000"
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 5
|
||||||
|
rightMargin: 5
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 5
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: url
|
||||||
|
objectName: "url"
|
||||||
|
placeholderText: "DApp URL"
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 5
|
||||||
|
rightMargin: 5
|
||||||
|
leftMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
return view;
|
Keys.onReturnPressed: {
|
||||||
|
if(/^https?/.test(this.text)) {
|
||||||
|
root.browser.view.open(this.text);
|
||||||
|
mainSplit.setView(root.browser.view, root.browser.menuItem);
|
||||||
|
} else {
|
||||||
|
addPlugin(this.text, {close: true, section: "apps"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Border
|
||||||
|
Rectangle {
|
||||||
|
id: divider
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: urlPane.bottom
|
||||||
|
}
|
||||||
|
z: -1
|
||||||
|
height: 1
|
||||||
|
color: "#CCCCCC"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mainView
|
||||||
|
color: "#00000000"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.top: divider.bottom
|
||||||
|
|
||||||
|
function createView(component) {
|
||||||
|
var view = component.createObject(mainView)
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -564,7 +708,7 @@ ApplicationWindow {
|
|||||||
if(ext == "html" || ext == "htm") {
|
if(ext == "html" || ext == "htm") {
|
||||||
eth.openHtml(path)
|
eth.openHtml(path)
|
||||||
}else if(ext == "qml"){
|
}else if(ext == "qml"){
|
||||||
addPlugin(path, {canClose: true, section: "apps"})
|
addPlugin(path, {close: true, section: "apps"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,7 +728,7 @@ ApplicationWindow {
|
|||||||
|
|
||||||
function addPeer(peer) {
|
function addPeer(peer) {
|
||||||
// We could just append the whole peer object but it cries if you try to alter them
|
// We could just append the whole peer object but it cries if you try to alter them
|
||||||
peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version})
|
peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version, caps: peer.caps})
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetPeers(){
|
function resetPeers(){
|
||||||
@ -627,10 +771,11 @@ ApplicationWindow {
|
|||||||
id: peerTable
|
id: peerTable
|
||||||
model: peerModel
|
model: peerModel
|
||||||
TableViewColumn{width: 100; role: "ip" ; title: "IP" }
|
TableViewColumn{width: 100; role: "ip" ; title: "IP" }
|
||||||
TableViewColumn{width: 60; role: "port" ; title: "Port" }
|
TableViewColumn{width: 60; role: "port" ; title: "Port" }
|
||||||
TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" }
|
TableViewColumn{width: 140; role: "lastResponse"; title: "Last event" }
|
||||||
TableViewColumn{width: 100; role: "latency"; title: "Latency" }
|
TableViewColumn{width: 100; role: "latency"; title: "Latency" }
|
||||||
TableViewColumn{width: 260; role: "version" ; title: "Version" }
|
TableViewColumn{width: 260; role: "version" ; title: "Version" }
|
||||||
|
TableViewColumn{width: 80; role: "caps" ; title: "Capabilities" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -641,8 +786,8 @@ ApplicationWindow {
|
|||||||
title: "About"
|
title: "About"
|
||||||
minimumWidth: 350
|
minimumWidth: 350
|
||||||
maximumWidth: 350
|
maximumWidth: 350
|
||||||
maximumHeight: 200
|
maximumHeight: 280
|
||||||
minimumHeight: 200
|
minimumHeight: 280
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: aboutIcon
|
id: aboutIcon
|
||||||
@ -652,14 +797,16 @@ ApplicationWindow {
|
|||||||
smooth: true
|
smooth: true
|
||||||
source: "../facet.png"
|
source: "../facet.png"
|
||||||
x: 10
|
x: 10
|
||||||
y: 10
|
y: 30
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.left: aboutIcon.right
|
anchors.left: aboutIcon.right
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 30
|
||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
text: "<h2>Ethereal - Aitne</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Maran Hidskes<br>Viktor Trón<br>"
|
text: "<h2>Mist (0.7.10)</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br>Felix Lange<br>Taylor Gerring<br>Daniel Nagy<br><h3>UX</h3>Alex van de Sande<br>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,29 +843,45 @@ ApplicationWindow {
|
|||||||
Window {
|
Window {
|
||||||
id: addPeerWin
|
id: addPeerWin
|
||||||
visible: false
|
visible: false
|
||||||
minimumWidth: 230
|
minimumWidth: 300
|
||||||
maximumWidth: 230
|
maximumWidth: 300
|
||||||
maximumHeight: 50
|
maximumHeight: 50
|
||||||
minimumHeight: 50
|
minimumHeight: 50
|
||||||
|
title: "Connect to peer"
|
||||||
|
|
||||||
TextField {
|
ComboBox {
|
||||||
id: addrField
|
id: addrField
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
anchors.right: addPeerButton.left
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
placeholderText: "address:port"
|
anchors.rightMargin: 10
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
eth.connectToPeer(addrField.text)
|
eth.connectToPeer(addrField.currentText)
|
||||||
addPeerWin.visible = false
|
addPeerWin.visible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editable: true
|
||||||
|
model: ListModel { id: pastPeers }
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
var ips = eth.pastPeers()
|
||||||
|
for(var i = 0; i < ips.length; i++) {
|
||||||
|
pastPeers.append({text: ips.get(i)})
|
||||||
|
}
|
||||||
|
|
||||||
|
pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
anchors.left: addrField.right
|
id: addPeerButton
|
||||||
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.leftMargin: 5
|
anchors.rightMargin: 10
|
||||||
text: "Add"
|
text: "Add"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
eth.connectToPeer(addrField.text)
|
eth.connectToPeer(addrField.currentText)
|
||||||
addPeerWin.visible = false
|
addPeerWin.visible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,9 +8,7 @@ import Ethereum 1.0
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
property var title: "Network"
|
property var title: "Block Chain"
|
||||||
property var iconSource: "../net.png"
|
|
||||||
property var secondary: "Hi"
|
|
||||||
property var menuItem
|
property var menuItem
|
||||||
|
|
||||||
objectName: "chainView"
|
objectName: "chainView"
|
||||||
@ -65,12 +63,12 @@ Rectangle {
|
|||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
property var row;
|
property var row
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Details"
|
text: "Details"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
popup.visible = true
|
popup.visible = true
|
||||||
popup.setDetails(blockModel.get(this.row))
|
popup.setDetails(blockModel.get(contextMenu.row))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +77,7 @@ Rectangle {
|
|||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Copy"
|
text: "Copy"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
copyToClipboard(blockModel.get(this.row).hash)
|
copyToClipboard(blockModel.get(contextMenu.row).hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +85,7 @@ Rectangle {
|
|||||||
text: "Dump State"
|
text: "Dump State"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
generalFileDialog.show(false, function(path) {
|
generalFileDialog.show(false, function(path) {
|
||||||
var hash = blockModel.get(this.row).hash;
|
var hash = blockModel.get(contextMenu.row).hash;
|
||||||
|
|
||||||
gui.dumpState(hash, path);
|
gui.dumpState(hash, path);
|
||||||
});
|
});
|
||||||
@ -99,23 +97,22 @@ Rectangle {
|
|||||||
|
|
||||||
|
|
||||||
function addBlock(block, initial) {
|
function addBlock(block, initial) {
|
||||||
var txs = JSON.parse(block.transactions);
|
|
||||||
var amount = 0
|
|
||||||
if(initial == undefined){
|
if(initial == undefined){
|
||||||
initial = false
|
initial = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if(txs != null){
|
var amount = block.transactions.length;
|
||||||
amount = txs.length
|
var txs = [];
|
||||||
}
|
for(var i = 0; i < block.transactions.length; i++) {
|
||||||
|
var tx = JSON.parse(block.transactions.getAsJson(i));
|
||||||
|
txs.push(tx);
|
||||||
|
}
|
||||||
|
|
||||||
if(initial){
|
if(initial){
|
||||||
blockModel.append({number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
|
blockModel.append({raw: block.raw, bloom: block.bloom, size: block.size, number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
|
||||||
} else {
|
} else {
|
||||||
blockModel.insert(0, {number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
|
blockModel.insert(0, {bloom: block.bloom, size: block.size, number: block.number, name: block.name, gasLimit: block.gasLimit, gasUsed: block.gasUsed, coinbase: block.coinbase, hash: block.hash, txs: txs, txAmount: amount, time: block.time, prettyTime: convertToPretty(block.time)})
|
||||||
}
|
}
|
||||||
|
|
||||||
//root.secondary.text = "#" + block.number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
@ -137,8 +134,9 @@ Rectangle {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
Text { text: '<h3>Block details</h3>'; color: "#F2F2F2"}
|
Text { text: '<h3>Block details</h3>'; color: "#F2F2F2"}
|
||||||
Text { text: '<b>Block number:</b> ' + number; color: "#F2F2F2"}
|
Text { text: '<b>Block number:</b> ' + number + " (Size: " + size + ")"; color: "#F2F2F2"}
|
||||||
Text { text: '<b>Hash:</b> ' + hash; color: "#F2F2F2"}
|
Text { text: '<b>Hash:</b> ' + hash; color: "#F2F2F2"}
|
||||||
|
Text { text: '<b>Bloom:</b> ' + bloom; color: "#F2F2F2"}
|
||||||
Text { text: '<b>Coinbase:</b> <' + name + '> ' + coinbase; color: "#F2F2F2"}
|
Text { text: '<b>Coinbase:</b> <' + name + '> ' + coinbase; color: "#F2F2F2"}
|
||||||
Text { text: '<b>Block found at:</b> ' + prettyTime; color: "#F2F2F2"}
|
Text { text: '<b>Block found at:</b> ' + prettyTime; color: "#F2F2F2"}
|
||||||
Text { text: '<b>Gas used:</b> ' + gasUsed + " / " + gasLimit; color: "#F2F2F2"}
|
Text { text: '<b>Gas used:</b> ' + gasUsed + " / " + gasLimit; color: "#F2F2F2"}
|
||||||
@ -213,7 +211,7 @@ Rectangle {
|
|||||||
anchors.topMargin: 10
|
anchors.topMargin: 10
|
||||||
text: "Debug contract"
|
text: "Debug contract"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if(tx.createsContract){
|
if(tx && tx.createsContract){
|
||||||
eth.startDbWithCode(tx.rawData)
|
eth.startDbWithCode(tx.rawData)
|
||||||
}else {
|
}else {
|
||||||
eth.startDbWithContractAndData(tx.address, tx.rawData)
|
eth.startDbWithContractAndData(tx.address, tx.rawData)
|
||||||
@ -225,11 +223,17 @@ Rectangle {
|
|||||||
text: "Contract"
|
text: "Contract"
|
||||||
anchors.top: contractLabel.bottom
|
anchors.top: contractLabel.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.bottom: popup.bottom
|
anchors.right: parent.right
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
width: parent.width - 30
|
|
||||||
height: 80
|
height: 80
|
||||||
anchors.leftMargin: 10
|
}
|
||||||
|
TextArea {
|
||||||
|
id: dumpData
|
||||||
|
anchors.top: contractData.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
height: 300
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property var transactionModel: ListModel {
|
property var transactionModel: ListModel {
|
||||||
@ -238,19 +242,20 @@ Rectangle {
|
|||||||
property var singleBlock: ListModel {
|
property var singleBlock: ListModel {
|
||||||
id: singleBlock
|
id: singleBlock
|
||||||
}
|
}
|
||||||
function setDetails(block){
|
function setDetails(bl){
|
||||||
singleBlock.set(0,block)
|
singleBlock.set(0, bl)
|
||||||
popup.height = 300
|
popup.height = 300
|
||||||
transactionModel.clear()
|
transactionModel.clear()
|
||||||
if(block.txs != undefined){
|
if(bl.txs !== undefined){
|
||||||
for(var i = 0; i < block.txs.count; ++i) {
|
for(var i = 0; i < bl.txs.count; i++) {
|
||||||
transactionModel.insert(0, block.txs.get(i))
|
transactionModel.insert(0, bl.txs.get(i))
|
||||||
}
|
}
|
||||||
if(block.txs.get(0).data){
|
if(bl.txs.count > 0 && bl.txs.get(0).data){
|
||||||
popup.showContractData(block.txs.get(0))
|
popup.showContractData(bl.txs.get(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
txView.forceActiveFocus()
|
txView.forceActiveFocus()
|
||||||
|
dumpData.text = bl.raw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,6 @@ import QtQuick.Controls.Styles 1.1
|
|||||||
import Ethereum 1.0
|
import Ethereum 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property var iconSource: "../tx.png"
|
|
||||||
property var title: "Transactions"
|
property var title: "Transactions"
|
||||||
property var menuItem
|
property var menuItem
|
||||||
|
|
@ -7,8 +7,7 @@ import QtQuick.Controls.Styles 1.1
|
|||||||
import Ethereum 1.0
|
import Ethereum 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property var title: "Information"
|
property var title: "Debug Info"
|
||||||
property var iconSource: "../heart.png"
|
|
||||||
property var menuItem
|
property var menuItem
|
||||||
|
|
||||||
objectName: "infoView"
|
objectName: "infoView"
|
||||||
@ -44,71 +43,84 @@ Rectangle {
|
|||||||
gui.setCustomIdentifier(text)
|
gui.setCustomIdentifier(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
property var addressModel: ListModel {
|
TextArea {
|
||||||
id: addressModel
|
objectName: "statsPane"
|
||||||
}
|
width: parent.width
|
||||||
TableView {
|
height: 200
|
||||||
id: addressView
|
selectByMouse: true
|
||||||
width: parent.width
|
readOnly: true
|
||||||
height: 200
|
font.family: "Courier"
|
||||||
anchors.bottom: logLayout.top
|
|
||||||
TableViewColumn{ role: "name"; title: "name" }
|
|
||||||
TableViewColumn{ role: "address"; title: "address"; width: 300}
|
|
||||||
|
|
||||||
model: addressModel
|
|
||||||
itemDelegate: Item {
|
|
||||||
Text {
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
leftMargin: 10
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
color: styleData.textColor
|
|
||||||
elide: styleData.elideMode
|
|
||||||
text: styleData.value
|
|
||||||
font.pixelSize: 11
|
|
||||||
MouseArea {
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
propagateComposedEvents: true
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
addressView.selection.clear()
|
|
||||||
addressView.selection.select(styleData.row)
|
|
||||||
|
|
||||||
if(mouse.button == Qt.RightButton) {
|
|
||||||
contextMenu.row = styleData.row;
|
|
||||||
contextMenu.popup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
id: contextMenu
|
|
||||||
property var row;
|
|
||||||
|
|
||||||
MenuItem {
|
|
||||||
text: "Copy"
|
|
||||||
onTriggered: {
|
|
||||||
copyToClipboard(addressModel.get(this.row).address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var logModel: ListModel {
|
|
||||||
id: logModel
|
|
||||||
}
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: logLayout
|
id: logLayout
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 200
|
height: 200
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: addressView
|
||||||
|
width: parent.width
|
||||||
|
height: 200
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: logLevelSlider.left
|
||||||
|
bottom: parent.bottom
|
||||||
|
top: parent.top
|
||||||
|
}
|
||||||
|
TableViewColumn{ role: "name"; title: "name" }
|
||||||
|
TableViewColumn{ role: "address"; title: "address"; width: 300}
|
||||||
|
|
||||||
|
property var addressModel: ListModel {
|
||||||
|
id: addressModel
|
||||||
|
}
|
||||||
|
|
||||||
|
model: addressModel
|
||||||
|
itemDelegate: Item {
|
||||||
|
Text {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 10
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
color: styleData.textColor
|
||||||
|
elide: styleData.elideMode
|
||||||
|
text: styleData.value
|
||||||
|
font.pixelSize: 11
|
||||||
|
MouseArea {
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
propagateComposedEvents: true
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
addressView.selection.clear()
|
||||||
|
addressView.selection.select(styleData.row)
|
||||||
|
|
||||||
|
if(mouse.button == Qt.RightButton) {
|
||||||
|
contextMenu.row = styleData.row;
|
||||||
|
contextMenu.popup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
id: contextMenu
|
||||||
|
property var row;
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Copy"
|
||||||
|
onTriggered: {
|
||||||
|
copyToClipboard(addressModel.get(this.row).address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
TableView {
|
TableView {
|
||||||
id: logView
|
id: logView
|
||||||
headerVisible: false
|
headerVisible: false
|
||||||
@ -123,6 +135,7 @@ Rectangle {
|
|||||||
|
|
||||||
model: logModel
|
model: logModel
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
Slider {
|
Slider {
|
||||||
id: logLevelSlider
|
id: logLevelSlider
|
||||||
@ -148,6 +161,10 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property var logModel: ListModel {
|
||||||
|
id: logModel
|
||||||
|
}
|
||||||
|
|
||||||
function addDebugMessage(message){
|
function addDebugMessage(message){
|
||||||
debuggerLog.append({value: message})
|
debuggerLog.append({value: message})
|
||||||
}
|
}
|
BIN
cmd/mist/assets/qml/views/jeffcoin/jeff.png
Normal file
After Width: | Height: | Size: 82 KiB |
190
cmd/mist/assets/qml/views/jeffcoin/jeffcoin.qml
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Dialogs 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
property var title: "JeffCoin"
|
||||||
|
property var iconSource: "./views/jeffcoin/jeff.png"
|
||||||
|
property var menuItem
|
||||||
|
property var filter
|
||||||
|
property var address: "fc0a9436890478bb9b1c6ed7455c2535366f4a99"
|
||||||
|
|
||||||
|
function insertTx(message, blockNumber) {
|
||||||
|
if(!message) return;
|
||||||
|
|
||||||
|
var from = message.from
|
||||||
|
var to = message.input.substr(24, 40)
|
||||||
|
var value = eth.fromNumber(message.input.substr(64, 64))
|
||||||
|
|
||||||
|
var me = eth.key().address;
|
||||||
|
if((to == me|| from == me) && message.input.length == 128) {
|
||||||
|
var to = eth.lookupName(to)
|
||||||
|
var from = eth.lookupName(from)
|
||||||
|
txModel.insert(0, {confirmations: blockNumber - message.number, from: from, to: to, value: value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBalance() {
|
||||||
|
var jeffCoinAmount = eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF"
|
||||||
|
menuItem.secondaryTitle = jeffCoinAmount
|
||||||
|
|
||||||
|
balance.text = "<b>Balance</b>: " + jeffCoinAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onReady() {
|
||||||
|
setBalance()
|
||||||
|
|
||||||
|
filter = new ethx.watch({latest: -1, to: address})
|
||||||
|
filter.changed(function(messages) {
|
||||||
|
setBalance()
|
||||||
|
|
||||||
|
var blockNumber = eth.block(-1).number;
|
||||||
|
for(var i = 0; i < messages.length; i++) {
|
||||||
|
insertTx(messages.get(i), blockNumber);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var blockNumber = eth.block(-1).number;
|
||||||
|
var msgs = filter.messages()
|
||||||
|
for(var i = msgs.length-1; i >= 0; i--) {
|
||||||
|
var message = JSON.parse(msgs.getAsJson(i))
|
||||||
|
|
||||||
|
insertTx(message, blockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
var chainChanged = ethx.watch("chain")
|
||||||
|
chainChanged.changed(function() {
|
||||||
|
for(var i = 0; i < txModel.count; i++) {
|
||||||
|
var entry = txModel.get(i);
|
||||||
|
entry.confirmations++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDestroy() {
|
||||||
|
filter.uninstall()
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 10
|
||||||
|
y: 40
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: balance
|
||||||
|
text: "<b>Balance</b>: " + eth.fromNumber(eth.storageAt(address, eth.key().address)) + " JΞF"
|
||||||
|
font.pixelSize: 24
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: newTxPane
|
||||||
|
color: "#ececec"
|
||||||
|
border.color: "#cccccc"
|
||||||
|
border.width: 1
|
||||||
|
anchors {
|
||||||
|
top: balance.bottom
|
||||||
|
topMargin: 10
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 5
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 5
|
||||||
|
}
|
||||||
|
height: 100
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: amountFields
|
||||||
|
spacing: 10
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 20
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "JΞF "
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's something off with the row layout where textfields won't listen to the width setting
|
||||||
|
Rectangle {
|
||||||
|
width: 50
|
||||||
|
height: 20
|
||||||
|
TextField {
|
||||||
|
id: txValue
|
||||||
|
width: parent.width
|
||||||
|
placeholderText: "0.00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: toFields
|
||||||
|
spacing: 10
|
||||||
|
anchors {
|
||||||
|
top: amountFields.bottom
|
||||||
|
topMargin: 5
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "To"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 200
|
||||||
|
height: 20
|
||||||
|
TextField {
|
||||||
|
id: txTo
|
||||||
|
width: parent.width
|
||||||
|
placeholderText: "Address or name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Send"
|
||||||
|
onClicked: {
|
||||||
|
var lookup = eth.lookupAddress(address)
|
||||||
|
if(lookup.length == 0)
|
||||||
|
lookup = address
|
||||||
|
|
||||||
|
eth.transact({from: eth.key().privateKey, to:lookup, gas: "9000", gasPrice: "10000000000000", data: ["0x"+txTo.text, txValue.text]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: newTxPane.bottom
|
||||||
|
topMargin: 10
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
TableView {
|
||||||
|
id: txTableView
|
||||||
|
anchors.fill : parent
|
||||||
|
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
|
||||||
|
TableViewColumn{ role: "from" ; title: "From" ; width: 280 }
|
||||||
|
TableViewColumn{ role: "to" ; title: "To" ; width: 280 }
|
||||||
|
TableViewColumn{ role: "confirmations" ; title: "Confirmations" ; width: 100 }
|
||||||
|
|
||||||
|
model: ListModel {
|
||||||
|
id: txModel
|
||||||
|
Component.onCompleted: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
254
cmd/mist/assets/qml/views/miner.qml
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Dialogs 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import QtQuick.Controls.Styles 1.1
|
||||||
|
import Ethereum 1.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
property var title: "Miner"
|
||||||
|
property var iconSource: "../miner.png"
|
||||||
|
property var menuItem
|
||||||
|
|
||||||
|
color: "#00000000"
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 10
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: mainPane
|
||||||
|
color: "#00000000"
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: localTxPane.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: menu
|
||||||
|
height: 25
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: tools
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Start"
|
||||||
|
onClicked: {
|
||||||
|
eth.setGasPrice(minGasPrice.text || "10000000000000");
|
||||||
|
if (eth.toggleMining()) {
|
||||||
|
this.text = "Stop";
|
||||||
|
} else {
|
||||||
|
this.text = "Start";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 2
|
||||||
|
width: 200
|
||||||
|
TextField {
|
||||||
|
id: minGasPrice
|
||||||
|
placeholderText: "Min Gas: 10000000000000"
|
||||||
|
width: 200
|
||||||
|
validator: RegExpValidator { regExp: /\d*/ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: menu.bottom
|
||||||
|
topMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "<b>Merged mining options</b>"
|
||||||
|
}
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: mergedMiningTable
|
||||||
|
height: 300
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
Component {
|
||||||
|
id: checkBoxDelegate
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: test
|
||||||
|
CheckBox {
|
||||||
|
anchors.fill: parent
|
||||||
|
checked: styleData.value
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
var model = mergedMiningModel.get(styleData.row)
|
||||||
|
|
||||||
|
if (this.checked) {
|
||||||
|
model.id = txModel.createLocalTx(model.address, "0", "5000", "0", "")
|
||||||
|
} else {
|
||||||
|
txModel.removeWithId(model.id);
|
||||||
|
model.id = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TableViewColumn{ role: "checked" ; title: "" ; width: 40 ; delegate: checkBoxDelegate }
|
||||||
|
TableViewColumn{ role: "name" ; title: "Name" ; width: 480 }
|
||||||
|
model: ListModel {
|
||||||
|
objectName: "mergedMiningModel"
|
||||||
|
id: mergedMiningModel
|
||||||
|
function addMergedMiningOption(model) {
|
||||||
|
this.append(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
/*
|
||||||
|
// XXX Temp. replace with above eventually
|
||||||
|
var tmpItems = ["JEVCoin", "Some coin", "Other coin", "Etc coin"];
|
||||||
|
var address = "e6716f9544a56c530d868e4bfbacb172315bdead";
|
||||||
|
for (var i = 0; i < tmpItems.length; i++) {
|
||||||
|
mergedMiningModel.append({checked: false, name: tmpItems[i], address: address, id: 0, itemId: i});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: localTxPane
|
||||||
|
color: "#ececec"
|
||||||
|
border.color: "#cccccc"
|
||||||
|
border.width: 1
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
height: 300
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 10
|
||||||
|
anchors.fill: parent
|
||||||
|
RowLayout {
|
||||||
|
id: newLocalTx
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 5
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 5
|
||||||
|
bottomMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Local tx"
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 250
|
||||||
|
color: "#00000000"
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 2
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: to
|
||||||
|
placeholderText: "To"
|
||||||
|
width: 250
|
||||||
|
validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
property var defaultGas: "5000"
|
||||||
|
id: gas
|
||||||
|
placeholderText: "Gas"
|
||||||
|
text: defaultGas
|
||||||
|
validator: RegExpValidator { regExp: /\d*/ }
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: gasPrice
|
||||||
|
placeholderText: "Price"
|
||||||
|
validator: RegExpValidator { regExp: /\d*/ }
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: value
|
||||||
|
placeholderText: "Amount"
|
||||||
|
text: "0"
|
||||||
|
validator: RegExpValidator { regExp: /\d*/ }
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: data
|
||||||
|
placeholderText: "Data"
|
||||||
|
validator: RegExpValidator { regExp: /[abcdefABCDEF1234567890]*/ }
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: "Create"
|
||||||
|
onClicked: {
|
||||||
|
if (to.text.length == 40 && gasPrice.text.length != 0 && value.text.length != 0 && gas.text.length != 0) {
|
||||||
|
txModel.createLocalTx(to.text, gasPrice.text, gas.text, value.text, data.text);
|
||||||
|
|
||||||
|
to.text = ""; gasPrice.text = "";
|
||||||
|
gas.text = gas.defaultGas;
|
||||||
|
value.text = "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: txTableView
|
||||||
|
anchors {
|
||||||
|
top: newLocalTx.bottom
|
||||||
|
topMargin: 5
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
TableViewColumn{ role: "to" ; title: "To" ; width: 480 }
|
||||||
|
TableViewColumn{ role: "gas" ; title: "Gas" ; width: 100 }
|
||||||
|
TableViewColumn{ role: "gasPrice" ; title: "Gas Price" ; width: 100 }
|
||||||
|
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
|
||||||
|
TableViewColumn{ role: "data" ; title: "Data" ; width: 100 }
|
||||||
|
|
||||||
|
model: ListModel {
|
||||||
|
id: txModel
|
||||||
|
Component.onCompleted: {
|
||||||
|
}
|
||||||
|
function removeWithId(id) {
|
||||||
|
for (var i = 0; i < this.count; i++) {
|
||||||
|
if (txModel.get(i).id == id) {
|
||||||
|
this.remove(i);
|
||||||
|
eth.removeLocalTransaction(id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLocalTx(to, gasPrice, gas, value, data) {
|
||||||
|
var id = eth.addLocalTransaction(to, data, gas, gasPrice, value)
|
||||||
|
txModel.insert(0, {to: to, gas: gas, gasPrice: gasPrice, value: value, data: data, id: id});
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,6 @@ import Ethereum 1.0
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property var title: "Pending Transactions"
|
property var title: "Pending Transactions"
|
||||||
property var iconSource: "../tx.png"
|
|
||||||
property var menuItem
|
property var menuItem
|
||||||
|
|
||||||
objectName: "pendingTxView"
|
objectName: "pendingTxView"
|
@ -7,8 +7,7 @@ import QtQuick.Controls.Styles 1.1
|
|||||||
import Ethereum 1.0
|
import Ethereum 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property var iconSource: "../new.png"
|
property var title: "New Transaction"
|
||||||
property var title: "New transaction"
|
|
||||||
property var menuItem
|
property var menuItem
|
||||||
|
|
||||||
objectName: "newTxView"
|
objectName: "newTxView"
|
||||||
@ -115,7 +114,7 @@ Rectangle {
|
|||||||
width: 50
|
width: 50
|
||||||
validator: RegExpValidator { regExp: /\d*/ }
|
validator: RegExpValidator { regExp: /\d*/ }
|
||||||
placeholderText: "Gas"
|
placeholderText: "Gas"
|
||||||
text: "500"
|
text: "5000"
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
id: atLabel
|
id: atLabel
|
||||||
@ -180,6 +179,8 @@ Rectangle {
|
|||||||
txResult.text = "Your transaction has been submitted:\n"
|
txResult.text = "Your transaction has been submitted:\n"
|
||||||
txOutput.text = res[0].address
|
txOutput.text = res[0].address
|
||||||
mainContractColumn.state = "DONE"
|
mainContractColumn.state = "DONE"
|
||||||
|
|
||||||
|
console.log(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,14 +9,20 @@ import Ethereum 1.0
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
property var title: "Wallet"
|
property var title: "Wallet"
|
||||||
property var iconSource: "../wallet.png"
|
property var iconSource: "../facet.png"
|
||||||
property var menuItem
|
property var menuItem
|
||||||
|
|
||||||
objectName: "walletView"
|
objectName: "walletView"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
function onReady() {
|
function onReady() {
|
||||||
menuItem.secondaryTitle = eth.numberToHuman(eth.balanceAt(eth.key().address))
|
setBalance()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBalance() {
|
||||||
|
balance.text = "<b>Balance</b>: " + eth.numberToHuman(eth.balanceAt(eth.key().address))
|
||||||
|
if(menuItem)
|
||||||
|
menuItem.secondaryTitle = eth.numberToHuman(eth.balanceAt(eth.key().address))
|
||||||
}
|
}
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
@ -39,7 +45,6 @@ Rectangle {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: balance
|
id: balance
|
||||||
text: "<b>Balance</b>: " + eth.numberToHuman(eth.balanceAt(eth.key().address))
|
|
||||||
font.pixelSize: 24
|
font.pixelSize: 24
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
@ -126,7 +131,6 @@ Rectangle {
|
|||||||
var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros;
|
var value = txValue.text + denomModel.get(valueDenom.currentIndex).zeros;
|
||||||
var gasPrice = "10000000000000"
|
var gasPrice = "10000000000000"
|
||||||
var res = eth.transact({from: eth.key().privateKey, to: txTo.text, value: value, gas: "500", gasPrice: gasPrice})
|
var res = eth.transact({from: eth.key().privateKey, to: txTo.text, value: value, gas: "500", gasPrice: gasPrice})
|
||||||
console.log(res)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,17 +148,36 @@ Rectangle {
|
|||||||
id: txTableView
|
id: txTableView
|
||||||
anchors.fill : parent
|
anchors.fill : parent
|
||||||
TableViewColumn{ role: "num" ; title: "#" ; width: 30 }
|
TableViewColumn{ role: "num" ; title: "#" ; width: 30 }
|
||||||
TableViewColumn{ role: "from" ; title: "From" ; width: 280 }
|
TableViewColumn{ role: "from" ; title: "From" ; width: 340 }
|
||||||
TableViewColumn{ role: "to" ; title: "To" ; width: 280 }
|
TableViewColumn{ role: "to" ; title: "To" ; width: 340 }
|
||||||
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
|
TableViewColumn{ role: "value" ; title: "Amount" ; width: 100 }
|
||||||
|
|
||||||
model: ListModel {
|
model: ListModel {
|
||||||
id: txModel
|
id: txModel
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
var messages = JSON.parse(eth.messages({latest: -1, from: "e6716f9544a56c530d868e4bfbacb172315bdead"}))
|
var me = eth.key().address;
|
||||||
|
var filterTo = ethx.watch({latest: -1, to: me});
|
||||||
|
var filterFrom = ethx.watch({latest: -1, from: me});
|
||||||
|
filterTo.changed(addTxs)
|
||||||
|
filterFrom.changed(addTxs)
|
||||||
|
|
||||||
|
addTxs(filterTo.messages())
|
||||||
|
addTxs(filterFrom.messages())
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTxs(messages) {
|
||||||
|
setBalance()
|
||||||
|
|
||||||
for(var i = 0; i < messages.length; i++) {
|
for(var i = 0; i < messages.length; i++) {
|
||||||
var message = messages[i];
|
var message = messages.get(i);
|
||||||
this.insert(0, {num: i, from: message.from, to: message.to, value: eth.numberToHuman(message.value)})
|
var to = eth.lookupName(message.to);
|
||||||
|
var from;
|
||||||
|
if(message.from.length == 0) {
|
||||||
|
from = "- MINED -";
|
||||||
|
} else {
|
||||||
|
from = eth.lookupName(message.from);
|
||||||
|
}
|
||||||
|
txModel.insert(0, {num: txModel.count, from: from, to: to, value: eth.numberToHuman(message.value)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
413
cmd/mist/assets/qml/webapp.qml
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import QtWebKit 3.0
|
||||||
|
import QtWebKit.experimental 1.0
|
||||||
|
import QtQuick.Controls 1.0;
|
||||||
|
import QtQuick.Controls.Styles 1.0
|
||||||
|
import QtQuick.Layouts 1.0;
|
||||||
|
import QtQuick.Window 2.1;
|
||||||
|
import Ethereum 1.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: window
|
||||||
|
property var title: "Browser"
|
||||||
|
property var iconSource: "../browser.png"
|
||||||
|
property var menuItem
|
||||||
|
|
||||||
|
property alias url: webview.url
|
||||||
|
property alias webView: webview
|
||||||
|
|
||||||
|
property var cleanPath: false
|
||||||
|
property var open: function(url) {
|
||||||
|
if(!window.cleanPath) {
|
||||||
|
var uri = url;
|
||||||
|
if(!/.*\:\/\/.*/.test(uri)) {
|
||||||
|
uri = "http://" + uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reg = /(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.eth)(.*)/
|
||||||
|
|
||||||
|
if(reg.test(uri)) {
|
||||||
|
uri.replace(reg, function(match, pre, domain, path) {
|
||||||
|
uri = pre;
|
||||||
|
|
||||||
|
var lookup = eth.lookupDomain(domain.substring(0, domain.length - 4));
|
||||||
|
var ip = [];
|
||||||
|
for(var i = 0, l = lookup.length; i < l; i++) {
|
||||||
|
ip.push(lookup.charCodeAt(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ip.length != 0) {
|
||||||
|
uri += lookup;
|
||||||
|
} else {
|
||||||
|
uri += domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
uri += path;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.cleanPath = true;
|
||||||
|
|
||||||
|
webview.url = uri;
|
||||||
|
|
||||||
|
//uriNav.text = uri.text.replace(/(^https?\:\/\/(?:www\.)?)([a-zA-Z0-9_\-]*\.\w{2,3})(.*)/, "$1$2<span style='color:#CCC'>$3</span>");
|
||||||
|
uriNav.text = uri;
|
||||||
|
} else {
|
||||||
|
// Prevent inf loop.
|
||||||
|
window.cleanPath = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
webview.url = "http://etherian.io"
|
||||||
|
}
|
||||||
|
|
||||||
|
signal messages(var messages, int id);
|
||||||
|
onMessages: {
|
||||||
|
// Bit of a cheat to get proper JSON
|
||||||
|
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||||
|
webview.postEvent("messages", [m, id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
objectName: "root"
|
||||||
|
id: root
|
||||||
|
anchors.fill: parent
|
||||||
|
state: "inspectorShown"
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: navBar
|
||||||
|
height: 40
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 7
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: back
|
||||||
|
onClicked: {
|
||||||
|
webview.goBack()
|
||||||
|
}
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Image {
|
||||||
|
source: "../back.png"
|
||||||
|
width: 30
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
anchors {
|
||||||
|
left: back.right
|
||||||
|
right: toggleInspector.left
|
||||||
|
leftMargin: 5
|
||||||
|
rightMargin: 5
|
||||||
|
}
|
||||||
|
text: "http://etherian.io"
|
||||||
|
id: uriNav
|
||||||
|
y: parent.height / 2 - this.height / 2
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
webview.url = this.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: toggleInspector
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
iconSource: "../bug.png"
|
||||||
|
onClicked: {
|
||||||
|
if(inspector.visible == true){
|
||||||
|
inspector.visible = false
|
||||||
|
}else{
|
||||||
|
inspector.visible = true
|
||||||
|
inspector.url = webview.experimental.remoteInspectorUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WebView {
|
||||||
|
objectName: "webView"
|
||||||
|
id: webview
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: parent.bottom
|
||||||
|
top: navBar.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
//property var cleanPath: false
|
||||||
|
onNavigationRequested: {
|
||||||
|
window.open(request.url.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendMessage(data) {
|
||||||
|
webview.experimental.postMessage(JSON.stringify(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
experimental.preferences.javascriptEnabled: true
|
||||||
|
experimental.preferences.navigatorQtObjectEnabled: true
|
||||||
|
experimental.preferences.developerExtrasEnabled: true
|
||||||
|
//experimental.userScripts: ["../ext/qt_messaging_adapter.js", "../ext/q.js", "../ext/big.js", "../ext/string.js", "../ext/html_messaging.js"]
|
||||||
|
experimental.userScripts: ["../ext/q.js", "../ext/eth.js/main.js", "../ext/eth.js/qt.js", "../ext/setup.js"]
|
||||||
|
experimental.onMessageReceived: {
|
||||||
|
console.log("[onMessageReceived]: ", message.data)
|
||||||
|
// TODO move to messaging.js
|
||||||
|
var data = JSON.parse(message.data)
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch(data.call) {
|
||||||
|
case "compile":
|
||||||
|
postData(data._id, eth.compile(data.args[0]))
|
||||||
|
break
|
||||||
|
|
||||||
|
case "coinbase":
|
||||||
|
postData(data._id, eth.coinBase())
|
||||||
|
|
||||||
|
case "account":
|
||||||
|
postData(data._id, eth.key().address);
|
||||||
|
|
||||||
|
case "isListening":
|
||||||
|
postData(data._id, eth.isListening())
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "isMining":
|
||||||
|
postData(data._id, eth.isMining())
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "peerCount":
|
||||||
|
postData(data._id, eth.peerCount())
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "countAt":
|
||||||
|
require(1)
|
||||||
|
postData(data._id, eth.txCountAt(data.args[0]))
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "codeAt":
|
||||||
|
require(1)
|
||||||
|
var code = eth.codeAt(data.args[0])
|
||||||
|
postData(data._id, code);
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "blockByNumber":
|
||||||
|
require(1)
|
||||||
|
var block = eth.blockByNumber(data.args[0])
|
||||||
|
postData(data._id, block)
|
||||||
|
break
|
||||||
|
|
||||||
|
case "blockByHash":
|
||||||
|
require(1)
|
||||||
|
var block = eth.blockByHash(data.args[0])
|
||||||
|
postData(data._id, block)
|
||||||
|
break
|
||||||
|
|
||||||
|
require(2)
|
||||||
|
var block = eth.blockByHash(data.args[0])
|
||||||
|
postData(data._id, block.transactions[data.args[1]])
|
||||||
|
break
|
||||||
|
|
||||||
|
case "transactionByHash":
|
||||||
|
case "transactionByNumber":
|
||||||
|
require(2)
|
||||||
|
|
||||||
|
var block;
|
||||||
|
if (data.call === "transactionByHash")
|
||||||
|
block = eth.blockByHash(data.args[0])
|
||||||
|
else
|
||||||
|
block = eth.blockByNumber(data.args[0])
|
||||||
|
|
||||||
|
var tx = block.transactions.get(data.args[1])
|
||||||
|
|
||||||
|
postData(data._id, tx)
|
||||||
|
break
|
||||||
|
|
||||||
|
case "uncleByHash":
|
||||||
|
case "uncleByNumber":
|
||||||
|
require(2)
|
||||||
|
|
||||||
|
var block;
|
||||||
|
if (data.call === "uncleByHash")
|
||||||
|
block = eth.blockByHash(data.args[0])
|
||||||
|
else
|
||||||
|
block = eth.blockByNumber(data.args[0])
|
||||||
|
|
||||||
|
var uncle = block.uncles.get(data.args[1])
|
||||||
|
|
||||||
|
postData(data._id, uncle)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "transact":
|
||||||
|
require(5)
|
||||||
|
|
||||||
|
var tx = eth.transact(data.args)
|
||||||
|
postData(data._id, tx)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "stateAt":
|
||||||
|
require(2);
|
||||||
|
|
||||||
|
var storage = eth.storageAt(data.args[0], data.args[1]);
|
||||||
|
postData(data._id, storage)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case "call":
|
||||||
|
require(1);
|
||||||
|
var ret = eth.call(data.args)
|
||||||
|
postData(data._id, ret)
|
||||||
|
break
|
||||||
|
|
||||||
|
case "balanceAt":
|
||||||
|
require(1);
|
||||||
|
|
||||||
|
postData(data._id, eth.balanceAt(data.args[0]));
|
||||||
|
break
|
||||||
|
|
||||||
|
case "watch":
|
||||||
|
require(2)
|
||||||
|
eth.watch(data.args[0], data.args[1])
|
||||||
|
|
||||||
|
case "disconnect":
|
||||||
|
require(1)
|
||||||
|
postData(data._id, null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "messages":
|
||||||
|
require(1);
|
||||||
|
|
||||||
|
var messages = JSON.parse(eth.getMessages(data.args[0]))
|
||||||
|
postData(data._id, messages)
|
||||||
|
break
|
||||||
|
|
||||||
|
case "mutan":
|
||||||
|
require(1)
|
||||||
|
|
||||||
|
var code = eth.compileMutan(data.args[0])
|
||||||
|
postData(data._id, "0x"+code)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "newFilterString":
|
||||||
|
require(1)
|
||||||
|
var id = eth.newFilterString(data.args[0])
|
||||||
|
postData(data._id, id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "newFilter":
|
||||||
|
require(1)
|
||||||
|
var id = eth.newFilter(data.args[0])
|
||||||
|
|
||||||
|
postData(data._id, id);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "getMessages":
|
||||||
|
require(1);
|
||||||
|
|
||||||
|
var messages = eth.messages(data.args[0]);
|
||||||
|
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||||
|
postData(data._id, m);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "deleteFilter":
|
||||||
|
require(1);
|
||||||
|
eth.uninstallFilter(data.args[0])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.log(data.call + ": " + e)
|
||||||
|
|
||||||
|
postData(data._id, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function post(seed, data) {
|
||||||
|
postData(data._id, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
function require(args, num) {
|
||||||
|
if(args.length < num) {
|
||||||
|
throw("required argument count of "+num+" got "+args.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function postData(seed, data) {
|
||||||
|
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
|
||||||
|
}
|
||||||
|
function postEvent(event, data) {
|
||||||
|
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWatchedCb(data, id) {
|
||||||
|
var messages = JSON.parse(data)
|
||||||
|
postEvent("watched:"+id, messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNewBlockCb(block) {
|
||||||
|
postEvent("block:new", block)
|
||||||
|
}
|
||||||
|
function onObjectChangeCb(stateObject) {
|
||||||
|
postEvent("object:"+stateObject.address(), stateObject)
|
||||||
|
}
|
||||||
|
function onStorageChangeCb(storageObject) {
|
||||||
|
var ev = ["storage", storageObject.stateAddress, storageObject.address].join(":");
|
||||||
|
postEvent(ev, [storageObject.address, storageObject.value])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: sizeGrip
|
||||||
|
color: "gray"
|
||||||
|
visible: false
|
||||||
|
height: 10
|
||||||
|
anchors {
|
||||||
|
left: root.left
|
||||||
|
right: root.right
|
||||||
|
}
|
||||||
|
y: Math.round(root.height * 2 / 3)
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
drag.target: sizeGrip
|
||||||
|
drag.minimumY: 0
|
||||||
|
drag.maximumY: root.height
|
||||||
|
drag.axis: Drag.YAxis
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebView {
|
||||||
|
id: inspector
|
||||||
|
visible: false
|
||||||
|
anchors {
|
||||||
|
left: root.left
|
||||||
|
right: root.right
|
||||||
|
top: sizeGrip.bottom
|
||||||
|
bottom: root.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "inspectorShown"
|
||||||
|
PropertyChanges {
|
||||||
|
target: inspector
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
134
cmd/mist/bindings.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogPrint writes to the GUI log.
|
||||||
|
func (gui *Gui) LogPrint(level logger.LogLevel, msg string) {
|
||||||
|
/*
|
||||||
|
str := strings.TrimRight(s, "\n")
|
||||||
|
lines := strings.Split(str, "\n")
|
||||||
|
|
||||||
|
view := gui.getObjectByName("infoView")
|
||||||
|
for _, line := range lines {
|
||||||
|
view.Call("addLog", line)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
func (gui *Gui) Transact(recipient, value, gas, gasPrice, d string) (string, error) {
|
||||||
|
var data string
|
||||||
|
if len(recipient) == 0 {
|
||||||
|
code, err := ethutil.Compile(d, false)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
data = ethutil.Bytes2Hex(code)
|
||||||
|
} else {
|
||||||
|
data = ethutil.Bytes2Hex(utils.FormatTransactionData(d))
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.pipe.Transact(gui.privateKey(), recipient, value, gas, gasPrice, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) SetCustomIdentifier(customIdentifier string) {
|
||||||
|
gui.clientIdentity.SetCustomIdentifier(customIdentifier)
|
||||||
|
gui.config.Save("id", customIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) GetCustomIdentifier() string {
|
||||||
|
return gui.clientIdentity.GetCustomIdentifier()
|
||||||
|
}
|
||||||
|
|
||||||
|
// functions that allow Gui to implement interface guilogger.LogSystem
|
||||||
|
func (gui *Gui) SetLogLevel(level logger.LogLevel) {
|
||||||
|
gui.logLevel = level
|
||||||
|
gui.stdLog.SetLogLevel(level)
|
||||||
|
gui.config.Save("loglevel", level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) GetLogLevel() logger.LogLevel {
|
||||||
|
return gui.logLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Gui) AddPlugin(pluginPath string) {
|
||||||
|
self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath}
|
||||||
|
|
||||||
|
json, _ := json.MarshalIndent(self.plugins, "", " ")
|
||||||
|
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Gui) RemovePlugin(pluginPath string) {
|
||||||
|
delete(self.plugins, pluginPath)
|
||||||
|
|
||||||
|
json, _ := json.MarshalIndent(self.plugins, "", " ")
|
||||||
|
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this extra function needed to give int typecast value to gui widget
|
||||||
|
// that sets initial loglevel to default
|
||||||
|
func (gui *Gui) GetLogLevelInt() int {
|
||||||
|
return int(gui.logLevel)
|
||||||
|
}
|
||||||
|
func (self *Gui) DumpState(hash, path string) {
|
||||||
|
var stateDump []byte
|
||||||
|
|
||||||
|
if len(hash) == 0 {
|
||||||
|
stateDump = self.eth.ChainManager().State().Dump()
|
||||||
|
} else {
|
||||||
|
var block *types.Block
|
||||||
|
if hash[0] == '#' {
|
||||||
|
i, _ := strconv.Atoi(hash[1:])
|
||||||
|
block = self.eth.ChainManager().GetBlockByNumber(uint64(i))
|
||||||
|
} else {
|
||||||
|
block = self.eth.ChainManager().GetBlock(ethutil.Hex2Bytes(hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
guilogger.Infof("block err: not found %s\n", hash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stateDump = block.State().Dump()
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile(path[7:], os.O_CREATE|os.O_RDWR, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
guilogger.Infoln("dump err: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
guilogger.Infof("dumped state (%s) to %s\n", hash, path)
|
||||||
|
|
||||||
|
file.Write(stateDump)
|
||||||
|
}
|
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -5,12 +22,13 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/eth-go/ethstate"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethvm"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/utils"
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
"gopkg.in/qml.v1"
|
"gopkg.in/qml.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,10 +37,10 @@ type DebuggerWindow struct {
|
|||||||
engine *qml.Engine
|
engine *qml.Engine
|
||||||
lib *UiLib
|
lib *UiLib
|
||||||
|
|
||||||
vm *ethvm.Vm
|
vm *vm.DebugVm
|
||||||
Db *Debugger
|
Db *Debugger
|
||||||
|
|
||||||
state *ethstate.State
|
state *state.StateDB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
|
func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
|
||||||
@ -36,7 +54,7 @@ func NewDebuggerWindow(lib *UiLib) *DebuggerWindow {
|
|||||||
|
|
||||||
win := component.CreateWindow(nil)
|
win := component.CreateWindow(nil)
|
||||||
|
|
||||||
w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: ðvm.Vm{}}
|
w := &DebuggerWindow{engine: engine, win: win, lib: lib, vm: &vm.DebugVm{}}
|
||||||
w.Db = NewDebugger(w)
|
w.Db = NewDebugger(w)
|
||||||
|
|
||||||
return w
|
return w
|
||||||
@ -63,7 +81,7 @@ func (self *DebuggerWindow) SetData(data string) {
|
|||||||
func (self *DebuggerWindow) SetAsm(data []byte) {
|
func (self *DebuggerWindow) SetAsm(data []byte) {
|
||||||
self.win.Root().Call("clearAsm")
|
self.win.Root().Call("clearAsm")
|
||||||
|
|
||||||
dis := ethchain.Disassemble(data)
|
dis := core.Disassemble(data)
|
||||||
for _, str := range dis {
|
for _, str := range dis {
|
||||||
self.win.Root().Call("setAsm", str)
|
self.win.Root().Call("setAsm", str)
|
||||||
}
|
}
|
||||||
@ -93,9 +111,7 @@ func (self *DebuggerWindow) ClearLog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
|
func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, dataStr string) {
|
||||||
if !self.Db.done {
|
self.Stop()
|
||||||
self.Db.Q <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -125,28 +141,24 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data
|
|||||||
keyPair = self.lib.eth.KeyManager().KeyPair()
|
keyPair = self.lib.eth.KeyManager().KeyPair()
|
||||||
)
|
)
|
||||||
|
|
||||||
state := self.lib.eth.StateManager().TransState()
|
statedb := self.lib.eth.ChainManager().TransState()
|
||||||
account := self.lib.eth.StateManager().TransState().GetAccount(keyPair.Address())
|
account := self.lib.eth.ChainManager().TransState().GetAccount(keyPair.Address())
|
||||||
contract := ethstate.NewStateObject([]byte{0})
|
contract := statedb.NewStateObject([]byte{0})
|
||||||
contract.Balance = value
|
contract.SetCode(script)
|
||||||
|
contract.SetBalance(value)
|
||||||
|
|
||||||
self.SetAsm(script)
|
self.SetAsm(script)
|
||||||
|
|
||||||
block := self.lib.eth.BlockChain().CurrentBlock
|
block := self.lib.eth.ChainManager().CurrentBlock()
|
||||||
|
|
||||||
callerClosure := ethvm.NewClosure(ðstate.Message{}, account, contract, script, gas, gasPrice)
|
env := utils.NewEnv(statedb, block, account.Address(), value)
|
||||||
env := utils.NewEnv(state, block, account.Address(), value)
|
|
||||||
vm := ethvm.New(env)
|
|
||||||
vm.Verbose = true
|
|
||||||
vm.Dbg = self.Db
|
|
||||||
|
|
||||||
self.vm = vm
|
|
||||||
self.Db.done = false
|
|
||||||
self.Logf("callsize %d", len(script))
|
self.Logf("callsize %d", len(script))
|
||||||
go func() {
|
go func() {
|
||||||
ret, g, err := callerClosure.Call(vm, data)
|
ret, err := env.Call(account, contract.Address(), data, gas, gasPrice, ethutil.Big0)
|
||||||
tot := new(big.Int).Mul(g, gasPrice)
|
//ret, g, err := callerClosure.Call(evm, data)
|
||||||
self.Logf("gas usage %v total price = %v (%v)", g, tot, ethutil.CurrencyToString(tot))
|
tot := new(big.Int).Mul(env.Gas, gasPrice)
|
||||||
|
self.Logf("gas usage %v total price = %v (%v)", env.Gas, tot, ethutil.CurrencyToString(tot))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
self.Logln("exited with errors:", err)
|
self.Logln("exited with errors:", err)
|
||||||
} else {
|
} else {
|
||||||
@ -157,7 +169,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.Reset()
|
statedb.Reset()
|
||||||
|
|
||||||
if !self.Db.interrupt {
|
if !self.Db.interrupt {
|
||||||
self.Db.done = true
|
self.Db.done = true
|
||||||
@ -185,6 +197,12 @@ func (self *DebuggerWindow) Continue() {
|
|||||||
self.Next()
|
self.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *DebuggerWindow) Stop() {
|
||||||
|
if !self.Db.done {
|
||||||
|
self.Db.Q <- true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *DebuggerWindow) ExecCommand(command string) {
|
func (self *DebuggerWindow) ExecCommand(command string) {
|
||||||
if len(command) > 0 {
|
if len(command) > 0 {
|
||||||
cmd := strings.Split(command, " ")
|
cmd := strings.Split(command, " ")
|
||||||
@ -246,13 +264,13 @@ type storeVal struct {
|
|||||||
Key, Value string
|
Key, Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Debugger) BreakHook(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.StateObject) bool {
|
func (self *Debugger) BreakHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
|
||||||
self.main.Logln("break on instr:", pc)
|
self.main.Logln("break on instr:", pc)
|
||||||
|
|
||||||
return self.halting(pc, op, mem, stack, stateObject)
|
return self.halting(pc, op, mem, stack, stateObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Debugger) StepHook(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.StateObject) bool {
|
func (self *Debugger) StepHook(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
|
||||||
return self.halting(pc, op, mem, stack, stateObject)
|
return self.halting(pc, op, mem, stack, stateObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,16 +282,27 @@ func (self *Debugger) BreakPoints() []int64 {
|
|||||||
return self.breakPoints
|
return self.breakPoints
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *ethvm.Stack, stateObject *ethstate.StateObject) bool {
|
func (d *Debugger) halting(pc int, op vm.OpCode, mem *vm.Memory, stack *vm.Stack, stateObject *state.StateObject) bool {
|
||||||
d.win.Root().Call("setInstruction", pc)
|
d.win.Root().Call("setInstruction", pc)
|
||||||
d.win.Root().Call("clearMem")
|
d.win.Root().Call("clearMem")
|
||||||
d.win.Root().Call("clearStack")
|
d.win.Root().Call("clearStack")
|
||||||
d.win.Root().Call("clearStorage")
|
d.win.Root().Call("clearStorage")
|
||||||
|
|
||||||
addr := 0
|
addr := 0
|
||||||
for i := 0; i+32 <= mem.Len(); i += 32 {
|
for i := 0; i+16 <= mem.Len(); i += 16 {
|
||||||
d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
|
dat := mem.Data()[i : i+16]
|
||||||
addr++
|
var str string
|
||||||
|
|
||||||
|
for _, d := range dat {
|
||||||
|
if unicode.IsGraphic(rune(d)) {
|
||||||
|
str += string(d)
|
||||||
|
} else {
|
||||||
|
str += "?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("%s % x", str, dat)})
|
||||||
|
addr += 16
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, val := range stack.Data() {
|
for _, val := range stack.Data() {
|
||||||
@ -284,6 +313,12 @@ func (d *Debugger) halting(pc int, op ethvm.OpCode, mem *ethvm.Memory, stack *et
|
|||||||
d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())})
|
d.win.Root().Call("setStorage", storeVal{fmt.Sprintf("% x", key), fmt.Sprintf("% x", node.Str())})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
stackFrameAt := new(big.Int).SetBytes(mem.Get(0, 32))
|
||||||
|
psize := mem.Len() - int(new(big.Int).SetBytes(mem.Get(0, 32)).Uint64())
|
||||||
|
d.win.Root().ObjectByName("stackFrame").Set("text", fmt.Sprintf(`<b>stack ptr</b>: %v`, stackFrameAt))
|
||||||
|
d.win.Root().ObjectByName("stackSize").Set("text", fmt.Sprintf(`<b>stack size</b>: %d`, psize))
|
||||||
|
d.win.Root().ObjectByName("memSize").Set("text", fmt.Sprintf(`<b>mem size</b>: %v`, mem.Len()))
|
||||||
|
|
||||||
out:
|
out:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
53
cmd/mist/errors.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gopkg.in/qml.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ErrorWindow(err error) {
|
||||||
|
engine := qml.NewEngine()
|
||||||
|
component, e := engine.LoadString("local", qmlErr)
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println("err:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
win := component.CreateWindow(nil)
|
||||||
|
win.Root().ObjectByName("label").Set("text", err.Error())
|
||||||
|
win.Show()
|
||||||
|
win.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
const qmlErr = `
|
||||||
|
import QtQuick 2.0; import QtQuick.Controls 1.0;
|
||||||
|
ApplicationWindow {
|
||||||
|
width: 600; height: 150;
|
||||||
|
flags: Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
|
||||||
|
title: "Error"
|
||||||
|
Text {
|
||||||
|
x: parent.width / 2 - this.width / 2;
|
||||||
|
y: parent.height / 2 - this.height / 2;
|
||||||
|
objectName: "label";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
143
cmd/mist/ext_app.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"github.com/ethereum/go-ethereum/javascript"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/ui/qt"
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
|
"gopkg.in/qml.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppContainer interface {
|
||||||
|
Create() error
|
||||||
|
Destroy()
|
||||||
|
|
||||||
|
Window() *qml.Window
|
||||||
|
Engine() *qml.Engine
|
||||||
|
|
||||||
|
NewBlock(*types.Block)
|
||||||
|
NewWatcher(chan bool)
|
||||||
|
Messages(state.Messages, string)
|
||||||
|
Post(string, int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtApplication struct {
|
||||||
|
*xeth.JSXEth
|
||||||
|
eth core.EthManager
|
||||||
|
|
||||||
|
events event.Subscription
|
||||||
|
watcherQuitChan chan bool
|
||||||
|
|
||||||
|
filters map[string]*core.Filter
|
||||||
|
|
||||||
|
container AppContainer
|
||||||
|
lib *UiLib
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
|
||||||
|
return &ExtApplication{
|
||||||
|
JSXEth: xeth.NewJSXEth(lib.eth),
|
||||||
|
eth: lib.eth,
|
||||||
|
watcherQuitChan: make(chan bool),
|
||||||
|
filters: make(map[string]*core.Filter),
|
||||||
|
container: container,
|
||||||
|
lib: lib,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *ExtApplication) run() {
|
||||||
|
// Set the "eth" api on to the containers context
|
||||||
|
context := app.container.Engine().Context()
|
||||||
|
context.SetVar("eth", app)
|
||||||
|
context.SetVar("ui", app.lib)
|
||||||
|
|
||||||
|
err := app.container.Create()
|
||||||
|
if err != nil {
|
||||||
|
guilogger.Errorln(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to events
|
||||||
|
mux := app.lib.eth.EventMux()
|
||||||
|
app.events = mux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
|
||||||
|
|
||||||
|
// Call the main loop
|
||||||
|
go app.mainLoop()
|
||||||
|
|
||||||
|
app.container.NewWatcher(app.watcherQuitChan)
|
||||||
|
|
||||||
|
win := app.container.Window()
|
||||||
|
win.Show()
|
||||||
|
win.Wait()
|
||||||
|
|
||||||
|
app.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *ExtApplication) stop() {
|
||||||
|
app.events.Unsubscribe()
|
||||||
|
|
||||||
|
// Kill the main loop
|
||||||
|
app.watcherQuitChan <- true
|
||||||
|
|
||||||
|
app.container.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *ExtApplication) mainLoop() {
|
||||||
|
for ev := range app.events.Chan() {
|
||||||
|
switch ev := ev.(type) {
|
||||||
|
case core.NewBlockEvent:
|
||||||
|
app.container.NewBlock(ev.Block)
|
||||||
|
|
||||||
|
case state.Messages:
|
||||||
|
for id, filter := range app.filters {
|
||||||
|
msgs := filter.FilterMessages(ev)
|
||||||
|
if len(msgs) > 0 {
|
||||||
|
app.container.Messages(msgs, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ExtApplication) Watch(filterOptions map[string]interface{}, identifier string) {
|
||||||
|
self.filters[identifier] = qt.NewFilterFromMap(filterOptions, self.eth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ExtApplication) GetMessages(object map[string]interface{}) string {
|
||||||
|
filter := qt.NewFilterFromMap(object, self.eth)
|
||||||
|
|
||||||
|
messages := filter.Find()
|
||||||
|
var msgs []javascript.JSMessage
|
||||||
|
for _, m := range messages {
|
||||||
|
msgs = append(msgs, javascript.NewJSMessage(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(msgs)
|
||||||
|
if err != nil {
|
||||||
|
return "{\"error\":" + err.Error() + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(b)
|
||||||
|
}
|
@ -1,37 +1,61 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bitbucket.org/kardianos/osext"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"bitbucket.org/kardianos/osext"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Identifier string
|
var (
|
||||||
var KeyRing string
|
Identifier string
|
||||||
var KeyStore string
|
KeyRing string
|
||||||
var StartRpc bool
|
KeyStore string
|
||||||
var RpcPort int
|
StartRpc bool
|
||||||
var UseUPnP bool
|
StartWebSockets bool
|
||||||
var OutboundPort string
|
RpcPort int
|
||||||
var ShowGenesis bool
|
UseUPnP bool
|
||||||
var AddPeer string
|
OutboundPort string
|
||||||
var MaxPeer int
|
ShowGenesis bool
|
||||||
var GenAddr bool
|
AddPeer string
|
||||||
var UseSeed bool
|
MaxPeer int
|
||||||
var SecretFile string
|
GenAddr bool
|
||||||
var ExportDir string
|
UseSeed bool
|
||||||
var NonInteractive bool
|
SecretFile string
|
||||||
var Datadir string
|
ExportDir string
|
||||||
var LogFile string
|
NonInteractive bool
|
||||||
var ConfigFile string
|
Datadir string
|
||||||
var DebugFile string
|
LogFile string
|
||||||
var LogLevel int
|
ConfigFile string
|
||||||
|
DebugFile string
|
||||||
|
LogLevel int
|
||||||
|
VmType int
|
||||||
|
)
|
||||||
|
|
||||||
// flags specific to gui client
|
// flags specific to gui client
|
||||||
var AssetPath string
|
var AssetPath string
|
||||||
@ -43,7 +67,7 @@ func defaultAssetPath() string {
|
|||||||
// assume a debug build and use the source directory as
|
// assume a debug build and use the source directory as
|
||||||
// asset directory.
|
// asset directory.
|
||||||
pwd, _ := os.Getwd()
|
pwd, _ := os.Getwd()
|
||||||
if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal") {
|
if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist") {
|
||||||
assetPath = path.Join(pwd, "assets")
|
assetPath = path.Join(pwd, "assets")
|
||||||
} else {
|
} else {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
@ -52,7 +76,7 @@ func defaultAssetPath() string {
|
|||||||
exedir, _ := osext.ExecutableFolder()
|
exedir, _ := osext.ExecutableFolder()
|
||||||
assetPath = filepath.Join(exedir, "../Resources")
|
assetPath = filepath.Join(exedir, "../Resources")
|
||||||
case "linux":
|
case "linux":
|
||||||
assetPath = "/usr/share/ethereal"
|
assetPath = "/usr/share/mist"
|
||||||
case "windows":
|
case "windows":
|
||||||
assetPath = "./assets"
|
assetPath = "./assets"
|
||||||
default:
|
default:
|
||||||
@ -63,7 +87,7 @@ func defaultAssetPath() string {
|
|||||||
}
|
}
|
||||||
func defaultDataDir() string {
|
func defaultDataDir() string {
|
||||||
usr, _ := user.Current()
|
usr, _ := user.Current()
|
||||||
return path.Join(usr.HomeDir, ".ethereal")
|
return path.Join(usr.HomeDir, ".mist")
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini")
|
var defaultConfigFile = path.Join(defaultDataDir(), "conf.ini")
|
||||||
@ -74,14 +98,16 @@ func Init() {
|
|||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flag.IntVar(&VmType, "vm", 0, "Virtual Machine type: 0-1: standard, debug")
|
||||||
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
|
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
|
||||||
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
|
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
|
||||||
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
||||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
flag.BoolVar(&UseUPnP, "upnp", true, "enable UPnP support")
|
||||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||||
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||||
|
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
||||||
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
||||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
||||||
@ -91,9 +117,13 @@ func Init() {
|
|||||||
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
|
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
|
||||||
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
|
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
|
||||||
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
|
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
|
||||||
flag.IntVar(&LogLevel, "loglevel", int(ethlog.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
|
flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
|
||||||
|
|
||||||
flag.StringVar(&AssetPath, "asset_path", defaultAssetPath(), "absolute path to GUI assets directory")
|
flag.StringVar(&AssetPath, "asset_path", defaultAssetPath(), "absolute path to GUI assets directory")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if VmType >= int(vm.MaxVmTy) {
|
||||||
|
log.Fatal("Invalid VM type ", VmType)
|
||||||
|
}
|
||||||
}
|
}
|
533
cmd/mist/gui.go
Normal file
@ -0,0 +1,533 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
|
"github.com/ethereum/go-ethereum/wire"
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
|
"gopkg.in/qml.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func LoadExtension(path string) (uintptr, error) {
|
||||||
|
lib, err := ffi.NewLibrary(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr := so()
|
||||||
|
|
||||||
|
err = lib.Close()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr.Interface().(uintptr), nil
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
|
||||||
|
fmt.Printf("Fetched vec with addr: %#x\n", vec)
|
||||||
|
if errr != nil {
|
||||||
|
fmt.Println(errr)
|
||||||
|
} else {
|
||||||
|
context.SetVar("vec", (unsafe.Pointer)(vec))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
var guilogger = logger.NewLogger("GUI")
|
||||||
|
|
||||||
|
type Gui struct {
|
||||||
|
// The main application window
|
||||||
|
win *qml.Window
|
||||||
|
// QML Engine
|
||||||
|
engine *qml.Engine
|
||||||
|
component *qml.Common
|
||||||
|
qmlDone bool
|
||||||
|
// The ethereum interface
|
||||||
|
eth *eth.Ethereum
|
||||||
|
|
||||||
|
// The public Ethereum library
|
||||||
|
uiLib *UiLib
|
||||||
|
|
||||||
|
txDb *ethdb.LDBDatabase
|
||||||
|
|
||||||
|
logLevel logger.LogLevel
|
||||||
|
open bool
|
||||||
|
|
||||||
|
pipe *xeth.JSXEth
|
||||||
|
|
||||||
|
Session string
|
||||||
|
clientIdentity *wire.SimpleClientIdentity
|
||||||
|
config *ethutil.ConfigManager
|
||||||
|
|
||||||
|
plugins map[string]plugin
|
||||||
|
|
||||||
|
miner *miner.Miner
|
||||||
|
stdLog logger.LogSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create GUI, but doesn't start it
|
||||||
|
func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *wire.SimpleClientIdentity, session string, logLevel int) *Gui {
|
||||||
|
db, err := ethdb.NewLDBDatabase("tx_database")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe := xeth.NewJSXEth(ethereum)
|
||||||
|
gui := &Gui{eth: ethereum, txDb: db, pipe: pipe, logLevel: logger.LogLevel(logLevel), Session: session, open: false, clientIdentity: clientIdentity, config: config, plugins: make(map[string]plugin)}
|
||||||
|
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json"))
|
||||||
|
json.Unmarshal([]byte(data), &gui.plugins)
|
||||||
|
|
||||||
|
return gui
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) Start(assetPath string) {
|
||||||
|
defer gui.txDb.Close()
|
||||||
|
|
||||||
|
guilogger.Infoln("Starting GUI")
|
||||||
|
|
||||||
|
// Register ethereum functions
|
||||||
|
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
|
||||||
|
Init: func(p *xeth.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
|
||||||
|
}, {
|
||||||
|
Init: func(p *xeth.JSTransaction, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
|
||||||
|
}, {
|
||||||
|
Init: func(p *xeth.KeyVal, obj qml.Object) { p.Key = ""; p.Value = "" },
|
||||||
|
}})
|
||||||
|
// Create a new QML engine
|
||||||
|
gui.engine = qml.NewEngine()
|
||||||
|
context := gui.engine.Context()
|
||||||
|
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
|
||||||
|
|
||||||
|
// Expose the eth library and the ui library to QML
|
||||||
|
context.SetVar("gui", gui)
|
||||||
|
context.SetVar("eth", gui.uiLib)
|
||||||
|
|
||||||
|
// Load the main QML interface
|
||||||
|
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||||
|
|
||||||
|
var win *qml.Window
|
||||||
|
var err error
|
||||||
|
var addlog = false
|
||||||
|
if len(data) == 0 {
|
||||||
|
win, err = gui.showKeyImport(context)
|
||||||
|
} else {
|
||||||
|
win, err = gui.showWallet(context)
|
||||||
|
addlog = true
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
guilogger.Errorln("asset not found: you can set an alternative asset path on the command line using option 'asset_path'", err)
|
||||||
|
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.open = true
|
||||||
|
win.Show()
|
||||||
|
|
||||||
|
// only add the gui guilogger after window is shown otherwise slider wont be shown
|
||||||
|
if addlog {
|
||||||
|
logger.AddLogSystem(gui)
|
||||||
|
}
|
||||||
|
win.Wait()
|
||||||
|
|
||||||
|
// need to silence gui guilogger after window closed otherwise logsystem hangs (but do not save loglevel)
|
||||||
|
gui.logLevel = logger.Silence
|
||||||
|
gui.open = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) Stop() {
|
||||||
|
if gui.open {
|
||||||
|
gui.logLevel = logger.Silence
|
||||||
|
gui.open = false
|
||||||
|
gui.win.Hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.uiLib.jsEngine.Stop()
|
||||||
|
|
||||||
|
guilogger.Infoln("Stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) showWallet(context *qml.Context) (*qml.Window, error) {
|
||||||
|
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/main.qml"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.win = gui.createWindow(component)
|
||||||
|
|
||||||
|
gui.update()
|
||||||
|
|
||||||
|
return gui.win, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The done handler will be called by QML when all views have been loaded
|
||||||
|
func (gui *Gui) Done() {
|
||||||
|
gui.qmlDone = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) ImportKey(filePath string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) showKeyImport(context *qml.Context) (*qml.Window, error) {
|
||||||
|
context.SetVar("lib", gui)
|
||||||
|
component, err := gui.engine.LoadFile(gui.uiLib.AssetPath("qml/first_run.qml"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return gui.createWindow(component), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) createWindow(comp qml.Object) *qml.Window {
|
||||||
|
win := comp.CreateWindow(nil)
|
||||||
|
|
||||||
|
gui.win = win
|
||||||
|
gui.uiLib.win = win
|
||||||
|
|
||||||
|
return gui.win
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) ImportAndSetPrivKey(secret string) bool {
|
||||||
|
err := gui.eth.KeyManager().InitFromString(gui.Session, 0, secret)
|
||||||
|
if err != nil {
|
||||||
|
guilogger.Errorln("unable to import: ", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guilogger.Errorln("successfully imported: ", err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) CreateAndSetPrivKey() (string, string, string, string) {
|
||||||
|
err := gui.eth.KeyManager().Init(gui.Session, 0, true)
|
||||||
|
if err != nil {
|
||||||
|
guilogger.Errorln("unable to create key: ", err)
|
||||||
|
return "", "", "", ""
|
||||||
|
}
|
||||||
|
return gui.eth.KeyManager().KeyPair().AsStrings()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) setInitialChain(ancientBlocks bool) {
|
||||||
|
sBlk := gui.eth.ChainManager().LastBlockHash()
|
||||||
|
blk := gui.eth.ChainManager().GetBlock(sBlk)
|
||||||
|
for ; blk != nil; blk = gui.eth.ChainManager().GetBlock(sBlk) {
|
||||||
|
sBlk = blk.PrevHash
|
||||||
|
|
||||||
|
gui.processBlock(blk, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) loadAddressBook() {
|
||||||
|
view := gui.getObjectByName("infoView")
|
||||||
|
nameReg := gui.pipe.World().Config().Get("NameReg")
|
||||||
|
if nameReg != nil {
|
||||||
|
nameReg.EachStorage(func(name string, value *ethutil.Value) {
|
||||||
|
if name[0] != 0 {
|
||||||
|
value.Decode()
|
||||||
|
|
||||||
|
view.Call("addAddress", struct{ Name, Address string }{name, ethutil.Bytes2Hex(value.Bytes())})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Gui) loadMergedMiningOptions() {
|
||||||
|
view := self.getObjectByName("mergedMiningModel")
|
||||||
|
|
||||||
|
nameReg := self.pipe.World().Config().Get("MergeMining")
|
||||||
|
if nameReg != nil {
|
||||||
|
i := 0
|
||||||
|
nameReg.EachStorage(func(name string, value *ethutil.Value) {
|
||||||
|
if name[0] != 0 {
|
||||||
|
value.Decode()
|
||||||
|
|
||||||
|
view.Call("addMergedMiningOption", struct {
|
||||||
|
Checked bool
|
||||||
|
Name, Address string
|
||||||
|
Id, ItemId int
|
||||||
|
}{false, name, ethutil.Bytes2Hex(value.Bytes()), 0, i})
|
||||||
|
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
||||||
|
pipe := xeth.New(gui.eth)
|
||||||
|
nameReg := pipe.World().Config().Get("NameReg")
|
||||||
|
addr := gui.address()
|
||||||
|
|
||||||
|
var inout string
|
||||||
|
if bytes.Compare(tx.Sender(), addr) == 0 {
|
||||||
|
inout = "send"
|
||||||
|
} else {
|
||||||
|
inout = "recv"
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ptx = xeth.NewJSTx(tx, pipe.World().State())
|
||||||
|
send = nameReg.Storage(tx.From())
|
||||||
|
rec = nameReg.Storage(tx.To())
|
||||||
|
s, r string
|
||||||
|
)
|
||||||
|
|
||||||
|
if core.MessageCreatesContract(tx) {
|
||||||
|
rec = nameReg.Storage(core.AddressFromMessage(tx))
|
||||||
|
}
|
||||||
|
|
||||||
|
if send.Len() != 0 {
|
||||||
|
s = strings.Trim(send.Str(), "\x00")
|
||||||
|
} else {
|
||||||
|
s = ethutil.Bytes2Hex(tx.Sender())
|
||||||
|
}
|
||||||
|
if rec.Len() != 0 {
|
||||||
|
r = strings.Trim(rec.Str(), "\x00")
|
||||||
|
} else {
|
||||||
|
if core.MessageCreatesContract(tx) {
|
||||||
|
r = ethutil.Bytes2Hex(core.AddressFromMessage(tx))
|
||||||
|
} else {
|
||||||
|
r = ethutil.Bytes2Hex(tx.To())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptx.Sender = s
|
||||||
|
ptx.Address = r
|
||||||
|
|
||||||
|
if window == "post" {
|
||||||
|
//gui.getObjectByName("transactionView").Call("addTx", ptx, inout)
|
||||||
|
} else {
|
||||||
|
gui.getObjectByName("pendingTxView").Call("addTx", ptx, inout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) readPreviousTransactions() {
|
||||||
|
it := gui.txDb.NewIterator()
|
||||||
|
for it.Next() {
|
||||||
|
tx := types.NewTransactionFromBytes(it.Value())
|
||||||
|
|
||||||
|
gui.insertTransaction("post", tx)
|
||||||
|
|
||||||
|
}
|
||||||
|
it.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) processBlock(block *types.Block, initial bool) {
|
||||||
|
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00")
|
||||||
|
b := xeth.NewJSBlock(block)
|
||||||
|
b.Name = name
|
||||||
|
|
||||||
|
gui.getObjectByName("chainView").Call("addBlock", b, initial)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) setWalletValue(amount, unconfirmedFunds *big.Int) {
|
||||||
|
var str string
|
||||||
|
if unconfirmedFunds != nil {
|
||||||
|
pos := "+"
|
||||||
|
if unconfirmedFunds.Cmp(big.NewInt(0)) < 0 {
|
||||||
|
pos = "-"
|
||||||
|
}
|
||||||
|
val := ethutil.CurrencyToString(new(big.Int).Abs(ethutil.BigCopy(unconfirmedFunds)))
|
||||||
|
str = fmt.Sprintf("%v (%s %v)", ethutil.CurrencyToString(amount), pos, val)
|
||||||
|
} else {
|
||||||
|
str = fmt.Sprintf("%v", ethutil.CurrencyToString(amount))
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.win.Root().Call("setWalletValue", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Gui) getObjectByName(objectName string) qml.Object {
|
||||||
|
return self.win.Root().ObjectByName(objectName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple go routine function that updates the list of peers in the GUI
|
||||||
|
func (gui *Gui) update() {
|
||||||
|
// We have to wait for qml to be done loading all the windows.
|
||||||
|
for !gui.qmlDone {
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
go gui.setInitialChain(false)
|
||||||
|
gui.loadAddressBook()
|
||||||
|
gui.loadMergedMiningOptions()
|
||||||
|
gui.setPeerInfo()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, plugin := range gui.plugins {
|
||||||
|
guilogger.Infoln("Loading plugin ", plugin.Name)
|
||||||
|
|
||||||
|
gui.win.Root().Call("addPlugin", plugin.Path, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
peerUpdateTicker := time.NewTicker(5 * time.Second)
|
||||||
|
generalUpdateTicker := time.NewTicker(500 * time.Millisecond)
|
||||||
|
statsUpdateTicker := time.NewTicker(5 * time.Second)
|
||||||
|
|
||||||
|
state := gui.eth.ChainManager().TransState()
|
||||||
|
|
||||||
|
gui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(state.GetAccount(gui.address()).Balance())))
|
||||||
|
|
||||||
|
lastBlockLabel := gui.getObjectByName("lastBlockLabel")
|
||||||
|
miningLabel := gui.getObjectByName("miningLabel")
|
||||||
|
|
||||||
|
events := gui.eth.EventMux().Subscribe(
|
||||||
|
eth.ChainSyncEvent{},
|
||||||
|
eth.PeerListEvent{},
|
||||||
|
core.NewBlockEvent{},
|
||||||
|
core.TxPreEvent{},
|
||||||
|
core.TxPostEvent{},
|
||||||
|
)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer events.Unsubscribe()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ev, isopen := <-events.Chan():
|
||||||
|
if !isopen {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch ev := ev.(type) {
|
||||||
|
case core.NewBlockEvent:
|
||||||
|
gui.processBlock(ev.Block, false)
|
||||||
|
if bytes.Compare(ev.Block.Coinbase, gui.address()) == 0 {
|
||||||
|
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
case core.TxPreEvent:
|
||||||
|
tx := ev.Tx
|
||||||
|
|
||||||
|
tstate := gui.eth.ChainManager().TransState()
|
||||||
|
cstate := gui.eth.ChainManager().State()
|
||||||
|
|
||||||
|
taccount := tstate.GetAccount(gui.address())
|
||||||
|
caccount := cstate.GetAccount(gui.address())
|
||||||
|
unconfirmedFunds := new(big.Int).Sub(taccount.Balance(), caccount.Balance())
|
||||||
|
|
||||||
|
gui.setWalletValue(taccount.Balance(), unconfirmedFunds)
|
||||||
|
gui.insertTransaction("pre", tx)
|
||||||
|
|
||||||
|
case core.TxPostEvent:
|
||||||
|
tx := ev.Tx
|
||||||
|
object := state.GetAccount(gui.address())
|
||||||
|
|
||||||
|
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
|
||||||
|
object.SubAmount(tx.Value())
|
||||||
|
|
||||||
|
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||||
|
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
|
||||||
|
object.AddAmount(tx.Value())
|
||||||
|
|
||||||
|
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.setWalletValue(object.Balance(), nil)
|
||||||
|
state.UpdateStateObject(object)
|
||||||
|
|
||||||
|
case eth.PeerListEvent:
|
||||||
|
gui.setPeerInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-peerUpdateTicker.C:
|
||||||
|
gui.setPeerInfo()
|
||||||
|
case <-generalUpdateTicker.C:
|
||||||
|
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number.String()
|
||||||
|
lastBlockLabel.Set("text", statusText)
|
||||||
|
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
|
||||||
|
|
||||||
|
blockLength := gui.eth.BlockPool().BlocksProcessed
|
||||||
|
chainLength := gui.eth.BlockPool().ChainLength
|
||||||
|
|
||||||
|
var (
|
||||||
|
pct float64 = 1.0 / float64(chainLength) * float64(blockLength)
|
||||||
|
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
|
||||||
|
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
|
||||||
|
)
|
||||||
|
dlWidget.Set("value", pct)
|
||||||
|
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
|
||||||
|
|
||||||
|
case <-statsUpdateTicker.C:
|
||||||
|
gui.setStatsPane()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) setStatsPane() {
|
||||||
|
var memStats runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&memStats)
|
||||||
|
|
||||||
|
statsPane := gui.getObjectByName("statsPane")
|
||||||
|
statsPane.Set("text", fmt.Sprintf(`###### Mist %s (%s) #######
|
||||||
|
|
||||||
|
eth %d (p2p = %d)
|
||||||
|
|
||||||
|
CPU: # %d
|
||||||
|
Goroutines: # %d
|
||||||
|
CGoCalls: # %d
|
||||||
|
|
||||||
|
Alloc: %d
|
||||||
|
Heap Alloc: %d
|
||||||
|
|
||||||
|
CGNext: %x
|
||||||
|
NumGC: %d
|
||||||
|
`, Version, runtime.Version(),
|
||||||
|
eth.ProtocolVersion, eth.P2PVersion,
|
||||||
|
runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
|
||||||
|
memStats.Alloc, memStats.HeapAlloc,
|
||||||
|
memStats.NextGC, memStats.NumGC,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) setPeerInfo() {
|
||||||
|
gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers))
|
||||||
|
gui.win.Root().Call("resetPeers")
|
||||||
|
for _, peer := range gui.pipe.Peers() {
|
||||||
|
gui.win.Root().Call("addPeer", peer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) privateKey() string {
|
||||||
|
return ethutil.Bytes2Hex(gui.eth.KeyManager().PrivateKey())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) address() []byte {
|
||||||
|
return gui.eth.KeyManager().Address()
|
||||||
|
}
|
@ -1,3 +1,20 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -10,11 +27,11 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go/ethchain"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/eth-go/ethpipe"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethstate"
|
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
|
||||||
"github.com/ethereum/go-ethereum/javascript"
|
"github.com/ethereum/go-ethereum/javascript"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
"github.com/howeyc/fsnotify"
|
"github.com/howeyc/fsnotify"
|
||||||
"gopkg.in/qml.v1"
|
"gopkg.in/qml.v1"
|
||||||
)
|
)
|
||||||
@ -81,12 +98,12 @@ func (app *HtmlApplication) NewWatcher(quitChan chan bool) {
|
|||||||
|
|
||||||
app.watcher, err = fsnotify.NewWatcher()
|
app.watcher, err = fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Infoln("Could not create new auto-reload watcher:", err)
|
guilogger.Infoln("Could not create new auto-reload watcher:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = app.watcher.Watch(app.RootFolder())
|
err = app.watcher.Watch(app.RootFolder())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Infoln("Could not start auto-reload watcher:", err)
|
guilogger.Infoln("Could not start auto-reload watcher:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, folder := range app.RecursiveFolders() {
|
for _, folder := range app.RecursiveFolders() {
|
||||||
@ -102,11 +119,11 @@ func (app *HtmlApplication) NewWatcher(quitChan chan bool) {
|
|||||||
app.watcher.Close()
|
app.watcher.Close()
|
||||||
break out
|
break out
|
||||||
case <-app.watcher.Event:
|
case <-app.watcher.Event:
|
||||||
//logger.Debugln("Got event:", ev)
|
//guilogger.Debugln("Got event:", ev)
|
||||||
app.webView.Call("reload")
|
app.webView.Call("reload")
|
||||||
case err := <-app.watcher.Error:
|
case err := <-app.watcher.Error:
|
||||||
// TODO: Do something here
|
// TODO: Do something here
|
||||||
logger.Infoln("Watcher error:", err)
|
guilogger.Infoln("Watcher error:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -121,12 +138,12 @@ func (app *HtmlApplication) Window() *qml.Window {
|
|||||||
return app.win
|
return app.win
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *HtmlApplication) NewBlock(block *ethchain.Block) {
|
func (app *HtmlApplication) NewBlock(block *types.Block) {
|
||||||
b := ðpipe.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
|
b := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||||
app.webView.Call("onNewBlockCb", b)
|
app.webView.Call("onNewBlockCb", b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *HtmlApplication) Messages(messages ethstate.Messages, id string) {
|
func (self *HtmlApplication) Messages(messages state.Messages, id string) {
|
||||||
var msgs []javascript.JSMessage
|
var msgs []javascript.JSMessage
|
||||||
for _, m := range messages {
|
for _, m := range messages {
|
||||||
msgs = append(msgs, javascript.NewJSMessage(m))
|
msgs = append(msgs, javascript.NewJSMessage(m))
|
@ -1,18 +1,37 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/eth-go"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/utils"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"gopkg.in/qml.v1"
|
"gopkg.in/qml.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ClientIdentifier = "Ethereal"
|
ClientIdentifier = "Mist"
|
||||||
Version = "0.6.3"
|
Version = "0.7.10"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ethereum *eth.Ethereum
|
var ethereum *eth.Ethereum
|
||||||
@ -21,21 +40,25 @@ func run() error {
|
|||||||
// precedence: code-internal flag default < config file < environment variables < command line
|
// precedence: code-internal flag default < config file < environment variables < command line
|
||||||
Init() // parsing command line
|
Init() // parsing command line
|
||||||
|
|
||||||
config := utils.InitConfig(ConfigFile, Datadir, "ETH")
|
tstart := time.Now()
|
||||||
|
config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
|
||||||
|
|
||||||
utils.InitDataDir(Datadir)
|
utils.InitDataDir(Datadir)
|
||||||
|
|
||||||
utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
|
stdLog := utils.InitLogging(Datadir, LogFile, LogLevel, DebugFile)
|
||||||
|
|
||||||
db := utils.NewDatabase()
|
db := utils.NewDatabase()
|
||||||
|
err := utils.DBSanityCheck(db)
|
||||||
|
if err != nil {
|
||||||
|
ErrorWindow(err)
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
|
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
|
||||||
|
|
||||||
// create, import, export keys
|
// create, import, export keys
|
||||||
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
|
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
|
||||||
|
|
||||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
|
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
|
||||||
|
|
||||||
ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
|
ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
|
||||||
|
|
||||||
if ShowGenesis {
|
if ShowGenesis {
|
||||||
@ -47,11 +70,15 @@ func run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
|
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
|
||||||
|
gui.stdLog = stdLog
|
||||||
|
|
||||||
utils.RegisterInterrupt(func(os.Signal) {
|
utils.RegisterInterrupt(func(os.Signal) {
|
||||||
gui.Stop()
|
gui.Stop()
|
||||||
})
|
})
|
||||||
utils.StartEthereum(ethereum, UseSeed)
|
go utils.StartEthereum(ethereum, UseSeed)
|
||||||
|
|
||||||
|
fmt.Println("ETH stack took", time.Since(tstart))
|
||||||
|
|
||||||
// gui blocks the main thread
|
// gui blocks the main thread
|
||||||
gui.Start(AssetPath)
|
gui.Start(AssetPath)
|
||||||
|
|
||||||
@ -64,7 +91,6 @@ func main() {
|
|||||||
// This is a bit of a cheat, but ey!
|
// This is a bit of a cheat, but ey!
|
||||||
os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999")
|
os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999")
|
||||||
|
|
||||||
//qml.Init(nil)
|
|
||||||
qml.Run(run)
|
qml.Run(run)
|
||||||
|
|
||||||
var interrupted = false
|
var interrupted = false
|
||||||
@ -74,6 +100,10 @@ func main() {
|
|||||||
|
|
||||||
utils.HandleInterrupt()
|
utils.HandleInterrupt()
|
||||||
|
|
||||||
|
if StartWebSockets {
|
||||||
|
utils.StartWebSockets(ethereum)
|
||||||
|
}
|
||||||
|
|
||||||
// we need to run the interrupt callbacks in case gui is closed
|
// we need to run the interrupt callbacks in case gui is closed
|
||||||
// this skips if we got here by actual interrupt stopping the GUI
|
// this skips if we got here by actual interrupt stopping the GUI
|
||||||
if !interrupted {
|
if !interrupted {
|
||||||
@ -81,5 +111,5 @@ func main() {
|
|||||||
}
|
}
|
||||||
// this blocks the thread
|
// this blocks the thread
|
||||||
ethereum.WaitForShutdown()
|
ethereum.WaitForShutdown()
|
||||||
ethlog.Flush()
|
logger.Flush()
|
||||||
}
|
}
|
85
cmd/mist/qml_container.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
|
"gopkg.in/qml.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QmlApplication struct {
|
||||||
|
win *qml.Window
|
||||||
|
engine *qml.Engine
|
||||||
|
lib *UiLib
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQmlApplication(path string, lib *UiLib) *QmlApplication {
|
||||||
|
engine := qml.NewEngine()
|
||||||
|
return &QmlApplication{engine: engine, path: path, lib: lib}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *QmlApplication) Create() error {
|
||||||
|
path := string(app.path)
|
||||||
|
|
||||||
|
// For some reason for windows we get /c:/path/to/something, windows doesn't like the first slash but is fine with the others so we are removing it
|
||||||
|
if app.path[0] == '/' && runtime.GOOS == "windows" {
|
||||||
|
path = app.path[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
component, err := app.engine.LoadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
guilogger.Warnln(err)
|
||||||
|
}
|
||||||
|
app.win = component.CreateWindow(nil)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *QmlApplication) Destroy() {
|
||||||
|
app.engine.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *QmlApplication) NewWatcher(quitChan chan bool) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events
|
||||||
|
func (app *QmlApplication) NewBlock(block *types.Block) {
|
||||||
|
pblock := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||||
|
app.win.Call("onNewBlockCb", pblock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *QmlApplication) Messages(msgs state.Messages, id string) {
|
||||||
|
fmt.Println("IMPLEMENT QML APPLICATION MESSAGES METHOD")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
func (app *QmlApplication) Engine() *qml.Engine {
|
||||||
|
return app.engine
|
||||||
|
}
|
||||||
|
func (app *QmlApplication) Window() *qml.Window {
|
||||||
|
return app.win
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *QmlApplication) Post(data string, s int) {}
|
374
cmd/mist/ui_lib.go
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/javascript"
|
||||||
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/ui/qt"
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
|
"gopkg.in/qml.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type memAddr struct {
|
||||||
|
Num string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Library that has some basic functionality exposed
|
||||||
|
type UiLib struct {
|
||||||
|
*xeth.JSXEth
|
||||||
|
engine *qml.Engine
|
||||||
|
eth *eth.Ethereum
|
||||||
|
connected bool
|
||||||
|
assetPath string
|
||||||
|
// The main application window
|
||||||
|
win *qml.Window
|
||||||
|
Db *Debugger
|
||||||
|
DbWindow *DebuggerWindow
|
||||||
|
|
||||||
|
jsEngine *javascript.JSRE
|
||||||
|
|
||||||
|
filterCallbacks map[int][]int
|
||||||
|
|
||||||
|
miner *miner.Miner
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
||||||
|
lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
|
||||||
|
lib.miner = miner.New(eth.KeyManager().Address(), eth)
|
||||||
|
|
||||||
|
return lib
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) Notef(args []interface{}) {
|
||||||
|
guilogger.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) LookupDomain(domain string) string {
|
||||||
|
world := self.World()
|
||||||
|
|
||||||
|
if len(domain) > 32 {
|
||||||
|
domain = string(crypto.Sha3([]byte(domain)))
|
||||||
|
}
|
||||||
|
data := world.Config().Get("DnsReg").StorageString(domain).Bytes()
|
||||||
|
|
||||||
|
// Left padded = A record, Right padded = CNAME
|
||||||
|
if len(data) > 0 && data[0] == 0 {
|
||||||
|
data = bytes.TrimLeft(data, "\x00")
|
||||||
|
var ipSlice []string
|
||||||
|
for _, d := range data {
|
||||||
|
ipSlice = append(ipSlice, strconv.Itoa(int(d)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(ipSlice, ".")
|
||||||
|
} else {
|
||||||
|
data = bytes.TrimRight(data, "\x00")
|
||||||
|
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) LookupName(addr string) string {
|
||||||
|
var (
|
||||||
|
nameReg = self.World().Config().Get("NameReg")
|
||||||
|
lookup = nameReg.Storage(ethutil.Hex2Bytes(addr))
|
||||||
|
)
|
||||||
|
|
||||||
|
if lookup.Len() != 0 {
|
||||||
|
return strings.Trim(lookup.Str(), "\x00")
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) LookupAddress(name string) string {
|
||||||
|
var (
|
||||||
|
nameReg = self.World().Config().Get("NameReg")
|
||||||
|
lookup = nameReg.Storage(ethutil.RightPadBytes([]byte(name), 32))
|
||||||
|
)
|
||||||
|
|
||||||
|
if lookup.Len() != 0 {
|
||||||
|
return ethutil.Bytes2Hex(lookup.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) PastPeers() *ethutil.List {
|
||||||
|
return ethutil.NewList(eth.PastPeers())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) ImportTx(rlpTx string) {
|
||||||
|
tx := types.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
|
||||||
|
err := self.eth.TxPool().Add(tx)
|
||||||
|
if err != nil {
|
||||||
|
guilogger.Infoln("import tx failed ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) EvalJavascriptFile(path string) {
|
||||||
|
self.jsEngine.LoadExtFile(path[7:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) EvalJavascriptString(str string) string {
|
||||||
|
value, err := self.jsEngine.Run(str)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%v", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) OpenQml(path string) {
|
||||||
|
container := NewQmlApplication(path[7:], ui)
|
||||||
|
app := NewExtApplication(container, ui)
|
||||||
|
|
||||||
|
go app.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) OpenHtml(path string) {
|
||||||
|
container := NewHtmlApplication(path, ui)
|
||||||
|
app := NewExtApplication(container, ui)
|
||||||
|
|
||||||
|
go app.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) OpenBrowser() {
|
||||||
|
ui.OpenHtml("file://" + ui.AssetPath("ext/home.html"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) Muted(content string) {
|
||||||
|
component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml"))
|
||||||
|
if err != nil {
|
||||||
|
guilogger.Debugln(err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
win := component.CreateWindow(nil)
|
||||||
|
go func() {
|
||||||
|
path := "file://" + ui.AssetPath("muted/index.html")
|
||||||
|
win.Set("url", path)
|
||||||
|
|
||||||
|
win.Show()
|
||||||
|
win.Wait()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) Connect(button qml.Object) {
|
||||||
|
if !ui.connected {
|
||||||
|
ui.eth.Start(true)
|
||||||
|
ui.connected = true
|
||||||
|
button.Set("enabled", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) ConnectToPeer(addr string) {
|
||||||
|
ui.eth.ConnectToPeer(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UiLib) AssetPath(p string) string {
|
||||||
|
return path.Join(ui.assetPath, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) StartDbWithContractAndData(contractHash, data string) {
|
||||||
|
dbWindow := NewDebuggerWindow(self)
|
||||||
|
object := self.eth.ChainManager().State().GetStateObject(ethutil.Hex2Bytes(contractHash))
|
||||||
|
if len(object.Code) > 0 {
|
||||||
|
dbWindow.SetCode("0x" + ethutil.Bytes2Hex(object.Code))
|
||||||
|
}
|
||||||
|
dbWindow.SetData("0x" + data)
|
||||||
|
|
||||||
|
dbWindow.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) StartDbWithCode(code string) {
|
||||||
|
dbWindow := NewDebuggerWindow(self)
|
||||||
|
dbWindow.SetCode("0x" + code)
|
||||||
|
dbWindow.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) StartDebugger() {
|
||||||
|
dbWindow := NewDebuggerWindow(self)
|
||||||
|
|
||||||
|
dbWindow.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
|
||||||
|
filter := qt.NewFilterFromMap(object, self.eth)
|
||||||
|
filter.MessageCallback = func(messages state.Messages) {
|
||||||
|
self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
|
||||||
|
}
|
||||||
|
id = self.eth.InstallFilter(filter)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) NewFilterString(typ string) (id int) {
|
||||||
|
filter := core.NewFilter(self.eth)
|
||||||
|
filter.BlockCallback = func(block *types.Block) {
|
||||||
|
if self.win != nil && self.win.Root() != nil {
|
||||||
|
self.win.Root().Call("invokeFilterCallback", "{}", id)
|
||||||
|
} else {
|
||||||
|
fmt.Println("QML is lagging")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id = self.eth.InstallFilter(filter)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) Messages(id int) *ethutil.List {
|
||||||
|
filter := self.eth.GetFilter(id)
|
||||||
|
if filter != nil {
|
||||||
|
messages := xeth.ToJSMessages(filter.Find())
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethutil.EmptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) UninstallFilter(id int) {
|
||||||
|
self.eth.UninstallFilter(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToTxParams(object map[string]interface{}) map[string]string {
|
||||||
|
// Default values
|
||||||
|
if object["from"] == nil {
|
||||||
|
object["from"] = ""
|
||||||
|
}
|
||||||
|
if object["to"] == nil {
|
||||||
|
object["to"] = ""
|
||||||
|
}
|
||||||
|
if object["value"] == nil {
|
||||||
|
object["value"] = ""
|
||||||
|
}
|
||||||
|
if object["gas"] == nil {
|
||||||
|
object["gas"] = ""
|
||||||
|
}
|
||||||
|
if object["gasPrice"] == nil {
|
||||||
|
object["gasPrice"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataStr string
|
||||||
|
var data []string
|
||||||
|
if list, ok := object["data"].(*qml.List); ok {
|
||||||
|
list.Convert(&data)
|
||||||
|
} else if str, ok := object["data"].(string); ok {
|
||||||
|
data = []string{str}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, str := range data {
|
||||||
|
if ethutil.IsHex(str) {
|
||||||
|
str = str[2:]
|
||||||
|
|
||||||
|
if len(str) != 64 {
|
||||||
|
str = ethutil.LeftPadString(str, 64)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStr += str
|
||||||
|
}
|
||||||
|
object["data"] = dataStr
|
||||||
|
|
||||||
|
conv := make(map[string]string)
|
||||||
|
for key, value := range object {
|
||||||
|
if v, ok := value.(string); ok {
|
||||||
|
conv[key] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
|
||||||
|
object := mapToTxParams(params)
|
||||||
|
|
||||||
|
return self.JSXEth.Transact(
|
||||||
|
object["from"],
|
||||||
|
object["to"],
|
||||||
|
object["value"],
|
||||||
|
object["gas"],
|
||||||
|
object["gasPrice"],
|
||||||
|
object["data"],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) Compile(code string) (string, error) {
|
||||||
|
bcode, err := ethutil.Compile(code, false)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethutil.Bytes2Hex(bcode), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) Call(params map[string]interface{}) (string, error) {
|
||||||
|
object := mapToTxParams(params)
|
||||||
|
|
||||||
|
return self.JSXEth.Execute(
|
||||||
|
object["to"],
|
||||||
|
object["value"],
|
||||||
|
object["gas"],
|
||||||
|
object["gasPrice"],
|
||||||
|
object["data"],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) AddLocalTransaction(to, data, gas, gasPrice, value string) int {
|
||||||
|
return self.miner.AddLocalTx(&miner.LocalTx{
|
||||||
|
To: ethutil.Hex2Bytes(to),
|
||||||
|
Data: ethutil.Hex2Bytes(data),
|
||||||
|
Gas: gas,
|
||||||
|
GasPrice: gasPrice,
|
||||||
|
Value: value,
|
||||||
|
}) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) RemoveLocalTransaction(id int) {
|
||||||
|
self.miner.RemoveLocalTx(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) SetGasPrice(price string) {
|
||||||
|
self.miner.MinAcceptedGasPrice = ethutil.Big(price)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *UiLib) ToggleMining() bool {
|
||||||
|
if !self.miner.Mining() {
|
||||||
|
self.miner.Start()
|
||||||
|
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
self.miner.Stop()
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
40
cmd/peerserver/main.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.InfoLevel))
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
marshaled := elliptic.Marshal(crypto.S256(), key.PublicKey.X, key.PublicKey.Y)
|
||||||
|
|
||||||
|
srv := p2p.Server{
|
||||||
|
MaxPeers: 10,
|
||||||
|
Identity: p2p.NewSimpleClientIdentity("Ethereum(G)", "0.1", "Peer Server Two", string(marshaled)),
|
||||||
|
ListenAddr: ":30301",
|
||||||
|
NAT: p2p.UPNP(),
|
||||||
|
}
|
||||||
|
if err := srv.Start(); err != nil {
|
||||||
|
fmt.Println("could not start server:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add seed peers
|
||||||
|
seed, err := net.ResolveTCPAddr("tcp", "poc-7.ethdev.com:30300")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("couldn't resolve:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
srv.SuggestPeer(seed.IP, seed.Port, nil)
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
@ -13,18 +13,18 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"bitbucket.org/kardianos/osext"
|
"bitbucket.org/kardianos/osext"
|
||||||
"github.com/ethereum/eth-go"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/eth-go/ethcrypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/eth-go/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/eth-go/ethlog"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/eth-go/ethminer"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/eth-go/ethpipe"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/eth-go/ethrpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/go-ethereum/wire"
|
||||||
"github.com/ethereum/eth-go/ethwire"
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logger = ethlog.NewLogger("CLI")
|
var clilogger = logger.NewLogger("CLI")
|
||||||
var interruptCallbacks = []func(os.Signal){}
|
var interruptCallbacks = []func(os.Signal){}
|
||||||
|
|
||||||
// Register interrupt handlers callbacks
|
// Register interrupt handlers callbacks
|
||||||
@ -38,7 +38,7 @@ func HandleInterrupt() {
|
|||||||
go func() {
|
go func() {
|
||||||
signal.Notify(c, os.Interrupt)
|
signal.Notify(c, os.Interrupt)
|
||||||
for sig := range c {
|
for sig := range c {
|
||||||
logger.Errorf("Shutting down (%v) ... \n", sig)
|
clilogger.Errorf("Shutting down (%v) ... \n", sig)
|
||||||
RunInterruptCallbacks(sig)
|
RunInterruptCallbacks(sig)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -80,6 +80,16 @@ func confirm(message string) bool {
|
|||||||
return r == "y"
|
return r == "y"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DBSanityCheck(db ethutil.Database) error {
|
||||||
|
d, _ := db.Get([]byte("ProtocolVersion"))
|
||||||
|
protov := ethutil.NewValue(d).Uint()
|
||||||
|
if protov != eth.ProtocolVersion && protov != 0 {
|
||||||
|
return fmt.Errorf("Database version mismatch. Protocol(%d / %d). `rm -rf %s`", protov, eth.ProtocolVersion, ethutil.Config.ExecPath+"/database")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func InitDataDir(Datadir string) {
|
func InitDataDir(Datadir string) {
|
||||||
_, err := os.Stat(Datadir)
|
_, err := os.Stat(Datadir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -90,33 +100,39 @@ func InitDataDir(Datadir string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitLogging(Datadir string, LogFile string, LogLevel int, DebugFile string) {
|
func InitLogging(Datadir string, LogFile string, LogLevel int, DebugFile string) logger.LogSystem {
|
||||||
var writer io.Writer
|
var writer io.Writer
|
||||||
if LogFile == "" {
|
if LogFile == "" {
|
||||||
writer = os.Stdout
|
writer = os.Stdout
|
||||||
} else {
|
} else {
|
||||||
writer = openLogFile(Datadir, LogFile)
|
writer = openLogFile(Datadir, LogFile)
|
||||||
}
|
}
|
||||||
ethlog.AddLogSystem(ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.LogLevel(LogLevel)))
|
|
||||||
|
sys := logger.NewStdLogSystem(writer, log.LstdFlags, logger.LogLevel(LogLevel))
|
||||||
|
logger.AddLogSystem(sys)
|
||||||
if DebugFile != "" {
|
if DebugFile != "" {
|
||||||
writer = openLogFile(Datadir, DebugFile)
|
writer = openLogFile(Datadir, DebugFile)
|
||||||
ethlog.AddLogSystem(ethlog.NewStdLogSystem(writer, log.LstdFlags, ethlog.DebugLevel))
|
logger.AddLogSystem(logger.NewStdLogSystem(writer, log.LstdFlags, logger.DebugLevel))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return sys
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitConfig(ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager {
|
func InitConfig(vmType int, ConfigFile string, Datadir string, EnvPrefix string) *ethutil.ConfigManager {
|
||||||
InitDataDir(Datadir)
|
InitDataDir(Datadir)
|
||||||
return ethutil.ReadConfig(ConfigFile, Datadir, EnvPrefix)
|
cfg := ethutil.ReadConfig(ConfigFile, Datadir, EnvPrefix)
|
||||||
|
cfg.VmType = vmType
|
||||||
|
|
||||||
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func exit(err error) {
|
func exit(err error) {
|
||||||
status := 0
|
status := 0
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
clilogger.Errorln("Fatal: ", err)
|
||||||
logger.Errorln("Fatal: ", err)
|
|
||||||
status = 1
|
status = 1
|
||||||
}
|
}
|
||||||
ethlog.Flush()
|
logger.Flush()
|
||||||
os.Exit(status)
|
os.Exit(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,15 +144,14 @@ func NewDatabase() ethutil.Database {
|
|||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientIdentity(clientIdentifier, version, customIdentifier string) *ethwire.SimpleClientIdentity {
|
func NewClientIdentity(clientIdentifier, version, customIdentifier string) *wire.SimpleClientIdentity {
|
||||||
logger.Infoln("identity created")
|
return wire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier)
|
||||||
return ethwire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEthereum(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager *ethcrypto.KeyManager, usePnp bool, OutboundPort string, MaxPeer int) *eth.Ethereum {
|
func NewEthereum(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, usePnp bool, OutboundPort string, MaxPeer int) *eth.Ethereum {
|
||||||
ethereum, err := eth.New(db, clientIdentity, keyManager, eth.CapDefault, usePnp)
|
ethereum, err := eth.New(db, clientIdentity, keyManager, eth.CapDefault, usePnp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalln("eth start err:", err)
|
clilogger.Fatalln("eth start err:", err)
|
||||||
}
|
}
|
||||||
ethereum.Port = OutboundPort
|
ethereum.Port = OutboundPort
|
||||||
ethereum.MaxPeers = MaxPeer
|
ethereum.MaxPeers = MaxPeer
|
||||||
@ -144,26 +159,26 @@ func NewEthereum(db ethutil.Database, clientIdentity ethwire.ClientIdentity, key
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StartEthereum(ethereum *eth.Ethereum, UseSeed bool) {
|
func StartEthereum(ethereum *eth.Ethereum, UseSeed bool) {
|
||||||
logger.Infof("Starting %s", ethereum.ClientIdentity())
|
clilogger.Infof("Starting %s", ethereum.ClientIdentity())
|
||||||
ethereum.Start(UseSeed)
|
ethereum.Start(UseSeed)
|
||||||
RegisterInterrupt(func(sig os.Signal) {
|
RegisterInterrupt(func(sig os.Signal) {
|
||||||
ethereum.Stop()
|
ethereum.Stop()
|
||||||
ethlog.Flush()
|
logger.Flush()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ShowGenesis(ethereum *eth.Ethereum) {
|
func ShowGenesis(ethereum *eth.Ethereum) {
|
||||||
logger.Infoln(ethereum.BlockChain().Genesis())
|
clilogger.Infoln(ethereum.ChainManager().Genesis())
|
||||||
exit(nil)
|
exit(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKeyManager(KeyStore string, Datadir string, db ethutil.Database) *ethcrypto.KeyManager {
|
func NewKeyManager(KeyStore string, Datadir string, db ethutil.Database) *crypto.KeyManager {
|
||||||
var keyManager *ethcrypto.KeyManager
|
var keyManager *crypto.KeyManager
|
||||||
switch {
|
switch {
|
||||||
case KeyStore == "db":
|
case KeyStore == "db":
|
||||||
keyManager = ethcrypto.NewDBKeyManager(db)
|
keyManager = crypto.NewDBKeyManager(db)
|
||||||
case KeyStore == "file":
|
case KeyStore == "file":
|
||||||
keyManager = ethcrypto.NewFileKeyManager(Datadir)
|
keyManager = crypto.NewFileKeyManager(Datadir)
|
||||||
default:
|
default:
|
||||||
exit(fmt.Errorf("unknown keystore type: %s", KeyStore))
|
exit(fmt.Errorf("unknown keystore type: %s", KeyStore))
|
||||||
}
|
}
|
||||||
@ -176,7 +191,7 @@ func DefaultAssetPath() string {
|
|||||||
// assume a debug build and use the source directory as
|
// assume a debug build and use the source directory as
|
||||||
// asset directory.
|
// asset directory.
|
||||||
pwd, _ := os.Getwd()
|
pwd, _ := os.Getwd()
|
||||||
if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "ethereal") {
|
if pwd == path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist") {
|
||||||
assetPath = path.Join(pwd, "assets")
|
assetPath = path.Join(pwd, "assets")
|
||||||
} else {
|
} else {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
@ -185,7 +200,7 @@ func DefaultAssetPath() string {
|
|||||||
exedir, _ := osext.ExecutableFolder()
|
exedir, _ := osext.ExecutableFolder()
|
||||||
assetPath = filepath.Join(exedir, "../Resources")
|
assetPath = filepath.Join(exedir, "../Resources")
|
||||||
case "linux":
|
case "linux":
|
||||||
assetPath = "/usr/share/ethereal"
|
assetPath = "/usr/share/mist"
|
||||||
case "windows":
|
case "windows":
|
||||||
assetPath = "./assets"
|
assetPath = "./assets"
|
||||||
default:
|
default:
|
||||||
@ -195,7 +210,7 @@ func DefaultAssetPath() string {
|
|||||||
return assetPath
|
return assetPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func KeyTasks(keyManager *ethcrypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) {
|
func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch {
|
switch {
|
||||||
@ -224,22 +239,23 @@ func KeyTasks(keyManager *ethcrypto.KeyManager, KeyRing string, GenAddr bool, Se
|
|||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clilogger.Infof("Main address %x\n", keyManager.Address())
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartRpc(ethereum *eth.Ethereum, RpcPort int) {
|
func StartRpc(ethereum *eth.Ethereum, RpcPort int) {
|
||||||
var err error
|
var err error
|
||||||
ethereum.RpcServer, err = ethrpc.NewJsonRpcServer(ethpipe.NewJSPipe(ethereum), RpcPort)
|
ethereum.RpcServer, err = rpc.NewJsonRpcServer(xeth.NewJSXEth(ethereum), RpcPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err)
|
clilogger.Errorf("Could not start RPC interface (port %v): %v", RpcPort, err)
|
||||||
} else {
|
} else {
|
||||||
go ethereum.RpcServer.Start()
|
go ethereum.RpcServer.Start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var miner *ethminer.Miner
|
var gminer *miner.Miner
|
||||||
|
|
||||||
func GetMiner() *ethminer.Miner {
|
func GetMiner() *miner.Miner {
|
||||||
return miner
|
return gminer
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartMining(ethereum *eth.Ethereum) bool {
|
func StartMining(ethereum *eth.Ethereum) bool {
|
||||||
@ -248,16 +264,16 @@ func StartMining(ethereum *eth.Ethereum) bool {
|
|||||||
addr := ethereum.KeyManager().Address()
|
addr := ethereum.KeyManager().Address()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
logger.Infoln("Start mining")
|
clilogger.Infoln("Start mining")
|
||||||
if miner == nil {
|
if gminer == nil {
|
||||||
miner = ethminer.NewDefaultMiner(addr, ethereum)
|
gminer = miner.New(addr, ethereum)
|
||||||
}
|
}
|
||||||
// Give it some time to connect with peers
|
// Give it some time to connect with peers
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
for !ethereum.IsUpToDate() {
|
for !ethereum.IsUpToDate() {
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
}
|
}
|
||||||
miner.Start()
|
gminer.Start()
|
||||||
}()
|
}()
|
||||||
RegisterInterrupt(func(os.Signal) {
|
RegisterInterrupt(func(os.Signal) {
|
||||||
StopMining(ethereum)
|
StopMining(ethereum)
|
||||||
@ -281,9 +297,9 @@ func FormatTransactionData(data string) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StopMining(ethereum *eth.Ethereum) bool {
|
func StopMining(ethereum *eth.Ethereum) bool {
|
||||||
if ethereum.Mining && miner != nil {
|
if ethereum.Mining && gminer != nil {
|
||||||
miner.Stop()
|
gminer.Stop()
|
||||||
logger.Infoln("Stopped mining")
|
clilogger.Infoln("Stopped mining")
|
||||||
ethereum.Mining = false
|
ethereum.Mining = false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -294,14 +310,14 @@ func StopMining(ethereum *eth.Ethereum) bool {
|
|||||||
|
|
||||||
// Replay block
|
// Replay block
|
||||||
func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
||||||
block := ethereum.BlockChain().GetBlock(hash)
|
block := ethereum.ChainManager().GetBlock(hash)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return fmt.Errorf("unknown block %x", hash)
|
return fmt.Errorf("unknown block %x", hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
parent := ethereum.BlockChain().GetBlock(block.PrevHash)
|
parent := ethereum.ChainManager().GetBlock(block.PrevHash)
|
||||||
|
|
||||||
_, err := ethereum.StateManager().ApplyDiff(parent.State(), parent, block)
|
_, err := ethereum.BlockManager().TransitionState(parent.State(), parent, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
70
cmd/utils/vm_env.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VMEnv struct {
|
||||||
|
state *state.StateDB
|
||||||
|
block *types.Block
|
||||||
|
|
||||||
|
transactor []byte
|
||||||
|
value *big.Int
|
||||||
|
|
||||||
|
depth int
|
||||||
|
Gas *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEnv(state *state.StateDB, block *types.Block, transactor []byte, value *big.Int) *VMEnv {
|
||||||
|
return &VMEnv{
|
||||||
|
state: state,
|
||||||
|
block: block,
|
||||||
|
transactor: transactor,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *VMEnv) Origin() []byte { return self.transactor }
|
||||||
|
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
||||||
|
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
||||||
|
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
||||||
|
func (self *VMEnv) Time() int64 { return self.block.Time }
|
||||||
|
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
||||||
|
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
|
||||||
|
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||||
|
func (self *VMEnv) State() *state.StateDB { return self.state }
|
||||||
|
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
|
||||||
|
func (self *VMEnv) Depth() int { return self.depth }
|
||||||
|
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||||
|
func (self *VMEnv) AddLog(log state.Log) {
|
||||||
|
self.state.AddLog(log)
|
||||||
|
}
|
||||||
|
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
||||||
|
return vm.Transfer(from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
|
||||||
|
return core.NewExecution(self, addr, data, gas, price, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||||
|
exe := self.vm(addr, data, gas, price, value)
|
||||||
|
ret, err := exe.Call(addr, caller)
|
||||||
|
self.Gas = exe.Gas
|
||||||
|
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
func (self *VMEnv) CallCode(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||||
|
exe := self.vm(caller.Address(), data, gas, price, value)
|
||||||
|
return exe.Call(addr, caller)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *VMEnv) Create(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) {
|
||||||
|
exe := self.vm(addr, data, gas, price, value)
|
||||||
|
return exe.Create(caller)
|
||||||
|
}
|
161
cmd/utils/websockets.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/websocket"
|
||||||
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func args(v ...interface{}) []interface{} {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebSocketServer struct {
|
||||||
|
ethereum *eth.Ethereum
|
||||||
|
filterCallbacks map[int][]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebSocketServer(eth *eth.Ethereum) *WebSocketServer {
|
||||||
|
return &WebSocketServer{eth, make(map[int][]int)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *WebSocketServer) Serv() {
|
||||||
|
pipe := xeth.NewJSXEth(self.ethereum)
|
||||||
|
|
||||||
|
wsServ := websocket.NewServer("/eth", ":40404")
|
||||||
|
wsServ.MessageFunc(func(c *websocket.Client, msg *websocket.Message) {
|
||||||
|
switch msg.Call {
|
||||||
|
case "compile":
|
||||||
|
data := ethutil.NewValue(msg.Args)
|
||||||
|
bcode, err := ethutil.Compile(data.Get(0).Str(), false)
|
||||||
|
if err != nil {
|
||||||
|
c.Write(args(nil, err.Error()), msg.Seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
code := ethutil.Bytes2Hex(bcode)
|
||||||
|
c.Write(args(code, nil), msg.Seed)
|
||||||
|
case "getBlockByNumber":
|
||||||
|
args := msg.Arguments()
|
||||||
|
|
||||||
|
block := pipe.BlockByNumber(int32(args.Get(0).Uint()))
|
||||||
|
c.Write(block, msg.Seed)
|
||||||
|
|
||||||
|
case "getKey":
|
||||||
|
c.Write(pipe.Key().PrivateKey, msg.Seed)
|
||||||
|
case "transact":
|
||||||
|
if mp, ok := msg.Args[0].(map[string]interface{}); ok {
|
||||||
|
object := mapToTxParams(mp)
|
||||||
|
c.Write(
|
||||||
|
args(pipe.Transact(object["from"], object["to"], object["value"], object["gas"], object["gasPrice"], object["data"])),
|
||||||
|
msg.Seed,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
case "getCoinBase":
|
||||||
|
c.Write(pipe.CoinBase(), msg.Seed)
|
||||||
|
|
||||||
|
case "getIsListening":
|
||||||
|
c.Write(pipe.IsListening(), msg.Seed)
|
||||||
|
|
||||||
|
case "getIsMining":
|
||||||
|
c.Write(pipe.IsMining(), msg.Seed)
|
||||||
|
|
||||||
|
case "getPeerCoint":
|
||||||
|
c.Write(pipe.PeerCount(), msg.Seed)
|
||||||
|
|
||||||
|
case "getCountAt":
|
||||||
|
args := msg.Arguments()
|
||||||
|
|
||||||
|
c.Write(pipe.TxCountAt(args.Get(0).Str()), msg.Seed)
|
||||||
|
|
||||||
|
case "getCodeAt":
|
||||||
|
args := msg.Arguments()
|
||||||
|
|
||||||
|
c.Write(len(pipe.CodeAt(args.Get(0).Str())), msg.Seed)
|
||||||
|
|
||||||
|
case "getBlockByHash":
|
||||||
|
args := msg.Arguments()
|
||||||
|
|
||||||
|
c.Write(pipe.BlockByHash(args.Get(0).Str()), msg.Seed)
|
||||||
|
|
||||||
|
case "getStorageAt":
|
||||||
|
args := msg.Arguments()
|
||||||
|
|
||||||
|
c.Write(pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str()), msg.Seed)
|
||||||
|
|
||||||
|
case "getBalanceAt":
|
||||||
|
args := msg.Arguments()
|
||||||
|
|
||||||
|
c.Write(pipe.BalanceAt(args.Get(0).Str()), msg.Seed)
|
||||||
|
|
||||||
|
case "getSecretToAddress":
|
||||||
|
args := msg.Arguments()
|
||||||
|
|
||||||
|
c.Write(pipe.SecretToAddress(args.Get(0).Str()), msg.Seed)
|
||||||
|
|
||||||
|
case "newFilter":
|
||||||
|
case "newFilterString":
|
||||||
|
case "messages":
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
wsServ.Listen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartWebSockets(eth *eth.Ethereum) {
|
||||||
|
sock := NewWebSocketServer(eth)
|
||||||
|
go sock.Serv()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO This is starting to become a generic method. Move to utils
|
||||||
|
func mapToTxParams(object map[string]interface{}) map[string]string {
|
||||||
|
// Default values
|
||||||
|
if object["from"] == nil {
|
||||||
|
object["from"] = ""
|
||||||
|
}
|
||||||
|
if object["to"] == nil {
|
||||||
|
object["to"] = ""
|
||||||
|
}
|
||||||
|
if object["value"] == nil {
|
||||||
|
object["value"] = ""
|
||||||
|
}
|
||||||
|
if object["gas"] == nil {
|
||||||
|
object["gas"] = ""
|
||||||
|
}
|
||||||
|
if object["gasPrice"] == nil {
|
||||||
|
object["gasPrice"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataStr string
|
||||||
|
var data []string
|
||||||
|
if str, ok := object["data"].(string); ok {
|
||||||
|
data = []string{str}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, str := range data {
|
||||||
|
if ethutil.IsHex(str) {
|
||||||
|
str = str[2:]
|
||||||
|
|
||||||
|
if len(str) != 64 {
|
||||||
|
str = ethutil.LeftPadString(str, 64)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStr += str
|
||||||
|
}
|
||||||
|
object["data"] = dataStr
|
||||||
|
|
||||||
|
conv := make(map[string]string)
|
||||||
|
for key, value := range object {
|
||||||
|
if v, ok := value.(string); ok {
|
||||||
|
conv[key] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conv
|
||||||
|
}
|
84
compression/rle/read_write.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package rle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
token byte = 0xfe
|
||||||
|
emptyShaToken = 0xfd
|
||||||
|
emptyListShaToken = 0xfe
|
||||||
|
tokenToken = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
var empty = crypto.Sha3([]byte(""))
|
||||||
|
var emptyList = crypto.Sha3([]byte{0x80})
|
||||||
|
|
||||||
|
func Decompress(dat []byte) ([]byte, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
for i := 0; i < len(dat); i++ {
|
||||||
|
if dat[i] == token {
|
||||||
|
if i+1 < len(dat) {
|
||||||
|
switch dat[i+1] {
|
||||||
|
case emptyShaToken:
|
||||||
|
buf.Write(empty)
|
||||||
|
case emptyListShaToken:
|
||||||
|
buf.Write(emptyList)
|
||||||
|
case tokenToken:
|
||||||
|
buf.WriteByte(token)
|
||||||
|
default:
|
||||||
|
buf.Write(make([]byte, int(dat[i+1]-2)))
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("error reading bytes. token encountered without proceeding bytes")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf.WriteByte(dat[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressChunk(dat []byte) (ret []byte, n int) {
|
||||||
|
switch {
|
||||||
|
case dat[0] == token:
|
||||||
|
return []byte{token, tokenToken}, 1
|
||||||
|
case len(dat) > 1 && dat[0] == 0x0 && dat[1] == 0x0:
|
||||||
|
j := 0
|
||||||
|
for j <= 254 && j < len(dat) {
|
||||||
|
if dat[j] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
return []byte{token, byte(j + 2)}, j
|
||||||
|
case len(dat) >= 32:
|
||||||
|
if dat[0] == empty[0] && bytes.Compare(dat[:32], empty) == 0 {
|
||||||
|
return []byte{token, emptyShaToken}, 32
|
||||||
|
} else if dat[0] == emptyList[0] && bytes.Compare(dat[:32], emptyList) == 0 {
|
||||||
|
return []byte{token, emptyListShaToken}, 32
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return dat[:1], 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Compress(dat []byte) []byte {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for i < len(dat) {
|
||||||
|
b, n := compressChunk(dat[i:])
|
||||||
|
buf.Write(b)
|
||||||
|
i += n
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
118
compression/rle/read_write_test.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package rle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
checker "gopkg.in/check.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test(t *testing.T) { checker.TestingT(t) }
|
||||||
|
|
||||||
|
type CompressionRleSuite struct{}
|
||||||
|
|
||||||
|
var _ = checker.Suite(&CompressionRleSuite{})
|
||||||
|
|
||||||
|
func (s *CompressionRleSuite) TestDecompressSimple(c *checker.C) {
|
||||||
|
exp := []byte{0xc5, 0xd2, 0x46, 0x1, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x3, 0xc0, 0xe5, 0x0, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x4, 0x5d, 0x85, 0xa4, 0x70}
|
||||||
|
res, err := Decompress([]byte{token, 0xfd})
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(res, checker.DeepEquals, exp)
|
||||||
|
// if bytes.Compare(res, exp) != 0 {
|
||||||
|
// t.Error("empty sha3", res)
|
||||||
|
// }
|
||||||
|
|
||||||
|
exp = []byte{0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x1, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21}
|
||||||
|
res, err = Decompress([]byte{token, 0xfe})
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(res, checker.DeepEquals, exp)
|
||||||
|
// if bytes.Compare(res, exp) != 0 {
|
||||||
|
// t.Error("0x80 sha3", res)
|
||||||
|
// }
|
||||||
|
|
||||||
|
res, err = Decompress([]byte{token, 0xff})
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(res, checker.DeepEquals, []byte{token})
|
||||||
|
// if bytes.Compare(res, []byte{token}) != 0 {
|
||||||
|
// t.Error("token", res)
|
||||||
|
// }
|
||||||
|
|
||||||
|
res, err = Decompress([]byte{token, 12})
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(res, checker.DeepEquals, make([]byte, 10))
|
||||||
|
// if bytes.Compare(res, make([]byte, 10)) != 0 {
|
||||||
|
// t.Error("10 * zero", res)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// func TestDecompressMulti(t *testing.T) {
|
||||||
|
// res, err := Decompress([]byte{token, 0xfd, token, 0xfe, token, 12})
|
||||||
|
// if err != nil {
|
||||||
|
// t.Error(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var exp []byte
|
||||||
|
// exp = append(exp, crypto.Sha3([]byte(""))...)
|
||||||
|
// exp = append(exp, crypto.Sha3([]byte{0x80})...)
|
||||||
|
// exp = append(exp, make([]byte, 10)...)
|
||||||
|
|
||||||
|
// if bytes.Compare(res, res) != 0 {
|
||||||
|
// t.Error("Expected", exp, "result", res)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func TestCompressSimple(t *testing.T) {
|
||||||
|
// res := Compress([]byte{0, 0, 0, 0, 0})
|
||||||
|
// if bytes.Compare(res, []byte{token, 7}) != 0 {
|
||||||
|
// t.Error("5 * zero", res)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// res = Compress(crypto.Sha3([]byte("")))
|
||||||
|
// if bytes.Compare(res, []byte{token, emptyShaToken}) != 0 {
|
||||||
|
// t.Error("empty sha", res)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// res = Compress(crypto.Sha3([]byte{0x80}))
|
||||||
|
// if bytes.Compare(res, []byte{token, emptyListShaToken}) != 0 {
|
||||||
|
// t.Error("empty list sha", res)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// res = Compress([]byte{token})
|
||||||
|
// if bytes.Compare(res, []byte{token, tokenToken}) != 0 {
|
||||||
|
// t.Error("token", res)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func TestCompressMulti(t *testing.T) {
|
||||||
|
// in := []byte{0, 0, 0, 0, 0}
|
||||||
|
// in = append(in, crypto.Sha3([]byte(""))...)
|
||||||
|
// in = append(in, crypto.Sha3([]byte{0x80})...)
|
||||||
|
// in = append(in, token)
|
||||||
|
// res := Compress(in)
|
||||||
|
|
||||||
|
// exp := []byte{token, 7, token, emptyShaToken, token, emptyListShaToken, token, tokenToken}
|
||||||
|
// if bytes.Compare(res, exp) != 0 {
|
||||||
|
// t.Error("expected", exp, "got", res)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func TestCompressDecompress(t *testing.T) {
|
||||||
|
// var in []byte
|
||||||
|
|
||||||
|
// for i := 0; i < 20; i++ {
|
||||||
|
// in = append(in, []byte{0, 0, 0, 0, 0}...)
|
||||||
|
// in = append(in, crypto.Sha3([]byte(""))...)
|
||||||
|
// in = append(in, crypto.Sha3([]byte{0x80})...)
|
||||||
|
// in = append(in, []byte{123, 2, 19, 89, 245, 254, 255, token, 98, 233}...)
|
||||||
|
// in = append(in, token)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// c := Compress(in)
|
||||||
|
// d, err := Decompress(c)
|
||||||
|
// if err != nil {
|
||||||
|
// t.Error(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if bytes.Compare(d, in) != 0 {
|
||||||
|
// t.Error("multi failed\n", d, "\n", in)
|
||||||
|
// }
|
||||||
|
// }
|
12
core/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
#
|
||||||
|
# If you find yourself ignoring temporary files generated by your text editor
|
||||||
|
# or operating system, you probably want to add a global ignore instead:
|
||||||
|
# git config --global core.excludesfile ~/.gitignore_global
|
||||||
|
|
||||||
|
/tmp
|
||||||
|
*/**/*un~
|
||||||
|
*un~
|
||||||
|
.DS_Store
|
||||||
|
*/**/.DS_Store
|
||||||
|
|
50
core/asm.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Disassemble(script []byte) (asm []string) {
|
||||||
|
pc := new(big.Int)
|
||||||
|
for {
|
||||||
|
if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the memory location of pc
|
||||||
|
val := script[pc.Int64()]
|
||||||
|
// Get the opcode (it must be an opcode!)
|
||||||
|
op := vm.OpCode(val)
|
||||||
|
|
||||||
|
asm = append(asm, fmt.Sprintf("%04v: %v", pc, op))
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8,
|
||||||
|
vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15,
|
||||||
|
vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22,
|
||||||
|
vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29,
|
||||||
|
vm.PUSH30, vm.PUSH31, vm.PUSH32:
|
||||||
|
pc.Add(pc, ethutil.Big1)
|
||||||
|
a := int64(op) - int64(vm.PUSH1) + 1
|
||||||
|
if int(pc.Int64()+a) > len(script) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := script[pc.Int64() : pc.Int64()+a]
|
||||||
|
if len(data) == 0 {
|
||||||
|
data = []byte{0}
|
||||||
|
}
|
||||||
|
asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data))
|
||||||
|
|
||||||
|
pc.Add(pc, big.NewInt(a-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pc.Add(pc, ethutil.Big1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return asm
|
||||||
|
}
|
371
core/block_manager.go
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"container/list"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
|
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
"github.com/ethereum/go-ethereum/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var statelogger = logger.NewLogger("BLOCK")
|
||||||
|
|
||||||
|
type Peer interface {
|
||||||
|
Inbound() bool
|
||||||
|
LastSend() time.Time
|
||||||
|
LastPong() int64
|
||||||
|
Host() []byte
|
||||||
|
Port() uint16
|
||||||
|
Version() string
|
||||||
|
PingTime() string
|
||||||
|
Connected() *int32
|
||||||
|
Caps() *ethutil.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
type EthManager interface {
|
||||||
|
BlockManager() *BlockManager
|
||||||
|
ChainManager() *ChainManager
|
||||||
|
TxPool() *TxPool
|
||||||
|
Broadcast(msgType wire.MsgType, data []interface{})
|
||||||
|
PeerCount() int
|
||||||
|
IsMining() bool
|
||||||
|
IsListening() bool
|
||||||
|
Peers() *list.List
|
||||||
|
KeyManager() *crypto.KeyManager
|
||||||
|
ClientIdentity() wire.ClientIdentity
|
||||||
|
Db() ethutil.Database
|
||||||
|
EventMux() *event.TypeMux
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockManager struct {
|
||||||
|
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||||
|
mutex sync.Mutex
|
||||||
|
// Canonical block chain
|
||||||
|
bc *ChainManager
|
||||||
|
// non-persistent key/value memory storage
|
||||||
|
mem map[string]*big.Int
|
||||||
|
// Proof of work used for validating
|
||||||
|
Pow pow.PoW
|
||||||
|
|
||||||
|
txpool *TxPool
|
||||||
|
|
||||||
|
// The last attempted block is mainly used for debugging purposes
|
||||||
|
// This does not have to be a valid block and will be set during
|
||||||
|
// 'Process' & canonical validation.
|
||||||
|
lastAttemptedBlock *types.Block
|
||||||
|
|
||||||
|
events event.Subscription
|
||||||
|
|
||||||
|
eventMux *event.TypeMux
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockManager {
|
||||||
|
sm := &BlockManager{
|
||||||
|
mem: make(map[string]*big.Int),
|
||||||
|
Pow: ezp.New(),
|
||||||
|
bc: chainManager,
|
||||||
|
eventMux: eventMux,
|
||||||
|
txpool: txpool,
|
||||||
|
}
|
||||||
|
|
||||||
|
return sm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) {
|
||||||
|
coinbase := statedb.GetOrNewStateObject(block.Coinbase)
|
||||||
|
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
||||||
|
|
||||||
|
// Process the transactions on to current block
|
||||||
|
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return receipts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BlockManager) ApplyTransactions(coinbase *state.StateObject, state *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) {
|
||||||
|
var (
|
||||||
|
receipts types.Receipts
|
||||||
|
handled, unhandled types.Transactions
|
||||||
|
erroneous types.Transactions
|
||||||
|
totalUsedGas = big.NewInt(0)
|
||||||
|
err error
|
||||||
|
cumulativeSum = new(big.Int)
|
||||||
|
)
|
||||||
|
|
||||||
|
done:
|
||||||
|
for i, tx := range txs {
|
||||||
|
// If we are mining this block and validating we want to set the logs back to 0
|
||||||
|
state.EmptyLogs()
|
||||||
|
|
||||||
|
txGas := new(big.Int).Set(tx.Gas())
|
||||||
|
|
||||||
|
cb := state.GetStateObject(coinbase.Address())
|
||||||
|
st := NewStateTransition(cb, tx, state, block)
|
||||||
|
_, err = st.TransitionState()
|
||||||
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case IsNonceErr(err):
|
||||||
|
err = nil // ignore error
|
||||||
|
continue
|
||||||
|
case IsGasLimitErr(err):
|
||||||
|
unhandled = txs[i:]
|
||||||
|
|
||||||
|
break done
|
||||||
|
default:
|
||||||
|
statelogger.Infoln(err)
|
||||||
|
erroneous = append(erroneous, tx)
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txGas.Sub(txGas, st.gas)
|
||||||
|
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
|
||||||
|
|
||||||
|
// Update the state with pending changes
|
||||||
|
state.Update(txGas)
|
||||||
|
|
||||||
|
cumulative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas))
|
||||||
|
receipt := types.NewReceipt(state.Root(), cumulative)
|
||||||
|
receipt.SetLogs(state.Logs())
|
||||||
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
|
chainlogger.Debugln(receipt)
|
||||||
|
|
||||||
|
// Notify all subscribers
|
||||||
|
if !transientProcess {
|
||||||
|
go self.eventMux.Post(TxPostEvent{tx})
|
||||||
|
}
|
||||||
|
|
||||||
|
receipts = append(receipts, receipt)
|
||||||
|
handled = append(handled, tx)
|
||||||
|
|
||||||
|
if ethutil.Config.Diff && ethutil.Config.DiffType == "all" {
|
||||||
|
state.CreateOutputForDiff()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block.Reward = cumulativeSum
|
||||||
|
block.GasUsed = totalUsedGas
|
||||||
|
|
||||||
|
return receipts, handled, unhandled, erroneous, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Messages, err error) {
|
||||||
|
// Processing a blocks may never happen simultaneously
|
||||||
|
sm.mutex.Lock()
|
||||||
|
defer sm.mutex.Unlock()
|
||||||
|
|
||||||
|
if sm.bc.HasBlock(block.Hash()) {
|
||||||
|
return nil, nil, &KnownBlockError{block.Number, block.Hash()}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sm.bc.HasBlock(block.PrevHash) {
|
||||||
|
return nil, nil, ParentError(block.PrevHash)
|
||||||
|
}
|
||||||
|
parent := sm.bc.GetBlock(block.PrevHash)
|
||||||
|
|
||||||
|
return sm.ProcessWithParent(block, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
|
||||||
|
sm.lastAttemptedBlock = block
|
||||||
|
|
||||||
|
state := parent.State().Copy()
|
||||||
|
|
||||||
|
// Defer the Undo on the Trie. If the block processing happened
|
||||||
|
// we don't want to undo but since undo only happens on dirty
|
||||||
|
// nodes this won't happen because Commit would have been called
|
||||||
|
// before that.
|
||||||
|
defer state.Reset()
|
||||||
|
|
||||||
|
// Block validation
|
||||||
|
if err = sm.ValidateBlock(block, parent); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
receipts, err := sm.TransitionState(state, parent, block)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rbloom := types.CreateBloom(receipts)
|
||||||
|
if bytes.Compare(rbloom, block.LogsBloom) != 0 {
|
||||||
|
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
txSha := types.DeriveSha(block.Transactions())
|
||||||
|
if bytes.Compare(txSha, block.TxSha) != 0 {
|
||||||
|
err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
receiptSha := types.DeriveSha(receipts)
|
||||||
|
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
|
||||||
|
//chainlogger.Debugf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
|
||||||
|
fmt.Printf("%x\n", ethutil.Encode(receipts))
|
||||||
|
err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = sm.AccumelateRewards(state, block, parent); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Update(ethutil.Big0)
|
||||||
|
|
||||||
|
if !block.State().Cmp(state) {
|
||||||
|
err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the new total difficulty and sync back to the db
|
||||||
|
if td, ok := sm.CalculateTD(block); ok {
|
||||||
|
// Sync the current block's state to the database and cancelling out the deferred Undo
|
||||||
|
state.Sync()
|
||||||
|
|
||||||
|
messages := state.Manifest().Messages
|
||||||
|
state.Manifest().Reset()
|
||||||
|
|
||||||
|
chainlogger.Infof("Processed block #%d (%x...)\n", block.Number, block.Hash()[0:4])
|
||||||
|
|
||||||
|
sm.txpool.RemoveSet(block.Transactions())
|
||||||
|
|
||||||
|
return td, messages, nil
|
||||||
|
} else {
|
||||||
|
return nil, nil, errors.New("total diff failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
|
||||||
|
uncleDiff := new(big.Int)
|
||||||
|
for _, uncle := range block.Uncles {
|
||||||
|
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||||
|
td := new(big.Int)
|
||||||
|
td = td.Add(sm.bc.Td(), uncleDiff)
|
||||||
|
td = td.Add(td, block.Difficulty)
|
||||||
|
|
||||||
|
// The new TD will only be accepted if the new difficulty is
|
||||||
|
// is greater than the previous.
|
||||||
|
if td.Cmp(sm.bc.Td()) > 0 {
|
||||||
|
return td, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates the current block. Returns an error if the block was invalid,
|
||||||
|
// an uncle or anything that isn't on the current block chain.
|
||||||
|
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||||
|
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
||||||
|
expd := CalcDifficulty(block, parent)
|
||||||
|
if expd.Cmp(block.Difficulty) < 0 {
|
||||||
|
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
||||||
|
}
|
||||||
|
|
||||||
|
diff := block.Time - parent.Time
|
||||||
|
if diff < 0 {
|
||||||
|
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock().Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX
|
||||||
|
// New blocks must be within the 15 minute range of the last block.
|
||||||
|
if diff > int64(15*time.Minute) {
|
||||||
|
return ValidationError("Block is too far in the future of last block (> 15 minutes)")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Verify the nonce of the block. Return an error if it's not valid
|
||||||
|
if !sm.Pow.Verify(block /*block.HashNoNonce(), block.Difficulty, block.Nonce*/) {
|
||||||
|
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Nonce))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error {
|
||||||
|
reward := new(big.Int).Set(BlockReward)
|
||||||
|
|
||||||
|
knownUncles := ethutil.Set(parent.Uncles)
|
||||||
|
nonces := ethutil.NewSet(block.Nonce)
|
||||||
|
for _, uncle := range block.Uncles {
|
||||||
|
if nonces.Include(uncle.Nonce) {
|
||||||
|
// Error not unique
|
||||||
|
return UncleError("Uncle not unique")
|
||||||
|
}
|
||||||
|
|
||||||
|
uncleParent := sm.bc.GetBlock(uncle.PrevHash)
|
||||||
|
if uncleParent == nil {
|
||||||
|
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {
|
||||||
|
return UncleError("Uncle too old")
|
||||||
|
}
|
||||||
|
|
||||||
|
if knownUncles.Include(uncle.Hash()) {
|
||||||
|
return UncleError("Uncle in chain")
|
||||||
|
}
|
||||||
|
|
||||||
|
nonces.Insert(uncle.Nonce)
|
||||||
|
|
||||||
|
r := new(big.Int)
|
||||||
|
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
|
||||||
|
|
||||||
|
uncleAccount := statedb.GetAccount(uncle.Coinbase)
|
||||||
|
uncleAccount.AddAmount(r)
|
||||||
|
|
||||||
|
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the account associated with the coinbase
|
||||||
|
account := statedb.GetAccount(block.Coinbase)
|
||||||
|
// Reward amount of ether to the coinbase address
|
||||||
|
account.AddAmount(reward)
|
||||||
|
|
||||||
|
statedb.Manifest().AddMessage(&state.Message{
|
||||||
|
To: block.Coinbase,
|
||||||
|
Input: nil,
|
||||||
|
Origin: nil,
|
||||||
|
Block: block.Hash(), Timestamp: block.Time, Coinbase: block.Coinbase, Number: block.Number,
|
||||||
|
Value: new(big.Int).Add(reward, block.Reward),
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) {
|
||||||
|
if !sm.bc.HasBlock(block.PrevHash) {
|
||||||
|
return nil, ParentError(block.PrevHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
sm.lastAttemptedBlock = block
|
||||||
|
|
||||||
|
var (
|
||||||
|
parent = sm.bc.GetBlock(block.PrevHash)
|
||||||
|
state = parent.State().Copy()
|
||||||
|
)
|
||||||
|
|
||||||
|
defer state.Reset()
|
||||||
|
|
||||||
|
sm.TransitionState(state, parent, block)
|
||||||
|
sm.AccumelateRewards(state, block, parent)
|
||||||
|
|
||||||
|
return state.Manifest().Messages, nil
|
||||||
|
}
|
355
core/chain_manager.go
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
var chainlogger = logger.NewLogger("CHAIN")
|
||||||
|
|
||||||
|
func AddTestNetFunds(block *types.Block) {
|
||||||
|
for _, addr := range []string{
|
||||||
|
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||||
|
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
||||||
|
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
|
||||||
|
"6c386a4b26f73c802f34673f7248bb118f97424a",
|
||||||
|
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
|
||||||
|
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
|
||||||
|
"e6716f9544a56c530d868e4bfbacb172315bdead",
|
||||||
|
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
|
||||||
|
} {
|
||||||
|
codedAddr := ethutil.Hex2Bytes(addr)
|
||||||
|
account := block.State().GetAccount(codedAddr)
|
||||||
|
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
|
||||||
|
block.State().UpdateStateObject(account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
||||||
|
diff := new(big.Int)
|
||||||
|
|
||||||
|
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
|
||||||
|
if block.Time >= parent.Time+5 {
|
||||||
|
diff.Sub(parent.Difficulty, adjust)
|
||||||
|
} else {
|
||||||
|
diff.Add(parent.Difficulty, adjust)
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChainManager struct {
|
||||||
|
//eth EthManager
|
||||||
|
processor types.BlockProcessor
|
||||||
|
eventMux *event.TypeMux
|
||||||
|
genesisBlock *types.Block
|
||||||
|
// Last known total difficulty
|
||||||
|
mu sync.RWMutex
|
||||||
|
td *big.Int
|
||||||
|
lastBlockNumber uint64
|
||||||
|
currentBlock *types.Block
|
||||||
|
lastBlockHash []byte
|
||||||
|
|
||||||
|
transState *state.StateDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) Td() *big.Int {
|
||||||
|
self.mu.RLock()
|
||||||
|
defer self.mu.RUnlock()
|
||||||
|
|
||||||
|
return self.td
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) LastBlockNumber() uint64 {
|
||||||
|
self.mu.RLock()
|
||||||
|
defer self.mu.RUnlock()
|
||||||
|
|
||||||
|
return self.lastBlockNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) LastBlockHash() []byte {
|
||||||
|
self.mu.RLock()
|
||||||
|
defer self.mu.RUnlock()
|
||||||
|
|
||||||
|
return self.lastBlockHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) CurrentBlock() *types.Block {
|
||||||
|
self.mu.RLock()
|
||||||
|
defer self.mu.RUnlock()
|
||||||
|
|
||||||
|
return self.currentBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChainManager(mux *event.TypeMux) *ChainManager {
|
||||||
|
bc := &ChainManager{}
|
||||||
|
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
|
||||||
|
bc.eventMux = mux
|
||||||
|
|
||||||
|
bc.setLastBlock()
|
||||||
|
|
||||||
|
bc.transState = bc.State().Copy()
|
||||||
|
|
||||||
|
return bc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
||||||
|
self.processor = proc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) State() *state.StateDB {
|
||||||
|
return self.CurrentBlock().State()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) TransState() *state.StateDB {
|
||||||
|
return self.transState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *ChainManager) setLastBlock() {
|
||||||
|
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
||||||
|
if len(data) != 0 {
|
||||||
|
// Prep genesis
|
||||||
|
AddTestNetFunds(bc.genesisBlock)
|
||||||
|
|
||||||
|
block := types.NewBlockFromBytes(data)
|
||||||
|
bc.currentBlock = block
|
||||||
|
bc.lastBlockHash = block.Hash()
|
||||||
|
bc.lastBlockNumber = block.Number.Uint64()
|
||||||
|
|
||||||
|
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||||
|
bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||||
|
} else {
|
||||||
|
bc.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
chainlogger.Infof("Last block (#%d) %x\n", bc.lastBlockNumber, bc.currentBlock.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block creation & chain handling
|
||||||
|
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
|
||||||
|
bc.mu.RLock()
|
||||||
|
defer bc.mu.RUnlock()
|
||||||
|
|
||||||
|
var root interface{}
|
||||||
|
hash := ZeroHash256
|
||||||
|
|
||||||
|
if bc.CurrentBlock != nil {
|
||||||
|
root = bc.currentBlock.Root()
|
||||||
|
hash = bc.lastBlockHash
|
||||||
|
}
|
||||||
|
|
||||||
|
block := types.CreateBlock(
|
||||||
|
root,
|
||||||
|
hash,
|
||||||
|
coinbase,
|
||||||
|
ethutil.BigPow(2, 32),
|
||||||
|
nil,
|
||||||
|
"")
|
||||||
|
|
||||||
|
parent := bc.currentBlock
|
||||||
|
if parent != nil {
|
||||||
|
block.Difficulty = CalcDifficulty(block, parent)
|
||||||
|
block.Number = new(big.Int).Add(bc.currentBlock.Number, ethutil.Big1)
|
||||||
|
block.GasLimit = block.CalcGasLimit(bc.currentBlock)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *ChainManager) Reset() {
|
||||||
|
bc.mu.Lock()
|
||||||
|
defer bc.mu.Unlock()
|
||||||
|
|
||||||
|
AddTestNetFunds(bc.genesisBlock)
|
||||||
|
|
||||||
|
bc.genesisBlock.Trie().Sync()
|
||||||
|
// Prepare the genesis block
|
||||||
|
bc.write(bc.genesisBlock)
|
||||||
|
bc.insert(bc.genesisBlock)
|
||||||
|
bc.currentBlock = bc.genesisBlock
|
||||||
|
|
||||||
|
bc.setTotalDifficulty(ethutil.Big("0"))
|
||||||
|
|
||||||
|
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||||
|
bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) Export() []byte {
|
||||||
|
self.mu.RLock()
|
||||||
|
defer self.mu.RUnlock()
|
||||||
|
|
||||||
|
chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Number)
|
||||||
|
|
||||||
|
blocks := make([]*types.Block, int(self.currentBlock.Number.Int64())+1)
|
||||||
|
for block := self.currentBlock; block != nil; block = self.GetBlock(block.PrevHash) {
|
||||||
|
blocks[block.Number.Int64()] = block
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethutil.Encode(blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *ChainManager) insert(block *types.Block) {
|
||||||
|
encodedBlock := block.RlpEncode()
|
||||||
|
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
||||||
|
bc.currentBlock = block
|
||||||
|
bc.lastBlockHash = block.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *ChainManager) write(block *types.Block) {
|
||||||
|
bc.writeBlockInfo(block)
|
||||||
|
|
||||||
|
encodedBlock := block.RlpEncode()
|
||||||
|
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
func (bc *ChainManager) Genesis() *types.Block {
|
||||||
|
return bc.genesisBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block fetching methods
|
||||||
|
func (bc *ChainManager) HasBlock(hash []byte) bool {
|
||||||
|
data, _ := ethutil.Config.Db.Get(hash)
|
||||||
|
return len(data) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
|
||||||
|
block := self.GetBlock(hash)
|
||||||
|
if block == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
|
||||||
|
for i := uint64(0); i < max; i++ {
|
||||||
|
chain = append(chain, block.Hash())
|
||||||
|
|
||||||
|
if block.Number.Cmp(ethutil.Big0) <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
block = self.GetBlock(block.PrevHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
||||||
|
data, _ := ethutil.Config.Db.Get(hash)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.NewBlockFromBytes(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||||
|
self.mu.RLock()
|
||||||
|
defer self.mu.RUnlock()
|
||||||
|
|
||||||
|
block := self.currentBlock
|
||||||
|
for ; block != nil; block = self.GetBlock(block.PrevHash) {
|
||||||
|
if block.Number.Uint64() == num {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if block != nil && block.Number.Uint64() == 0 && num != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
|
||||||
|
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
|
||||||
|
bc.td = td
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
|
||||||
|
parent := self.GetBlock(block.PrevHash)
|
||||||
|
if parent == nil {
|
||||||
|
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentTd := parent.BlockInfo().TD
|
||||||
|
|
||||||
|
uncleDiff := new(big.Int)
|
||||||
|
for _, uncle := range block.Uncles {
|
||||||
|
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||||
|
}
|
||||||
|
|
||||||
|
td := new(big.Int)
|
||||||
|
td = td.Add(parentTd, uncleDiff)
|
||||||
|
td = td.Add(td, block.Difficulty)
|
||||||
|
|
||||||
|
return td, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo {
|
||||||
|
bi := types.BlockInfo{}
|
||||||
|
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||||
|
bi.RlpDecode(data)
|
||||||
|
|
||||||
|
return bi
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unexported method for writing extra non-essential block info to the db
|
||||||
|
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
|
||||||
|
bc.lastBlockNumber++
|
||||||
|
bi := types.BlockInfo{Number: bc.lastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.td}
|
||||||
|
|
||||||
|
// For now we use the block hash with the words "info" appended as key
|
||||||
|
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *ChainManager) Stop() {
|
||||||
|
if bc.CurrentBlock != nil {
|
||||||
|
chainlogger.Infoln("Stopped")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||||
|
for _, block := range chain {
|
||||||
|
td, messages, err := self.processor.Process(block)
|
||||||
|
if err != nil {
|
||||||
|
if IsKnownBlockErr(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
chainlogger.Infof("block #%v process failed (%x)\n", block.Number, block.Hash()[:4])
|
||||||
|
chainlogger.Infoln(block)
|
||||||
|
chainlogger.Infoln(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mu.Lock()
|
||||||
|
{
|
||||||
|
|
||||||
|
self.write(block)
|
||||||
|
if td.Cmp(self.td) > 0 {
|
||||||
|
if block.Number.Cmp(new(big.Int).Add(self.currentBlock.Number, ethutil.Big1)) < 0 {
|
||||||
|
chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Number, block.Hash()[:4], self.currentBlock.Number, self.currentBlock.Hash()[:4])
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setTotalDifficulty(td)
|
||||||
|
self.insert(block)
|
||||||
|
self.transState = self.currentBlock.State().Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
self.mu.Unlock()
|
||||||
|
|
||||||
|
self.eventMux.Post(NewBlockEvent{block})
|
||||||
|
self.eventMux.Post(messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|