Compare commits
884 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72c8b79035 | ||
|
|
c7d8ee1dd7 | ||
|
|
ce9f4c65d3 | ||
|
|
ab0ad1925b | ||
|
|
6bb106a784 | ||
|
|
4e0ec4e66b | ||
|
|
b58a3ed0ad | ||
|
|
b803f57db6 | ||
|
|
b457f46c81 | ||
|
|
b4520c5886 | ||
|
|
ca67dc251f | ||
|
|
aea289e79e | ||
|
|
ef0eda0c39 | ||
|
|
af7dc3676b | ||
|
|
fa570f297e | ||
|
|
9cce6a47d4 | ||
|
|
bff0bedfa9 | ||
|
|
d6c80c1672 | ||
|
|
512343003d | ||
|
|
3adb640d2b | ||
|
|
ea589a17a4 | ||
|
|
a3883ca5d9 | ||
|
|
5be0163cde | ||
|
|
55454b2f2d | ||
|
|
a4dc85543b | ||
|
|
d8394bded7 | ||
|
|
d89b8d904f | ||
|
|
8e866ee551 | ||
|
|
6dfc1bd474 | ||
|
|
c84e5745fa | ||
|
|
751d15e4be | ||
|
|
8b6c5be6a9 | ||
|
|
3502b36232 | ||
|
|
046b8bbc8a | ||
|
|
6fa4b2dec5 | ||
|
|
83bcb819da | ||
|
|
b7e8621846 | ||
|
|
d5c19f8719 | ||
|
|
8fa56add47 | ||
|
|
e45ee6cd72 | ||
|
|
e344286c32 | ||
|
|
45d7c60608 | ||
|
|
a3e39c9858 | ||
|
|
8ab6d89810 | ||
|
|
1b3b9b2887 | ||
|
|
89ae0f0ea0 | ||
|
|
06cae8a535 | ||
|
|
12ac992ffb | ||
|
|
3b23c2e86d | ||
|
|
0f9b2218da | ||
|
|
12c42bce9b | ||
|
|
eb3d33ed6f | ||
|
|
a139c6d216 | ||
|
|
50f5ccc4f2 | ||
|
|
94db961975 | ||
|
|
38f3747cde | ||
|
|
4fec2f2fc2 | ||
|
|
c96d8a742b | ||
|
|
c2de31e586 | ||
|
|
4b11d79829 | ||
|
|
c35abe4196 | ||
|
|
a9a278a6e1 | ||
|
|
fb07ffa676 | ||
|
|
940a88fa4e | ||
|
|
708e495c28 | ||
|
|
460681ead9 | ||
|
|
2a7ed457dc | ||
|
|
35bbf11ba5 | ||
|
|
4f1e1e8870 | ||
|
|
cf505139f1 | ||
|
|
cdba22a2cb | ||
|
|
1df6361753 | ||
|
|
a6c6c7c070 | ||
|
|
ed6f2877d7 | ||
|
|
53c408f549 | ||
|
|
64704c456f | ||
|
|
0edf8e3f1b | ||
|
|
53a04e1686 | ||
|
|
d356c6640d | ||
|
|
e8e087fc37 | ||
|
|
181738a736 | ||
|
|
1ac1224cd3 | ||
|
|
3687df8da2 | ||
|
|
67eee6aeb7 | ||
|
|
83ba9df85b | ||
|
|
83f706186f | ||
|
|
eea5f6f232 | ||
|
|
38af7f35fc | ||
|
|
eea3de00c1 | ||
|
|
4d66e8d06d | ||
|
|
010cfb7d67 | ||
|
|
f9a8aac036 | ||
|
|
3d28823be7 | ||
|
|
492317abd7 | ||
|
|
ab22d5e278 | ||
|
|
ce5f704dd5 | ||
|
|
696dc05dda | ||
|
|
589083ad7a | ||
|
|
1fda997370 | ||
|
|
7c335e8764 | ||
|
|
5b1d33d5fa | ||
|
|
e435407080 | ||
|
|
b180be7526 | ||
|
|
90fb473d87 | ||
|
|
3650f2d51c | ||
|
|
3090324366 | ||
|
|
130901e820 | ||
|
|
801b8191ef | ||
|
|
469f9c26e7 | ||
|
|
b5f510ead7 | ||
|
|
b4f8bba843 | ||
|
|
2bb79614f6 | ||
|
|
c077dc652d | ||
|
|
1b6f2a3e92 | ||
|
|
9136c81f05 | ||
|
|
912e5599d9 | ||
|
|
ff0095ac5e | ||
|
|
bf9fedc4ee | ||
|
|
78ba54da6b | ||
|
|
c90e279ab5 | ||
|
|
215602de08 | ||
|
|
13b832f959 | ||
|
|
789bb9c852 | ||
|
|
aada1f547b | ||
|
|
60718225ac | ||
|
|
1a2fc03083 | ||
|
|
61e8baee0c | ||
|
|
dbc5f55da9 | ||
|
|
81b7698428 | ||
|
|
4ce4bad383 | ||
|
|
23d25a079b | ||
|
|
d19bdb642e | ||
|
|
2e0246a6c2 | ||
|
|
349098e7b2 | ||
|
|
dc3b1ecdd0 | ||
|
|
7fb33796b1 | ||
|
|
ec886ddefb | ||
|
|
4ded4181a6 | ||
|
|
60691d03e0 | ||
|
|
869fa399b1 | ||
|
|
7af953fd62 | ||
|
|
5acbcb0d57 | ||
|
|
7000c394b2 | ||
|
|
7c0d894ccf | ||
|
|
f092c10de5 | ||
|
|
7f5e6b3a0a | ||
|
|
5e9dc185a5 | ||
|
|
0e94ac0111 | ||
|
|
0b5bbf6048 | ||
|
|
14c9cbd40e | ||
|
|
c4466878cf | ||
|
|
c584221fa2 | ||
|
|
70ba050c06 | ||
|
|
f2281dc38a | ||
|
|
18a2e6265d | ||
|
|
0d593199d0 | ||
|
|
832c4a7565 | ||
|
|
34d22a35dd | ||
|
|
c880716f16 | ||
|
|
f20f4c74d2 | ||
|
|
1fdfa1e6c6 | ||
|
|
4ee49f03df | ||
|
|
52d5278a62 | ||
|
|
35a2b87174 | ||
|
|
664bb2becd | ||
|
|
8865b95818 | ||
|
|
03600dce97 | ||
|
|
8b607efc40 | ||
|
|
9f1af6b3e8 | ||
|
|
c0952ba44b | ||
|
|
9475a2e474 | ||
|
|
5baed0c158 | ||
|
|
97c259858c | ||
|
|
0f90696528 | ||
|
|
ac974a180d | ||
|
|
052380b8de | ||
|
|
23a29439c0 | ||
|
|
a8617e2862 | ||
|
|
6945a6b320 | ||
|
|
b375c9adbf | ||
|
|
88a727739b | ||
|
|
5f3ae4dee3 | ||
|
|
5cbe834024 | ||
|
|
eea5b86cc4 | ||
|
|
09c512abaa | ||
|
|
d42d816e7f | ||
|
|
17effd15ab | ||
|
|
9982eba188 | ||
|
|
aafe527d4a | ||
|
|
e4c53a460e | ||
|
|
ca01596a65 | ||
|
|
4f2415b483 | ||
|
|
f680eacdf2 | ||
|
|
f6dcca3ebb | ||
|
|
1a642fc624 | ||
|
|
c4469514db | ||
|
|
59daf95859 | ||
|
|
3a6e04ba15 | ||
|
|
b26f86d6ff | ||
|
|
570215acae | ||
|
|
a511264433 | ||
|
|
06f25d2b27 | ||
|
|
a3bd355b16 | ||
|
|
1c1cd8b164 | ||
|
|
a898109522 | ||
|
|
3694b10e22 | ||
|
|
39f25d20a8 | ||
|
|
61c1646b43 | ||
|
|
1b9396dcbb | ||
|
|
05be4b5b0e | ||
|
|
f8f8bf0fea | ||
|
|
6643b6a306 | ||
|
|
7036ab26d7 | ||
|
|
9b55169251 | ||
|
|
ff13c9c186 | ||
|
|
879e7305ca | ||
|
|
1ff827b2ea | ||
|
|
e7f1204fa4 | ||
|
|
9a2c4a34ee | ||
|
|
f4c421f77a | ||
|
|
fe84718b55 | ||
|
|
15d71a01d5 | ||
|
|
abe30f2578 | ||
|
|
fdae48547b | ||
|
|
68ca8df22f | ||
|
|
b8da38f4e4 | ||
|
|
1f103ab7f1 | ||
|
|
da6cf33aac | ||
|
|
73f08b376f | ||
|
|
ed34d1fee7 | ||
|
|
e9f4f1d13c | ||
|
|
e5bb330be5 | ||
|
|
e23e22cb81 | ||
|
|
7434026f5f | ||
|
|
5319d33bc6 | ||
|
|
702311b6b2 | ||
|
|
2511bc20e0 | ||
|
|
ce5a6eabae | ||
|
|
581cd97ba1 | ||
|
|
fa2f99641f | ||
|
|
c40f7db1ab | ||
|
|
be1363e943 | ||
|
|
c313039526 | ||
|
|
73db5dda8c | ||
|
|
5a8fb77fb2 | ||
|
|
4b6ebcbb61 | ||
|
|
bc056c41bc | ||
|
|
5b54442a48 | ||
|
|
dc14cce7a9 | ||
|
|
4f278ba715 | ||
|
|
1036fcca36 | ||
|
|
6b286a5dee | ||
|
|
c8ae675b95 | ||
|
|
4186467129 | ||
|
|
726884afcb | ||
|
|
5a2f3e700b | ||
|
|
911fc74346 | ||
|
|
1388f4d27e | ||
|
|
5b87327a43 | ||
|
|
2fc1679886 | ||
|
|
7ba5652bea | ||
|
|
1e9aebbc86 | ||
|
|
61ec361182 | ||
|
|
7781cbbc57 | ||
|
|
f3b806b471 | ||
|
|
6496405f30 | ||
|
|
85a6dc5e8c | ||
|
|
856bbfc9c8 | ||
|
|
ebaa43fa4c | ||
|
|
2d44b3ebea | ||
|
|
984493db30 | ||
|
|
47848769ff | ||
|
|
a8f6f5b974 | ||
|
|
1e13f474cb | ||
|
|
c69c3a9a46 | ||
|
|
3216b143c2 | ||
|
|
cc963d42a0 | ||
|
|
3f95a62e4f | ||
|
|
7f9a9e2e82 | ||
|
|
375ecd4ada | ||
|
|
8875dccd11 | ||
|
|
4c2301fdf6 | ||
|
|
346594c406 | ||
|
|
2609e2db5c | ||
|
|
692d5b4e08 | ||
|
|
c4af7464e5 | ||
|
|
5a1dc3eb8a | ||
|
|
adcbfcf8d6 | ||
|
|
b57607e7d3 | ||
|
|
2f159d4f45 | ||
|
|
93e1040d07 | ||
|
|
403c07c305 | ||
|
|
6253308e2e | ||
|
|
33a014013a | ||
|
|
4637215ab2 | ||
|
|
ceaedbbd7f | ||
|
|
52f8b1a1d7 | ||
|
|
e6d1250185 | ||
|
|
d04e5e49d0 | ||
|
|
a72436f330 | ||
|
|
467403f437 | ||
|
|
bbaac0c6a9 | ||
|
|
753fd4bda3 | ||
|
|
57d2c8c94a | ||
|
|
870ec89e9a | ||
|
|
0c3b70f2fb | ||
|
|
7a367698fe | ||
|
|
c77e99814b | ||
|
|
4ddd69cc55 | ||
|
|
ef4a61c769 | ||
|
|
7dfb5ff5bd | ||
|
|
6b391312ab | ||
|
|
6e62d77e4d | ||
|
|
0281cca9af | ||
|
|
018e2403b1 | ||
|
|
61f4534e2a | ||
|
|
69667317c1 | ||
|
|
530aac0682 | ||
|
|
6e7d8f90b5 | ||
|
|
65bf1086a2 | ||
|
|
98220442b4 | ||
|
|
3c3d2ef2b9 | ||
|
|
67066eb32a | ||
|
|
631df0fe56 | ||
|
|
485870296d | ||
|
|
6e5a1c00dc | ||
|
|
ba487eaaca | ||
|
|
eb0986c3f9 | ||
|
|
baf5be09dc | ||
|
|
f3aed42dd6 | ||
|
|
44531e3009 | ||
|
|
2177a0179e | ||
|
|
e1f333021f | ||
|
|
4040d73c60 | ||
|
|
61bd47ccc1 | ||
|
|
915c366056 | ||
|
|
6888a968f9 | ||
|
|
4e8e5bbb86 | ||
|
|
5752869824 | ||
|
|
39a12622ae | ||
|
|
dca88ff85c | ||
|
|
ab9843cb00 | ||
|
|
72fd1fa58d | ||
|
|
51eac1926f | ||
|
|
c1aed4af45 | ||
|
|
c12447c7c5 | ||
|
|
502bacea82 | ||
|
|
da5ba4ccc8 | ||
|
|
1f2426226b | ||
|
|
c0eb30b604 | ||
|
|
caca3a8048 | ||
|
|
f929419676 | ||
|
|
bc53e78a04 | ||
|
|
3dbea1a7a1 | ||
|
|
644d1db5ef | ||
|
|
07d97d5b26 | ||
|
|
c658b46fe1 | ||
|
|
8a5c9a8c70 | ||
|
|
3e4a525520 | ||
|
|
74ce0f738e | ||
|
|
1073d56245 | ||
|
|
7aa4484a03 | ||
|
|
6c96b60a63 | ||
|
|
ac1900a0fc | ||
|
|
bd3bc5c168 | ||
|
|
9ed43230ca | ||
|
|
7abe9ec4cc | ||
|
|
5ed2527663 | ||
|
|
b87c7cac54 | ||
|
|
0b34579b04 | ||
|
|
31291ebd35 | ||
|
|
e8e0b08f17 | ||
|
|
491ce61a76 | ||
|
|
e12a26dac5 | ||
|
|
7bf1889af1 | ||
|
|
139a2b7b0d | ||
|
|
f230c719d8 | ||
|
|
8703449dfe | ||
|
|
108590d924 | ||
|
|
b2cf5d2958 | ||
|
|
ee5ed44761 | ||
|
|
346913b7f6 | ||
|
|
18a8d2f67f | ||
|
|
3ae10915e4 | ||
|
|
949b9c85ca | ||
|
|
c21c19bd6c | ||
|
|
29125e830b | ||
|
|
1ac4676f4b | ||
|
|
fe5301cebf | ||
|
|
7262601123 | ||
|
|
fcb6cc1e76 | ||
|
|
ff716e7799 | ||
|
|
9c595aff95 | ||
|
|
98dbd1fdc7 | ||
|
|
a6df452841 | ||
|
|
4eab8672b8 | ||
|
|
6e2821b4db | ||
|
|
1564fae011 | ||
|
|
30e40079ca | ||
|
|
65a1d88907 | ||
|
|
e590874a81 | ||
|
|
b28e742683 | ||
|
|
62fdfb937a | ||
|
|
8814195122 | ||
|
|
e2095d4a5d | ||
|
|
78a38e9825 | ||
|
|
712101b63d | ||
|
|
1f18dbb17c | ||
|
|
0d8001adea | ||
|
|
1e44a62494 | ||
|
|
e8b13cb77e | ||
|
|
3f2fd610d9 | ||
|
|
25604dc577 | ||
|
|
0cfc6bf2a6 | ||
|
|
90d7823acb | ||
|
|
b4ae1b6528 | ||
|
|
f442665c46 | ||
|
|
c22498066b | ||
|
|
637883f52b | ||
|
|
f0c0da8551 | ||
|
|
10f42a2b39 | ||
|
|
29baccd857 | ||
|
|
f9814381a7 | ||
|
|
771ea47d37 | ||
|
|
c54beba932 | ||
|
|
a809621f63 | ||
|
|
7ad556346e | ||
|
|
5737c21340 | ||
|
|
77a5ce6bf3 | ||
|
|
fb503f523b | ||
|
|
bf7677ce69 | ||
|
|
1ce8c1cf82 | ||
|
|
4ddccfa5e5 | ||
|
|
5795d332c8 | ||
|
|
823054dc34 | ||
|
|
2ce15f429b | ||
|
|
143411aaf0 | ||
|
|
7d9548400d | ||
|
|
1b1941a896 | ||
|
|
4848c384cd | ||
|
|
d4f38d3894 | ||
|
|
e6acbc5a58 | ||
|
|
8dd4db5d85 | ||
|
|
d436eddc6a | ||
|
|
faf8e9ec6a | ||
|
|
7e6a571cba | ||
|
|
6bee9cd1e4 | ||
|
|
fb2b77e991 | ||
|
|
b914912c06 | ||
|
|
0ce04845de | ||
|
|
073365d5d9 | ||
|
|
3c27587d83 | ||
|
|
dbccc700f1 | ||
|
|
9f90ff2e59 | ||
|
|
4d9c8926b1 | ||
|
|
070695b348 | ||
|
|
c6e97d4dc5 | ||
|
|
a24e78fa92 | ||
|
|
21e0cd7781 | ||
|
|
ed45760425 | ||
|
|
0ca4250bd4 | ||
|
|
5059619947 | ||
|
|
edb228839e | ||
|
|
a9c93c797d | ||
|
|
f826ac35e3 | ||
|
|
9dc512349a | ||
|
|
3ecf16a492 | ||
|
|
584a76ab70 | ||
|
|
e339cde790 | ||
|
|
82a6a0848a | ||
|
|
441cb7980b | ||
|
|
5f523f6966 | ||
|
|
993650f3d6 | ||
|
|
ab330301eb | ||
|
|
bab85dd789 | ||
|
|
acae643a4a | ||
|
|
01f825b0e1 | ||
|
|
796637b31a | ||
|
|
484ecfaf47 | ||
|
|
c082af6f74 | ||
|
|
494eb4ab6b | ||
|
|
fe678dcd2f | ||
|
|
da5de4a6ff | ||
|
|
526c61e2c0 | ||
|
|
c7f608ec74 | ||
|
|
6816f8b489 | ||
|
|
cd58b5ff1f | ||
|
|
bca100cdb0 | ||
|
|
c63452e25d | ||
|
|
7e779327eb | ||
|
|
9f1e08663d | ||
|
|
91122d9193 | ||
|
|
c7d21841a4 | ||
|
|
701692b7d3 | ||
|
|
787a30cd8e | ||
|
|
ca6edcaf71 | ||
|
|
8d171a37f8 | ||
|
|
e9bd41b3f1 | ||
|
|
aa67bd5d00 | ||
|
|
802d684994 | ||
|
|
434e018584 | ||
|
|
4839294c86 | ||
|
|
71bbcd54ff | ||
|
|
3db353f356 | ||
|
|
f4c5ea8378 | ||
|
|
6a97a4a11e | ||
|
|
79f754e6ac | ||
|
|
e3e2fcc285 | ||
|
|
00e61a9100 | ||
|
|
f3107214f4 | ||
|
|
78e001284f | ||
|
|
9caa868603 | ||
|
|
202c54d423 | ||
|
|
1051004aee | ||
|
|
a1a002f4da | ||
|
|
5ef41ed53e | ||
|
|
67721f3413 | ||
|
|
34bf2452c3 | ||
|
|
8fbb98fb3f | ||
|
|
1792725651 | ||
|
|
47ff3a9bee | ||
|
|
ac3a706f0d | ||
|
|
9b5483a71b | ||
|
|
38c31c880f | ||
|
|
0bed5976e3 | ||
|
|
19a6725430 | ||
|
|
48a54efcb2 | ||
|
|
c0b0dc5219 | ||
|
|
683a26c830 | ||
|
|
1a16f335fa | ||
|
|
4145be863b | ||
|
|
e9f1b38984 | ||
|
|
edd90f153c | ||
|
|
dec390a89f | ||
|
|
41348dead4 | ||
|
|
c473ab97c7 | ||
|
|
0cd1de769b | ||
|
|
5ae8e8a9ca | ||
|
|
796d72f48e | ||
|
|
86a8584252 | ||
|
|
6509e90c36 | ||
|
|
09ecd0c583 | ||
|
|
a6d6adc57f | ||
|
|
6be5744be4 | ||
|
|
31b4437b93 | ||
|
|
458afcd230 | ||
|
|
cf5ae81ced | ||
|
|
08e46432c5 | ||
|
|
6f11621734 | ||
|
|
6004c394d6 | ||
|
|
609d83f92c | ||
|
|
4ef71f8a82 | ||
|
|
b47218521f | ||
|
|
c9cee8fd52 | ||
|
|
75e779029f | ||
|
|
3016d54f13 | ||
|
|
05e7c133fb | ||
|
|
fd57100190 | ||
|
|
fc86b1799a | ||
|
|
9546e0c8c2 | ||
|
|
4fe6c148ef | ||
|
|
9e872788c7 | ||
|
|
da95f77996 | ||
|
|
6f758bc7b1 | ||
|
|
b37b3cd1fc | ||
|
|
e81069ab3d | ||
|
|
bf25f17880 | ||
|
|
6201532c64 | ||
|
|
3094ae9df9 | ||
|
|
59f6931e06 | ||
|
|
95a601d053 | ||
|
|
5d7ef54d02 | ||
|
|
b34d46cbc8 | ||
|
|
0bb78814a6 | ||
|
|
4938d5dde5 | ||
|
|
4817b94d0b | ||
|
|
2a470deeaf | ||
|
|
861d829420 | ||
|
|
09ee6e1477 | ||
|
|
10fc74eb81 | ||
|
|
986f485b3e | ||
|
|
af1088ef61 | ||
|
|
13bc922e54 | ||
|
|
1f39a1bd76 | ||
|
|
13efb6586d | ||
|
|
a48ed02433 | ||
|
|
24c43513a6 | ||
|
|
a6d267abc0 | ||
|
|
c8877d4098 | ||
|
|
1318f536c9 | ||
|
|
bc2f86e806 | ||
|
|
107f0fa4c6 | ||
|
|
a5190449da | ||
|
|
114d4433a9 | ||
|
|
bd4498cffc | ||
|
|
6771539a90 | ||
|
|
123b6ae62e | ||
|
|
5d6a0d4dae | ||
|
|
6ecc63002b | ||
|
|
b3ea766bd5 | ||
|
|
e2a90ce159 | ||
|
|
8057e8df43 | ||
|
|
98b7c07171 | ||
|
|
3d76417353 | ||
|
|
2060ea5de3 | ||
|
|
79eb8f7ace | ||
|
|
0ebcc200c3 | ||
|
|
27f3f42ce2 | ||
|
|
dab967ace8 | ||
|
|
485d4aa8f3 | ||
|
|
799ae29ac4 | ||
|
|
00b209a29e | ||
|
|
581e510c2d | ||
|
|
9053c0dfd9 | ||
|
|
d32ce37484 | ||
|
|
1020560af6 | ||
|
|
118588ef6c | ||
|
|
6b9314eaa9 | ||
|
|
686bf443e6 | ||
|
|
93054ef24b | ||
|
|
1620eabd9d | ||
|
|
7fcb082cad | ||
|
|
68d16b723a | ||
|
|
ec9c6e9783 | ||
|
|
3b083d545d | ||
|
|
5400447395 | ||
|
|
ed8425b8b9 | ||
|
|
7d7d4822a5 | ||
|
|
80528c5344 | ||
|
|
6c24d9897a | ||
|
|
e346f3058e | ||
|
|
dc8da3743d | ||
|
|
6563576ae9 | ||
|
|
db9397890e | ||
|
|
bd1e3fc606 | ||
|
|
fe5f5b28d9 | ||
|
|
ee41fa6f30 | ||
|
|
84ecab0eaf | ||
|
|
3fde07b2e1 | ||
|
|
3c2f13f88b | ||
|
|
52f10242e2 | ||
|
|
9e719f088f | ||
|
|
6552256981 | ||
|
|
d1934363e7 | ||
|
|
cdbcfaa7de | ||
|
|
6ecc855c34 | ||
|
|
30ecd045fa | ||
|
|
f9e64e0965 | ||
|
|
0ecbb3ec02 | ||
|
|
3bb5ad7204 | ||
|
|
8e078b1d83 | ||
|
|
0c4d2fbc70 | ||
|
|
e4614e49ae | ||
|
|
8f56606cac | ||
|
|
af90fbfb33 | ||
|
|
938c3707fc | ||
|
|
1b95af18ff | ||
|
|
272ebc1ef7 | ||
|
|
cfd50538bb | ||
|
|
08abf67a51 | ||
|
|
981554cf74 | ||
|
|
979af3d314 | ||
|
|
57d1f2b4d3 | ||
|
|
1fa95ac236 | ||
|
|
25dc1c2155 | ||
|
|
61ec02248a | ||
|
|
ecd7caa93d | ||
|
|
aea26dcc64 | ||
|
|
1b8f299df2 | ||
|
|
6e2e08628a | ||
|
|
fb0e6cf0d0 | ||
|
|
a57c45bb1c | ||
|
|
b2cc1c54f8 | ||
|
|
21dad1d41e | ||
|
|
1d42b7f0d1 | ||
|
|
cddc33bb24 | ||
|
|
cb7ad2366d | ||
|
|
25536c5ffb | ||
|
|
842b75c0e6 | ||
|
|
8b0ba97cf2 | ||
|
|
b84682168d | ||
|
|
f20f9f376e | ||
|
|
24838bbcd3 | ||
|
|
7a00d97977 | ||
|
|
ac3de4c5fc | ||
|
|
28c731881f | ||
|
|
a7a46f4253 | ||
|
|
528497b86a | ||
|
|
32c32ecfda | ||
|
|
e30839e85f | ||
|
|
a4c7843a07 | ||
|
|
f0c6d17ad8 | ||
|
|
66c0638f3b | ||
|
|
eec7364760 | ||
|
|
b10094508f | ||
|
|
8e8679807d | ||
|
|
629da8f8bf | ||
|
|
10a346476a | ||
|
|
d1f5284fe6 | ||
|
|
849f5d9a44 | ||
|
|
7a76916143 | ||
|
|
2a829b1f1a | ||
|
|
44c68221a8 | ||
|
|
e36c4ecc98 | ||
|
|
01d399ad66 | ||
|
|
9376796bdb | ||
|
|
f135c09b36 | ||
|
|
4e85015836 | ||
|
|
f56c065f57 | ||
|
|
adc3457a89 | ||
|
|
fa261ebd02 | ||
|
|
7fdb87ad38 | ||
|
|
baeda93474 | ||
|
|
7a28f72abc | ||
|
|
1ab06ce2d2 | ||
|
|
b0cc44aabb | ||
|
|
c983efe895 | ||
|
|
650948feed | ||
|
|
4e76cce0b7 | ||
|
|
9e9045ab94 | ||
|
|
d86333af6b | ||
|
|
28b5906d9e | ||
|
|
24f6d8296b | ||
|
|
9c5e35548d | ||
|
|
92b5b5644f | ||
|
|
8fb47b52f5 | ||
|
|
14361cc7b1 | ||
|
|
2257bc8e2f | ||
|
|
461b2d4853 | ||
|
|
941f2380c4 | ||
|
|
cb31220a4a | ||
|
|
9e09d5b6bf | ||
|
|
a04c5b180a | ||
|
|
db7a8c4ac7 | ||
|
|
fac356c701 | ||
|
|
90eb61091a | ||
|
|
f6998cb04e | ||
|
|
897a94641e | ||
|
|
3f677c6168 | ||
|
|
1cd93e4ceb | ||
|
|
03b96a7c0a | ||
|
|
869fa6fda8 | ||
|
|
bc2f5586ee | ||
|
|
16f435b906 | ||
|
|
0a170efaa5 | ||
|
|
1356d6d8d5 | ||
|
|
0a9114203b | ||
|
|
2b05eb43a9 | ||
|
|
dd2c27958c | ||
|
|
6737484eda | ||
|
|
9fe991db1c | ||
|
|
6cf441fc9e | ||
|
|
99e37844fd | ||
|
|
8348147a4f | ||
|
|
bd7273061e | ||
|
|
4f447c50b2 | ||
|
|
692cd10d4a | ||
|
|
86446d713a | ||
|
|
d1487b3177 | ||
|
|
8e7a08f865 | ||
|
|
0d75d01c84 | ||
|
|
1c75e8eb47 | ||
|
|
431b27d3e1 | ||
|
|
d97cf34138 | ||
|
|
e6f75bccfe | ||
|
|
c039ab79b5 | ||
|
|
9436e88d27 | ||
|
|
652f5032a2 | ||
|
|
d7a7f034db | ||
|
|
27c32d3629 | ||
|
|
0d2993e46d | ||
|
|
e4168c2985 | ||
|
|
9d3771458d | ||
|
|
ff0ce70169 | ||
|
|
811d165458 | ||
|
|
d57944ffb9 | ||
|
|
991f0cac6e | ||
|
|
b2d338bf35 | ||
|
|
ef80698deb | ||
|
|
e12a5159a8 | ||
|
|
0455aa96bf | ||
|
|
9aaedad64f | ||
|
|
ea6b0ec164 | ||
|
|
679d6c6f2b | ||
|
|
ec96091369 | ||
|
|
0a535bf485 | ||
|
|
dcaff6f4c8 | ||
|
|
5e7d42e4a4 | ||
|
|
99a13c4e66 | ||
|
|
060205ab27 | ||
|
|
9f775a7673 | ||
|
|
d477670cb9 | ||
|
|
54c9c382e7 | ||
|
|
9108a3bb50 | ||
|
|
c1cced3662 | ||
|
|
5ea4c22868 | ||
|
|
f1b7d8ab34 | ||
|
|
9c9ddaccec | ||
|
|
68a08df9c3 | ||
|
|
1e6d889fc7 | ||
|
|
443115f885 | ||
|
|
cab073ba00 | ||
|
|
7f9589d678 | ||
|
|
899c1a4b0e | ||
|
|
e3f7b70c38 | ||
|
|
6e49ff1d98 | ||
|
|
0c7f998c25 | ||
|
|
23ea4798e0 | ||
|
|
2632310b6a | ||
|
|
c4dd156113 | ||
|
|
06a7ca221c | ||
|
|
04931618ed | ||
|
|
dbc4d85f0a | ||
|
|
bd45cd4a5e | ||
|
|
8c02211dc3 | ||
|
|
eb18e7ade7 | ||
|
|
8ddd508a44 | ||
|
|
857809f693 | ||
|
|
ca0045482c | ||
|
|
1c2c683ae3 | ||
|
|
7c8d404cf8 | ||
|
|
d293f94a6f | ||
|
|
a60d0e440d | ||
|
|
9e294d577a | ||
|
|
6d5d419e14 | ||
|
|
1b4d9c2d39 | ||
|
|
a91e562021 | ||
|
|
d7f690c8ba | ||
|
|
7f1ff152ca | ||
|
|
beb6438ef5 | ||
|
|
e25a660a11 | ||
|
|
a7887fa9f1 | ||
|
|
a6915778bb | ||
|
|
249f81cbc5 | ||
|
|
373036bb7a | ||
|
|
ed296312aa | ||
|
|
d27c36cf75 | ||
|
|
c737056000 | ||
|
|
6f5bd845ad | ||
|
|
fed4864939 | ||
|
|
acd7192b17 | ||
|
|
21cb08586b | ||
|
|
4d1cb01da0 | ||
|
|
0e548ce7c5 | ||
|
|
236692cfd5 | ||
|
|
322dfbcd78 | ||
|
|
1bad20ae38 | ||
|
|
113c35af0a | ||
|
|
5c47116889 | ||
|
|
23fc5517b5 | ||
|
|
7e948a088f | ||
|
|
102bc7809f | ||
|
|
c2bd1a0e76 | ||
|
|
de72643898 | ||
|
|
e0f71b0c17 | ||
|
|
66f3c50842 | ||
|
|
373fdb65e9 | ||
|
|
58a1671076 | ||
|
|
e0a21e5aae | ||
|
|
3f33370e7d | ||
|
|
06fa900504 | ||
|
|
29d7a0e3c9 | ||
|
|
e8106016c8 | ||
|
|
d716bae3d5 | ||
|
|
f48b09b76e | ||
|
|
f54944bbfc | ||
|
|
ff722cac72 | ||
|
|
eeee90def5 | ||
|
|
47a02480c4 | ||
|
|
bbefdec973 | ||
|
|
f864f72bb5 | ||
|
|
6623de4e61 | ||
|
|
33284e988e | ||
|
|
9d7d6f7108 | ||
|
|
81b52c7336 | ||
|
|
fb17ae7751 | ||
|
|
9bcb589785 | ||
|
|
2e00f656d0 | ||
|
|
87f893265d | ||
|
|
d4205da484 | ||
|
|
ca0d1f5eb7 | ||
|
|
e76a545970 | ||
|
|
ec7282d05c | ||
|
|
ea3083bd3b |
@@ -1,3 +1,3 @@
|
|||||||
[target.x86_64-pc-windows-msvc]
|
[target.x86_64-pc-windows-msvc]
|
||||||
# Link the C runtime statically ; https://github.com/paritytech/parity/issues/6643
|
# Link the C runtime statically ; https://github.com/paritytech/parity-ethereum/issues/6643
|
||||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||||
|
|||||||
14
.github/CONTRIBUTING.md
vendored
14
.github/CONTRIBUTING.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Do you have a question?
|
## Do you have a question?
|
||||||
|
|
||||||
Check out our [Basic Usage](https://github.com/paritytech/parity/wiki/Basic-Usage), [Configuration](https://github.com/paritytech/parity/wiki/Configuring-Parity), and [FAQ](https://github.com/paritytech/parity/wiki/FAQ) articles on our [wiki](https://github.com/paritytech/parity/wiki)!
|
Check out our [Basic Usage](https://wiki.parity.io/Basic-Usage), [Configuration](https://wiki.parity.io/Configuring-Parity-Ethereum), and [FAQ](https://wiki.parity.io/FAQ) articles on our [wiki](https://wiki.parity.io/)!
|
||||||
|
|
||||||
See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange.
|
See also frequently asked questions [tagged with `parity`](https://ethereum.stackexchange.com/questions/tagged/parity?sort=votes&pageSize=50) on Stack Exchange.
|
||||||
|
|
||||||
@@ -10,11 +10,11 @@ See also frequently asked questions [tagged with `parity`](https://ethereum.stac
|
|||||||
|
|
||||||
Do **not** open an issue on Github if you think your discovered bug could be a **security-relevant vulnerability**. Please, read our [security policy](../SECURITY.md) instead.
|
Do **not** open an issue on Github if you think your discovered bug could be a **security-relevant vulnerability**. Please, read our [security policy](../SECURITY.md) instead.
|
||||||
|
|
||||||
Otherwise, just create a [new issue](https://github.com/paritytech/parity/issues/new) in our repository and state:
|
Otherwise, just create a [new issue](https://github.com/paritytech/parity-ethereum/issues/new) in our repository and state:
|
||||||
|
|
||||||
- What's your Parity version?
|
- What's your Parity Ethereum version?
|
||||||
- What's your operating system and version?
|
- What's your operating system and version?
|
||||||
- How did you install parity?
|
- How did you install Parity Ethereum?
|
||||||
- Is your node fully synchronized?
|
- Is your node fully synchronized?
|
||||||
- Did you try turning it off and on again?
|
- Did you try turning it off and on again?
|
||||||
|
|
||||||
@@ -22,12 +22,12 @@ Also, try to include **steps to reproduce** the issue and expand on the **actual
|
|||||||
|
|
||||||
## Contribute!
|
## Contribute!
|
||||||
|
|
||||||
If you would like to contribute to Parity, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity/compare).
|
If you would like to contribute to Parity Ethereum, please **fork it**, fix bugs or implement features, and [propose a pull request](https://github.com/paritytech/parity-ethereum/compare).
|
||||||
|
|
||||||
Please, refer to the [Coding Guide](https://github.com/paritytech/parity/wiki/Coding-guide) in our wiki for more details about hacking on Parity.
|
Please, refer to the [Coding Guide](https://wiki.parity.io/Coding-guide) in our wiki for more details about hacking on Parity.
|
||||||
|
|
||||||
## License.
|
## License.
|
||||||
|
|
||||||
By contributing to Parity, you agree that your contributions will be licensed under the [GPLv3 License](../LICENSE).
|
By contributing to Parity Ethereum, you agree that your contributions will be licensed under the [GPLv3 License](../LICENSE).
|
||||||
|
|
||||||
Each contributor has to sign our Contributor License Agreement. The purpose of the CLA is to ensure that the guardian of a project's outputs has the necessary ownership or grants of rights over all contributions to allow them to distribute under the chosen license. You can read and sign our full Contributor License Agreement at [cla.parity.io](https://cla.parity.io) before submitting a pull request.
|
Each contributor has to sign our Contributor License Agreement. The purpose of the CLA is to ensure that the guardian of a project's outputs has the necessary ownership or grants of rights over all contributions to allow them to distribute under the chosen license. You can read and sign our full Contributor License Agreement at [cla.parity.io](https://cla.parity.io) before submitting a pull request.
|
||||||
|
|||||||
14
.github/ISSUE_TEMPLATE.md
vendored
14
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,13 +1,11 @@
|
|||||||
_Before filing a new issue, please **provide the following information**._
|
_Before filing a new issue, please **provide the following information**._
|
||||||
|
|
||||||
> I'm running:
|
- **Parity Ethereum version**: 0.0.0
|
||||||
>
|
- **Operating system**: Windows / MacOS / Linux
|
||||||
> - **Which Parity version?**: 0.0.0
|
- **Installation**: homebrew / one-line installer / built from source
|
||||||
> - **Which operating system?**: Windows / MacOS / Linux
|
- **Fully synchronized**: no / yes
|
||||||
> - **How installed?**: via installer / homebrew / binaries / from source
|
- **Network**: ethereum / ropsten / kovan / ...
|
||||||
> - **Are you fully synchronized?**: no / yes
|
- **Restarted**: no / yes
|
||||||
> - **Which network are you connected to?**: ethereum / ropsten / kovan / ...
|
|
||||||
> - **Did you try to restart the node?**: no / yes
|
|
||||||
|
|
||||||
_Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._
|
_Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._
|
||||||
|
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -38,7 +38,8 @@ node_modules
|
|||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
out/
|
out/
|
||||||
|
parity-clib-examples/cpp/build/
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
rls/
|
||||||
/parity.*
|
/parity.*
|
||||||
|
|||||||
451
.gitlab-ci.yml
451
.gitlab-ci.yml
@@ -1,245 +1,246 @@
|
|||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
- push-release
|
|
||||||
- build
|
- build
|
||||||
|
- publish
|
||||||
|
- optional
|
||||||
|
|
||||||
|
image: parity/rust-parity-ethereum-build:xenial
|
||||||
variables:
|
variables:
|
||||||
RUST_BACKTRACE: "1"
|
GIT_STRATEGY: fetch
|
||||||
RUSTFLAGS: ""
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
CARGOFLAGS: ""
|
CI_SERVER_NAME: "GitLab CI"
|
||||||
CI_SERVER_NAME: "GitLab CI"
|
CARGO_HOME: "${CI_PROJECT_DIR}/.cargo"
|
||||||
LIBSSL: "libssl1.0.0 (>=1.0.0)"
|
CARGO_TARGET: x86_64-unknown-linux-gnu
|
||||||
CARGO_HOME: $CI_PROJECT_DIR/cargo
|
|
||||||
cache:
|
.no_git: &no_git #disable git strategy
|
||||||
key: "$CI_BUILD_STAGE-$CI_BUILD_REF_NAME"
|
variables:
|
||||||
paths:
|
GIT_STRATEGY: none
|
||||||
- target/
|
GIT_SUBMODULE_STRATEGY: none
|
||||||
- cargo/
|
|
||||||
untracked: true
|
.releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries")
|
||||||
linux-stable:
|
only: &releaseable_branches
|
||||||
stage: build
|
|
||||||
image: parity/rust:gitlab-ci
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- rustup default stable
|
|
||||||
# ARGUMENTS: 1. BUILD_PLATFORM (target for binaries) 2. PLATFORM (target for cargo) 3. ARC (architecture) 4. & 5. CC & CXX flags 6. binary identifier
|
|
||||||
- scripts/gitlab-build.sh x86_64-unknown-linux-gnu x86_64-unknown-linux-gnu amd64 gcc g++ ubuntu
|
|
||||||
tags:
|
|
||||||
- rust-stable
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "stable-x86_64-unknown-linux-gnu_parity"
|
|
||||||
linux-stable-debian:
|
|
||||||
stage: build
|
|
||||||
image: parity/rust-debian:gitlab-ci
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- export LIBSSL="libssl1.1 (>=1.1.0)"
|
|
||||||
- scripts/gitlab-build.sh x86_64-unknown-debian-gnu x86_64-unknown-linux-gnu amd64 gcc g++ debian
|
|
||||||
tags:
|
|
||||||
- rust-debian
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "stable-x86_64-unknown-debian-gnu_parity"
|
|
||||||
linux-centos:
|
|
||||||
stage: build
|
|
||||||
image: parity/rust-centos:gitlab-ci
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- scripts/gitlab-build.sh x86_64-unknown-centos-gnu x86_64-unknown-linux-gnu x86_64 gcc g++ centos
|
|
||||||
tags:
|
|
||||||
- rust-centos
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "x86_64-unknown-centos-gnu_parity"
|
|
||||||
linux-i686:
|
|
||||||
stage: build
|
|
||||||
image: parity/rust-i686:gitlab-ci
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- scripts/gitlab-build.sh i686-unknown-linux-gnu i686-unknown-linux-gnu i386 gcc g++ ubuntu
|
|
||||||
tags:
|
|
||||||
- rust-i686
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "i686-unknown-linux-gnu"
|
|
||||||
linux-armv7:
|
|
||||||
stage: build
|
|
||||||
image: parity/rust-armv7:gitlab-ci
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- scripts/gitlab-build.sh armv7-unknown-linux-gnueabihf armv7-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++ ubuntu
|
|
||||||
tags:
|
|
||||||
- rust-arm
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "armv7_unknown_linux_gnueabihf_parity"
|
|
||||||
linux-arm:
|
|
||||||
stage: build
|
|
||||||
image: parity/rust-arm:gitlab-ci
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- scripts/gitlab-build.sh arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabihf armhf arm-linux-gnueabihf-gcc arm-linux-gnueabihf-g++ ubuntu
|
|
||||||
tags:
|
|
||||||
- rust-arm
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "arm-unknown-linux-gnueabihf_parity"
|
|
||||||
linux-aarch64:
|
|
||||||
stage: build
|
|
||||||
image: parity/rust-arm64:gitlab-ci
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- scripts/gitlab-build.sh aarch64-unknown-linux-gnu aarch64-unknown-linux-gnu arm64 aarch64-linux-gnu-gcc aarch64-linux-gnu-g++ ubuntu
|
|
||||||
tags:
|
|
||||||
- rust-arm
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "aarch64-unknown-linux-gnu_parity"
|
|
||||||
linux-snap:
|
|
||||||
stage: build
|
|
||||||
image: snapcore/snapcraft:stable
|
|
||||||
only:
|
|
||||||
- stable
|
- stable
|
||||||
- beta
|
- beta
|
||||||
- tags
|
- tags
|
||||||
- triggers
|
- schedules
|
||||||
script:
|
|
||||||
- scripts/gitlab-build.sh x86_64-unknown-snap-gnu x86_64-unknown-linux-gnu amd64 gcc g++ snap
|
.collect_artifacts: &collect_artifacts
|
||||||
tags:
|
|
||||||
- rust-stable
|
|
||||||
artifacts:
|
artifacts:
|
||||||
|
name: "${CI_JOB_NAME}_${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}"
|
||||||
|
when: on_success
|
||||||
|
expire_in: 1 mos
|
||||||
paths:
|
paths:
|
||||||
- parity.zip
|
- artifacts/
|
||||||
name: "stable-x86_64-unknown-snap-gnu_parity"
|
|
||||||
darwin:
|
.docker-cache-status: &docker-cache-status
|
||||||
stage: build
|
dependencies: []
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- scripts/gitlab-build.sh x86_64-apple-darwin x86_64-apple-darwin macos gcc g++ macos
|
|
||||||
tags:
|
|
||||||
- osx
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "x86_64-apple-darwin_parity"
|
|
||||||
windows:
|
|
||||||
cache:
|
|
||||||
key: "%CI_BUILD_STAGE%-%CI_BUILD_REF_NAME%"
|
|
||||||
untracked: true
|
|
||||||
stage: build
|
|
||||||
only:
|
|
||||||
- beta
|
|
||||||
- tags
|
|
||||||
- stable
|
|
||||||
- triggers
|
|
||||||
script:
|
|
||||||
- sh scripts/gitlab-build.sh x86_64-pc-windows-msvc x86_64-pc-windows-msvc installer "" "" windows
|
|
||||||
tags:
|
|
||||||
- rust-windows
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- parity.zip
|
|
||||||
name: "x86_64-pc-windows-msvc_parity"
|
|
||||||
docker-build:
|
|
||||||
stage: build
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
- triggers
|
|
||||||
before_script:
|
before_script:
|
||||||
- docker info
|
- sccache -s
|
||||||
script:
|
after_script:
|
||||||
- if [ "$CI_BUILD_REF_NAME" == "beta-release" ]; then DOCKER_TAG="latest"; else DOCKER_TAG=$CI_BUILD_REF_NAME; fi
|
- sccache -s
|
||||||
- echo "Tag:" $DOCKER_TAG
|
|
||||||
- docker login -u $Docker_Hub_User_Parity -p $Docker_Hub_Pass_Parity
|
|
||||||
- scripts/docker-build.sh $DOCKER_TAG
|
|
||||||
- docker logout
|
|
||||||
tags:
|
tags:
|
||||||
- docker
|
- linux-docker
|
||||||
test-coverage:
|
|
||||||
stage: test
|
|
||||||
only:
|
cargo-check 0 3:
|
||||||
- master
|
stage: test
|
||||||
|
<<: *docker-cache-status
|
||||||
script:
|
script:
|
||||||
- scripts/gitlab-test.sh test-coverage
|
- time cargo check --target $CARGO_TARGET --locked --no-default-features
|
||||||
tags:
|
|
||||||
- kcov
|
cargo-check 1 3:
|
||||||
allow_failure: true
|
stage: test
|
||||||
test-rust-stable:
|
<<: *docker-cache-status
|
||||||
stage: test
|
|
||||||
image: parity/rust:gitlab-ci
|
|
||||||
script:
|
script:
|
||||||
- scripts/gitlab-test.sh stable
|
- time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --no-default-features
|
||||||
tags:
|
|
||||||
- rust-stable
|
cargo-check 2 3:
|
||||||
test-rust-beta:
|
stage: test
|
||||||
stage: test
|
<<: *docker-cache-status
|
||||||
only:
|
|
||||||
- triggers
|
|
||||||
- master
|
|
||||||
image: parity/rust:gitlab-ci
|
|
||||||
script:
|
script:
|
||||||
- scripts/gitlab-test.sh beta
|
- time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --features "mio"
|
||||||
tags:
|
|
||||||
- rust-beta
|
cargo-audit:
|
||||||
allow_failure: true
|
stage: test
|
||||||
test-rust-nightly:
|
|
||||||
stage: test
|
|
||||||
only:
|
|
||||||
- triggers
|
|
||||||
- master
|
|
||||||
image: parity/rust:gitlab-ci
|
|
||||||
script:
|
script:
|
||||||
- scripts/gitlab-test.sh nightly
|
- cargo audit
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- linux-docker
|
||||||
- rust-nightly
|
|
||||||
allow_failure: true
|
validate-chainspecs:
|
||||||
push-release:
|
stage: test
|
||||||
stage: push-release
|
<<: *docker-cache-status
|
||||||
|
script:
|
||||||
|
- ./scripts/gitlab/validate-chainspecs.sh
|
||||||
|
|
||||||
|
test-cpp:
|
||||||
|
stage: build
|
||||||
|
<<: *docker-cache-status
|
||||||
|
script:
|
||||||
|
- ./scripts/gitlab/test-cpp.sh
|
||||||
|
|
||||||
|
test-linux:
|
||||||
|
stage: build
|
||||||
|
<<: *docker-cache-status
|
||||||
|
script:
|
||||||
|
- ./scripts/gitlab/test-linux.sh
|
||||||
|
|
||||||
|
build-linux: &build-linux
|
||||||
|
stage: build
|
||||||
|
only: *releaseable_branches
|
||||||
|
<<: *docker-cache-status
|
||||||
|
script:
|
||||||
|
- scripts/gitlab/build-linux.sh
|
||||||
|
<<: *collect_artifacts
|
||||||
|
|
||||||
|
build-linux-i386:
|
||||||
|
<<: *build-linux
|
||||||
|
image: parity/rust-parity-ethereum-build:i386
|
||||||
|
variables:
|
||||||
|
CARGO_TARGET: i686-unknown-linux-gnu
|
||||||
|
|
||||||
|
build-linux-arm64:
|
||||||
|
<<: *build-linux
|
||||||
|
image: parity/rust-parity-ethereum-build:arm64
|
||||||
|
variables:
|
||||||
|
CARGO_TARGET: aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
|
build-linux-armhf:
|
||||||
|
<<: *build-linux
|
||||||
|
image: parity/rust-parity-ethereum-build:armhf
|
||||||
|
variables:
|
||||||
|
CARGO_TARGET: armv7-unknown-linux-gnueabihf
|
||||||
|
|
||||||
|
build-darwin:
|
||||||
|
stage: build
|
||||||
|
only: *releaseable_branches
|
||||||
|
variables:
|
||||||
|
CARGO_TARGET: x86_64-apple-darwin
|
||||||
|
CC: gcc
|
||||||
|
CXX: g++
|
||||||
|
script:
|
||||||
|
- scripts/gitlab/build-linux.sh
|
||||||
|
tags:
|
||||||
|
- rust-osx
|
||||||
|
<<: *collect_artifacts
|
||||||
|
|
||||||
|
build-windows:
|
||||||
|
stage: build
|
||||||
|
only: *releaseable_branches
|
||||||
|
variables:
|
||||||
|
CARGO_TARGET: x86_64-pc-windows-msvc
|
||||||
|
script:
|
||||||
|
- sh scripts/gitlab/build-windows.sh
|
||||||
|
tags:
|
||||||
|
- rust-windows
|
||||||
|
<<: *collect_artifacts
|
||||||
|
|
||||||
|
publish-docker:
|
||||||
|
stage: publish
|
||||||
|
only: *releaseable_branches
|
||||||
|
cache: {}
|
||||||
|
dependencies:
|
||||||
|
- build-linux
|
||||||
|
tags:
|
||||||
|
- shell
|
||||||
|
script:
|
||||||
|
- scripts/gitlab/publish-docker.sh parity
|
||||||
|
|
||||||
|
publish-snap: &publish-snap
|
||||||
|
stage: publish
|
||||||
|
only: *releaseable_branches
|
||||||
|
image: snapcore/snapcraft
|
||||||
|
variables:
|
||||||
|
BUILD_ARCH: amd64
|
||||||
|
cache: {}
|
||||||
|
dependencies:
|
||||||
|
- build-linux
|
||||||
|
tags:
|
||||||
|
- linux-docker
|
||||||
|
script:
|
||||||
|
- scripts/gitlab/publish-snap.sh
|
||||||
|
<<: *collect_artifacts
|
||||||
|
|
||||||
|
publish-snap-i386:
|
||||||
|
<<: *publish-snap
|
||||||
|
variables:
|
||||||
|
BUILD_ARCH: i386
|
||||||
|
dependencies:
|
||||||
|
- build-linux-i386
|
||||||
|
|
||||||
|
publish-snap-arm64:
|
||||||
|
<<: *publish-snap
|
||||||
|
variables:
|
||||||
|
BUILD_ARCH: arm64
|
||||||
|
dependencies:
|
||||||
|
- build-linux-arm64
|
||||||
|
|
||||||
|
publish-snap-armhf:
|
||||||
|
<<: *publish-snap
|
||||||
|
variables:
|
||||||
|
BUILD_ARCH: armhf
|
||||||
|
dependencies:
|
||||||
|
- build-linux-armhf
|
||||||
|
|
||||||
|
publish-onchain:
|
||||||
|
stage: publish
|
||||||
|
only: *releaseable_branches
|
||||||
|
cache: {}
|
||||||
|
dependencies:
|
||||||
|
- build-linux
|
||||||
|
- build-darwin
|
||||||
|
- build-windows
|
||||||
|
script:
|
||||||
|
- scripts/gitlab/publish-onchain.sh
|
||||||
|
tags:
|
||||||
|
- linux-docker
|
||||||
|
|
||||||
|
publish-awss3-release:
|
||||||
|
image: parity/awscli:latest
|
||||||
|
stage: publish
|
||||||
|
only: *releaseable_branches
|
||||||
|
<<: *no_git
|
||||||
|
cache: {}
|
||||||
|
dependencies:
|
||||||
|
- build-linux
|
||||||
|
- build-darwin
|
||||||
|
- build-windows
|
||||||
|
script:
|
||||||
|
- echo "__________Push binaries to AWS S3____________"
|
||||||
|
- case "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" in
|
||||||
|
(beta|stable|nightly)
|
||||||
|
export BUCKET=releases.parity.io/ethereum;
|
||||||
|
;;
|
||||||
|
(*)
|
||||||
|
export BUCKET=builds-parity;
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
- aws s3 sync ./artifacts s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/
|
||||||
|
- echo "__________Read from S3____________"
|
||||||
|
- aws s3 ls s3://${BUCKET}/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}} --recursive --human-readable --summarize
|
||||||
|
tags:
|
||||||
|
- linux-docker
|
||||||
|
|
||||||
|
publish-docs:
|
||||||
|
stage: publish
|
||||||
|
# <<: *no_git
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
- triggers
|
except:
|
||||||
image: parity/rust:gitlab-ci
|
- nightly
|
||||||
|
cache: {}
|
||||||
script:
|
script:
|
||||||
- scripts/gitlab-push-release.sh
|
- scripts/gitlab/publish-docs.sh
|
||||||
tags:
|
tags:
|
||||||
- curl
|
- linux-docker
|
||||||
|
|
||||||
|
build-android:
|
||||||
|
stage: build
|
||||||
|
image: parity/rust-android:gitlab-ci
|
||||||
|
variables:
|
||||||
|
CARGO_TARGET: armv7-linux-androideabi
|
||||||
|
script:
|
||||||
|
- scripts/gitlab/build-linux.sh
|
||||||
|
tags:
|
||||||
|
- linux-docker
|
||||||
|
<<: *collect_artifacts
|
||||||
|
|||||||
536
CHANGELOG.md
536
CHANGELOG.md
@@ -1,386 +1,168 @@
|
|||||||
## Parity [v1.9.3](https://github.com/paritytech/parity/releases/tag/v1.9.3) (2018-02-20)
|
## Parity-Ethereum [v2.3.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.3.0) (2019-01-16)
|
||||||
|
|
||||||
Parity 1.9.3 is a bug-fix release to improve performance and stability.
|
Parity-Ethereum 2.3.0-beta is a consensus-relevant security release that reverts Constantinople on the Ethereum network. Upgrading is mandatory for Ethereum, and strongly recommended for other networks.
|
||||||
|
|
||||||
|
- **Consensus** - Ethereum Network: Pull Constantinople protocol upgrade on Ethereum (#10189)
|
||||||
|
- Read more: [Security Alert: Ethereum Constantinople Postponement](https://blog.ethereum.org/2019/01/15/security-alert-ethereum-constantinople-postponement/)
|
||||||
|
- **Networking** - All networks: Ping nodes from discovery (#10167)
|
||||||
|
- **Wasm** - Kovan Network: Update pwasm-utils to 0.6.1 (#10134)
|
||||||
|
|
||||||
|
Other notable changes:
|
||||||
|
|
||||||
|
- Existing blocks in the database are now kept when restoring a Snapshot. (#8643)
|
||||||
|
- Block and transaction propagation is improved significantly. (#9954)
|
||||||
|
- The ERC-191 Signed Data Standard is now supported by `personal_sign191`. (#9701)
|
||||||
|
- Add support for ERC-191/712 `eth_signTypedData` as a standard for machine-verifiable and human-readable typed data signing with Ethereum keys. (#9631)
|
||||||
|
- Add support for ERC-1186 `eth_getProof` (#9001)
|
||||||
|
- Add experimental RPCs flag to enable ERC-191, ERC-712, and ERC-1186 APIs via `--jsonrpc-experimental` (#9928)
|
||||||
|
- Make `CALLCODE` to trace value to be the code address. (#9881)
|
||||||
|
|
||||||
|
Configuration changes:
|
||||||
|
|
||||||
|
- The EIP-98 transition is now disabled by default. If you previously had no `eip98transition` specified in your chain specification, you would enable this now manually on block `0x0`. (#9955)
|
||||||
|
- Also, unknown fields in chain specs are now rejected. (#9972)
|
||||||
|
- The Tendermint engine was removed from Parity Ethereum and is no longer available and maintained. (#9980)
|
||||||
|
- Ropsten testnet data and keys moved from `test/` to `ropsten/` subdir. To reuse your old keys and data either copy or symlink them to the new location. (#10123)
|
||||||
|
- Strict empty steps validation (#10041)
|
||||||
|
- If you have a chain with`empty_steps` already running, some blocks most likely contain non-strict entries (unordered or duplicated empty steps). In this release `strict_empty_steps_transition` is enabled by default at block `0x0` for any chain with `empty_steps`.
|
||||||
|
- If your network uses `empty_steps` you **must** (A) plan a hard fork and change `strict_empty_steps_transition` to the desired fork block and (B) update the clients of the whole network to 2.2.7-stable / 2.3.0-beta. If for some reason you don't want to do this please set`strict_empty_steps_transition` to `0xfffffffff` to disable it.
|
||||||
|
|
||||||
|
_Note:_ This release marks Parity 2.3 as _beta_. All versions of Parity 2.2 are now considered _stable_.
|
||||||
|
|
||||||
The full list of included changes:
|
The full list of included changes:
|
||||||
|
|
||||||
- Backports ([#7945](https://github.com/paritytech/parity/pull/7945))
|
- Backports for 2.3.0 beta ([#10164](https://github.com/paritytech/parity-ethereum/pull/10164))
|
||||||
- ECIP 1041 - Remove Difficulty Bomb ([#7905](https://github.com/paritytech/parity/pull/7905))
|
- Snap: fix path in script ([#10157](https://github.com/paritytech/parity-ethereum/pull/10157))
|
||||||
- spec: Validate required divisor fields are not 0 ([#7933](https://github.com/paritytech/parity/pull/7933))
|
- Make sure parent block is not in importing queue when importing ancient blocks ([#10138](https://github.com/paritytech/parity-ethereum/pull/10138))
|
||||||
- Kovan WASM fork code ([#7849](https://github.com/paritytech/parity/pull/7849))
|
- Ci: re-enable snap publishing ([#10142](https://github.com/paritytech/parity-ethereum/pull/10142))
|
||||||
- Gitlab Cargo Cache ([#7944](https://github.com/paritytech/parity/pull/7944))
|
- Hf in POA Core (2019-01-18) - Constantinople ([#10155](https://github.com/paritytech/parity-ethereum/pull/10155))
|
||||||
- Bump react-qr-reader ([#7943](https://github.com/paritytech/parity/pull/7943))
|
- Update EWF's tobalaba chainspec ([#10152](https://github.com/paritytech/parity-ethereum/pull/10152))
|
||||||
- Update react-qr-reader
|
- Replace ethcore-logger with env-logger. ([#10102](https://github.com/paritytech/parity-ethereum/pull/10102))
|
||||||
- Explicit webrtc-adapter dependency (package-lock workaround)
|
- Finality: dont require chain head to be in the chain ([#10054](https://github.com/paritytech/parity-ethereum/pull/10054))
|
||||||
- Iframe with allow (QR, new Chrome policy)
|
- Remove caching for node connections ([#10143](https://github.com/paritytech/parity-ethereum/pull/10143))
|
||||||
- Backport of [#7844](https://github.com/paritytech/parity/pull/7844) and [#7917](https://github.com/paritytech/parity/pull/7917) to beta ([#7940](https://github.com/paritytech/parity/pull/7940))
|
- Blooms file iterator empty on out of range position. ([#10145](https://github.com/paritytech/parity-ethereum/pull/10145))
|
||||||
- Randomize the peer we dispatch to
|
- Autogen docs for the "Configuring Parity Ethereum" wiki page. ([#10067](https://github.com/paritytech/parity-ethereum/pull/10067))
|
||||||
- Fix a division by zero in light client RPC handler
|
- Misc: bump license header to 2019 ([#10135](https://github.com/paritytech/parity-ethereum/pull/10135))
|
||||||
- Wallet allowJsEval: true ([#7913](https://github.com/paritytech/parity/pull/7913))
|
- Hide most of the logs from cpp example. ([#10139](https://github.com/paritytech/parity-ethereum/pull/10139))
|
||||||
- Wallet allowJsEval: true
|
- Don't try to send oversized packets ([#10042](https://github.com/paritytech/parity-ethereum/pull/10042))
|
||||||
- Fix unsafe wallet.
|
- Private tx enabled flag added into STATUS packet ([#9999](https://github.com/paritytech/parity-ethereum/pull/9999))
|
||||||
- Enable unsafe-eval for all dapps.
|
- Update pwasm-utils to 0.6.1 ([#10134](https://github.com/paritytech/parity-ethereum/pull/10134))
|
||||||
- Fix CSP for dapps that require eval. ([#7867](https://github.com/paritytech/parity/pull/7867)) ([#7903](https://github.com/paritytech/parity/pull/7903))
|
- Extract blockchain from ethcore ([#10114](https://github.com/paritytech/parity-ethereum/pull/10114))
|
||||||
- Add allowJsEval to manifest.
|
- Ethcore: update hardcoded headers ([#10123](https://github.com/paritytech/parity-ethereum/pull/10123))
|
||||||
- Enable 'unsafe-eval' if requested in manifest.
|
- Identity fix ([#10128](https://github.com/paritytech/parity-ethereum/pull/10128))
|
||||||
- Fix snap build beta ([#7895](https://github.com/paritytech/parity/pull/7895))
|
- Use LenCachingMutex to optimize verification. ([#10117](https://github.com/paritytech/parity-ethereum/pull/10117))
|
||||||
- Fix snapcraft grade to stable ([#7894](https://github.com/paritytech/parity/pull/7894))
|
- Pyethereum keystore support ([#9710](https://github.com/paritytech/parity-ethereum/pull/9710))
|
||||||
- Backport Master CI PRs to Beta ([#7890](https://github.com/paritytech/parity/pull/7890))
|
- Bump rocksdb-sys to 0.5.5 ([#10124](https://github.com/paritytech/parity-ethereum/pull/10124))
|
||||||
- Add binary identifiers and sha256sum to builds ([#7830](https://github.com/paritytech/parity/pull/7830))
|
- Parity-clib: `async C bindings to RPC requests` + `subscribe/unsubscribe to websocket events` ([#9920](https://github.com/paritytech/parity-ethereum/pull/9920))
|
||||||
- Fix checksums and auto-update push ([#7846](https://github.com/paritytech/parity/pull/7846))
|
- Refactor (hardware wallet) : reduce the number of threads ([#9644](https://github.com/paritytech/parity-ethereum/pull/9644))
|
||||||
- Update gitlab-build.sh ([#7855](https://github.com/paritytech/parity/pull/7855))
|
- Hf in POA Sokol (2019-01-04) ([#10077](https://github.com/paritytech/parity-ethereum/pull/10077))
|
||||||
- Fix installer binary names for macos and windows ([#7881](https://github.com/paritytech/parity/pull/7881))
|
- Fix broken links ([#10119](https://github.com/paritytech/parity-ethereum/pull/10119))
|
||||||
- Update gitlab-test.sh ([#7883](https://github.com/paritytech/parity/pull/7883))
|
- Follow-up to [#10105](https://github.com/paritytech/parity-ethereum/issues/10105) ([#10107](https://github.com/paritytech/parity-ethereum/pull/10107))
|
||||||
- Fix snapcraft nightly ([#7884](https://github.com/paritytech/parity/pull/7884))
|
- Move EIP-712 crate back to parity-ethereum ([#10106](https://github.com/paritytech/parity-ethereum/pull/10106))
|
||||||
- Backport Core PRs to beta ([#7891](https://github.com/paritytech/parity/pull/7891))
|
- Move a bunch of stuff around ([#10101](https://github.com/paritytech/parity-ethereum/pull/10101))
|
||||||
- Update back-references more aggressively after answering from cache ([#7578](https://github.com/paritytech/parity/pull/7578))
|
- Revert "Add --frozen when running cargo ([#10081](https://github.com/paritytech/parity-ethereum/pull/10081))" ([#10105](https://github.com/paritytech/parity-ethereum/pull/10105))
|
||||||
- Updated WASM Runtime & new interpreter (wasmi) ([#7796](https://github.com/paritytech/parity/pull/7796))
|
- Fix left over small grumbles on whitespaces ([#10084](https://github.com/paritytech/parity-ethereum/pull/10084))
|
||||||
- Adjust storage update evm-style ([#7812](https://github.com/paritytech/parity/pull/7812))
|
- Add --frozen when running cargo ([#10081](https://github.com/paritytech/parity-ethereum/pull/10081))
|
||||||
- Add new EF ropstens nodes ([#7824](https://github.com/paritytech/parity/pull/7824))
|
- Fix pubsub new_blocks notifications to include all blocks ([#9987](https://github.com/paritytech/parity-ethereum/pull/9987))
|
||||||
- Store updater metadata in a single place ([#7832](https://github.com/paritytech/parity/pull/7832))
|
- Update some dependencies for compilation with pc-windows-gnu ([#10082](https://github.com/paritytech/parity-ethereum/pull/10082))
|
||||||
- WASM: Disable internal memory ([#7842](https://github.com/paritytech/parity/pull/7842))
|
- Fill transaction hash on ethGetLog of light client. ([#9938](https://github.com/paritytech/parity-ethereum/pull/9938))
|
||||||
- Add a timeout for light client sync requests ([#7848](https://github.com/paritytech/parity/pull/7848))
|
- Update changelog update for 2.2.5-beta and 2.1.10-stable ([#10064](https://github.com/paritytech/parity-ethereum/pull/10064))
|
||||||
- Flush keyfiles. Resolves [#7632](https://github.com/paritytech/parity/issues/7632) ([#7868](https://github.com/paritytech/parity/pull/7868))
|
- Implement len caching for parking_lot RwLock ([#10032](https://github.com/paritytech/parity-ethereum/pull/10032))
|
||||||
- Fix wallet import ([#7873](https://github.com/paritytech/parity/pull/7873))
|
- Update parking_lot to 0.7 ([#10050](https://github.com/paritytech/parity-ethereum/pull/10050))
|
||||||
|
- Bump crossbeam. ([#10048](https://github.com/paritytech/parity-ethereum/pull/10048))
|
||||||
|
- Ethcore: enable constantinople on ethereum ([#10031](https://github.com/paritytech/parity-ethereum/pull/10031))
|
||||||
|
- Strict empty steps validation ([#10041](https://github.com/paritytech/parity-ethereum/pull/10041))
|
||||||
|
- Center the Subtitle, use some CAPS ([#10034](https://github.com/paritytech/parity-ethereum/pull/10034))
|
||||||
|
- Change test miner max memory to malloc reports. ([#10024](https://github.com/paritytech/parity-ethereum/pull/10024))
|
||||||
|
- Sort the storage for private state ([#10018](https://github.com/paritytech/parity-ethereum/pull/10018))
|
||||||
|
- Fix: test corpus_inaccessible panic ([#10019](https://github.com/paritytech/parity-ethereum/pull/10019))
|
||||||
|
- Ci: move future releases to ethereum subdir on s3 ([#10017](https://github.com/paritytech/parity-ethereum/pull/10017))
|
||||||
|
- Light(on_demand): decrease default time window to 10 secs ([#10016](https://github.com/paritytech/parity-ethereum/pull/10016))
|
||||||
|
- Light client : failsafe crate (circuit breaker) ([#9790](https://github.com/paritytech/parity-ethereum/pull/9790))
|
||||||
|
- Lencachingmutex ([#9988](https://github.com/paritytech/parity-ethereum/pull/9988))
|
||||||
|
- Version and notification for private contract wrapper added ([#9761](https://github.com/paritytech/parity-ethereum/pull/9761))
|
||||||
|
- Handle failing case for update account cache in require ([#9989](https://github.com/paritytech/parity-ethereum/pull/9989))
|
||||||
|
- Add tokio runtime to ethcore io worker ([#9979](https://github.com/paritytech/parity-ethereum/pull/9979))
|
||||||
|
- Move daemonize before creating account provider ([#10003](https://github.com/paritytech/parity-ethereum/pull/10003))
|
||||||
|
- Docs: update changelogs ([#9990](https://github.com/paritytech/parity-ethereum/pull/9990))
|
||||||
|
- Fix daemonize ([#10000](https://github.com/paritytech/parity-ethereum/pull/10000))
|
||||||
|
- Fix Bloom migration ([#9992](https://github.com/paritytech/parity-ethereum/pull/9992))
|
||||||
|
- Remove tendermint engine support ([#9980](https://github.com/paritytech/parity-ethereum/pull/9980))
|
||||||
|
- Calculate gas for deployment transaction ([#9840](https://github.com/paritytech/parity-ethereum/pull/9840))
|
||||||
|
- Fix unstable peers and slowness in sync ([#9967](https://github.com/paritytech/parity-ethereum/pull/9967))
|
||||||
|
- Adds parity_verifySignature RPC method ([#9507](https://github.com/paritytech/parity-ethereum/pull/9507))
|
||||||
|
- Improve block and transaction propagation ([#9954](https://github.com/paritytech/parity-ethereum/pull/9954))
|
||||||
|
- Deny unknown fields for chainspec ([#9972](https://github.com/paritytech/parity-ethereum/pull/9972))
|
||||||
|
- Fix docker build ([#9971](https://github.com/paritytech/parity-ethereum/pull/9971))
|
||||||
|
- Ci: rearrange pipeline by logic ([#9970](https://github.com/paritytech/parity-ethereum/pull/9970))
|
||||||
|
- Add changelogs for 2.0.9, 2.1.4, 2.1.6, and 2.2.1 ([#9963](https://github.com/paritytech/parity-ethereum/pull/9963))
|
||||||
|
- Add Error message when sync is still in progress. ([#9475](https://github.com/paritytech/parity-ethereum/pull/9475))
|
||||||
|
- Make CALLCODE to trace value to be the code address ([#9881](https://github.com/paritytech/parity-ethereum/pull/9881))
|
||||||
|
- Fix light client informant while syncing ([#9932](https://github.com/paritytech/parity-ethereum/pull/9932))
|
||||||
|
- Add a optional json dump state to evm-bin ([#9706](https://github.com/paritytech/parity-ethereum/pull/9706))
|
||||||
|
- Disable EIP-98 transition by default ([#9955](https://github.com/paritytech/parity-ethereum/pull/9955))
|
||||||
|
- Remove secret_store runtimes. ([#9888](https://github.com/paritytech/parity-ethereum/pull/9888))
|
||||||
|
- Fix a deadlock ([#9952](https://github.com/paritytech/parity-ethereum/pull/9952))
|
||||||
|
- Chore(eip712): remove unused `failure-derive` ([#9958](https://github.com/paritytech/parity-ethereum/pull/9958))
|
||||||
|
- Do not use the home directory as the working dir in docker ([#9834](https://github.com/paritytech/parity-ethereum/pull/9834))
|
||||||
|
- Prevent silent errors in daemon mode, closes [#9367](https://github.com/paritytech/parity-ethereum/issues/9367) ([#9946](https://github.com/paritytech/parity-ethereum/pull/9946))
|
||||||
|
- Fix empty steps ([#9939](https://github.com/paritytech/parity-ethereum/pull/9939))
|
||||||
|
- Adjust requests costs for light client ([#9925](https://github.com/paritytech/parity-ethereum/pull/9925))
|
||||||
|
- Eip-1186: add `eth_getProof` RPC-Method ([#9001](https://github.com/paritytech/parity-ethereum/pull/9001))
|
||||||
|
- Missing blocks in filter_changes RPC ([#9947](https://github.com/paritytech/parity-ethereum/pull/9947))
|
||||||
|
- Allow rust-nightly builds fail in nightly builds ([#9944](https://github.com/paritytech/parity-ethereum/pull/9944))
|
||||||
|
- Update eth-secp256k1 to include fix for BSDs ([#9935](https://github.com/paritytech/parity-ethereum/pull/9935))
|
||||||
|
- Unbreak build on rust -stable ([#9934](https://github.com/paritytech/parity-ethereum/pull/9934))
|
||||||
|
- Keep existing blocks when restoring a Snapshot ([#8643](https://github.com/paritytech/parity-ethereum/pull/8643))
|
||||||
|
- Add experimental RPCs flag ([#9928](https://github.com/paritytech/parity-ethereum/pull/9928))
|
||||||
|
- Clarify poll lifetime ([#9922](https://github.com/paritytech/parity-ethereum/pull/9922))
|
||||||
|
- Docs(require rust 1.30) ([#9923](https://github.com/paritytech/parity-ethereum/pull/9923))
|
||||||
|
- Use block header for building finality ([#9914](https://github.com/paritytech/parity-ethereum/pull/9914))
|
||||||
|
- Simplify cargo audit ([#9918](https://github.com/paritytech/parity-ethereum/pull/9918))
|
||||||
|
- Light-fetch: Differentiate between out-of-gas/manual throw and use required gas from response on failure ([#9824](https://github.com/paritytech/parity-ethereum/pull/9824))
|
||||||
|
- Eip 191 ([#9701](https://github.com/paritytech/parity-ethereum/pull/9701))
|
||||||
|
- Fix(logger): `reqwest` no longer a dependency ([#9908](https://github.com/paritytech/parity-ethereum/pull/9908))
|
||||||
|
- Remove rust-toolchain file ([#9906](https://github.com/paritytech/parity-ethereum/pull/9906))
|
||||||
|
- Foundation: 6692865, ropsten: 4417537, kovan: 9363457 ([#9907](https://github.com/paritytech/parity-ethereum/pull/9907))
|
||||||
|
- Ethcore: use Machine::verify_transaction on parent block ([#9900](https://github.com/paritytech/parity-ethereum/pull/9900))
|
||||||
|
- Chore(rpc-tests): remove unused rand ([#9896](https://github.com/paritytech/parity-ethereum/pull/9896))
|
||||||
|
- Fix: Intermittent failing CI due to addr in use ([#9885](https://github.com/paritytech/parity-ethereum/pull/9885))
|
||||||
|
- Chore(bump docopt): 0.8 -> 1.0 ([#9889](https://github.com/paritytech/parity-ethereum/pull/9889))
|
||||||
|
- Use expect ([#9883](https://github.com/paritytech/parity-ethereum/pull/9883))
|
||||||
|
- Use Weak reference in PubSubClient ([#9886](https://github.com/paritytech/parity-ethereum/pull/9886))
|
||||||
|
- Ci: nuke the gitlab caches ([#9855](https://github.com/paritytech/parity-ethereum/pull/9855))
|
||||||
|
- Remove unused code ([#9884](https://github.com/paritytech/parity-ethereum/pull/9884))
|
||||||
|
- Fix json tracer overflow ([#9873](https://github.com/paritytech/parity-ethereum/pull/9873))
|
||||||
|
- Allow to seal work on latest block ([#9876](https://github.com/paritytech/parity-ethereum/pull/9876))
|
||||||
|
- Fix docker script ([#9854](https://github.com/paritytech/parity-ethereum/pull/9854))
|
||||||
|
- Health endpoint ([#9847](https://github.com/paritytech/parity-ethereum/pull/9847))
|
||||||
|
- Gitlab-ci: make android release build succeed ([#9743](https://github.com/paritytech/parity-ethereum/pull/9743))
|
||||||
|
- Clean up existing benchmarks ([#9839](https://github.com/paritytech/parity-ethereum/pull/9839))
|
||||||
|
- Update Callisto block reward code to support HF1 ([#9811](https://github.com/paritytech/parity-ethereum/pull/9811))
|
||||||
|
- Option to disable keep alive for JSON-RPC http transport ([#9848](https://github.com/paritytech/parity-ethereum/pull/9848))
|
||||||
|
- Classic.json Bootnode Update ([#9828](https://github.com/paritytech/parity-ethereum/pull/9828))
|
||||||
|
- Support MIX. ([#9767](https://github.com/paritytech/parity-ethereum/pull/9767))
|
||||||
|
- Ci: remove failing tests for android, windows, and macos ([#9788](https://github.com/paritytech/parity-ethereum/pull/9788))
|
||||||
|
- Implement NoProof for json tests and update tests reference (replaces [#9744](https://github.com/paritytech/parity-ethereum/issues/9744)) ([#9814](https://github.com/paritytech/parity-ethereum/pull/9814))
|
||||||
|
- Chore(bump regex) ([#9842](https://github.com/paritytech/parity-ethereum/pull/9842))
|
||||||
|
- Ignore global cache for patched accounts ([#9752](https://github.com/paritytech/parity-ethereum/pull/9752))
|
||||||
|
- Move state root verification before gas used ([#9841](https://github.com/paritytech/parity-ethereum/pull/9841))
|
||||||
|
- Fix(docker-aarch64) : cross-compile config ([#9798](https://github.com/paritytech/parity-ethereum/pull/9798))
|
||||||
|
- Version: bump nightly to 2.3.0 ([#9819](https://github.com/paritytech/parity-ethereum/pull/9819))
|
||||||
|
- Tests modification for windows CI ([#9671](https://github.com/paritytech/parity-ethereum/pull/9671))
|
||||||
|
- Eip-712 implementation ([#9631](https://github.com/paritytech/parity-ethereum/pull/9631))
|
||||||
|
- Fix typo ([#9826](https://github.com/paritytech/parity-ethereum/pull/9826))
|
||||||
|
- Clean up serde rename and use rename_all = camelCase when possible ([#9823](https://github.com/paritytech/parity-ethereum/pull/9823))
|
||||||
|
|
||||||
## Parity [v1.9.2](https://github.com/paritytech/parity/releases/tag/v1.9.2) (2018-02-02)
|
## Previous releases
|
||||||
|
|
||||||
Parity 1.9.2 is a bug-fix release to improve performance and stability. It adds additional bootnodes for the Ropsten test network.
|
- [CHANGELOG-2.2](docs/CHANGELOG-2.2.md) (_stable_)
|
||||||
|
- [CHANGELOG-2.1](docs/CHANGELOG-2.1.md) (EOL: 2019-01-16)
|
||||||
The full list of included changes:
|
- [CHANGELOG-2.0](docs/CHANGELOG-2.0.md) (EOL: 2018-11-15)
|
||||||
|
- [CHANGELOG-1.11](docs/CHANGELOG-1.11.md) (EOL: 2018-09-19)
|
||||||
- Backports beta ([#7780](https://github.com/paritytech/parity/pull/7780))
|
- [CHANGELOG-1.10](docs/CHANGELOG-1.10.md) (EOL: 2018-07-18)
|
||||||
- Bump beta to 1.9.2
|
- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (EOL: 2018-05-09)
|
||||||
- Update ropsten.json ([#7776](https://github.com/paritytech/parity/pull/7776))
|
- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22)
|
||||||
- Snapcraft push beta
|
|
||||||
|
|
||||||
## Parity [v1.9.1](https://github.com/paritytech/parity/releases/tag/v1.9.1) (2018-02-01)
|
|
||||||
|
|
||||||
Parity 1.9.1 is a bug-fix release to improve performance and stability. It restores ERC-20 token balances, improves networking, fixes database corruptions on client shutdown, and fixes issues with the `--password` command-line flag. Happy syncing, fellow Ethereans!
|
|
||||||
|
|
||||||
In addition, this stabilizes Kovan and other Proof-of-Authority networks. If you run a network with AuRa engine, updating is highly encouraged!
|
|
||||||
|
|
||||||
The full list of included changes:
|
|
||||||
|
|
||||||
- Beta Backports ([#7756](https://github.com/paritytech/parity/pull/7756))
|
|
||||||
- Filter-out nodes.json ([#7716](https://github.com/paritytech/parity/pull/7716))
|
|
||||||
- Filter-out nodes.json
|
|
||||||
- network: sort node table nodes by failure ratio
|
|
||||||
- network: fix node table tests
|
|
||||||
- network: fit node failure percentage into buckets of 5%
|
|
||||||
- network: consider number of attempts in sorting of node table
|
|
||||||
- network: fix node table grumbles
|
|
||||||
- Fix client not being dropped on shutdown ([#7695](https://github.com/paritytech/parity/pull/7695))
|
|
||||||
- parity: wait for client to drop on shutdown
|
|
||||||
- parity: fix grumbles in shutdown wait
|
|
||||||
- parity: increase shutdown timeouts
|
|
||||||
- Wrap --help output to 120 characters ([#7626](https://github.com/paritytech/parity/pull/7626))
|
|
||||||
- Update Clap dependency and remove workarounds
|
|
||||||
- WIP
|
|
||||||
- Remove line breaks in help messages for now
|
|
||||||
- Multiple values can only be separated by commas (closes [#7428](https://github.com/paritytech/parity/issues/7428))
|
|
||||||
- Grumbles; refactor repeating code; add constant
|
|
||||||
- Use a single Wrapper rather than allocate a new one for each call
|
|
||||||
- Wrap --help to 120 characters rather than 100 characte
|
|
||||||
- Token filter balances (throttle) ([#7742](https://github.com/paritytech/parity/pull/7742))
|
|
||||||
- Token filter balances (throttle)
|
|
||||||
- Cleanups
|
|
||||||
- Remove unused uniq
|
|
||||||
- Update @parity/shared to 2.2.23
|
|
||||||
- Remove unused code paths
|
|
||||||
- Bump beta to 1.9.1 ([#7751](https://github.com/paritytech/parity/pull/7751))
|
|
||||||
- Explicitly add branch name ([#7754](https://github.com/paritytech/parity/pull/7754))
|
|
||||||
- Explicitly add branch name
|
|
||||||
- Fix cargo update branch to beta
|
|
||||||
- Revert revert revert ([#7715](https://github.com/paritytech/parity/pull/7715))
|
|
||||||
- This reverts commit 568dc33.
|
|
||||||
|
|
||||||
## Parity [v1.9.0](https://github.com/paritytech/parity/releases/tag/v1.9.0) "Velocity" (2018-01-25)
|
|
||||||
|
|
||||||
We are happy to announce our newest Parity 1.9 release. Among others, it enables the following features:
|
|
||||||
|
|
||||||
- It integrates the fully reworked Parity Wallet and DApps browser (a.k.a. "UI 2.0", [#6819](https://github.com/paritytech/parity/pull/6819)).
|
|
||||||
- It enables devp2p snappy compression ([#6683](https://github.com/paritytech/parity/pull/6683)).
|
|
||||||
- AuRa Proof-of-Authority chains now disable uncles by default ([#7006](https://github.com/paritytech/parity/pull/7006)). Existing PoA chains can go through a "maximum uncle count transition" to achieve more stability ([#7196](https://github.com/paritytech/parity/pull/7196)).
|
|
||||||
- Added Expanse's Byzantium hard-fork ([#7463](https://github.com/paritytech/parity/pull/7463)).
|
|
||||||
- Added support for Ellaism chain ([#7222](https://github.com/paritytech/parity/pull/7222)).
|
|
||||||
|
|
||||||
Further, users upgrading from 1.8 should acknowledge the following changes:
|
|
||||||
|
|
||||||
- Fixed DELEGATECALL's from/to field ([#7568](https://github.com/paritytech/parity/pull/7568)).
|
|
||||||
- Set zero nonce and gas price for calls by default ([#6954](https://github.com/paritytech/parity/pull/6954)).
|
|
||||||
- Create pending blocks with all transactions from the queue ([#6942](https://github.com/paritytech/parity/pull/6942)).
|
|
||||||
- Remove RPC parameter leniency now that Mist formats correctly ([#6651](https://github.com/paritytech/parity/pull/6651)). Parity stops accepting decimal-formatted block numbers and stops parsing the empty string as empty bytes.
|
|
||||||
- Public nodes do not support the user interface anymore. If you are running a public node, please stay on the 1.8 branch of the stable releases.
|
|
||||||
|
|
||||||
Additional noteworthy changes:
|
|
||||||
|
|
||||||
- `ethstore` and `ethkey` have been significantly improved ([#6961](https://github.com/paritytech/parity/pull/6961)):
|
|
||||||
- `ethstore` now supports brute forcing pre-sale wallets given a password list for recovery.
|
|
||||||
- `ethkey` now supports multi-threaded generation of prefix-matching addresses.
|
|
||||||
- `ethkey` now supports prefix-matching brain wallets.
|
|
||||||
- `ethkey` now supports brain-wallets recovery-phrases lookup. This helps to find a correct phrase if you know the address you want to get yet you made a typo backing the phrase up, or forgot a word.
|
|
||||||
|
|
||||||
Read more about Parity 1.9 in our [blog post](http://paritytech.io/velocity-the-fastest-parity-released/).
|
|
||||||
|
|
||||||
The full list of included changes:
|
|
||||||
|
|
||||||
- Add scroll when when too many accounts ([#7677](https://github.com/paritytech/parity/pull/7677)) ([#7679](https://github.com/paritytech/parity/pull/7679))
|
|
||||||
- Update installer.nsi
|
|
||||||
- Fix conditions in gitlab-test ([#7676](https://github.com/paritytech/parity/pull/7676))
|
|
||||||
- Fix conditions in gitlab-test
|
|
||||||
- Update gitlab-test.sh
|
|
||||||
- Remove cargo cache
|
|
||||||
- Backports to beta ([#7660](https://github.com/paritytech/parity/pull/7660))
|
|
||||||
- Improve handling of RocksDB corruption ([#7630](https://github.com/paritytech/parity/pull/7630))
|
|
||||||
- Kvdb-rocksdb: update rust-rocksdb version
|
|
||||||
- Kvdb-rocksdb: mark corruptions and attempt repair on db open
|
|
||||||
- Kvdb-rocksdb: better corruption detection on open
|
|
||||||
- Kvdb-rocksdb: add corruption_file_name const
|
|
||||||
- Kvdb-rocksdb: rename mark_corruption to check_for_corruption
|
|
||||||
- Hardening of CSP ([#7621](https://github.com/paritytech/parity/pull/7621))
|
|
||||||
- Fixed delegatecall's from/to ([#7568](https://github.com/paritytech/parity/pull/7568))
|
|
||||||
- Fixed delegatecall's from/to, closes [#7166](https://github.com/paritytech/parity/issues/7166)
|
|
||||||
- Added tests for delegatecall traces, [#7167](https://github.com/paritytech/parity/issues/7167)
|
|
||||||
- Light client RPCs ([#7603](https://github.com/paritytech/parity/pull/7603))
|
|
||||||
- Implement registrar.
|
|
||||||
- Implement eth_getCode
|
|
||||||
- Don't wait for providers.
|
|
||||||
- Don't wait for providers.
|
|
||||||
- Fix linting and wasm tests.
|
|
||||||
- Problem: AttachedProtocols don't get registered ([#7610](https://github.com/paritytech/parity/pull/7610))
|
|
||||||
- Fix Temporarily Invalid blocks handling ([#7613](https://github.com/paritytech/parity/pull/7613))
|
|
||||||
- Handle temporarily invalid blocks in sync.
|
|
||||||
- Fix tests.
|
|
||||||
- Add docker build for beta ([#7671](https://github.com/paritytech/parity/pull/7671))
|
|
||||||
- Add docker build for beta
|
|
||||||
- Add cargo cache
|
|
||||||
- Fix snapcraft build for beta ([#7670](https://github.com/paritytech/parity/pull/7670))
|
|
||||||
- Update Parity.pkgproj
|
|
||||||
- update gitlab build from master
|
|
||||||
- Update references to dapp sources ([#7634](https://github.com/paritytech/parity/pull/7634)) ([#7636](https://github.com/paritytech/parity/pull/7636))
|
|
||||||
- Update tokenreg ([#7618](https://github.com/paritytech/parity/pull/7618)) ([#7619](https://github.com/paritytech/parity/pull/7619))
|
|
||||||
- Fix cache:key ([#7598](https://github.com/paritytech/parity/pull/7598))
|
|
||||||
- Make 1.9 beta ([#7533](https://github.com/paritytech/parity/pull/7533))
|
|
||||||
- Trigger js-precompiled ([#7535](https://github.com/paritytech/parity/pull/7535))
|
|
||||||
- RocksDB fix ([#7512](https://github.com/paritytech/parity/pull/7512))
|
|
||||||
- Update js-api ([#7510](https://github.com/paritytech/parity/pull/7510))
|
|
||||||
- Expose default gas price percentile configuration in CLI ([#7497](https://github.com/paritytech/parity/pull/7497))
|
|
||||||
- Use https connection ([#7503](https://github.com/paritytech/parity/pull/7503))
|
|
||||||
- More thorough changes detection ([#7472](https://github.com/paritytech/parity/pull/7472))
|
|
||||||
- Fix small layout issues ([#7500](https://github.com/paritytech/parity/pull/7500))
|
|
||||||
- Show all accounts on Topbar ([#7498](https://github.com/paritytech/parity/pull/7498))
|
|
||||||
- Update Parity Mainnet Bootnodes ([#7476](https://github.com/paritytech/parity/pull/7476))
|
|
||||||
- Fixed panic when io is not available for export block ([#7495](https://github.com/paritytech/parity/pull/7495))
|
|
||||||
- Advance AuRa step as far as we can and prevent invalid blocks. ([#7451](https://github.com/paritytech/parity/pull/7451))
|
|
||||||
- Update package-lock in js-old ([#7494](https://github.com/paritytech/parity/pull/7494))
|
|
||||||
- Update issue template and readme ([#7450](https://github.com/paritytech/parity/pull/7450))
|
|
||||||
- Update package-lock.json pinned versions ([#7492](https://github.com/paritytech/parity/pull/7492))
|
|
||||||
- Explicit pre-precompiled push checkout ([#7474](https://github.com/paritytech/parity/pull/7474))
|
|
||||||
- Trigger js-precompiled ([#7473](https://github.com/paritytech/parity/pull/7473))
|
|
||||||
- Expanse Byzantium update w/ correct metropolis difficulty increment divisor ([#7463](https://github.com/paritytech/parity/pull/7463))
|
|
||||||
- Updated icons ([#7469](https://github.com/paritytech/parity/pull/7469))
|
|
||||||
- Cleanup certifications ([#7454](https://github.com/paritytech/parity/pull/7454))
|
|
||||||
- Fix css lint (updated stylelint) ([#7471](https://github.com/paritytech/parity/pull/7471))
|
|
||||||
- Upgrade markdown-loader & marked ([#7467](https://github.com/paritytech/parity/pull/7467))
|
|
||||||
- Remove JS test for removed code ([#7461](https://github.com/paritytech/parity/pull/7461))
|
|
||||||
- Pull in dapp-status ([#7457](https://github.com/paritytech/parity/pull/7457))
|
|
||||||
- Bump openssl crate ([#7455](https://github.com/paritytech/parity/pull/7455))
|
|
||||||
- Signer updates from global Redux state ([#7452](https://github.com/paritytech/parity/pull/7452))
|
|
||||||
- Remove expanse chain ([#7437](https://github.com/paritytech/parity/pull/7437))
|
|
||||||
- Store tokens with repeatable id ([#7435](https://github.com/paritytech/parity/pull/7435))
|
|
||||||
- Strict config parsing ([#7433](https://github.com/paritytech/parity/pull/7433))
|
|
||||||
- Upgrade to RocksDB 5.8.8 and tune settings to reduce space amplification ([#7348](https://github.com/paritytech/parity/pull/7348))
|
|
||||||
- Fix status layout ([#7432](https://github.com/paritytech/parity/pull/7432))
|
|
||||||
- Fix tracing failed calls. ([#7412](https://github.com/paritytech/parity/pull/7412))
|
|
||||||
- Problem: sending any Whisper message fails ([#7421](https://github.com/paritytech/parity/pull/7421))
|
|
||||||
- Wait for future blocks in AuRa ([#7368](https://github.com/paritytech/parity/pull/7368))
|
|
||||||
- Fix final feature. ([#7426](https://github.com/paritytech/parity/pull/7426))
|
|
||||||
- Use RwLock for state DB ([#7425](https://github.com/paritytech/parity/pull/7425))
|
|
||||||
- Update branding on UI ([#7370](https://github.com/paritytech/parity/pull/7370))
|
|
||||||
- Changelog for 1.8.5 and 1.7.11 ([#7401](https://github.com/paritytech/parity/pull/7401))
|
|
||||||
- Added checking tx-type using transactions permission contract for miners ([#7359](https://github.com/paritytech/parity/pull/7359))
|
|
||||||
- Standalone dir crate, replaces [#7383](https://github.com/paritytech/parity/issues/7383) ([#7409](https://github.com/paritytech/parity/pull/7409))
|
|
||||||
- SecretStore: secretstore_signRawHash method ([#7336](https://github.com/paritytech/parity/pull/7336))
|
|
||||||
- SecretStore: return error 404 when there's no key shares for given key on all nodes ([#7331](https://github.com/paritytech/parity/pull/7331))
|
|
||||||
- SecretStore: PoA integration initial version ([#7101](https://github.com/paritytech/parity/pull/7101))
|
|
||||||
- Update bootnodes ([#7363](https://github.com/paritytech/parity/pull/7363))
|
|
||||||
- Fix default CORS settings. ([#7387](https://github.com/paritytech/parity/pull/7387))
|
|
||||||
- Fix version ([#7390](https://github.com/paritytech/parity/pull/7390))
|
|
||||||
- Wasm runtime update ([#7356](https://github.com/paritytech/parity/pull/7356))
|
|
||||||
- Parity-version pr reopen ([#7136](https://github.com/paritytech/parity/pull/7136))
|
|
||||||
- Get rid of clippy remainings. ([#7355](https://github.com/paritytech/parity/pull/7355))
|
|
||||||
- Avoid using ok_or with allocated argument ([#7357](https://github.com/paritytech/parity/pull/7357))
|
|
||||||
- Make accounts refresh time configurable. ([#7345](https://github.com/paritytech/parity/pull/7345))
|
|
||||||
- Enable traces for DEV chain ([#7327](https://github.com/paritytech/parity/pull/7327))
|
|
||||||
- Problem: AuRa's unsafeties around step duration ([#7282](https://github.com/paritytech/parity/pull/7282))
|
|
||||||
- Problem: Cargo.toml file contains [project] key ([#7346](https://github.com/paritytech/parity/pull/7346))
|
|
||||||
- Fix broken flex modal layouts ([#7343](https://github.com/paritytech/parity/pull/7343))
|
|
||||||
- Fix dappIcon & Fix Signer Pending ([#7338](https://github.com/paritytech/parity/pull/7338))
|
|
||||||
- Fix wallet token/badge icons not showing up ([#7333](https://github.com/paritytech/parity/pull/7333))
|
|
||||||
- Add Ellaism coin in chain config ([#7222](https://github.com/paritytech/parity/pull/7222))
|
|
||||||
- Update bootnodes ([#7296](https://github.com/paritytech/parity/pull/7296))
|
|
||||||
- Adds `personal_signTransaction` RPC method ([#6991](https://github.com/paritytech/parity/pull/6991))
|
|
||||||
- Fix double initialization of embeded providers. ([#7326](https://github.com/paritytech/parity/pull/7326))
|
|
||||||
- Transaction Pool re-implementation ([#6994](https://github.com/paritytech/parity/pull/6994))
|
|
||||||
- UI package bump ([#7318](https://github.com/paritytech/parity/pull/7318))
|
|
||||||
- Test framework and basic test for whisper ([#7011](https://github.com/paritytech/parity/pull/7011))
|
|
||||||
- CI js-precompiled trigger ([#7316](https://github.com/paritytech/parity/pull/7316))
|
|
||||||
- Fix inject.js & Signer store duplication ([#7299](https://github.com/paritytech/parity/pull/7299))
|
|
||||||
- Detect different node, same-key signing in aura ([#7245](https://github.com/paritytech/parity/pull/7245))
|
|
||||||
- New warp enodes ([#7287](https://github.com/paritytech/parity/pull/7287))
|
|
||||||
- CSS fixes for v1 ([#7285](https://github.com/paritytech/parity/pull/7285))
|
|
||||||
- Wallet subscriptions & refresh ([#7283](https://github.com/paritytech/parity/pull/7283))
|
|
||||||
- Update inject web3 dependencies ([#7286](https://github.com/paritytech/parity/pull/7286))
|
|
||||||
- Some padding around dapp image ([#7276](https://github.com/paritytech/parity/pull/7276))
|
|
||||||
- Expand available middleware methods ([#7275](https://github.com/paritytech/parity/pull/7275))
|
|
||||||
- Inject parity script to all dapps // Expand dapps to any ZIP file ([#7260](https://github.com/paritytech/parity/pull/7260))
|
|
||||||
- New Homepage ([#7266](https://github.com/paritytech/parity/pull/7266))
|
|
||||||
- Update kovan HF block number. ([#7259](https://github.com/paritytech/parity/pull/7259))
|
|
||||||
- CHANGELOG for 1.7.10 and 1.8.4 ([#7265](https://github.com/paritytech/parity/pull/7265))
|
|
||||||
- Remove extraneous id hashing ([#7269](https://github.com/paritytech/parity/pull/7269))
|
|
||||||
- Simplify status + content display overlaps/page fixing ([#7264](https://github.com/paritytech/parity/pull/7264))
|
|
||||||
- UI redirect to 127.0.0.1 when localhost requested ([#7236](https://github.com/paritytech/parity/pull/7236))
|
|
||||||
- Usability improvements to security token Dialog [#7112](https://github.com/paritytech/parity/issues/7112) ([#7134](https://github.com/paritytech/parity/pull/7134))
|
|
||||||
- Don't display unneeded notifications ([#7237](https://github.com/paritytech/parity/pull/7237))
|
|
||||||
- Reduce max block timestamp drift to 15 seconds ([#7240](https://github.com/paritytech/parity/pull/7240))
|
|
||||||
- Increase allowed time drift to 10s. ([#7238](https://github.com/paritytech/parity/pull/7238))
|
|
||||||
- Improve building from source ([#7239](https://github.com/paritytech/parity/pull/7239))
|
|
||||||
- Fix/Update method permissions ([#7233](https://github.com/paritytech/parity/pull/7233))
|
|
||||||
- Fix aura difficulty race ([#7198](https://github.com/paritytech/parity/pull/7198))
|
|
||||||
- Dependency updates ([#7226](https://github.com/paritytech/parity/pull/7226))
|
|
||||||
- Display all dapps (shell) & wallet tabs (v1) by default ([#7213](https://github.com/paritytech/parity/pull/7213))
|
|
||||||
- Rework dapps list ([#7206](https://github.com/paritytech/parity/pull/7206))
|
|
||||||
- Add contributing guidelines and code of conduct. ([#7157](https://github.com/paritytech/parity/pull/7157))
|
|
||||||
- Make Signing Requests more visible ([#7204](https://github.com/paritytech/parity/pull/7204))
|
|
||||||
- Send each log as a separate notification ([#7175](https://github.com/paritytech/parity/pull/7175))
|
|
||||||
- Deleting a mistake comment in calc difficulty ([#7154](https://github.com/paritytech/parity/pull/7154))
|
|
||||||
- Maximum uncle count transition ([#7196](https://github.com/paritytech/parity/pull/7196))
|
|
||||||
- Update FirstRun for UI-2 ([#7195](https://github.com/paritytech/parity/pull/7195))
|
|
||||||
- Update mocha import stubs ([#7191](https://github.com/paritytech/parity/pull/7191))
|
|
||||||
- Escape inifinite loop in estimte_gas ([#7075](https://github.com/paritytech/parity/pull/7075))
|
|
||||||
- New account selector UI in top bar ([#7179](https://github.com/paritytech/parity/pull/7179))
|
|
||||||
- Removed ethcore-util dependency from ethcore-network ([#7180](https://github.com/paritytech/parity/pull/7180))
|
|
||||||
- WASM test runner utility upgrade ([#7147](https://github.com/paritytech/parity/pull/7147))
|
|
||||||
- React 16 ([#7174](https://github.com/paritytech/parity/pull/7174))
|
|
||||||
- Assorted improvements for ethstore and ethkey ([#6961](https://github.com/paritytech/parity/pull/6961))
|
|
||||||
- Delete unused package.json (dist bundles) ([#7173](https://github.com/paritytech/parity/pull/7173))
|
|
||||||
- Remove *.css.map & *.js.map ([#7168](https://github.com/paritytech/parity/pull/7168))
|
|
||||||
- Use git flag to remove old js artifacts ([#7165](https://github.com/paritytech/parity/pull/7165))
|
|
||||||
- Cleanup JS build artifacts ([#7164](https://github.com/paritytech/parity/pull/7164))
|
|
||||||
- Fixes typo in user config path ([#7159](https://github.com/paritytech/parity/pull/7159))
|
|
||||||
- Pull in new dapp-{methods,visible} dapps ([#7150](https://github.com/paritytech/parity/pull/7150))
|
|
||||||
- WASM test runner utility ([#7142](https://github.com/paritytech/parity/pull/7142))
|
|
||||||
- WASM Remove blockhash error ([#7121](https://github.com/paritytech/parity/pull/7121))
|
|
||||||
- ECIP-1039: Monetary policy rounding specification ([#7067](https://github.com/paritytech/parity/pull/7067))
|
|
||||||
- Fixed `RotatingLogger` after migrating to new arrayvec ([#7129](https://github.com/paritytech/parity/pull/7129))
|
|
||||||
- Push to correct shell branch ([#7135](https://github.com/paritytech/parity/pull/7135))
|
|
||||||
- Update js-precompiled ref, trigger JS build ([#7132](https://github.com/paritytech/parity/pull/7132))
|
|
||||||
- Fixed build && test ([#7128](https://github.com/paritytech/parity/pull/7128))
|
|
||||||
- Update packages, pull in compiled-only repos ([#7125](https://github.com/paritytech/parity/pull/7125))
|
|
||||||
- Cleanup top bar, add Home icon for navigation ([#7118](https://github.com/paritytech/parity/pull/7118))
|
|
||||||
- WASM storage_read and storage_write don't return anything ([#7110](https://github.com/paritytech/parity/pull/7110))
|
|
||||||
- Local dapp development URL ([#7100](https://github.com/paritytech/parity/pull/7100))
|
|
||||||
- Remove unused and duplicated files in js-old ([#7082](https://github.com/paritytech/parity/pull/7082))
|
|
||||||
- Optimize & group dapp requests ([#7083](https://github.com/paritytech/parity/pull/7083))
|
|
||||||
- WASM parse payload from panics ([#7097](https://github.com/paritytech/parity/pull/7097))
|
|
||||||
- Fix no-default-features. ([#7096](https://github.com/paritytech/parity/pull/7096))
|
|
||||||
- Updated eth-secp256k1 ([#7090](https://github.com/paritytech/parity/pull/7090))
|
|
||||||
- Improve Github Issue Template ([#7099](https://github.com/paritytech/parity/pull/7099))
|
|
||||||
- Changes necessary to upload crates to crates.io ([#7020](https://github.com/paritytech/parity/pull/7020))
|
|
||||||
- Reopened 6860 - iterate over both buffered and unbuffered database entries ([#7048](https://github.com/paritytech/parity/pull/7048))
|
|
||||||
- SecretStore: servers set change session api ([#6925](https://github.com/paritytech/parity/pull/6925))
|
|
||||||
- Disable uncles by default ([#7006](https://github.com/paritytech/parity/pull/7006))
|
|
||||||
- Squashed ethcore-network changes which introduce error-chain ([#7040](https://github.com/paritytech/parity/pull/7040))
|
|
||||||
- Removed redundant imports ([#7057](https://github.com/paritytech/parity/pull/7057))
|
|
||||||
- CHANGELOG for 1.7.8, 1.7.9, 1.8.2, and 1.8.3 ([#7055](https://github.com/paritytech/parity/pull/7055))
|
|
||||||
- Properly display Signer errors (Snackbar display popup) ([#7053](https://github.com/paritytech/parity/pull/7053))
|
|
||||||
- Add the desktop file for the snap ([#7059](https://github.com/paritytech/parity/pull/7059))
|
|
||||||
- Small performance gain in allocations ([#7054](https://github.com/paritytech/parity/pull/7054))
|
|
||||||
- Bump JSON-RPC version ([#7051](https://github.com/paritytech/parity/pull/7051))
|
|
||||||
- Fix nonce reservation ([#7025](https://github.com/paritytech/parity/pull/7025))
|
|
||||||
- Fixed ethstore-cli output ([#7052](https://github.com/paritytech/parity/pull/7052))
|
|
||||||
- Add mui for embed compilation ([#7049](https://github.com/paritytech/parity/pull/7049))
|
|
||||||
- Update the snap metadata to keep working strictly confined ([#6993](https://github.com/paritytech/parity/pull/6993))
|
|
||||||
- Remove unused js packages (dapp cleanups) ([#7046](https://github.com/paritytech/parity/pull/7046))
|
|
||||||
- Gitlog location update ([#7042](https://github.com/paritytech/parity/pull/7042))
|
|
||||||
- Move git logging to .git-release.log ([#7041](https://github.com/paritytech/parity/pull/7041))
|
|
||||||
- Start from rust root in release update step ([#7039](https://github.com/paritytech/parity/pull/7039))
|
|
||||||
- Complete token merge, remove unused files ([#7037](https://github.com/paritytech/parity/pull/7037))
|
|
||||||
- Add missing cargo-push.sh shell variable ([#7036](https://github.com/paritytech/parity/pull/7036))
|
|
||||||
- Fix npm start script ([#7034](https://github.com/paritytech/parity/pull/7034))
|
|
||||||
- Update executable flags on release scripts ([#7035](https://github.com/paritytech/parity/pull/7035))
|
|
||||||
- Fix v1 precompiled ([#7033](https://github.com/paritytech/parity/pull/7033))
|
|
||||||
- Push precompiled to correct branch (v1) ([#7031](https://github.com/paritytech/parity/pull/7031))
|
|
||||||
- Update v1 Wallet Dapp ([#6935](https://github.com/paritytech/parity/pull/6935))
|
|
||||||
- WASM tests update ([#7018](https://github.com/paritytech/parity/pull/7018))
|
|
||||||
- Events in WASM runtime ([#6967](https://github.com/paritytech/parity/pull/6967))
|
|
||||||
- Adds validate_node_url() and refactors boot node check ([#6907](https://github.com/paritytech/parity/pull/6907)) ([#6970](https://github.com/paritytech/parity/pull/6970))
|
|
||||||
- Fix windows build (with ui rebuild) ([#7016](https://github.com/paritytech/parity/pull/7016))
|
|
||||||
- Make CLI arguments parsing more backwards compatible ([#7004](https://github.com/paritytech/parity/pull/7004))
|
|
||||||
- Fixes for parity-extension ([#6990](https://github.com/paritytech/parity/pull/6990))
|
|
||||||
- Update ethcore-bigint ([#6992](https://github.com/paritytech/parity/pull/6992))
|
|
||||||
- Get local transactions by hash in the light client ([#6874](https://github.com/paritytech/parity/pull/6874))
|
|
||||||
- Warn when blacklisted account present in store ([#6875](https://github.com/paritytech/parity/pull/6875))
|
|
||||||
- Skip nonce check for gas estimation ([#6997](https://github.com/paritytech/parity/pull/6997))
|
|
||||||
- Creating pending block with all transactions from the queue ([#6942](https://github.com/paritytech/parity/pull/6942))
|
|
||||||
- Removes `MAX_TX_TO_IMPORT` from `ChainSync` ([#6976](https://github.com/paritytech/parity/pull/6976))
|
|
||||||
- SecretStore: versioned keys ([#6910](https://github.com/paritytech/parity/pull/6910))
|
|
||||||
- Removes `FUTURE_QUEUE_LIMITS_SHIFT` ([#6962](https://github.com/paritytech/parity/pull/6962))
|
|
||||||
- Set zero nonce and gas price for calls by default ([#6954](https://github.com/paritytech/parity/pull/6954))
|
|
||||||
- Add hint in ActionParams for splitting code/data ([#6957](https://github.com/paritytech/parity/pull/6957))
|
|
||||||
- Return decoded seal fields. ([#6932](https://github.com/paritytech/parity/pull/6932))
|
|
||||||
- Fix serialization of status in transaction receipts. ([#6926](https://github.com/paritytech/parity/pull/6926))
|
|
||||||
- Reserve nonces for signing ([#6834](https://github.com/paritytech/parity/pull/6834))
|
|
||||||
- Windows fixes ([#6921](https://github.com/paritytech/parity/pull/6921))
|
|
||||||
- Don't add {css,js}.map from dapps ([#6931](https://github.com/paritytech/parity/pull/6931))
|
|
||||||
- Fix JSON tracing for sub-calls. ([#6842](https://github.com/paritytech/parity/pull/6842))
|
|
||||||
- Shell updates (bonds, updated Dapps) ([#6897](https://github.com/paritytech/parity/pull/6897))
|
|
||||||
- Fix [#6228](https://github.com/paritytech/parity/issues/6228): do not display eth price in cli for etc ([#6877](https://github.com/paritytech/parity/pull/6877))
|
|
||||||
- Fix mining help ([#6885](https://github.com/paritytech/parity/pull/6885))
|
|
||||||
- Refactor static context check in CREATE. ([#6886](https://github.com/paritytech/parity/pull/6886))
|
|
||||||
- Cleanup some configuration options ([#6878](https://github.com/paritytech/parity/pull/6878))
|
|
||||||
- Fix serialization of non-localized transactions ([#6868](https://github.com/paritytech/parity/pull/6868))
|
|
||||||
- Updated ntp to version 0.3 ([#6854](https://github.com/paritytech/parity/pull/6854))
|
|
||||||
- Align README with 1.8 and prepare CHANGELOG with 1.8.1 ([#6833](https://github.com/paritytech/parity/pull/6833))
|
|
||||||
- Return error on timed unlock ([#6777](https://github.com/paritytech/parity/pull/6777))
|
|
||||||
- Fix dapps tests in master ([#6866](https://github.com/paritytech/parity/pull/6866))
|
|
||||||
- Ethstore optimizations ([#6827](https://github.com/paritytech/parity/pull/6827))
|
|
||||||
- Add ECIP1017 to Morden config ([#6810](https://github.com/paritytech/parity/pull/6810))
|
|
||||||
- Remove all package publishing to npm ([#6838](https://github.com/paritytech/parity/pull/6838))
|
|
||||||
- Util crates use tempdir crate instead of devtools to create temp path ([#6807](https://github.com/paritytech/parity/pull/6807))
|
|
||||||
- Trigger js build ([#6836](https://github.com/paritytech/parity/pull/6836))
|
|
||||||
- Clean-up scripts. ([#6832](https://github.com/paritytech/parity/pull/6832))
|
|
||||||
- Tweaked snapshot sync threshold ([#6829](https://github.com/paritytech/parity/pull/6829))
|
|
||||||
- Integrate UI 2 ([#6819](https://github.com/paritytech/parity/pull/6819))
|
|
||||||
- Refresh cached tokens based on registry info & random balances ([#6818](https://github.com/paritytech/parity/pull/6818))
|
|
||||||
- Change keypath derivation logic ([#6815](https://github.com/paritytech/parity/pull/6815))
|
|
||||||
- Refactors journaldb as a separate crate ([#6801](https://github.com/paritytech/parity/pull/6801))
|
|
||||||
- Trigger UI build. ([#6817](https://github.com/paritytech/parity/pull/6817))
|
|
||||||
- Bumped more crate versions ([#6809](https://github.com/paritytech/parity/pull/6809))
|
|
||||||
- Fix RPC compilation warnings. ([#6808](https://github.com/paritytech/parity/pull/6808))
|
|
||||||
- Remove internal ipc ([#6795](https://github.com/paritytech/parity/pull/6795))
|
|
||||||
- Consistent KeyValueDB errors ([#6792](https://github.com/paritytech/parity/pull/6792))
|
|
||||||
- Squash remaining warnings ([#6789](https://github.com/paritytech/parity/pull/6789))
|
|
||||||
- Forward-port [#6754](https://github.com/paritytech/parity/issues/6754) [#6755](https://github.com/paritytech/parity/issues/6755) ([#6785](https://github.com/paritytech/parity/pull/6785))
|
|
||||||
- Removed duplicated versions of clippy ([#6776](https://github.com/paritytech/parity/pull/6776))
|
|
||||||
- Updated ethabi to version 4.0 ([#6742](https://github.com/paritytech/parity/pull/6742))
|
|
||||||
- Updated rpc_cli and parity to rpassword 1.0 ([#6774](https://github.com/paritytech/parity/pull/6774))
|
|
||||||
- Fix sign data typo ([#6750](https://github.com/paritytech/parity/pull/6750))
|
|
||||||
- Refactoring/cache 6693 ([#6772](https://github.com/paritytech/parity/pull/6772))
|
|
||||||
- Fix CHANGLOG for 1.8.0 ([#6751](https://github.com/paritytech/parity/pull/6751))
|
|
||||||
- Removes redundant `mut` in service.rs.in ([#6775](https://github.com/paritytech/parity/pull/6775))
|
|
||||||
- Remove redundant `mut` ([#6773](https://github.com/paritytech/parity/pull/6773))
|
|
||||||
- Fixed kovan chain validation ([#6758](https://github.com/paritytech/parity/pull/6758))
|
|
||||||
- Removed redundant evm deps ([#6757](https://github.com/paritytech/parity/pull/6757))
|
|
||||||
- Fixed modexp gas calculation overflow ([#6741](https://github.com/paritytech/parity/pull/6741))
|
|
||||||
- Use cc 1.0 instead of gcc ([#6733](https://github.com/paritytech/parity/pull/6733))
|
|
||||||
- Version bump to 1.9.0 ([#6727](https://github.com/paritytech/parity/pull/6727))
|
|
||||||
- Fix badges not showing up ([#6730](https://github.com/paritytech/parity/pull/6730))
|
|
||||||
|
|
||||||
### Previous releases
|
|
||||||
|
|
||||||
- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (_stable_)
|
|
||||||
- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25)
|
- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25)
|
||||||
- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15)
|
- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15)
|
||||||
- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28)
|
- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28)
|
||||||
|
|||||||
5002
Cargo.lock
generated
5002
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
144
Cargo.toml
144
Cargo.toml
@@ -1,76 +1,77 @@
|
|||||||
[package]
|
[package]
|
||||||
description = "Parity Ethereum client"
|
description = "Parity Ethereum client"
|
||||||
name = "parity"
|
name = "parity-ethereum"
|
||||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||||
version = "1.10.0"
|
version = "2.4.1"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
blooms-db = { path = "util/blooms-db" }
|
||||||
env_logger = "0.4"
|
log = "0.4"
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
docopt = "0.8"
|
docopt = "1.0"
|
||||||
clap = "2"
|
clap = "2"
|
||||||
term_size = "0.3"
|
term_size = "0.3"
|
||||||
textwrap = "0.9"
|
textwrap = "0.9"
|
||||||
time = "0.1"
|
|
||||||
num_cpus = "1.2"
|
num_cpus = "1.2"
|
||||||
number_prefix = "0.2"
|
number_prefix = "0.2"
|
||||||
rpassword = "1.0"
|
rpassword = "1.0"
|
||||||
semver = "0.6"
|
semver = "0.9"
|
||||||
ansi_term = "0.10"
|
ansi_term = "0.10"
|
||||||
parking_lot = "0.5"
|
parking_lot = "0.7"
|
||||||
regex = "0.2"
|
regex = "1.0"
|
||||||
isatty = "0.1"
|
atty = "0.2.8"
|
||||||
toml = "0.4"
|
toml = "0.4"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
app_dirs = "1.1.1"
|
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
futures-cpupool = "0.1"
|
|
||||||
fdlimit = "0.1"
|
fdlimit = "0.1"
|
||||||
ws2_32-sys = "0.2"
|
|
||||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.10" }
|
jsonrpc-core = "10.0.1"
|
||||||
ethsync = { path = "sync" }
|
parity-bytes = "0.1"
|
||||||
ethcore = { path = "ethcore" }
|
common-types = { path = "ethcore/types" }
|
||||||
ethcore-bytes = { path = "util/bytes" }
|
ethcore = { path = "ethcore", features = ["parity"] }
|
||||||
|
ethcore-accounts = { path = "accounts", optional = true }
|
||||||
|
ethcore-blockchain = { path = "ethcore/blockchain" }
|
||||||
|
ethcore-call-contract = { path = "ethcore/call-contract"}
|
||||||
|
ethcore-db = { path = "ethcore/db" }
|
||||||
ethcore-io = { path = "util/io" }
|
ethcore-io = { path = "util/io" }
|
||||||
ethcore-light = { path = "ethcore/light" }
|
ethcore-light = { path = "ethcore/light" }
|
||||||
ethcore-logger = { path = "logger" }
|
ethcore-logger = { path = "parity/logger" }
|
||||||
ethcore-migrations = { path = "ethcore/migrations" }
|
|
||||||
ethcore-miner = { path = "miner" }
|
ethcore-miner = { path = "miner" }
|
||||||
ethcore-network = { path = "util/network" }
|
ethcore-network = { path = "util/network" }
|
||||||
ethcore-stratum = { path = "stratum" }
|
ethcore-private-tx = { path = "ethcore/private-tx" }
|
||||||
ethcore-transaction = { path = "ethcore/transaction" }
|
ethcore-service = { path = "ethcore/service" }
|
||||||
ethereum-types = "0.2"
|
ethcore-sync = { path = "ethcore/sync" }
|
||||||
node-filter = { path = "ethcore/node_filter" }
|
ethereum-types = "0.4"
|
||||||
ethkey = { path = "ethkey" }
|
ethkey = { path = "accounts/ethkey" }
|
||||||
node-health = { path = "dapps/node-health" }
|
ethstore = { path = "accounts/ethstore" }
|
||||||
rlp = { path = "util/rlp" }
|
node-filter = { path = "ethcore/node-filter" }
|
||||||
rpc-cli = { path = "rpc_cli" }
|
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||||
parity-hash-fetch = { path = "hash-fetch" }
|
cli-signer= { path = "cli-signer" }
|
||||||
|
parity-daemonize = "0.3"
|
||||||
|
parity-hash-fetch = { path = "updater/hash-fetch" }
|
||||||
parity-ipfs-api = { path = "ipfs" }
|
parity-ipfs-api = { path = "ipfs" }
|
||||||
parity-local-store = { path = "local-store" }
|
parity-local-store = { path = "miner/local-store" }
|
||||||
parity-reactor = { path = "util/reactor" }
|
parity-runtime = { path = "util/runtime" }
|
||||||
parity-rpc = { path = "rpc" }
|
parity-rpc = { path = "rpc" }
|
||||||
parity-rpc-client = { path = "rpc_client" }
|
|
||||||
parity-updater = { path = "updater" }
|
parity-updater = { path = "updater" }
|
||||||
parity-version = { path = "util/version" }
|
parity-version = { path = "util/version" }
|
||||||
parity-whisper = { path = "whisper" }
|
parity-whisper = { path = "whisper" }
|
||||||
path = { path = "util/path" }
|
parity-path = "0.1"
|
||||||
dir = { path = "util/dir" }
|
dir = { path = "util/dir" }
|
||||||
panic_hook = { path = "util/panic_hook" }
|
panic_hook = { path = "util/panic-hook" }
|
||||||
keccak-hash = { path = "util/hash" }
|
keccak-hash = "0.1"
|
||||||
migration = { path = "util/migration" }
|
migration-rocksdb = { path = "util/migration-rocksdb" }
|
||||||
kvdb = { path = "util/kvdb" }
|
kvdb = "0.1"
|
||||||
kvdb-rocksdb = { path = "util/kvdb-rocksdb" }
|
kvdb-rocksdb = "0.1.3"
|
||||||
journaldb = { path = "util/journaldb" }
|
journaldb = { path = "util/journaldb" }
|
||||||
|
|
||||||
parity-dapps = { path = "dapps", optional = true }
|
ethcore-secretstore = { path = "secret-store", optional = true }
|
||||||
ethcore-secretstore = { path = "secret_store", optional = true }
|
|
||||||
|
registrar = { path = "util/registrar" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rustc_version = "0.2"
|
rustc_version = "0.2"
|
||||||
@@ -79,57 +80,66 @@ rustc_version = "0.2"
|
|||||||
pretty_assertions = "0.1"
|
pretty_assertions = "0.1"
|
||||||
ipnetwork = "0.12.6"
|
ipnetwork = "0.12.6"
|
||||||
tempdir = "0.3"
|
tempdir = "0.3"
|
||||||
|
fake-fetch = { path = "util/fake-fetch" }
|
||||||
|
lazy_static = "1.2.0"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = "0.2"
|
winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] }
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
|
||||||
daemonize = "0.2"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ui-precompiled"]
|
default = ["accounts"]
|
||||||
ui = [
|
accounts = ["ethcore-accounts", "parity-rpc/accounts"]
|
||||||
"ui-enabled",
|
miner-debug = ["ethcore/miner-debug"]
|
||||||
"parity-dapps/ui",
|
|
||||||
]
|
|
||||||
ui-precompiled = [
|
|
||||||
"ui-enabled",
|
|
||||||
"parity-dapps/ui-precompiled",
|
|
||||||
]
|
|
||||||
ui-enabled = ["dapps"]
|
|
||||||
dapps = ["parity-dapps"]
|
|
||||||
jit = ["ethcore/jit"]
|
|
||||||
json-tests = ["ethcore/json-tests"]
|
json-tests = ["ethcore/json-tests"]
|
||||||
|
ci-skip-tests = ["ethcore/ci-skip-tests"]
|
||||||
test-heavy = ["ethcore/test-heavy"]
|
test-heavy = ["ethcore/test-heavy"]
|
||||||
evm-debug = ["ethcore/evm-debug"]
|
evm-debug = ["ethcore/evm-debug"]
|
||||||
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||||
slow-blocks = ["ethcore/slow-blocks"]
|
slow-blocks = ["ethcore/slow-blocks"]
|
||||||
secretstore = ["ethcore-secretstore"]
|
secretstore = ["ethcore-secretstore", "ethcore-secretstore/accounts"]
|
||||||
final = ["parity-version/final"]
|
final = ["parity-version/final"]
|
||||||
|
deadlock_detection = ["parking_lot/deadlock_detection"]
|
||||||
|
# to create a memory profile (requires nightly rust), use e.g.
|
||||||
|
# `heaptrack /path/to/parity <parity params>`,
|
||||||
|
# to visualize a memory profile, use `heaptrack_gui`
|
||||||
|
# or
|
||||||
|
# `valgrind --tool=massif /path/to/parity <parity params>`
|
||||||
|
# and `massif-visualizer` for visualization
|
||||||
|
memory_profiling = []
|
||||||
|
# hardcode version number 1.3.7 of parity to force an update
|
||||||
|
# in order to manually test that parity fall-over to the local version
|
||||||
|
# in case of invalid or deprecated command line arguments are entered
|
||||||
|
test-updater = ["parity-updater/test-updater"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "parity/lib.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
path = "parity/main.rs"
|
path = "parity/main.rs"
|
||||||
name = "parity"
|
name = "parity"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = false
|
debug = false
|
||||||
lto = false
|
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
# This should only list projects that are not
|
||||||
|
# in the dependency tree in any other way
|
||||||
|
# (i.e. pretty much only standalone CLI tools)
|
||||||
members = [
|
members = [
|
||||||
|
"accounts/ethkey/cli",
|
||||||
|
"accounts/ethstore/cli",
|
||||||
"chainspec",
|
"chainspec",
|
||||||
"dapps/js-glue",
|
|
||||||
"ethcore/wasm/run",
|
"ethcore/wasm/run",
|
||||||
"ethcore/types",
|
|
||||||
"ethkey/cli",
|
|
||||||
"ethstore/cli",
|
|
||||||
"evmbin",
|
"evmbin",
|
||||||
"miner",
|
"parity-clib",
|
||||||
"transaction-pool",
|
"whisper/cli",
|
||||||
"whisper",
|
"util/triehash-ethereum",
|
||||||
"util/rlp_compress"
|
"util/keccak-hasher",
|
||||||
|
"util/patricia-trie-ethereum",
|
||||||
|
"util/fastmap"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
heapsize = { git = "https://github.com/cheme/heapsize.git", branch = "ec-macfix" }
|
||||||
|
|||||||
193
README.md
193
README.md
@@ -1,116 +1,75 @@
|
|||||||
# Parity - fast, light, and robust Ethereum client
|

|
||||||
|
|
||||||
## [» Download the latest release «](https://github.com/paritytech/parity/releases/latest)
|
<h2 align="center">The Fastest and most Advanced Ethereum Client.</h2>
|
||||||
|
|
||||||
[](https://gitlab.parity.io/parity/parity/commits/master)
|
<p align="center"><strong><a href="https://github.com/paritytech/parity-ethereum/releases/latest">» Download the latest release «</a></strong></p>
|
||||||
[](https://codecov.io/gh/paritytech/parity)
|
|
||||||
[](https://build.snapcraft.io/user/paritytech/parity)
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
|
||||||
|
|
||||||
|
<p align="center"><a href="https://gitlab.parity.io/parity/parity-ethereum/commits/master" target="_blank"><img src="https://gitlab.parity.io/parity/parity-ethereum/badges/master/build.svg" /></a>
|
||||||
|
<a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank"><img src="https://img.shields.io/badge/license-GPL%20v3-green.svg" /></a></p>
|
||||||
|
|
||||||
### Join the chat!
|
**Built for mission-critical use**: Miners, service providers, and exchanges need fast synchronisation and maximum uptime. Parity Ethereum provides the core infrastructure essential for speedy and reliable services.
|
||||||
|
|
||||||
Get in touch with us on Gitter:
|
- Clean, modular codebase for easy customisation
|
||||||
[](https://gitter.im/paritytech/parity)
|
- Advanced CLI-based client
|
||||||
[](https://gitter.im/paritytech/parity.js)
|
- Minimal memory and storage footprint
|
||||||
[](https://gitter.im/paritytech/parity/miners)
|
- Synchronise in hours, not days with Warp Sync
|
||||||
[](https://gitter.im/paritytech/parity-poa)
|
- Modular for light integration into your service or product
|
||||||
|
|
||||||
Or join our community on Matrix:
|
## Technical Overview
|
||||||
[](https://riot.im/app/#/group/+parity:matrix.parity.io)
|
|
||||||
|
|
||||||
Official website: https://parity.io
|
Parity Ethereum's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity Ethereum using the sophisticated and cutting-edge **Rust programming language**. Parity Ethereum is licensed under the GPLv3 and can be used for all your Ethereum needs.
|
||||||
|
|
||||||
Be sure to check out [our wiki](https://paritytech.github.io/wiki/) and the [internal documentation](https://paritytech.github.io/parity/ethcore/index.html) for more information.
|
By default, Parity Ethereum runs a JSON-RPC HTTP server on port `:8545` and a Web-Sockets server on port `:8546`. This is fully configurable and supports a number of APIs.
|
||||||
|
|
||||||
----
|
If you run into problems while using Parity Ethereum, check out the [wiki for documentation](https://wiki.parity.io/), feel free to [file an issue in this repository](https://github.com/paritytech/parity-ethereum/issues/new), or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help! **For security-critical issues**, please refer to the security policy outlined in [SECURITY.md](SECURITY.md).
|
||||||
|
|
||||||
## About Parity
|
Parity Ethereum's current beta-release is 2.1. You can download it at [the releases page](https://github.com/paritytech/parity-ethereum/releases) or follow the instructions below to build from source. Please, mind the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions.
|
||||||
|
|
||||||
Parity's goal is to be the fastest, lightest, and most secure Ethereum client. We are developing Parity using the sophisticated and cutting-edge Rust programming language. Parity is licensed under the GPLv3, and can be used for all your Ethereum needs.
|
## Build Dependencies
|
||||||
|
|
||||||
Parity comes with a built-in wallet. To access [Parity Wallet](http://web3.site/) simply go to http://web3.site/ (if you don't have access to the internet, but still want to use the service, you can also use http://127.0.0.1:8180/). It includes various functionality allowing you to:
|
Parity Ethereum requires **latest stable Rust version** to build.
|
||||||
|
|
||||||
- create and manage your Ethereum accounts;
|
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have `rustup`, you can install it like this:
|
||||||
- manage your Ether and any Ethereum tokens;
|
|
||||||
- create and register your own tokens;
|
|
||||||
- and much more.
|
|
||||||
|
|
||||||
By default, Parity will also run a JSONRPC server on `127.0.0.1:8545` and a websockets server on `127.0.0.1:8546`. This is fully configurable and supports a number of APIs.
|
|
||||||
|
|
||||||
If you run into an issue while using Parity, feel free to file one in this repository or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help!
|
|
||||||
|
|
||||||
**For security-critical issues**, please refer to the security policy outlined in [SECURITY.MD](SECURITY.md).
|
|
||||||
|
|
||||||
Parity's current release is 1.9. You can download it at https://github.com/paritytech/parity/releases or follow the instructions below to build from source.
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
## Build dependencies
|
|
||||||
|
|
||||||
**Parity requires Rust version 1.23.0 to build**
|
|
||||||
|
|
||||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
|
||||||
|
|
||||||
- Linux:
|
- Linux:
|
||||||
```bash
|
|
||||||
$ curl https://sh.rustup.rs -sSf | sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Parity also requires `gcc`, `g++`, `libssl-dev`/`openssl`, `libudev-dev` and `pkg-config` packages to be installed.
|
|
||||||
|
|
||||||
- OSX:
|
|
||||||
```bash
|
|
||||||
$ curl https://sh.rustup.rs -sSf | sh
|
|
||||||
```
|
|
||||||
|
|
||||||
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
|
||||||
|
|
||||||
- Windows
|
|
||||||
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from
|
|
||||||
https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the msvc toolchain:
|
|
||||||
```bash
|
```bash
|
||||||
$ rustup default stable-x86_64-pc-windows-msvc
|
$ curl https://sh.rustup.rs -sSf | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Once you have rustup, install Parity or download and build from source
|
Parity Ethereum also requires `gcc`, `g++`, `libudev-dev`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||||
|
|
||||||
----
|
- OSX:
|
||||||
|
```bash
|
||||||
|
$ curl https://sh.rustup.rs -sSf | sh
|
||||||
|
```
|
||||||
|
|
||||||
## Install from the snap store
|
`clang` is required. It comes with Xcode command line tools or can be installed with homebrew.
|
||||||
|
|
||||||
In any of the [supported Linux distros](https://snapcraft.io/docs/core/install):
|
- Windows
|
||||||
|
Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the `rustup` installer from
|
||||||
|
https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the `msvc` toolchain:
|
||||||
|
```bash
|
||||||
|
$ rustup default stable-x86_64-pc-windows-msvc
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have `rustup` installed, then you need to install:
|
||||||
|
* [Perl](https://www.perl.org)
|
||||||
|
* [Yasm](https://yasm.tortall.net)
|
||||||
|
|
||||||
|
Make sure that these binaries are in your `PATH`. After that, you should be able to build Parity Ethereum from source.
|
||||||
|
|
||||||
|
## Build from Source Code
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo snap install parity
|
# download Parity Ethereum code
|
||||||
```
|
$ git clone https://github.com/paritytech/parity-ethereum
|
||||||
|
$ cd parity-ethereum
|
||||||
Or, if you want to contribute testing the upcoming release:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo snap install parity --beta
|
|
||||||
```
|
|
||||||
|
|
||||||
And to test the latest code landed into the master branch:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo snap install parity --edge
|
|
||||||
```
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
## Build from source
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# download Parity code
|
|
||||||
$ git clone https://github.com/paritytech/parity
|
|
||||||
$ cd parity
|
|
||||||
|
|
||||||
# build in release mode
|
# build in release mode
|
||||||
$ cargo build --release
|
$ cargo build --release --features final
|
||||||
```
|
```
|
||||||
|
|
||||||
This will produce an executable in the `./target/release` subdirectory.
|
This produces an executable in the `./target/release` subdirectory.
|
||||||
|
|
||||||
Note: if cargo fails to parse manifest try:
|
Note: if cargo fails to parse manifest try:
|
||||||
|
|
||||||
@@ -118,19 +77,13 @@ Note: if cargo fails to parse manifest try:
|
|||||||
$ ~/.cargo/bin/cargo build --release
|
$ ~/.cargo/bin/cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: When compiling a crate and you receive the following error:
|
Note, when compiling a crate and you receive errors, it's in most cases your outdated version of Rust, or some of your crates have to be recompiled. Cleaning the repository will most likely solve the issue if you are on the latest stable version of Rust, try:
|
||||||
|
|
||||||
```
|
|
||||||
error: the crate is compiled with the panic strategy `abort` which is incompatible with this crate's strategy of `unwind`
|
|
||||||
```
|
|
||||||
|
|
||||||
Cleaning the repository will most likely solve the issue, try:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo clean
|
$ cargo clean
|
||||||
```
|
```
|
||||||
|
|
||||||
This will always compile the latest nightly builds. If you want to build stable or beta, do a
|
This always compiles the latest nightly builds. If you want to build stable or beta, do a
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git checkout stable
|
$ git checkout stable
|
||||||
@@ -142,38 +95,62 @@ or
|
|||||||
$ git checkout beta
|
$ git checkout beta
|
||||||
```
|
```
|
||||||
|
|
||||||
first.
|
## Simple One-Line Installer for Mac and Linux
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
## Simple one-line installer for Mac and Ubuntu
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash <(curl https://get.parity.io -Lk)
|
bash <(curl https://get.parity.io -L)
|
||||||
```
|
```
|
||||||
|
|
||||||
The one-line installer always defaults to the latest beta release. To install a stable release, run:
|
The one-line installer always defaults to the latest beta release. To install a stable release, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash <(curl https://get.parity.io -Lk) -r stable
|
bash <(curl https://get.parity.io -L) -r stable
|
||||||
```
|
```
|
||||||
|
|
||||||
## Start Parity
|
## Start Parity Ethereum
|
||||||
|
|
||||||
### Manually
|
### Manually
|
||||||
|
|
||||||
To start Parity manually, just run
|
To start Parity Ethereum manually, just run
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ ./target/release/parity
|
$ ./target/release/parity
|
||||||
```
|
```
|
||||||
|
|
||||||
and Parity will begin syncing the Ethereum blockchain.
|
so Parity Ethereum begins syncing the Ethereum blockchain.
|
||||||
|
|
||||||
### Using systemd service file
|
### Using `systemd` service file
|
||||||
|
|
||||||
To start Parity as a regular user using systemd init:
|
To start Parity Ethereum as a regular user using `systemd` init:
|
||||||
|
|
||||||
1. Copy `./scripts/parity.service` to your
|
1. Copy `./scripts/parity.service` to your
|
||||||
systemd user directory (usually `~/.config/systemd/user`).
|
`systemd` user directory (usually `~/.config/systemd/user`).
|
||||||
2. To configure Parity, write a `/etc/parity/config.toml` config file, see [Configuring Parity](https://paritytech.github.io/wiki/Configuring-Parity) for details.
|
2. Copy release to bin folder, write `sudo install ./target/release/parity /usr/bin/parity`
|
||||||
|
3. To configure Parity Ethereum, write a `/etc/parity/config.toml` config file, see [Configuring Parity Ethereum](https://paritytech.github.io/wiki/Configuring-Parity) for details.
|
||||||
|
|
||||||
|
## Parity Ethereum toolchain
|
||||||
|
|
||||||
|
In addition to the Parity Ethereum client, there are additional tools in this repository available:
|
||||||
|
|
||||||
|
- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum.
|
||||||
|
- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding.
|
||||||
|
- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management.
|
||||||
|
- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator.
|
||||||
|
- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC.
|
||||||
|
|
||||||
|
## Join the chat!
|
||||||
|
|
||||||
|
Questions? Get in touch with us on Gitter:
|
||||||
|
[](https://gitter.im/paritytech/parity)
|
||||||
|
[](https://gitter.im/paritytech/parity.js)
|
||||||
|
[](https://gitter.im/paritytech/parity/miners)
|
||||||
|
[](https://gitter.im/paritytech/parity-poa)
|
||||||
|
|
||||||
|
Alternatively, join our community on Matrix:
|
||||||
|
[](https://riot.im/app/#/group/+parity:matrix.parity.io)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Official website: https://parity.io
|
||||||
|
|
||||||
|
Be sure to [check out our wiki](https://wiki.parity.io) for more information.
|
||||||
|
|||||||
28
accounts/Cargo.toml
Normal file
28
accounts/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
description = "Account management for Parity Ethereum"
|
||||||
|
homepage = "http://parity.io"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
name = "ethcore-accounts"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
common-types = { path = "../ethcore/types" }
|
||||||
|
ethkey = { path = "ethkey" }
|
||||||
|
ethstore = { path = "ethstore" }
|
||||||
|
log = "0.4"
|
||||||
|
parking_lot = "0.7"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies]
|
||||||
|
hardware-wallet = { path = "hw" }
|
||||||
|
|
||||||
|
[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))'.dependencies]
|
||||||
|
fake-hardware-wallet = { path = "fake-hardware-wallet" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
ethereum-types = "0.4"
|
||||||
|
tempdir = "0.3"
|
||||||
@@ -6,12 +6,16 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
edit-distance = "2.0"
|
edit-distance = "2.0"
|
||||||
|
parity-crypto = "0.3.0"
|
||||||
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
|
||||||
ethereum-types = "0.2"
|
ethereum-types = "0.4"
|
||||||
lazy_static = "1.0"
|
lazy_static = "1.0"
|
||||||
log = "0.3"
|
log = "0.4"
|
||||||
|
memzero = { path = "../../util/memzero" }
|
||||||
parity-wordlist = "1.2"
|
parity-wordlist = "1.2"
|
||||||
|
quick-error = "1.2.2"
|
||||||
rand = "0.4"
|
rand = "0.4"
|
||||||
rust-crypto = "0.2"
|
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
tiny-keccak = "1.3"
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
tiny-keccak = "1.4"
|
||||||
@@ -1,19 +1,12 @@
|
|||||||
# ethkey
|
## ethkey-cli
|
||||||
|
|
||||||
[![Build Status][travis-image]][travis-url]
|
Parity Ethereum keys generator.
|
||||||
|
|
||||||
[travis-image]: https://travis-ci.org/paritytech/ethkey.svg?branch=master
|
|
||||||
[travis-url]: https://travis-ci.org/paritytech/ethkey
|
|
||||||
|
|
||||||
Ethereum keys generator.
|
|
||||||
|
|
||||||
[Documentation](http://paritytech.github.io/ethkey/ethkey/index.html)
|
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
Ethereum keys generator.
|
Parity Ethereum keys generator.
|
||||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
ethkey info <secret-or-phrase> [options]
|
ethkey info <secret-or-phrase> [options]
|
||||||
@@ -27,17 +20,17 @@ Usage:
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Display this message and exit.
|
-h, --help Display this message and exit.
|
||||||
-s, --secret Display only the secret.
|
-s, --secret Display only the secret key.
|
||||||
-p, --public Display only the public.
|
-p, --public Display only the public key.
|
||||||
-a, --address Display only the address.
|
-a, --address Display only the address.
|
||||||
-b, --brain Use parity brain wallet algorithm.
|
-b, --brain Use parity brain wallet algorithm. Not recommended.
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
info Display public and address of the secret.
|
info Display public key and address of the secret.
|
||||||
generate random Generates new random ethereum key.
|
generate random Generates new random Ethereum key.
|
||||||
generate prefix Random generation, but address must start with a prefix.
|
generate prefix Random generation, but address must start with a prefix ("vanity address").
|
||||||
sign Sign message using secret.
|
sign Sign message using a secret key.
|
||||||
verify Verify signer of the signature.
|
verify Verify signer of the signature by public key or address.
|
||||||
recover Try to find brain phrase matching given address from partial phrase.
|
recover Try to find brain phrase matching given address from partial phrase.
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -218,10 +211,11 @@ public: 4e19a5fdae82596e1485c69b687c9cc52b5078e5b0668ef3ce8543cd90e712cb00df822
|
|||||||
address: 00cf3711cbd3a1512570639280758118ba0b2bcb
|
address: 00cf3711cbd3a1512570639280758118ba0b2bcb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Parity Ethereum toolchain
|
||||||
|
_This project is a part of the Parity Ethereum toolchain._
|
||||||
|
|
||||||
# Parity toolchain
|
- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum.
|
||||||
*this project is a part of the parity toolchain*
|
- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding.
|
||||||
|
- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management.
|
||||||
- [**ethkey**](https://github.com/paritytech/ethkey) - Ethereum keys generator and signer.
|
- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator.
|
||||||
- [**ethstore**](https://github.com/paritytech/ethstore) - Ethereum key management.
|
- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC.
|
||||||
- [**ethabi**](https://github.com/paritytech/ethabi) - Ethereum function calls encoding.
|
|
||||||
@@ -4,10 +4,10 @@ version = "0.1.0"
|
|||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
docopt = "0.8"
|
docopt = "1.0"
|
||||||
env_logger = "0.4"
|
env_logger = "0.5"
|
||||||
ethkey = { path = "../" }
|
ethkey = { path = "../" }
|
||||||
panic_hook = { path = "../../util/panic_hook" }
|
panic_hook = { path = "../../../util/panic-hook" }
|
||||||
parity-wordlist="1.2"
|
parity-wordlist="1.2"
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
@@ -31,11 +31,11 @@ use std::{env, fmt, process, io, sync};
|
|||||||
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use ethkey::{KeyPair, Random, Brain, BrainPrefix, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address, brain_recover};
|
use ethkey::{KeyPair, Random, Brain, BrainPrefix, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address, brain_recover};
|
||||||
use rustc_hex::{ToHex, FromHex, FromHexError};
|
use rustc_hex::{FromHex, FromHexError};
|
||||||
|
|
||||||
pub const USAGE: &'static str = r#"
|
const USAGE: &'static str = r#"
|
||||||
Ethereum keys generator.
|
Parity Ethereum keys generator.
|
||||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
ethkey info <secret-or-phrase> [options]
|
ethkey info <secret-or-phrase> [options]
|
||||||
@@ -49,17 +49,17 @@ Usage:
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Display this message and exit.
|
-h, --help Display this message and exit.
|
||||||
-s, --secret Display only the secret.
|
-s, --secret Display only the secret key.
|
||||||
-p, --public Display only the public.
|
-p, --public Display only the public key.
|
||||||
-a, --address Display only the address.
|
-a, --address Display only the address.
|
||||||
-b, --brain Use parity brain wallet algorithm.
|
-b, --brain Use parity brain wallet algorithm. Not recommended.
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
info Display public and address of the secret.
|
info Display public key and address of the secret.
|
||||||
generate random Generates new random ethereum key.
|
generate random Generates new random Ethereum key.
|
||||||
generate prefix Random generation, but address must start with a prefix.
|
generate prefix Random generation, but address must start with a prefix ("vanity address").
|
||||||
sign Sign message using secret.
|
sign Sign message using a secret key.
|
||||||
verify Verify signer of the signature.
|
verify Verify signer of the signature by public key or address.
|
||||||
recover Try to find brain phrase matching given address from partial phrase.
|
recover Try to find brain phrase matching given address from partial phrase.
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@@ -161,15 +161,16 @@ impl DisplayMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
panic_hook::set();
|
panic_hook::set_abort();
|
||||||
env_logger::init().expect("Logger initialized only once.");
|
env_logger::try_init().expect("Logger initialized only once.");
|
||||||
|
|
||||||
match execute(env::args()) {
|
match execute(env::args()) {
|
||||||
Ok(ok) => println!("{}", ok),
|
Ok(ok) => println!("{}", ok),
|
||||||
|
Err(Error::Docopt(ref e)) => e.exit(),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("{}", err);
|
eprintln!("{}", err);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,9 +181,9 @@ fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
|
|||||||
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
|
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
|
||||||
None => format!("{}", keypair)
|
None => format!("{}", keypair)
|
||||||
},
|
},
|
||||||
DisplayMode::Secret => format!("{}", keypair.secret().to_hex()),
|
DisplayMode::Secret => format!("{:x}", keypair.secret()),
|
||||||
DisplayMode::Public => format!("{:?}", keypair.public()),
|
DisplayMode::Public => format!("{:x}", keypair.public()),
|
||||||
DisplayMode::Address => format!("{:?}", keypair.address()),
|
DisplayMode::Address => format!("{:x}", keypair.address()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use keccak::Keccak256;
|
use keccak::Keccak256;
|
||||||
use super::{KeyPair, Generator, Secret};
|
use super::{KeyPair, Generator, Secret};
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use super::{Generator, KeyPair, Error, Brain};
|
use super::{Generator, KeyPair, Error, Brain};
|
||||||
use parity_wordlist as wordlist;
|
use parity_wordlist as wordlist;
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
@@ -21,7 +21,6 @@ use parity_wordlist;
|
|||||||
|
|
||||||
use super::{Address, Brain, Generator};
|
use super::{Address, Brain, Generator};
|
||||||
|
|
||||||
|
|
||||||
/// Tries to find a phrase for address, given the number
|
/// Tries to find a phrase for address, given the number
|
||||||
/// of expected words and a partial phrase.
|
/// of expected words and a partial phrase.
|
||||||
///
|
///
|
||||||
@@ -150,7 +149,6 @@ impl Iterator for PhrasesIterator {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::PhrasesIterator;
|
use super::PhrasesIterator;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_generate_possible_combinations() {
|
fn should_generate_possible_combinations() {
|
||||||
let mut it = PhrasesIterator::new(vec![
|
let mut it = PhrasesIterator::new(vec![
|
||||||
189
accounts/ethkey/src/crypto.rs
Normal file
189
accounts/ethkey/src/crypto.rs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use secp256k1;
|
||||||
|
use std::io;
|
||||||
|
use parity_crypto::error::SymmError;
|
||||||
|
|
||||||
|
quick_error! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Secp(e: secp256k1::Error) {
|
||||||
|
display("secp256k1 error: {}", e)
|
||||||
|
cause(e)
|
||||||
|
from()
|
||||||
|
}
|
||||||
|
Io(e: io::Error) {
|
||||||
|
display("i/o error: {}", e)
|
||||||
|
cause(e)
|
||||||
|
from()
|
||||||
|
}
|
||||||
|
InvalidMessage {
|
||||||
|
display("invalid message")
|
||||||
|
}
|
||||||
|
Symm(e: SymmError) {
|
||||||
|
cause(e)
|
||||||
|
from()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ECDH functions
|
||||||
|
pub mod ecdh {
|
||||||
|
use secp256k1::{self, ecdh, key};
|
||||||
|
use super::Error;
|
||||||
|
use {Secret, Public, SECP256K1};
|
||||||
|
|
||||||
|
/// Agree on a shared secret
|
||||||
|
pub fn agree(secret: &Secret, public: &Public) -> Result<Secret, Error> {
|
||||||
|
let context = &SECP256K1;
|
||||||
|
let pdata = {
|
||||||
|
let mut temp = [4u8; 65];
|
||||||
|
(&mut temp[1..65]).copy_from_slice(&public[0..64]);
|
||||||
|
temp
|
||||||
|
};
|
||||||
|
|
||||||
|
let publ = key::PublicKey::from_slice(context, &pdata)?;
|
||||||
|
let sec = key::SecretKey::from_slice(context, &secret)?;
|
||||||
|
let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec);
|
||||||
|
|
||||||
|
Secret::from_unsafe_slice(&shared[0..32])
|
||||||
|
.map_err(|_| Error::Secp(secp256k1::Error::InvalidSecretKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ECIES function
|
||||||
|
pub mod ecies {
|
||||||
|
use parity_crypto::{aes, digest, hmac, is_equal};
|
||||||
|
use ethereum_types::H128;
|
||||||
|
use super::{ecdh, Error};
|
||||||
|
use {Random, Generator, Public, Secret};
|
||||||
|
|
||||||
|
/// Encrypt a message with a public key, writing an HMAC covering both
|
||||||
|
/// the plaintext and authenticated data.
|
||||||
|
///
|
||||||
|
/// Authenticated data may be empty.
|
||||||
|
pub fn encrypt(public: &Public, auth_data: &[u8], plain: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
|
let r = Random.generate()?;
|
||||||
|
let z = ecdh::agree(r.secret(), public)?;
|
||||||
|
let mut key = [0u8; 32];
|
||||||
|
kdf(&z, &[0u8; 0], &mut key);
|
||||||
|
|
||||||
|
let ekey = &key[0..16];
|
||||||
|
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
|
||||||
|
|
||||||
|
let mut msg = vec![0u8; 1 + 64 + 16 + plain.len() + 32];
|
||||||
|
msg[0] = 0x04u8;
|
||||||
|
{
|
||||||
|
let msgd = &mut msg[1..];
|
||||||
|
msgd[0..64].copy_from_slice(r.public());
|
||||||
|
let iv = H128::random();
|
||||||
|
msgd[64..80].copy_from_slice(&iv);
|
||||||
|
{
|
||||||
|
let cipher = &mut msgd[(64 + 16)..(64 + 16 + plain.len())];
|
||||||
|
aes::encrypt_128_ctr(ekey, &iv, plain, cipher)?;
|
||||||
|
}
|
||||||
|
let mut hmac = hmac::Signer::with(&mkey);
|
||||||
|
{
|
||||||
|
let cipher_iv = &msgd[64..(64 + 16 + plain.len())];
|
||||||
|
hmac.update(cipher_iv);
|
||||||
|
}
|
||||||
|
hmac.update(auth_data);
|
||||||
|
let sig = hmac.sign();
|
||||||
|
msgd[(64 + 16 + plain.len())..].copy_from_slice(&sig);
|
||||||
|
}
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypt a message with a secret key, checking HMAC for ciphertext
|
||||||
|
/// and authenticated data validity.
|
||||||
|
pub fn decrypt(secret: &Secret, auth_data: &[u8], encrypted: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
|
let meta_len = 1 + 64 + 16 + 32;
|
||||||
|
if encrypted.len() < meta_len || encrypted[0] < 2 || encrypted[0] > 4 {
|
||||||
|
return Err(Error::InvalidMessage); //invalid message: publickey
|
||||||
|
}
|
||||||
|
|
||||||
|
let e = &encrypted[1..];
|
||||||
|
let p = Public::from_slice(&e[0..64]);
|
||||||
|
let z = ecdh::agree(secret, &p)?;
|
||||||
|
let mut key = [0u8; 32];
|
||||||
|
kdf(&z, &[0u8; 0], &mut key);
|
||||||
|
|
||||||
|
let ekey = &key[0..16];
|
||||||
|
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
|
||||||
|
|
||||||
|
let clen = encrypted.len() - meta_len;
|
||||||
|
let cipher_with_iv = &e[64..(64+16+clen)];
|
||||||
|
let cipher_iv = &cipher_with_iv[0..16];
|
||||||
|
let cipher_no_iv = &cipher_with_iv[16..];
|
||||||
|
let msg_mac = &e[(64+16+clen)..];
|
||||||
|
|
||||||
|
// Verify tag
|
||||||
|
let mut hmac = hmac::Signer::with(&mkey);
|
||||||
|
hmac.update(cipher_with_iv);
|
||||||
|
hmac.update(auth_data);
|
||||||
|
let mac = hmac.sign();
|
||||||
|
|
||||||
|
if !is_equal(&mac.as_ref()[..], msg_mac) {
|
||||||
|
return Err(Error::InvalidMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut msg = vec![0u8; clen];
|
||||||
|
aes::decrypt_128_ctr(ekey, cipher_iv, cipher_no_iv, &mut msg[..])?;
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kdf(secret: &Secret, s1: &[u8], dest: &mut [u8]) {
|
||||||
|
// SEC/ISO/Shoup specify counter size SHOULD be equivalent
|
||||||
|
// to size of hash output, however, it also notes that
|
||||||
|
// the 4 bytes is okay. NIST specifies 4 bytes.
|
||||||
|
let mut ctr = 1u32;
|
||||||
|
let mut written = 0usize;
|
||||||
|
while written < dest.len() {
|
||||||
|
let mut hasher = digest::Hasher::sha256();
|
||||||
|
let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8];
|
||||||
|
hasher.update(&ctrs);
|
||||||
|
hasher.update(secret);
|
||||||
|
hasher.update(s1);
|
||||||
|
let d = hasher.finish();
|
||||||
|
&mut dest[written..(written + 32)].copy_from_slice(&d);
|
||||||
|
written += 32;
|
||||||
|
ctr += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::ecies;
|
||||||
|
use {Random, Generator};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ecies_shared() {
|
||||||
|
let kp = Random.generate().unwrap();
|
||||||
|
let message = b"So many books, so little time";
|
||||||
|
|
||||||
|
let shared = b"shared";
|
||||||
|
let wrong_shared = b"incorrect";
|
||||||
|
let encrypted = ecies::encrypt(kp.public(), shared, message).unwrap();
|
||||||
|
assert!(encrypted[..] != message[..]);
|
||||||
|
assert_eq!(encrypted[0], 0x04);
|
||||||
|
|
||||||
|
assert!(ecies::decrypt(kp.secret(), wrong_shared, &encrypted).is_err());
|
||||||
|
let decrypted = ecies::decrypt(kp.secret(), shared, &encrypted).unwrap();
|
||||||
|
assert_eq!(decrypted[..message.len()], message[..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::{fmt, error};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Crypto error
|
/// Crypto error
|
||||||
@@ -51,6 +51,12 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Crypto error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Into<String> for Error {
|
impl Into<String> for Error {
|
||||||
fn into(self) -> String {
|
fn into(self) -> String {
|
||||||
format!("{}", self)
|
format!("{}", self)
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Extended keys
|
//! Extended keys
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ impl ExtendedSecret {
|
|||||||
pub fn derive<T>(&self, index: Derivation<T>) -> ExtendedSecret where T: Label {
|
pub fn derive<T>(&self, index: Derivation<T>) -> ExtendedSecret where T: Label {
|
||||||
let (derived_key, next_chain_code) = derivation::private(*self.secret, self.chain_code, index);
|
let (derived_key, next_chain_code) = derivation::private(*self.secret, self.chain_code, index);
|
||||||
|
|
||||||
let derived_secret = Secret::from_slice(&*derived_key);
|
let derived_secret = Secret::from(derived_key.0);
|
||||||
|
|
||||||
ExtendedSecret::with_code(derived_secret, next_chain_code)
|
ExtendedSecret::with_code(derived_secret, next_chain_code)
|
||||||
}
|
}
|
||||||
@@ -207,10 +207,7 @@ impl ExtendedKeyPair {
|
|||||||
// Work is based on BIP0032
|
// Work is based on BIP0032
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||||
mod derivation {
|
mod derivation {
|
||||||
|
use parity_crypto::hmac;
|
||||||
use rcrypto::hmac::Hmac;
|
|
||||||
use rcrypto::mac::Mac;
|
|
||||||
use rcrypto::sha2::Sha512;
|
|
||||||
use ethereum_types::{U256, U512, H512, H256};
|
use ethereum_types::{U256, U512, H512, H256};
|
||||||
use secp256k1::key::{SecretKey, PublicKey};
|
use secp256k1::key::{SecretKey, PublicKey};
|
||||||
use SECP256K1;
|
use SECP256K1;
|
||||||
@@ -243,10 +240,8 @@ mod derivation {
|
|||||||
let private: U256 = private_key.into();
|
let private: U256 = private_key.into();
|
||||||
|
|
||||||
// produces 512-bit derived hmac (I)
|
// produces 512-bit derived hmac (I)
|
||||||
let mut hmac = Hmac::new(Sha512::new(), &*chain_code);
|
let skey = hmac::SigKey::sha512(&*chain_code);
|
||||||
let mut i_512 = [0u8; 64];
|
let i_512 = hmac::sign(&skey, &data[..]);
|
||||||
hmac.input(&data[..]);
|
|
||||||
hmac.raw_result(&mut i_512);
|
|
||||||
|
|
||||||
// left most 256 bits are later added to original private key
|
// left most 256 bits are later added to original private key
|
||||||
let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into();
|
let hmac_key: U256 = H256::from_slice(&i_512[0..32]).into();
|
||||||
@@ -322,10 +317,8 @@ mod derivation {
|
|||||||
index.store(&mut data[33..(33 + T::len())]);
|
index.store(&mut data[33..(33 + T::len())]);
|
||||||
|
|
||||||
// HMAC512SHA produces [derived private(256); new chain code(256)]
|
// HMAC512SHA produces [derived private(256); new chain code(256)]
|
||||||
let mut hmac = Hmac::new(Sha512::new(), &*chain_code);
|
let skey = hmac::SigKey::sha512(&*chain_code);
|
||||||
let mut i_512 = [0u8; 64];
|
let i_512 = hmac::sign(&skey, &data[..]);
|
||||||
hmac.input(&data[..]);
|
|
||||||
hmac.raw_result(&mut i_512);
|
|
||||||
|
|
||||||
let new_private = H256::from(&i_512[0..32]);
|
let new_private = H256::from(&i_512[0..32]);
|
||||||
let new_chain_code = H256::from(&i_512[32..64]);
|
let new_chain_code = H256::from(&i_512[32..64]);
|
||||||
@@ -370,10 +363,8 @@ mod derivation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn seed_pair(seed: &[u8]) -> (H256, H256) {
|
pub fn seed_pair(seed: &[u8]) -> (H256, H256) {
|
||||||
let mut hmac = Hmac::new(Sha512::new(), b"Bitcoin seed");
|
let skey = hmac::SigKey::sha512(b"Bitcoin seed");
|
||||||
let mut i_512 = [0u8; 64];
|
let i_512 = hmac::sign(&skey, seed);
|
||||||
hmac.input(seed);
|
|
||||||
hmac.raw_result(&mut i_512);
|
|
||||||
|
|
||||||
let master_key = H256::from_slice(&i_512[0..32]);
|
let master_key = H256::from_slice(&i_512[0..32]);
|
||||||
let chain_code = H256::from_slice(&i_512[32..64]);
|
let chain_code = H256::from_slice(&i_512[32..64]);
|
||||||
@@ -400,7 +391,7 @@ mod tests {
|
|||||||
|
|
||||||
fn test_extended<F>(f: F, test_private: H256) where F: Fn(ExtendedSecret) -> ExtendedSecret {
|
fn test_extended<F>(f: F, test_private: H256) where F: Fn(ExtendedSecret) -> ExtendedSecret {
|
||||||
let (private_seed, chain_code) = master_chain_basic();
|
let (private_seed, chain_code) = master_chain_basic();
|
||||||
let extended_secret = ExtendedSecret::with_code(Secret::from_slice(&*private_seed), chain_code);
|
let extended_secret = ExtendedSecret::with_code(Secret::from(private_seed.0), chain_code);
|
||||||
let derived = f(extended_secret);
|
let derived = f(extended_secret);
|
||||||
assert_eq!(**derived.as_raw(), test_private);
|
assert_eq!(**derived.as_raw(), test_private);
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tiny_keccak::Keccak;
|
use tiny_keccak::Keccak;
|
||||||
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use secp256k1::key;
|
use secp256k1::key;
|
||||||
@@ -1,41 +1,48 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// #![warn(missing_docs)]
|
// #![warn(missing_docs)]
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate crypto as rcrypto;
|
|
||||||
extern crate edit_distance;
|
extern crate edit_distance;
|
||||||
|
extern crate parity_crypto;
|
||||||
extern crate ethereum_types;
|
extern crate ethereum_types;
|
||||||
|
extern crate memzero;
|
||||||
extern crate parity_wordlist;
|
extern crate parity_wordlist;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate quick_error;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate rustc_hex;
|
extern crate rustc_hex;
|
||||||
extern crate secp256k1;
|
extern crate secp256k1;
|
||||||
|
extern crate serde;
|
||||||
extern crate tiny_keccak;
|
extern crate tiny_keccak;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
mod brain;
|
mod brain;
|
||||||
mod brain_prefix;
|
mod brain_prefix;
|
||||||
mod error;
|
mod error;
|
||||||
mod keypair;
|
mod keypair;
|
||||||
mod keccak;
|
mod keccak;
|
||||||
|
mod password;
|
||||||
mod prefix;
|
mod prefix;
|
||||||
mod random;
|
mod random;
|
||||||
mod signature;
|
mod signature;
|
||||||
@@ -43,6 +50,7 @@ mod secret;
|
|||||||
mod extended;
|
mod extended;
|
||||||
|
|
||||||
pub mod brain_recover;
|
pub mod brain_recover;
|
||||||
|
pub mod crypto;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
|
|
||||||
pub use self::parity_wordlist::Error as WordlistError;
|
pub use self::parity_wordlist::Error as WordlistError;
|
||||||
@@ -51,6 +59,7 @@ pub use self::brain_prefix::BrainPrefix;
|
|||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use self::keypair::{KeyPair, public_to_address};
|
pub use self::keypair::{KeyPair, public_to_address};
|
||||||
pub use self::math::public_is_valid;
|
pub use self::math::public_is_valid;
|
||||||
|
pub use self::password::Password;
|
||||||
pub use self::prefix::Prefix;
|
pub use self::prefix::Prefix;
|
||||||
pub use self::random::Random;
|
pub use self::random::Random;
|
||||||
pub use self::signature::{sign, verify_public, verify_address, recover, Signature};
|
pub use self::signature::{sign, verify_public, verify_address, recover, Signature};
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use super::{SECP256K1, Public, Secret, Error};
|
use super::{SECP256K1, Public, Secret, Error};
|
||||||
use secp256k1::key;
|
use secp256k1::key;
|
||||||
59
accounts/ethkey/src/password.rs
Normal file
59
accounts/ethkey/src/password.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::{fmt, ptr};
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Password(String);
|
||||||
|
|
||||||
|
impl fmt::Debug for Password {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Password(******)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Password {
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.0.as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom drop impl to zero out memory.
|
||||||
|
impl Drop for Password {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
for byte_ref in self.0.as_mut_vec() {
|
||||||
|
ptr::write_volatile(byte_ref, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Password {
|
||||||
|
fn from(s: String) -> Password {
|
||||||
|
Password(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for Password {
|
||||||
|
fn from(s: &'a str) -> Password {
|
||||||
|
Password::from(String::from(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use super::{Random, Generator, KeyPair, Error};
|
use super::{Random, Generator, KeyPair, Error};
|
||||||
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use rand::os::OsRng;
|
use rand::os::OsRng;
|
||||||
use super::{Generator, KeyPair, SECP256K1};
|
use super::{Generator, KeyPair, SECP256K1};
|
||||||
@@ -1,56 +1,72 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use rustc_hex::ToHex;
|
use rustc_hex::ToHex;
|
||||||
|
use secp256k1::constants::{SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE};
|
||||||
use secp256k1::key;
|
use secp256k1::key;
|
||||||
use ethereum_types::H256;
|
use ethereum_types::H256;
|
||||||
|
use memzero::Memzero;
|
||||||
use {Error, SECP256K1};
|
use {Error, SECP256K1};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct Secret {
|
pub struct Secret {
|
||||||
inner: H256,
|
inner: Memzero<H256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToHex for Secret {
|
impl ToHex for Secret {
|
||||||
fn to_hex(&self) -> String {
|
fn to_hex(&self) -> String {
|
||||||
self.inner.to_hex()
|
format!("{:x}", *self.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::LowerHex for Secret {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.inner.fmt(fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Secret {
|
impl fmt::Debug for Secret {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.inner.fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Secret {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(fmt, "Secret: 0x{:x}{:x}..{:x}{:x}", self.inner[0], self.inner[1], self.inner[30], self.inner[31])
|
write!(fmt, "Secret: 0x{:x}{:x}..{:x}{:x}", self.inner[0], self.inner[1], self.inner[30], self.inner[31])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Secret {
|
impl Secret {
|
||||||
pub fn from_slice(key: &[u8]) -> Self {
|
/// Creates a `Secret` from the given slice, returning `None` if the slice length != 32.
|
||||||
assert_eq!(32, key.len(), "Caller should provide 32-byte length slice");
|
pub fn from_slice(key: &[u8]) -> Option<Self> {
|
||||||
|
if key.len() != 32 {
|
||||||
|
return None
|
||||||
|
}
|
||||||
let mut h = H256::default();
|
let mut h = H256::default();
|
||||||
h.copy_from_slice(&key[0..32]);
|
h.copy_from_slice(&key[0..32]);
|
||||||
Secret { inner: h }
|
Some(Secret { inner: Memzero::from(h) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates zero key, which is invalid for crypto operations, but valid for math operation.
|
/// Creates zero key, which is invalid for crypto operations, but valid for math operation.
|
||||||
pub fn zero() -> Self {
|
pub fn zero() -> Self {
|
||||||
Secret { inner: Default::default() }
|
Secret { inner: Memzero::from(H256::default()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Imports and validates the key.
|
/// Imports and validates the key.
|
||||||
@@ -168,7 +184,7 @@ impl Secret {
|
|||||||
if self.is_zero() {
|
if self.is_zero() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
match pow {
|
match pow {
|
||||||
0 => *self = key::ONE_KEY.into(),
|
0 => *self = key::ONE_KEY.into(),
|
||||||
1 => (),
|
1 => (),
|
||||||
@@ -196,9 +212,15 @@ impl FromStr for Secret {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<[u8; 32]> for Secret {
|
||||||
|
fn from(k: [u8; 32]) -> Self {
|
||||||
|
Secret { inner: Memzero::from(H256(k)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<H256> for Secret {
|
impl From<H256> for Secret {
|
||||||
fn from(s: H256) -> Self {
|
fn from(s: H256) -> Self {
|
||||||
Secret::from_slice(&s)
|
s.0.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +232,9 @@ impl From<&'static str> for Secret {
|
|||||||
|
|
||||||
impl From<key::SecretKey> for Secret {
|
impl From<key::SecretKey> for Secret {
|
||||||
fn from(key: key::SecretKey) -> Self {
|
fn from(key: key::SecretKey) -> Self {
|
||||||
Self::from_slice(&key[0..32])
|
let mut a = [0; SECP256K1_SECRET_KEY_SIZE];
|
||||||
|
a.copy_from_slice(&key[0 .. SECP256K1_SECRET_KEY_SIZE]);
|
||||||
|
a.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::cmp::PartialEq;
|
use std::cmp::PartialEq;
|
||||||
21
ethstore/Cargo.toml → accounts/ethstore/Cargo.toml
Executable file → Normal file
21
ethstore/Cargo.toml → accounts/ethstore/Cargo.toml
Executable file → Normal file
@@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ethstore"
|
name = "ethstore"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
log = "0.4"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
rand = "0.4"
|
rand = "0.4"
|
||||||
ethkey = { path = "../ethkey" }
|
ethkey = { path = "../ethkey" }
|
||||||
@@ -12,16 +12,19 @@ serde = "1.0"
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
rust-crypto = "0.2.36"
|
tiny-keccak = "1.4"
|
||||||
tiny-keccak = "1.3"
|
|
||||||
time = "0.1.34"
|
time = "0.1.34"
|
||||||
itertools = "0.5"
|
itertools = "0.5"
|
||||||
parking_lot = "0.5"
|
parking_lot = "0.7"
|
||||||
ethcrypto = { path = "../ethcrypto" }
|
parity-crypto = "0.3.0"
|
||||||
ethereum-types = "0.2"
|
ethereum-types = "0.4"
|
||||||
dir = { path = "../util/dir" }
|
dir = { path = "../../util/dir" }
|
||||||
smallvec = "0.4"
|
smallvec = "0.6"
|
||||||
parity-wordlist = "1.0"
|
parity-wordlist = "1.0"
|
||||||
tempdir = "0.3"
|
tempdir = "0.3"
|
||||||
|
lazy_static = "1.2.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
matches = "0.1"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@@ -1,19 +1,12 @@
|
|||||||
# ethstore
|
## ethstore-cli
|
||||||
|
|
||||||
[![Build Status][travis-image]][travis-url]
|
Parity Ethereum key management.
|
||||||
|
|
||||||
[travis-image]: https://travis-ci.org/paritytech/ethstore.svg?branch=master
|
|
||||||
[travis-url]: https://travis-ci.org/paritytech/ethstore
|
|
||||||
|
|
||||||
Ethereum key management.
|
|
||||||
|
|
||||||
[Documentation](http://paritytech.github.io/ethstore/ethstore/index.html)
|
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
Ethereum key management.
|
Parity Ethereum key management tool.
|
||||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||||
@@ -35,19 +28,19 @@ Usage:
|
|||||||
Options:
|
Options:
|
||||||
-h, --help Display this message and exit.
|
-h, --help Display this message and exit.
|
||||||
--dir DIR Specify the secret store directory. It may be either
|
--dir DIR Specify the secret store directory. It may be either
|
||||||
parity, parity-test, geth, geth-test
|
parity, parity-(chain), geth, geth-test
|
||||||
or a path [default: parity].
|
or a path [default: parity].
|
||||||
--vault VAULT Specify vault to use in this operation.
|
--vault VAULT Specify vault to use in this operation.
|
||||||
--vault-pwd VAULTPWD Specify vault password to use in this operation. Please note
|
--vault-pwd VAULTPWD Specify vault password to use in this operation. Please note
|
||||||
that this option is required when vault option is set.
|
that this option is required when vault option is set.
|
||||||
Otherwise it is ignored.
|
Otherwise it is ignored.
|
||||||
--src DIR Specify import source. It may be either
|
--src DIR Specify import source. It may be either
|
||||||
parity, parity-test, get, geth-test
|
parity, parity-(chain), geth, geth-test
|
||||||
or a path [default: geth].
|
or a path [default: geth].
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
insert Save account with password.
|
insert Save account with password.
|
||||||
change-pwd Change account password.
|
change-pwd Change password.
|
||||||
list List accounts.
|
list List accounts.
|
||||||
import Import accounts from src.
|
import Import accounts from src.
|
||||||
import-wallet Import presale wallet.
|
import-wallet Import presale wallet.
|
||||||
@@ -59,7 +52,7 @@ Commands:
|
|||||||
create-vault Create new vault.
|
create-vault Create new vault.
|
||||||
change-vault-pwd Change vault password.
|
change-vault-pwd Change vault password.
|
||||||
move-to-vault Move account to vault from another vault/root directory.
|
move-to-vault Move account to vault from another vault/root directory.
|
||||||
move-from-vault Move account to root directory from given vault or root.
|
move-from-vault Move account to root directory from given vault.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
@@ -337,11 +330,11 @@ ethstore move-from-vault 00e63fdb87ceb815ec96ae185b8f7381a0b4a5ea vault1 vault1_
|
|||||||
OK
|
OK
|
||||||
```
|
```
|
||||||
|
|
||||||
--
|
## Parity Ethereum toolchain
|
||||||
|
_This project is a part of the Parity Ethereum toolchain._
|
||||||
|
|
||||||
# Parity toolchain
|
- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum.
|
||||||
*this project is a part of the parity toolchain*
|
- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding.
|
||||||
|
- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management.
|
||||||
- [**ethkey**](https://github.com/paritytech/ethkey) - Ethereum keys generator and signer.
|
- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator.
|
||||||
- [**ethstore**](https://github.com/paritytech/ethstore) - Ethereum key management.
|
- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC.
|
||||||
- [**ethabi**](https://github.com/paritytech/ethabi) - Ethereum function calls encoding.
|
|
||||||
@@ -1,20 +1,24 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ethstore-cli"
|
name = "ethstore-cli"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
docopt = "0.8"
|
docopt = "1.0"
|
||||||
|
env_logger = "0.5"
|
||||||
num_cpus = "1.6"
|
num_cpus = "1.6"
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
parking_lot = "0.5"
|
parking_lot = "0.7"
|
||||||
ethstore = { path = "../" }
|
ethstore = { path = "../" }
|
||||||
dir = { path = '../../util/dir' }
|
dir = { path = '../../../util/dir' }
|
||||||
panic_hook = { path = "../../util/panic_hook" }
|
panic_hook = { path = "../../../util/panic-hook" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "ethstore"
|
name = "ethstore"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempdir = "0.3.5"
|
||||||
66
accounts/ethstore/cli/src/crack.rs
Normal file
66
accounts/ethstore/cli/src/crack.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::{cmp, thread};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
use ethstore::{ethkey::Password, PresaleWallet, Error};
|
||||||
|
use num_cpus;
|
||||||
|
|
||||||
|
pub fn run(passwords: VecDeque<Password>, wallet_path: &str) -> Result<(), Error> {
|
||||||
|
let passwords = Arc::new(Mutex::new(passwords));
|
||||||
|
|
||||||
|
let mut handles = Vec::new();
|
||||||
|
|
||||||
|
for _ in 0..num_cpus::get() {
|
||||||
|
let passwords = passwords.clone();
|
||||||
|
let wallet = PresaleWallet::open(&wallet_path)?;
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
|
look_for_password(passwords, wallet);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_for_password(passwords: Arc<Mutex<VecDeque<Password>>>, wallet: PresaleWallet) {
|
||||||
|
let mut counter = 0;
|
||||||
|
while !passwords.lock().is_empty() {
|
||||||
|
let package = {
|
||||||
|
let mut passwords = passwords.lock();
|
||||||
|
let len = passwords.len();
|
||||||
|
passwords.split_off(cmp::min(len, 32))
|
||||||
|
};
|
||||||
|
for pass in package {
|
||||||
|
counter += 1;
|
||||||
|
match wallet.decrypt(&pass) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Found password: {}", pass.as_str());
|
||||||
|
passwords.lock().clear();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
_ if counter % 100 == 0 => print!("."),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
extern crate dir;
|
extern crate dir;
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
@@ -23,6 +23,8 @@ extern crate parking_lot;
|
|||||||
extern crate rustc_hex;
|
extern crate rustc_hex;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
|
extern crate env_logger;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
@@ -32,20 +34,20 @@ use std::{env, process, fs, fmt};
|
|||||||
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory};
|
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory};
|
||||||
use ethstore::ethkey::Address;
|
use ethstore::ethkey::{Address, Password};
|
||||||
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef};
|
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef};
|
||||||
|
|
||||||
mod crack;
|
mod crack;
|
||||||
|
|
||||||
pub const USAGE: &'static str = r#"
|
pub const USAGE: &'static str = r#"
|
||||||
Ethereum key management.
|
Parity Ethereum key management tool.
|
||||||
Copyright 2016, 2017 Parity Technologies (UK) Ltd
|
Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
ethstore insert <secret> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||||
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
ethstore change-pwd <address> <old-pwd> <new-pwd> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||||
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
ethstore list [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||||
ethstore import [--src DIR] [--dir DIR]
|
ethstore import [<password>] [--src DIR] [--dir DIR]
|
||||||
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
ethstore import-wallet <path> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||||
ethstore find-wallet-pass <path> <password>
|
ethstore find-wallet-pass <path> <password>
|
||||||
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
ethstore remove <address> <password> [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD]
|
||||||
@@ -68,7 +70,7 @@ Options:
|
|||||||
that this option is required when vault option is set.
|
that this option is required when vault option is set.
|
||||||
Otherwise it is ignored.
|
Otherwise it is ignored.
|
||||||
--src DIR Specify import source. It may be either
|
--src DIR Specify import source. It may be either
|
||||||
parity, parity-(chain), get, geth-test
|
parity, parity-(chain), geth, geth-test
|
||||||
or a path [default: geth].
|
or a path [default: geth].
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
@@ -145,30 +147,35 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
panic_hook::set();
|
panic_hook::set_abort();
|
||||||
|
if env::var("RUST_LOG").is_err() {
|
||||||
|
env::set_var("RUST_LOG", "warn")
|
||||||
|
}
|
||||||
|
env_logger::try_init().expect("Logger initialized only once.");
|
||||||
|
|
||||||
match execute(env::args()) {
|
match execute(env::args()) {
|
||||||
Ok(result) => println!("{}", result),
|
Ok(result) => println!("{}", result),
|
||||||
|
Err(Error::Docopt(ref e)) => e.exit(),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("{}", err);
|
eprintln!("{}", err);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_dir(location: &str) -> Result<Box<KeyDirectory>, Error> {
|
fn key_dir(location: &str, password: Option<Password>) -> Result<Box<KeyDirectory>, Error> {
|
||||||
let dir: Box<KeyDirectory> = match location {
|
let dir: RootDiskDirectory = match location {
|
||||||
"geth" => Box::new(RootDiskDirectory::create(dir::geth(false))?),
|
"geth" => RootDiskDirectory::create(dir::geth(false))?,
|
||||||
"geth-test" => Box::new(RootDiskDirectory::create(dir::geth(true))?),
|
"geth-test" => RootDiskDirectory::create(dir::geth(true))?,
|
||||||
path if path.starts_with("parity") => {
|
path if path.starts_with("parity") => {
|
||||||
let chain = path.split('-').nth(1).unwrap_or("ethereum");
|
let chain = path.split('-').nth(1).unwrap_or("ethereum");
|
||||||
let path = dir::parity(chain);
|
let path = dir::parity(chain);
|
||||||
Box::new(RootDiskDirectory::create(path)?)
|
RootDiskDirectory::create(path)?
|
||||||
},
|
},
|
||||||
path => Box::new(RootDiskDirectory::create(path)?),
|
path => RootDiskDirectory::create(path)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(dir)
|
Ok(Box::new(dir.with_password(password)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Error> {
|
fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Error> {
|
||||||
@@ -191,7 +198,7 @@ fn open_args_vault_account(store: &EthStore, address: Address, args: &Args) -> R
|
|||||||
fn format_accounts(accounts: &[Address]) -> String {
|
fn format_accounts(accounts: &[Address]) -> String {
|
||||||
accounts.iter()
|
accounts.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, a)| format!("{:2}: 0x{:?}", i, a))
|
.map(|(i, a)| format!("{:2}: 0x{:x}", i, a))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("\n")
|
.join("\n")
|
||||||
}
|
}
|
||||||
@@ -200,27 +207,27 @@ fn format_vaults(vaults: &[String]) -> String {
|
|||||||
vaults.join("\n")
|
vaults.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_password(path: &str) -> Result<String, Error> {
|
fn load_password(path: &str) -> Result<Password, Error> {
|
||||||
let mut file = fs::File::open(path).map_err(|e| ethstore::Error::Custom(format!("Error opening password file {}: {}", path, e)))?;
|
let mut file = fs::File::open(path).map_err(|e| ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e)))?;
|
||||||
let mut password = String::new();
|
let mut password = String::new();
|
||||||
file.read_to_string(&mut password).map_err(|e| ethstore::Error::Custom(format!("Error reading password file {}: {}", path, e)))?;
|
file.read_to_string(&mut password).map_err(|e| ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e)))?;
|
||||||
// drop EOF
|
// drop EOF
|
||||||
let _ = password.pop();
|
let _ = password.pop();
|
||||||
Ok(password)
|
Ok(password.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
|
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
|
||||||
let args: Args = Docopt::new(USAGE)
|
let args: Args = Docopt::new(USAGE)
|
||||||
.and_then(|d| d.argv(command).deserialize())?;
|
.and_then(|d| d.argv(command).deserialize())?;
|
||||||
|
|
||||||
let store = EthStore::open(key_dir(&args.flag_dir)?)?;
|
let store = EthStore::open(key_dir(&args.flag_dir, None)?)?;
|
||||||
|
|
||||||
return if args.cmd_insert {
|
return if args.cmd_insert {
|
||||||
let secret = args.arg_secret.parse().map_err(|_| ethstore::Error::InvalidSecret)?;
|
let secret = args.arg_secret.parse().map_err(|_| ethstore::Error::InvalidSecret)?;
|
||||||
let password = load_password(&args.arg_password)?;
|
let password = load_password(&args.arg_password)?;
|
||||||
let vault_ref = open_args_vault(&store, &args)?;
|
let vault_ref = open_args_vault(&store, &args)?;
|
||||||
let account_ref = store.insert_account(vault_ref, secret, &password)?;
|
let account_ref = store.insert_account(vault_ref, secret, &password)?;
|
||||||
Ok(format!("0x{:?}", account_ref.address))
|
Ok(format!("0x{:x}", account_ref.address))
|
||||||
} else if args.cmd_change_pwd {
|
} else if args.cmd_change_pwd {
|
||||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||||
let old_pwd = load_password(&args.arg_old_pwd)?;
|
let old_pwd = load_password(&args.arg_old_pwd)?;
|
||||||
@@ -238,8 +245,13 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
.collect();
|
.collect();
|
||||||
Ok(format_accounts(&accounts))
|
Ok(format_accounts(&accounts))
|
||||||
} else if args.cmd_import {
|
} else if args.cmd_import {
|
||||||
let src = key_dir(&args.flag_src)?;
|
let password = match args.arg_password.as_ref() {
|
||||||
let dst = key_dir(&args.flag_dir)?;
|
"" => None,
|
||||||
|
_ => Some(load_password(&args.arg_password)?)
|
||||||
|
};
|
||||||
|
let src = key_dir(&args.flag_src, password)?;
|
||||||
|
let dst = key_dir(&args.flag_dir, None)?;
|
||||||
|
|
||||||
let accounts = import_accounts(&*src, &*dst)?;
|
let accounts = import_accounts(&*src, &*dst)?;
|
||||||
Ok(format_accounts(&accounts))
|
Ok(format_accounts(&accounts))
|
||||||
} else if args.cmd_import_wallet {
|
} else if args.cmd_import_wallet {
|
||||||
@@ -248,10 +260,10 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
let kp = wallet.decrypt(&password)?;
|
let kp = wallet.decrypt(&password)?;
|
||||||
let vault_ref = open_args_vault(&store, &args)?;
|
let vault_ref = open_args_vault(&store, &args)?;
|
||||||
let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
|
let account_ref = store.insert_account(vault_ref, kp.secret().clone(), &password)?;
|
||||||
Ok(format!("0x{:?}", account_ref.address))
|
Ok(format!("0x{:x}", account_ref.address))
|
||||||
} else if args.cmd_find_wallet_pass {
|
} else if args.cmd_find_wallet_pass {
|
||||||
let passwords = load_password(&args.arg_password)?;
|
let passwords = load_password(&args.arg_password)?;
|
||||||
let passwords = passwords.lines().map(str::to_owned).collect::<VecDeque<_>>();
|
let passwords = passwords.as_str().lines().map(|line| str::to_owned(line).into()).collect::<VecDeque<_>>();
|
||||||
crack::run(passwords, &args.arg_path)?;
|
crack::run(passwords, &args.arg_path)?;
|
||||||
Ok(format!("Password not found."))
|
Ok(format!("Password not found."))
|
||||||
} else if args.cmd_remove {
|
} else if args.cmd_remove {
|
||||||
@@ -266,13 +278,13 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
let password = load_password(&args.arg_password)?;
|
let password = load_password(&args.arg_password)?;
|
||||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||||
let signature = store.sign(&account_ref, &password, &message)?;
|
let signature = store.sign(&account_ref, &password, &message)?;
|
||||||
Ok(format!("0x{:?}", signature))
|
Ok(format!("0x{}", signature))
|
||||||
} else if args.cmd_public {
|
} else if args.cmd_public {
|
||||||
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
|
||||||
let password = load_password(&args.arg_password)?;
|
let password = load_password(&args.arg_password)?;
|
||||||
let account_ref = open_args_vault_account(&store, address, &args)?;
|
let account_ref = open_args_vault_account(&store, address, &args)?;
|
||||||
let public = store.public(&account_ref, &password)?;
|
let public = store.public(&account_ref, &password)?;
|
||||||
Ok(format!("0x{:?}", public))
|
Ok(format!("0x{:x}", public))
|
||||||
} else if args.cmd_list_vaults {
|
} else if args.cmd_list_vaults {
|
||||||
let vaults = store.list_vaults()?;
|
let vaults = store.list_vaults()?;
|
||||||
Ok(format_vaults(&vaults))
|
Ok(format_vaults(&vaults))
|
||||||
82
accounts/ethstore/cli/tests/cli.rs
Normal file
82
accounts/ethstore/cli/tests/cli.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
extern crate tempdir;
|
||||||
|
use std::process::Command;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
fn run(args: &[&str]) -> String {
|
||||||
|
let output = Command::new("cargo")
|
||||||
|
.args(&["run", "--"])
|
||||||
|
.args(args)
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert!(output.status.success());
|
||||||
|
String::from_utf8(output.stdout).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cli_cmd() {
|
||||||
|
Command::new("cargo")
|
||||||
|
.arg("build")
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let dir = TempDir::new("test-vault").unwrap();
|
||||||
|
|
||||||
|
let mut passwd = File::create(dir.path().join("test-password")).unwrap();
|
||||||
|
writeln!(passwd, "password").unwrap();
|
||||||
|
|
||||||
|
let mut passwd2 = File::create(dir.path().join("test-vault-addr")).unwrap();
|
||||||
|
writeln!(passwd2, "password2").unwrap();
|
||||||
|
|
||||||
|
let test_password_buf = dir.path().join("test-password");
|
||||||
|
let test_password: &str = test_password_buf.to_str().unwrap();
|
||||||
|
let dir_str: &str = dir.path().to_str().unwrap();
|
||||||
|
let test_vault_addr_buf = dir.path().join("test-vault-addr");
|
||||||
|
let test_vault_addr = test_vault_addr_buf.to_str().unwrap();
|
||||||
|
|
||||||
|
run(&["create-vault", "test-vault", test_password, "--dir", dir_str]);
|
||||||
|
|
||||||
|
let output = run(&["insert", "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
|
||||||
|
test_vault_addr,
|
||||||
|
"--dir", dir_str,
|
||||||
|
"--vault", "test-vault",
|
||||||
|
"--vault-pwd", test_password]);
|
||||||
|
let address = output.trim();
|
||||||
|
|
||||||
|
let output = run(&["list",
|
||||||
|
"--dir", dir_str,
|
||||||
|
"--vault", "test-vault",
|
||||||
|
"--vault-pwd", test_password]);
|
||||||
|
assert_eq!(output, " 0: 0xa8fa5dd30a87bb9e3288d604eb74949c515ab66e\n");
|
||||||
|
|
||||||
|
let output = run(&["sign", &address[2..],
|
||||||
|
test_vault_addr,
|
||||||
|
"7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
|
||||||
|
"--dir", dir_str,
|
||||||
|
"--vault", "test-vault",
|
||||||
|
"--vault-pwd", test_password]);
|
||||||
|
assert_eq!(output, "0x54ab6e5cf0c5cb40043fdca5d15d611a3a94285414a076dafecc8dc9c04183f413296a3defff61092c0bb478dc9887ec01070e1275234211208fb8f4be4a9b0101\n");
|
||||||
|
|
||||||
|
let output = run(&["public", &address[2..], test_vault_addr,
|
||||||
|
"--dir", dir_str,
|
||||||
|
"--vault", "test-vault",
|
||||||
|
"--vault-pwd", test_password]);
|
||||||
|
assert_eq!(output, "0x35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418\n");
|
||||||
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use json;
|
use json;
|
||||||
|
|
||||||
75
ethstore/src/account/crypto.rs → accounts/ethstore/src/account/crypto.rs
Executable file → Normal file
75
ethstore/src/account/crypto.rs → accounts/ethstore/src/account/crypto.rs
Executable file → Normal file
@@ -1,21 +1,22 @@
|
|||||||
// Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use ethkey::Secret;
|
use std::num::NonZeroU32;
|
||||||
|
use ethkey::{Password, Secret};
|
||||||
use {json, Error, crypto};
|
use {json, Error, crypto};
|
||||||
use crypto::Keccak256;
|
use crypto::Keccak256;
|
||||||
use random::Random;
|
use random::Random;
|
||||||
@@ -73,18 +74,19 @@ impl From<Crypto> for String {
|
|||||||
|
|
||||||
impl Crypto {
|
impl Crypto {
|
||||||
/// Encrypt account secret
|
/// Encrypt account secret
|
||||||
pub fn with_secret(secret: &Secret, password: &str, iterations: u32) -> Self {
|
pub fn with_secret(secret: &Secret, password: &Password, iterations: NonZeroU32) -> Result<Self, crypto::Error> {
|
||||||
Crypto::with_plain(&*secret, password, iterations)
|
Crypto::with_plain(&*secret, password, iterations)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt custom plain data
|
/// Encrypt custom plain data
|
||||||
pub fn with_plain(plain: &[u8], password: &str, iterations: u32) -> Self {
|
pub fn with_plain(plain: &[u8], password: &Password, iterations: NonZeroU32) -> Result<Self, crypto::Error> {
|
||||||
let salt: [u8; 32] = Random::random();
|
let salt: [u8; 32] = Random::random();
|
||||||
let iv: [u8; 16] = Random::random();
|
let iv: [u8; 16] = Random::random();
|
||||||
|
|
||||||
// two parts of derived key
|
// two parts of derived key
|
||||||
// DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits]
|
// DK = [ DK[0..15] DK[16..31] ] = [derived_left_bits, derived_right_bits]
|
||||||
let (derived_left_bits, derived_right_bits) = crypto::derive_key_iterations(password, &salt, iterations);
|
let (derived_left_bits, derived_right_bits) =
|
||||||
|
crypto::derive_key_iterations(password.as_bytes(), &salt, iterations);
|
||||||
|
|
||||||
// preallocated (on-stack in case of `Secret`) buffer to hold cipher
|
// preallocated (on-stack in case of `Secret`) buffer to hold cipher
|
||||||
// length = length(plain) as we are using CTR-approach
|
// length = length(plain) as we are using CTR-approach
|
||||||
@@ -92,28 +94,28 @@ impl Crypto {
|
|||||||
let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]);
|
let mut ciphertext: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; plain_len]);
|
||||||
|
|
||||||
// aes-128-ctr with initial vector of iv
|
// aes-128-ctr with initial vector of iv
|
||||||
crypto::aes::encrypt(&derived_left_bits, &iv, plain, &mut *ciphertext);
|
crypto::aes::encrypt_128_ctr(&derived_left_bits, &iv, plain, &mut *ciphertext)?;
|
||||||
|
|
||||||
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
|
// KECCAK(DK[16..31] ++ <ciphertext>), where DK[16..31] - derived_right_bits
|
||||||
let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256();
|
let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256();
|
||||||
|
|
||||||
Crypto {
|
Ok(Crypto {
|
||||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||||
iv: iv,
|
iv: iv,
|
||||||
}),
|
}),
|
||||||
ciphertext: ciphertext.into_vec(),
|
ciphertext: ciphertext.into_vec(),
|
||||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||||
dklen: crypto::KEY_LENGTH as u32,
|
dklen: crypto::KEY_LENGTH as u32,
|
||||||
salt: salt,
|
salt: salt.to_vec(),
|
||||||
c: iterations,
|
c: iterations,
|
||||||
prf: Prf::HmacSha256,
|
prf: Prf::HmacSha256,
|
||||||
}),
|
}),
|
||||||
mac: mac,
|
mac: mac,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to decrypt and convert result to account secret
|
/// Try to decrypt and convert result to account secret
|
||||||
pub fn secret(&self, password: &str) -> Result<Secret, Error> {
|
pub fn secret(&self, password: &Password) -> Result<Secret, Error> {
|
||||||
if self.ciphertext.len() > 32 {
|
if self.ciphertext.len() > 32 {
|
||||||
return Err(Error::InvalidSecret);
|
return Err(Error::InvalidSecret);
|
||||||
}
|
}
|
||||||
@@ -123,21 +125,21 @@ impl Crypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try to decrypt and return result as is
|
/// Try to decrypt and return result as is
|
||||||
pub fn decrypt(&self, password: &str) -> Result<Vec<u8>, Error> {
|
pub fn decrypt(&self, password: &Password) -> Result<Vec<u8>, Error> {
|
||||||
let expected_len = self.ciphertext.len();
|
let expected_len = self.ciphertext.len();
|
||||||
self.do_decrypt(password, expected_len)
|
self.do_decrypt(password, expected_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_decrypt(&self, password: &str, expected_len: usize) -> Result<Vec<u8>, Error> {
|
fn do_decrypt(&self, password: &Password, expected_len: usize) -> Result<Vec<u8>, Error> {
|
||||||
let (derived_left_bits, derived_right_bits) = match self.kdf {
|
let (derived_left_bits, derived_right_bits) = match self.kdf {
|
||||||
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password, ¶ms.salt, params.c),
|
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password.as_bytes(), ¶ms.salt, params.c),
|
||||||
Kdf::Scrypt(ref params) => crypto::derive_key_scrypt(password, ¶ms.salt, params.n, params.p, params.r)?,
|
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password.as_bytes(), ¶ms.salt, params.n, params.p, params.r)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
|
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
|
||||||
|
|
||||||
if mac != self.mac {
|
if !crypto::is_equal(&mac, &self.mac) {
|
||||||
return Err(Error::InvalidPassword);
|
return Err(Error::InvalidPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
|
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
|
||||||
@@ -148,7 +150,7 @@ impl Crypto {
|
|||||||
debug_assert!(expected_len >= self.ciphertext.len());
|
debug_assert!(expected_len >= self.ciphertext.len());
|
||||||
|
|
||||||
let from = expected_len - self.ciphertext.len();
|
let from = expected_len - self.ciphertext.len();
|
||||||
crypto::aes::decrypt(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut plain[from..]);
|
crypto::aes::decrypt_128_ctr(&derived_left_bits, ¶ms.iv, &self.ciphertext, &mut plain[from..])?;
|
||||||
Ok(plain.into_iter().collect())
|
Ok(plain.into_iter().collect())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -158,45 +160,52 @@ impl Crypto {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ethkey::{Generator, Random};
|
use ethkey::{Generator, Random};
|
||||||
use super::Crypto;
|
use super::{Crypto, Error, NonZeroU32};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn crypto_with_secret_create() {
|
fn crypto_with_secret_create() {
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240);
|
let passwd = "this is sparta".into();
|
||||||
let secret = crypto.secret("this is sparta").unwrap();
|
let crypto = Crypto::with_secret(keypair.secret(), &passwd, *ITERATIONS).unwrap();
|
||||||
|
let secret = crypto.secret(&passwd).unwrap();
|
||||||
assert_eq!(keypair.secret(), &secret);
|
assert_eq!(keypair.secret(), &secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn crypto_with_secret_invalid_password() {
|
fn crypto_with_secret_invalid_password() {
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let crypto = Crypto::with_secret(keypair.secret(), "this is sparta", 10240);
|
let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap();
|
||||||
let _ = crypto.secret("this is sparta!").unwrap();
|
assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn crypto_with_null_plain_data() {
|
fn crypto_with_null_plain_data() {
|
||||||
let original_data = b"";
|
let original_data = b"";
|
||||||
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240);
|
let passwd = "this is sparta".into();
|
||||||
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
|
let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap();
|
||||||
|
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||||
assert_eq!(original_data[..], *decrypted_data);
|
assert_eq!(original_data[..], *decrypted_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn crypto_with_tiny_plain_data() {
|
fn crypto_with_tiny_plain_data() {
|
||||||
let original_data = b"{}";
|
let original_data = b"{}";
|
||||||
let crypto = Crypto::with_plain(&original_data[..], "this is sparta", 10240);
|
let passwd = "this is sparta".into();
|
||||||
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
|
let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap();
|
||||||
|
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||||
assert_eq!(original_data[..], *decrypted_data);
|
assert_eq!(original_data[..], *decrypted_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn crypto_with_huge_plain_data() {
|
fn crypto_with_huge_plain_data() {
|
||||||
let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect();
|
let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect();
|
||||||
let crypto = Crypto::with_plain(&original_data, "this is sparta", 10240);
|
let passwd = "this is sparta".into();
|
||||||
let decrypted_data = crypto.decrypt("this is sparta").unwrap();
|
let crypto = Crypto::with_plain(&original_data, &passwd, *ITERATIONS).unwrap();
|
||||||
|
let decrypted_data = crypto.decrypt(&passwd).unwrap();
|
||||||
assert_eq!(&original_data, &decrypted_data);
|
assert_eq!(&original_data, &decrypted_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,21 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use json;
|
use json;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Prf {
|
pub enum Prf {
|
||||||
@@ -23,10 +24,10 @@ pub enum Prf {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Pbkdf2 {
|
pub struct Pbkdf2 {
|
||||||
pub c: u32,
|
pub c: NonZeroU32,
|
||||||
pub dklen: u32,
|
pub dklen: u32,
|
||||||
pub prf: Prf,
|
pub prf: Prf,
|
||||||
pub salt: [u8; 32],
|
pub salt: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@@ -35,7 +36,7 @@ pub struct Scrypt {
|
|||||||
pub p: u32,
|
pub p: u32,
|
||||||
pub n: u32,
|
pub n: u32,
|
||||||
pub r: u32,
|
pub r: u32,
|
||||||
pub salt: [u8; 32],
|
pub salt: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
11
ethstore/src/account/mod.rs → accounts/ethstore/src/account/mod.rs
Executable file → Normal file
11
ethstore/src/account/mod.rs → accounts/ethstore/src/account/mod.rs
Executable file → Normal file
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
mod cipher;
|
mod cipher;
|
||||||
mod crypto;
|
mod crypto;
|
||||||
@@ -25,4 +25,3 @@ pub use self::crypto::Crypto;
|
|||||||
pub use self::kdf::{Kdf, Pbkdf2, Scrypt, Prf};
|
pub use self::kdf::{Kdf, Pbkdf2, Scrypt, Prf};
|
||||||
pub use self::safe_account::SafeAccount;
|
pub use self::safe_account::SafeAccount;
|
||||||
pub use self::version::Version;
|
pub use self::version::Version;
|
||||||
|
|
||||||
129
ethstore/src/account/safe_account.rs → accounts/ethstore/src/account/safe_account.rs
Executable file → Normal file
129
ethstore/src/account/safe_account.rs → accounts/ethstore/src/account/safe_account.rs
Executable file → Normal file
@@ -1,24 +1,26 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use ethkey::{KeyPair, sign, Address, Signature, Message, Public, Secret};
|
use ethkey::{self, KeyPair, sign, Address, Password, Signature, Message, Public, Secret};
|
||||||
use crypto::ecdh::agree;
|
use ethkey::crypto::ecdh::agree;
|
||||||
use {json, Error, crypto};
|
use {json, Error};
|
||||||
use account::Version;
|
use account::Version;
|
||||||
|
use crypto;
|
||||||
use super::crypto::Crypto;
|
use super::crypto::Crypto;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
/// Account representation.
|
/// Account representation.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@@ -44,7 +46,7 @@ impl Into<json::KeyFile> for SafeAccount {
|
|||||||
json::KeyFile {
|
json::KeyFile {
|
||||||
id: From::from(self.id),
|
id: From::from(self.id),
|
||||||
version: self.version.into(),
|
version: self.version.into(),
|
||||||
address: self.address.into(),
|
address: Some(self.address.into()),
|
||||||
crypto: self.crypto.into(),
|
crypto: self.crypto.into(),
|
||||||
name: Some(self.name.into()),
|
name: Some(self.name.into()),
|
||||||
meta: Some(self.meta.into()),
|
meta: Some(self.meta.into()),
|
||||||
@@ -57,64 +59,91 @@ impl SafeAccount {
|
|||||||
pub fn create(
|
pub fn create(
|
||||||
keypair: &KeyPair,
|
keypair: &KeyPair,
|
||||||
id: [u8; 16],
|
id: [u8; 16],
|
||||||
password: &str,
|
password: &Password,
|
||||||
iterations: u32,
|
iterations: NonZeroU32,
|
||||||
name: String,
|
name: String,
|
||||||
meta: String
|
meta: String
|
||||||
) -> Self {
|
) -> Result<Self, crypto::Error> {
|
||||||
SafeAccount {
|
Ok(SafeAccount {
|
||||||
id: id,
|
id: id,
|
||||||
version: Version::V3,
|
version: Version::V3,
|
||||||
crypto: Crypto::with_secret(keypair.secret(), password, iterations),
|
crypto: Crypto::with_secret(keypair.secret(), password, iterations)?,
|
||||||
address: keypair.address(),
|
address: keypair.address(),
|
||||||
filename: None,
|
filename: None,
|
||||||
name: name,
|
name: name,
|
||||||
meta: meta,
|
meta: meta,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `SafeAccount` from the given `json`; if it was read from a
|
/// Create a new `SafeAccount` from the given `json`; if it was read from a
|
||||||
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
||||||
/// can be left `None`.
|
/// can be left `None`.
|
||||||
pub fn from_file(json: json::KeyFile, filename: Option<String>) -> Self {
|
/// In case `password` is provided, we will attempt to read the secret from the keyfile
|
||||||
SafeAccount {
|
/// and derive the address from it instead of reading it directly.
|
||||||
|
/// Providing password is required for `json::KeyFile`s with no address.
|
||||||
|
pub fn from_file(json: json::KeyFile, filename: Option<String>, password: &Option<Password>) -> Result<Self, Error> {
|
||||||
|
let crypto = Crypto::from(json.crypto);
|
||||||
|
let address = match (password, &json.address) {
|
||||||
|
(None, Some(json_address)) => json_address.into(),
|
||||||
|
(None, None) => Err(Error::Custom(
|
||||||
|
"This keystore does not contain address. You need to provide password to import it".into()))?,
|
||||||
|
(Some(password), json_address) => {
|
||||||
|
let derived_address = KeyPair::from_secret(
|
||||||
|
crypto.secret(&password).map_err(|_| Error::InvalidPassword)?
|
||||||
|
)?.address();
|
||||||
|
|
||||||
|
match json_address {
|
||||||
|
Some(json_address) => {
|
||||||
|
let json_address = json_address.into();
|
||||||
|
if derived_address != json_address {
|
||||||
|
warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}",
|
||||||
|
derived_address, json_address);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
derived_address
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(SafeAccount {
|
||||||
id: json.id.into(),
|
id: json.id.into(),
|
||||||
version: json.version.into(),
|
version: json.version.into(),
|
||||||
address: json.address.into(),
|
address,
|
||||||
crypto: json.crypto.into(),
|
crypto,
|
||||||
filename: filename,
|
filename,
|
||||||
name: json.name.unwrap_or(String::new()),
|
name: json.name.unwrap_or(String::new()),
|
||||||
meta: json.meta.unwrap_or("{}".to_owned()),
|
meta: json.meta.unwrap_or("{}".to_owned()),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `SafeAccount` from the given vault `json`; if it was read from a
|
/// Create a new `SafeAccount` from the given vault `json`; if it was read from a
|
||||||
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
||||||
/// can be left `None`.
|
/// can be left `None`.
|
||||||
pub fn from_vault_file(password: &str, json: json::VaultKeyFile, filename: Option<String>) -> Result<Self, Error> {
|
pub fn from_vault_file(password: &Password, json: json::VaultKeyFile, filename: Option<String>) -> Result<Self, Error> {
|
||||||
let meta_crypto: Crypto = json.metacrypto.into();
|
let meta_crypto: Crypto = json.metacrypto.into();
|
||||||
let meta_plain = meta_crypto.decrypt(password)?;
|
let meta_plain = meta_crypto.decrypt(password)?;
|
||||||
let meta_plain = json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
let meta_plain = json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||||
|
|
||||||
Ok(SafeAccount::from_file(json::KeyFile {
|
SafeAccount::from_file(json::KeyFile {
|
||||||
id: json.id,
|
id: json.id,
|
||||||
version: json.version,
|
version: json.version,
|
||||||
crypto: json.crypto,
|
crypto: json.crypto,
|
||||||
address: meta_plain.address,
|
address: Some(meta_plain.address),
|
||||||
name: meta_plain.name,
|
name: meta_plain.name,
|
||||||
meta: meta_plain.meta,
|
meta: meta_plain.meta,
|
||||||
}, filename))
|
}, filename, &None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `VaultKeyFile` from the given `self`
|
/// Create a new `VaultKeyFile` from the given `self`
|
||||||
pub fn into_vault_file(self, iterations: u32, password: &str) -> Result<json::VaultKeyFile, Error> {
|
pub fn into_vault_file(self, iterations: NonZeroU32, password: &Password) -> Result<json::VaultKeyFile, Error> {
|
||||||
let meta_plain = json::VaultKeyMeta {
|
let meta_plain = json::VaultKeyMeta {
|
||||||
address: self.address.into(),
|
address: self.address.into(),
|
||||||
name: Some(self.name),
|
name: Some(self.name),
|
||||||
meta: Some(self.meta),
|
meta: Some(self.meta),
|
||||||
};
|
};
|
||||||
let meta_plain = meta_plain.write().map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
let meta_plain = meta_plain.write().map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||||
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations);
|
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?;
|
||||||
|
|
||||||
Ok(json::VaultKeyFile {
|
Ok(json::VaultKeyFile {
|
||||||
id: self.id.into(),
|
id: self.id.into(),
|
||||||
@@ -125,36 +154,36 @@ impl SafeAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a message.
|
/// Sign a message.
|
||||||
pub fn sign(&self, password: &str, message: &Message) -> Result<Signature, Error> {
|
pub fn sign(&self, password: &Password, message: &Message) -> Result<Signature, Error> {
|
||||||
let secret = self.crypto.secret(password)?;
|
let secret = self.crypto.secret(password)?;
|
||||||
sign(&secret, message).map_err(From::from)
|
sign(&secret, message).map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt a message.
|
/// Decrypt a message.
|
||||||
pub fn decrypt(&self, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
pub fn decrypt(&self, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let secret = self.crypto.secret(password)?;
|
let secret = self.crypto.secret(password)?;
|
||||||
crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Agree on shared key.
|
/// Agree on shared key.
|
||||||
pub fn agree(&self, password: &str, other: &Public) -> Result<Secret, Error> {
|
pub fn agree(&self, password: &Password, other: &Public) -> Result<Secret, Error> {
|
||||||
let secret = self.crypto.secret(password)?;
|
let secret = self.crypto.secret(password)?;
|
||||||
agree(&secret, other).map_err(From::from)
|
agree(&secret, other).map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive public key.
|
/// Derive public key.
|
||||||
pub fn public(&self, password: &str) -> Result<Public, Error> {
|
pub fn public(&self, password: &Password) -> Result<Public, Error> {
|
||||||
let secret = self.crypto.secret(password)?;
|
let secret = self.crypto.secret(password)?;
|
||||||
Ok(KeyPair::from_secret(secret)?.public().clone())
|
Ok(KeyPair::from_secret(secret)?.public().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change account's password.
|
/// Change account's password.
|
||||||
pub fn change_password(&self, old_password: &str, new_password: &str, iterations: u32) -> Result<Self, Error> {
|
pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: NonZeroU32) -> Result<Self, Error> {
|
||||||
let secret = self.crypto.secret(old_password)?;
|
let secret = self.crypto.secret(old_password)?;
|
||||||
let result = SafeAccount {
|
let result = SafeAccount {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
version: self.version.clone(),
|
version: self.version.clone(),
|
||||||
crypto: Crypto::with_secret(&secret, new_password, iterations),
|
crypto: Crypto::with_secret(&secret, new_password, iterations)?,
|
||||||
address: self.address.clone(),
|
address: self.address.clone(),
|
||||||
filename: self.filename.clone(),
|
filename: self.filename.clone(),
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
@@ -164,7 +193,7 @@ impl SafeAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if password matches the account.
|
/// Check if password matches the account.
|
||||||
pub fn check_password(&self, password: &str) -> bool {
|
pub fn check_password(&self, password: &Password) -> bool {
|
||||||
self.crypto.secret(password).is_ok()
|
self.crypto.secret(password).is_ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,30 +201,34 @@ impl SafeAccount {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ethkey::{Generator, Random, verify_public, Message};
|
use ethkey::{Generator, Random, verify_public, Message};
|
||||||
use super::SafeAccount;
|
use super::{SafeAccount, NonZeroU32};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sign_and_verify_public() {
|
fn sign_and_verify_public() {
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let password = "hello world";
|
let password = "hello world".into();
|
||||||
let message = Message::default();
|
let message = Message::default();
|
||||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 10240, "Test".to_owned(), "{}".to_owned());
|
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned());
|
||||||
let signature = account.sign(password, &message).unwrap();
|
let signature = account.unwrap().sign(&password, &message).unwrap();
|
||||||
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
|
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn change_password() {
|
fn change_password() {
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let first_password = "hello world";
|
let first_password = "hello world".into();
|
||||||
let sec_password = "this is sparta";
|
let sec_password = "this is sparta".into();
|
||||||
let i = 10240;
|
|
||||||
let message = Message::default();
|
let message = Message::default();
|
||||||
let account = SafeAccount::create(&keypair, [0u8; 16], first_password, i, "Test".to_owned(), "{}".to_owned());
|
let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||||
let new_account = account.change_password(first_password, sec_password, i).unwrap();
|
let new_account = account.change_password(&first_password, &sec_password, *ITERATIONS).unwrap();
|
||||||
assert!(account.sign(first_password, &message).is_ok());
|
assert!(account.sign(&first_password, &message).is_ok());
|
||||||
assert!(account.sign(sec_password, &message).is_err());
|
assert!(account.sign(&sec_password, &message).is_err());
|
||||||
assert!(new_account.sign(first_password, &message).is_err());
|
assert!(new_account.sign(&first_password, &message).is_err());
|
||||||
assert!(new_account.sign(sec_password, &message).is_ok());
|
assert!(new_account.sign(&sec_password, &message).is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use json;
|
use json;
|
||||||
|
|
||||||
155
ethstore/src/accounts_dir/disk.rs → accounts/ethstore/src/accounts_dir/disk.rs
Executable file → Normal file
155
ethstore/src/accounts_dir/disk.rs → accounts/ethstore/src/accounts_dir/disk.rs
Executable file → Normal file
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@@ -23,6 +23,7 @@ use {json, SafeAccount, Error};
|
|||||||
use json::Uuid;
|
use json::Uuid;
|
||||||
use super::{KeyDirectory, VaultKeyDirectory, VaultKeyDirectoryProvider, VaultKey};
|
use super::{KeyDirectory, VaultKeyDirectory, VaultKeyDirectoryProvider, VaultKey};
|
||||||
use super::vault::{VAULT_FILE_NAME, VaultDiskDirectory};
|
use super::vault::{VAULT_FILE_NAME, VaultDiskDirectory};
|
||||||
|
use ethkey::Password;
|
||||||
|
|
||||||
const IGNORED_FILES: &'static [&'static str] = &[
|
const IGNORED_FILES: &'static [&'static str] = &[
|
||||||
"thumbs.db",
|
"thumbs.db",
|
||||||
@@ -33,22 +34,70 @@ const IGNORED_FILES: &'static [&'static str] = &[
|
|||||||
"vault.json",
|
"vault.json",
|
||||||
];
|
];
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
/// Find a unique filename that does not exist using four-letter random suffix.
|
||||||
fn restrict_permissions_to_owner(file_path: &Path) -> Result<(), i32> {
|
pub fn find_unique_filename_using_random_suffix(parent_path: &Path, original_filename: &str) -> io::Result<String> {
|
||||||
use std::ffi;
|
let mut path = parent_path.join(original_filename);
|
||||||
use libc;
|
let mut deduped_filename = original_filename.to_string();
|
||||||
|
|
||||||
let cstr = ffi::CString::new(&*file_path.to_string_lossy())
|
if path.exists() {
|
||||||
.map_err(|_| -1)?;
|
const MAX_RETRIES: usize = 500;
|
||||||
match unsafe { libc::chmod(cstr.as_ptr(), libc::S_IWUSR | libc::S_IRUSR) } {
|
let mut retries = 0;
|
||||||
0 => Ok(()),
|
|
||||||
x => Err(x),
|
while path.exists() {
|
||||||
|
if retries >= MAX_RETRIES {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "Exceeded maximum retries when deduplicating filename."));
|
||||||
|
}
|
||||||
|
|
||||||
|
let suffix = ::random::random_string(4);
|
||||||
|
deduped_filename = format!("{}-{}", original_filename, suffix);
|
||||||
|
path.set_file_name(&deduped_filename);
|
||||||
|
retries += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(deduped_filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
|
||||||
fn restrict_permissions_to_owner(_file_path: &Path) -> Result<(), i32> {
|
#[cfg(unix)]
|
||||||
Ok(())
|
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||||
|
use libc;
|
||||||
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
|
||||||
|
fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.mode((libc::S_IWUSR | libc::S_IRUSR) as u32)
|
||||||
|
.open(file_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new file and restrict permissions to owner only. It errors if the file already exists.
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||||
|
fs::OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(file_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||||
|
use libc;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let file = fs::File::create(file_path)?;
|
||||||
|
let mut permissions = file.metadata()?.permissions();
|
||||||
|
permissions.set_mode((libc::S_IWUSR | libc::S_IRUSR) as u32);
|
||||||
|
file.set_permissions(permissions)?;
|
||||||
|
|
||||||
|
Ok(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists.
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result<fs::File> {
|
||||||
|
fs::File::create(file_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Root keys directory implementation
|
/// Root keys directory implementation
|
||||||
@@ -58,6 +107,7 @@ pub type RootDiskDirectory = DiskDirectory<DiskKeyFileManager>;
|
|||||||
pub trait KeyFileManager: Send + Sync {
|
pub trait KeyFileManager: Send + Sync {
|
||||||
/// Read `SafeAccount` from given key file stream
|
/// Read `SafeAccount` from given key file stream
|
||||||
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read;
|
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read;
|
||||||
|
|
||||||
/// Write `SafeAccount` to given key file stream
|
/// Write `SafeAccount` to given key file stream
|
||||||
fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write;
|
fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write;
|
||||||
}
|
}
|
||||||
@@ -69,7 +119,10 @@ pub struct DiskDirectory<T> where T: KeyFileManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Keys file manager for root keys directory
|
/// Keys file manager for root keys directory
|
||||||
pub struct DiskKeyFileManager;
|
#[derive(Default)]
|
||||||
|
pub struct DiskKeyFileManager {
|
||||||
|
password: Option<Password>,
|
||||||
|
}
|
||||||
|
|
||||||
impl RootDiskDirectory {
|
impl RootDiskDirectory {
|
||||||
pub fn create<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
|
pub fn create<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> {
|
||||||
@@ -77,8 +130,13 @@ impl RootDiskDirectory {
|
|||||||
Ok(Self::at(path))
|
Ok(Self::at(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// allows to read keyfiles with given password (needed for keyfiles w/o address)
|
||||||
|
pub fn with_password(&self, password: Option<Password>) -> Self {
|
||||||
|
DiskDirectory::new(&self.path, DiskKeyFileManager { password })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn at<P>(path: P) -> Self where P: AsRef<Path> {
|
pub fn at<P>(path: P) -> Self where P: AsRef<Path> {
|
||||||
DiskDirectory::new(path, DiskKeyFileManager)
|
DiskDirectory::new(path, DiskKeyFileManager::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,20 +211,16 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to
|
/// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to
|
||||||
/// true, a random suffix is appended to the filename.
|
/// true, a random suffix is appended to the filename.
|
||||||
pub fn insert_with_filename(&self, account: SafeAccount, mut filename: String, dedup: bool) -> Result<SafeAccount, Error> {
|
pub fn insert_with_filename(&self, account: SafeAccount, mut filename: String, dedup: bool) -> Result<SafeAccount, Error> {
|
||||||
// path to keyfile
|
if dedup {
|
||||||
let mut keyfile_path = self.path.join(filename.as_str());
|
filename = find_unique_filename_using_random_suffix(&self.path, &filename)?;
|
||||||
|
|
||||||
// check for duplicate filename and append random suffix
|
|
||||||
if dedup && keyfile_path.exists() {
|
|
||||||
let suffix = ::random::random_string(4);
|
|
||||||
filename.push_str(&format!("-{}", suffix));
|
|
||||||
keyfile_path.set_file_name(&filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// path to keyfile
|
||||||
|
let keyfile_path = self.path.join(filename.as_str());
|
||||||
|
|
||||||
// update account filename
|
// update account filename
|
||||||
let original_account = account.clone();
|
let original_account = account.clone();
|
||||||
let mut account = account;
|
let mut account = account;
|
||||||
@@ -174,17 +228,16 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
|
|||||||
|
|
||||||
{
|
{
|
||||||
// save the file
|
// save the file
|
||||||
let mut file = fs::File::create(&keyfile_path)?;
|
let mut file = if dedup {
|
||||||
|
create_new_file_with_permissions_to_owner(&keyfile_path)?
|
||||||
|
} else {
|
||||||
|
replace_file_with_permissions_to_owner(&keyfile_path)?
|
||||||
|
};
|
||||||
|
|
||||||
// write key content
|
// write key content
|
||||||
self.key_manager.write(original_account, &mut file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
self.key_manager.write(original_account, &mut file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||||
|
|
||||||
file.flush()?;
|
file.flush()?;
|
||||||
|
|
||||||
if let Err(_) = restrict_permissions_to_owner(keyfile_path.as_path()) {
|
|
||||||
return Err(Error::Io(io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
file.sync_all()?;
|
file.sync_all()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +329,7 @@ impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager {
|
|||||||
impl KeyFileManager for DiskKeyFileManager {
|
impl KeyFileManager for DiskKeyFileManager {
|
||||||
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read {
|
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read {
|
||||||
let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
|
||||||
Ok(SafeAccount::from_file(key_file, filename))
|
SafeAccount::from_file(key_file, filename, &self.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write {
|
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write {
|
||||||
@@ -303,23 +356,28 @@ mod test {
|
|||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
use std::num::NonZeroU32;
|
||||||
use super::{KeyDirectory, RootDiskDirectory, VaultKey};
|
use super::{KeyDirectory, RootDiskDirectory, VaultKey};
|
||||||
use account::SafeAccount;
|
use account::SafeAccount;
|
||||||
use ethkey::{Random, Generator};
|
use ethkey::{Random, Generator};
|
||||||
use self::tempdir::TempDir;
|
use self::tempdir::TempDir;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_create_new_account() {
|
fn should_create_new_account() {
|
||||||
// given
|
// given
|
||||||
let mut dir = env::temp_dir();
|
let mut dir = env::temp_dir();
|
||||||
dir.push("ethstore_should_create_new_account");
|
dir.push("ethstore_should_create_new_account");
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let password = "hello world";
|
let password = "hello world".into();
|
||||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
|
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned());
|
||||||
let res = directory.insert(account);
|
let res = directory.insert(account.unwrap());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(res.is_ok(), "Should save account succesfuly.");
|
assert!(res.is_ok(), "Should save account succesfuly.");
|
||||||
@@ -335,11 +393,11 @@ mod test {
|
|||||||
let mut dir = env::temp_dir();
|
let mut dir = env::temp_dir();
|
||||||
dir.push("ethstore_should_handle_duplicate_filenames");
|
dir.push("ethstore_should_handle_duplicate_filenames");
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let password = "hello world";
|
let password = "hello world".into();
|
||||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
|
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap();
|
||||||
let filename = "test".to_string();
|
let filename = "test".to_string();
|
||||||
let dedup = true;
|
let dedup = true;
|
||||||
|
|
||||||
@@ -368,14 +426,14 @@ mod test {
|
|||||||
dir.push("should_create_new_vault");
|
dir.push("should_create_new_vault");
|
||||||
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
|
||||||
let vault_name = "vault";
|
let vault_name = "vault";
|
||||||
let password = "password";
|
let password = "password".into();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(directory.as_vault_provider().is_some());
|
assert!(directory.as_vault_provider().is_some());
|
||||||
|
|
||||||
// and when
|
// and when
|
||||||
let before_root_items_count = fs::read_dir(&dir).unwrap().count();
|
let before_root_items_count = fs::read_dir(&dir).unwrap().count();
|
||||||
let vault = directory.as_vault_provider().unwrap().create(vault_name, VaultKey::new(password, 1024));
|
let vault = directory.as_vault_provider().unwrap().create(vault_name, VaultKey::new(&password, *ITERATIONS));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(vault.is_ok());
|
assert!(vault.is_ok());
|
||||||
@@ -383,7 +441,7 @@ mod test {
|
|||||||
assert!(after_root_items_count > before_root_items_count);
|
assert!(after_root_items_count > before_root_items_count);
|
||||||
|
|
||||||
// and when
|
// and when
|
||||||
let vault = directory.as_vault_provider().unwrap().open(vault_name, VaultKey::new(password, 1024));
|
let vault = directory.as_vault_provider().unwrap().open(vault_name, VaultKey::new(&password, *ITERATIONS));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(vault.is_ok());
|
assert!(vault.is_ok());
|
||||||
@@ -400,8 +458,9 @@ mod test {
|
|||||||
let temp_path = TempDir::new("").unwrap();
|
let temp_path = TempDir::new("").unwrap();
|
||||||
let directory = RootDiskDirectory::create(&temp_path).unwrap();
|
let directory = RootDiskDirectory::create(&temp_path).unwrap();
|
||||||
let vault_provider = directory.as_vault_provider().unwrap();
|
let vault_provider = directory.as_vault_provider().unwrap();
|
||||||
vault_provider.create("vault1", VaultKey::new("password1", 1)).unwrap();
|
let iter = NonZeroU32::new(1).expect("1 > 0; qed");
|
||||||
vault_provider.create("vault2", VaultKey::new("password2", 1)).unwrap();
|
vault_provider.create("vault1", VaultKey::new(&"password1".into(), iter)).unwrap();
|
||||||
|
vault_provider.create("vault2", VaultKey::new(&"password2".into(), iter)).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let vaults = vault_provider.list_vaults().unwrap();
|
let vaults = vault_provider.list_vaults().unwrap();
|
||||||
@@ -422,9 +481,9 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let keypair = Random.generate().unwrap();
|
let keypair = Random.generate().unwrap();
|
||||||
let password = "test pass";
|
let password = "test pass".into();
|
||||||
let account = SafeAccount::create(&keypair, [0u8; 16], password, 1024, "Test".to_owned(), "{}".to_owned());
|
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned());
|
||||||
directory.insert(account).expect("Account should be inserted ok");
|
directory.insert(account.unwrap()).expect("Account should be inserted ok");
|
||||||
|
|
||||||
let new_hash = directory.files_hash().expect("New files hash should be calculated ok");
|
let new_hash = directory.files_hash().expect("New files hash should be calculated ok");
|
||||||
|
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use itertools::Itertools;
|
use itertools;
|
||||||
use ethkey::Address;
|
use ethkey::Address;
|
||||||
|
|
||||||
use {SafeAccount, Error};
|
use {SafeAccount, Error};
|
||||||
@@ -30,7 +30,7 @@ pub struct MemoryDirectory {
|
|||||||
|
|
||||||
impl KeyDirectory for MemoryDirectory {
|
impl KeyDirectory for MemoryDirectory {
|
||||||
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
|
||||||
Ok(self.accounts.read().values().cloned().flatten().collect())
|
Ok(itertools::Itertools::flatten(self.accounts.read().values().cloned()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
|
||||||
@@ -72,4 +72,3 @@ impl KeyDirectory for MemoryDirectory {
|
|||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
22
ethstore/src/accounts_dir/mod.rs → accounts/ethstore/src/accounts_dir/mod.rs
Executable file → Normal file
22
ethstore/src/accounts_dir/mod.rs → accounts/ethstore/src/accounts_dir/mod.rs
Executable file → Normal file
@@ -1,21 +1,23 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Accounts Directory
|
//! Accounts Directory
|
||||||
|
|
||||||
|
use ethkey::Password;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
use std::path::{PathBuf};
|
use std::path::{PathBuf};
|
||||||
use {SafeAccount, Error};
|
use {SafeAccount, Error};
|
||||||
|
|
||||||
@@ -35,12 +37,12 @@ pub enum SetKeyError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Vault key
|
/// Vault key
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub struct VaultKey {
|
pub struct VaultKey {
|
||||||
/// Vault password
|
/// Vault password
|
||||||
pub password: String,
|
pub password: Password,
|
||||||
/// Number of iterations to produce a derived key from password
|
/// Number of iterations to produce a derived key from password
|
||||||
pub iterations: u32,
|
pub iterations: NonZeroU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keys directory
|
/// Keys directory
|
||||||
@@ -95,9 +97,9 @@ pub use self::vault::VaultDiskDirectory;
|
|||||||
|
|
||||||
impl VaultKey {
|
impl VaultKey {
|
||||||
/// Create new vault key
|
/// Create new vault key
|
||||||
pub fn new(password: &str, iterations: u32) -> Self {
|
pub fn new(password: &Password, iterations: NonZeroU32) -> Self {
|
||||||
VaultKey {
|
VaultKey {
|
||||||
password: password.to_owned(),
|
password: password.clone(),
|
||||||
iterations: iterations,
|
iterations: iterations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
51
ethstore/src/accounts_dir/vault.rs → accounts/ethstore/src/accounts_dir/vault.rs
Executable file → Normal file
51
ethstore/src/accounts_dir/vault.rs → accounts/ethstore/src/accounts_dir/vault.rs
Executable file → Normal file
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
use std::path::{PathBuf, Path};
|
use std::path::{PathBuf, Path};
|
||||||
@@ -21,7 +21,7 @@ use {json, SafeAccount, Error};
|
|||||||
use crypto::Keccak256;
|
use crypto::Keccak256;
|
||||||
use super::super::account::Crypto;
|
use super::super::account::Crypto;
|
||||||
use super::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
|
use super::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
|
||||||
use super::disk::{DiskDirectory, KeyFileManager};
|
use super::disk::{self, DiskDirectory, KeyFileManager};
|
||||||
|
|
||||||
/// Name of vault metadata file
|
/// Name of vault metadata file
|
||||||
pub const VAULT_FILE_NAME: &'static str = "vault.json";
|
pub const VAULT_FILE_NAME: &'static str = "vault.json";
|
||||||
@@ -234,17 +234,16 @@ fn check_vault_name(name: &str) -> bool {
|
|||||||
|
|
||||||
/// Vault can be empty, but still must be pluggable => we store vault password in separate file
|
/// Vault can be empty, but still must be pluggable => we store vault password in separate file
|
||||||
fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef<Path> {
|
fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef<Path> {
|
||||||
let password_hash = key.password.keccak256();
|
let password_hash = key.password.as_bytes().keccak256();
|
||||||
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations);
|
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?;
|
||||||
|
|
||||||
let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into();
|
let vault_file_path = vault_dir_path.as_ref().join(VAULT_FILE_NAME);
|
||||||
vault_file_path.push(VAULT_FILE_NAME);
|
let temp_vault_file_name = disk::find_unique_filename_using_random_suffix(vault_dir_path.as_ref(), &VAULT_TEMP_FILE_NAME)?;
|
||||||
let mut temp_vault_file_path: PathBuf = vault_dir_path.as_ref().into();
|
let temp_vault_file_path = vault_dir_path.as_ref().join(&temp_vault_file_name);
|
||||||
temp_vault_file_path.push(VAULT_TEMP_FILE_NAME);
|
|
||||||
|
|
||||||
// this method is used to rewrite existing vault file
|
// this method is used to rewrite existing vault file
|
||||||
// => write to temporary file first, then rename temporary file to vault file
|
// => write to temporary file first, then rename temporary file to vault file
|
||||||
let mut vault_file = fs::File::create(&temp_vault_file_path)?;
|
let mut vault_file = disk::create_new_file_with_permissions_to_owner(&temp_vault_file_path)?;
|
||||||
let vault_file_contents = json::VaultFile {
|
let vault_file_contents = json::VaultFile {
|
||||||
crypto: crypto.into(),
|
crypto: crypto.into(),
|
||||||
meta: Some(meta.to_owned()),
|
meta: Some(meta.to_owned()),
|
||||||
@@ -268,7 +267,7 @@ fn read_vault_file<P>(vault_dir_path: P, key: Option<&VaultKey>) -> Result<Strin
|
|||||||
|
|
||||||
if let Some(key) = key {
|
if let Some(key) = key {
|
||||||
let password_bytes = vault_file_crypto.decrypt(&key.password)?;
|
let password_bytes = vault_file_crypto.decrypt(&key.password)?;
|
||||||
let password_hash = key.password.keccak256();
|
let password_hash = key.password.as_bytes().keccak256();
|
||||||
if password_hash != password_bytes.as_slice() {
|
if password_hash != password_bytes.as_slice() {
|
||||||
return Err(Error::InvalidPassword);
|
return Err(Error::InvalidPassword);
|
||||||
}
|
}
|
||||||
@@ -283,11 +282,17 @@ mod test {
|
|||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use super::VaultKey;
|
use super::VaultKey;
|
||||||
use super::{VAULT_FILE_NAME, check_vault_name, make_vault_dir_path, create_vault_file, read_vault_file, VaultDiskDirectory};
|
use super::{VAULT_FILE_NAME, check_vault_name, make_vault_dir_path, create_vault_file, read_vault_file, VaultDiskDirectory};
|
||||||
use self::tempdir::TempDir;
|
use self::tempdir::TempDir;
|
||||||
|
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_vault_name_succeeds() {
|
fn check_vault_name_succeeds() {
|
||||||
assert!(check_vault_name("vault"));
|
assert!(check_vault_name("vault"));
|
||||||
@@ -311,8 +316,10 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn make_vault_dir_path_succeeds() {
|
fn make_vault_dir_path_succeeds() {
|
||||||
assert_eq!(make_vault_dir_path("/home/user/parity", "vault", true).unwrap().to_str().unwrap(), "/home/user/parity/vault");
|
use std::path::Path;
|
||||||
assert_eq!(make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap().to_str().unwrap(), "/home/user/parity/*bad-name*");
|
|
||||||
|
assert_eq!(&make_vault_dir_path("/home/user/parity", "vault", true).unwrap(), &Path::new("/home/user/parity/vault"));
|
||||||
|
assert_eq!(&make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap(), &Path::new("/home/user/parity/*bad-name*"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -324,7 +331,7 @@ mod test {
|
|||||||
fn create_vault_file_succeeds() {
|
fn create_vault_file_succeeds() {
|
||||||
// given
|
// given
|
||||||
let temp_path = TempDir::new("").unwrap();
|
let temp_path = TempDir::new("").unwrap();
|
||||||
let key = VaultKey::new("password", 1024);
|
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||||
let mut vault_dir: PathBuf = temp_path.path().into();
|
let mut vault_dir: PathBuf = temp_path.path().into();
|
||||||
vault_dir.push("vault");
|
vault_dir.push("vault");
|
||||||
fs::create_dir_all(&vault_dir).unwrap();
|
fs::create_dir_all(&vault_dir).unwrap();
|
||||||
@@ -343,7 +350,7 @@ mod test {
|
|||||||
fn read_vault_file_succeeds() {
|
fn read_vault_file_succeeds() {
|
||||||
// given
|
// given
|
||||||
let temp_path = TempDir::new("").unwrap();
|
let temp_path = TempDir::new("").unwrap();
|
||||||
let key = VaultKey::new("password", 1024);
|
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||||
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"758696c8dc6378ab9b25bb42790da2f5"},"ciphertext":"54eb50683717d41caaeb12ea969f2c159daada5907383f26f327606a37dc7168","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"3c320fa566a1a7963ac8df68a19548d27c8f40bf92ef87c84594dcd5bbc402b6"},"mac":"9e5c2314c2a0781962db85611417c614bd6756666b6b1e93840f5b6ed895f003"}}"#;
|
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"758696c8dc6378ab9b25bb42790da2f5"},"ciphertext":"54eb50683717d41caaeb12ea969f2c159daada5907383f26f327606a37dc7168","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"3c320fa566a1a7963ac8df68a19548d27c8f40bf92ef87c84594dcd5bbc402b6"},"mac":"9e5c2314c2a0781962db85611417c614bd6756666b6b1e93840f5b6ed895f003"}}"#;
|
||||||
let dir: PathBuf = temp_path.path().into();
|
let dir: PathBuf = temp_path.path().into();
|
||||||
let mut vault_file_path: PathBuf = dir.clone();
|
let mut vault_file_path: PathBuf = dir.clone();
|
||||||
@@ -364,7 +371,7 @@ mod test {
|
|||||||
fn read_vault_file_fails() {
|
fn read_vault_file_fails() {
|
||||||
// given
|
// given
|
||||||
let temp_path = TempDir::new("").unwrap();
|
let temp_path = TempDir::new("").unwrap();
|
||||||
let key = VaultKey::new("password1", 1024);
|
let key = VaultKey::new(&"password1".into(), *ITERATIONS);
|
||||||
let dir: PathBuf = temp_path.path().into();
|
let dir: PathBuf = temp_path.path().into();
|
||||||
let mut vault_file_path: PathBuf = dir.clone();
|
let mut vault_file_path: PathBuf = dir.clone();
|
||||||
vault_file_path.push(VAULT_FILE_NAME);
|
vault_file_path.push(VAULT_FILE_NAME);
|
||||||
@@ -393,7 +400,7 @@ mod test {
|
|||||||
fn vault_directory_can_be_created() {
|
fn vault_directory_can_be_created() {
|
||||||
// given
|
// given
|
||||||
let temp_path = TempDir::new("").unwrap();
|
let temp_path = TempDir::new("").unwrap();
|
||||||
let key = VaultKey::new("password", 1024);
|
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||||
let dir: PathBuf = temp_path.path().into();
|
let dir: PathBuf = temp_path.path().into();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -413,7 +420,7 @@ mod test {
|
|||||||
fn vault_directory_cannot_be_created_if_already_exists() {
|
fn vault_directory_cannot_be_created_if_already_exists() {
|
||||||
// given
|
// given
|
||||||
let temp_path = TempDir::new("").unwrap();
|
let temp_path = TempDir::new("").unwrap();
|
||||||
let key = VaultKey::new("password", 1024);
|
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||||
let dir: PathBuf = temp_path.path().into();
|
let dir: PathBuf = temp_path.path().into();
|
||||||
let mut vault_dir = dir.clone();
|
let mut vault_dir = dir.clone();
|
||||||
vault_dir.push("vault");
|
vault_dir.push("vault");
|
||||||
@@ -430,7 +437,7 @@ mod test {
|
|||||||
fn vault_directory_cannot_be_opened_if_not_exists() {
|
fn vault_directory_cannot_be_opened_if_not_exists() {
|
||||||
// given
|
// given
|
||||||
let temp_path = TempDir::new("").unwrap();
|
let temp_path = TempDir::new("").unwrap();
|
||||||
let key = VaultKey::new("password", 1024);
|
let key = VaultKey::new(&"password".into(), *ITERATIONS);
|
||||||
let dir: PathBuf = temp_path.path().into();
|
let dir: PathBuf = temp_path.path().into();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
35
ethstore/src/error.rs → accounts/ethstore/src/error.rs
Executable file → Normal file
35
ethstore/src/error.rs → accounts/ethstore/src/error.rs
Executable file → Normal file
@@ -1,23 +1,23 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
use ethkey::Error as EthKeyError;
|
use ethkey::{self, Error as EthKeyError};
|
||||||
use crypto::Error as EthCryptoError;
|
use crypto::{self, Error as EthCryptoError};
|
||||||
use ethkey::DerivationError;
|
use ethkey::DerivationError;
|
||||||
|
|
||||||
/// Account-related errors.
|
/// Account-related errors.
|
||||||
@@ -49,6 +49,8 @@ pub enum Error {
|
|||||||
CreationFailed,
|
CreationFailed,
|
||||||
/// `EthKey` error
|
/// `EthKey` error
|
||||||
EthKey(EthKeyError),
|
EthKey(EthKeyError),
|
||||||
|
/// `ethkey::crypto::Error`
|
||||||
|
EthKeyCrypto(ethkey::crypto::Error),
|
||||||
/// `EthCrypto` error
|
/// `EthCrypto` error
|
||||||
EthCrypto(EthCryptoError),
|
EthCrypto(EthCryptoError),
|
||||||
/// Derivation error
|
/// Derivation error
|
||||||
@@ -73,6 +75,7 @@ impl fmt::Display for Error {
|
|||||||
Error::VaultNotFound => "Vault not found".into(),
|
Error::VaultNotFound => "Vault not found".into(),
|
||||||
Error::CreationFailed => "Account creation failed".into(),
|
Error::CreationFailed => "Account creation failed".into(),
|
||||||
Error::EthKey(ref err) => err.to_string(),
|
Error::EthKey(ref err) => err.to_string(),
|
||||||
|
Error::EthKeyCrypto(ref err) => err.to_string(),
|
||||||
Error::EthCrypto(ref err) => err.to_string(),
|
Error::EthCrypto(ref err) => err.to_string(),
|
||||||
Error::Derivation(ref err) => format!("Derivation error: {:?}", err),
|
Error::Derivation(ref err) => format!("Derivation error: {:?}", err),
|
||||||
Error::Custom(ref s) => s.clone(),
|
Error::Custom(ref s) => s.clone(),
|
||||||
@@ -94,12 +97,30 @@ impl From<EthKeyError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ethkey::crypto::Error> for Error {
|
||||||
|
fn from(err: ethkey::crypto::Error) -> Self {
|
||||||
|
Error::EthKeyCrypto(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<EthCryptoError> for Error {
|
impl From<EthCryptoError> for Error {
|
||||||
fn from(err: EthCryptoError) -> Self {
|
fn from(err: EthCryptoError) -> Self {
|
||||||
Error::EthCrypto(err)
|
Error::EthCrypto(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<crypto::error::ScryptError> for Error {
|
||||||
|
fn from(err: crypto::error::ScryptError) -> Self {
|
||||||
|
Error::EthCrypto(err.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crypto::error::SymmError> for Error {
|
||||||
|
fn from(err: crypto::error::SymmError) -> Self {
|
||||||
|
Error::EthCrypto(err.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<DerivationError> for Error {
|
impl From<DerivationError> for Error {
|
||||||
fn from(err: DerivationError) -> Self {
|
fn from(err: DerivationError) -> Self {
|
||||||
Error::Derivation(err)
|
Error::Derivation(err)
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! ethkey reexport to make documentation look pretty.
|
//! ethkey reexport to make documentation look pretty.
|
||||||
pub use _ethkey::*;
|
pub use _ethkey::*;
|
||||||
262
ethstore/src/ethstore.rs → accounts/ethstore/src/ethstore.rs
Executable file → Normal file
262
ethstore/src/ethstore.rs → accounts/ethstore/src/ethstore.rs
Executable file → Normal file
@@ -1,34 +1,40 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
use std::num::NonZeroU32;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
|
|
||||||
use crypto::KEY_ITERATIONS;
|
|
||||||
use random::Random;
|
use random::Random;
|
||||||
use ethkey::{self, Signature, Address, Message, Secret, Public, KeyPair, ExtendedKeyPair};
|
use ethkey::{self, Signature, Password, Address, Message, Secret, Public, KeyPair, ExtendedKeyPair};
|
||||||
use accounts_dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
|
use accounts_dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
|
||||||
use account::SafeAccount;
|
use account::SafeAccount;
|
||||||
use presale::PresaleWallet;
|
use presale::PresaleWallet;
|
||||||
use json::{self, Uuid, OpaqueKeyFile};
|
use json::{self, Uuid, OpaqueKeyFile};
|
||||||
use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation, OpaqueSecret};
|
use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation, OpaqueSecret};
|
||||||
|
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref KEY_ITERATIONS: NonZeroU32 =
|
||||||
|
NonZeroU32::new(crypto::KEY_ITERATIONS as u32).expect("KEY_ITERATIONS > 0; qed");
|
||||||
|
}
|
||||||
|
|
||||||
/// Accounts store.
|
/// Accounts store.
|
||||||
pub struct EthStore {
|
pub struct EthStore {
|
||||||
store: EthMultiStore,
|
store: EthMultiStore,
|
||||||
@@ -37,11 +43,11 @@ pub struct EthStore {
|
|||||||
impl EthStore {
|
impl EthStore {
|
||||||
/// Open a new accounts store with given key directory backend.
|
/// Open a new accounts store with given key directory backend.
|
||||||
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
|
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
|
||||||
Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
|
Self::open_with_iterations(directory, *KEY_ITERATIONS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a new account store with given key directory backend and custom number of iterations.
|
/// Open a new account store with given key directory backend and custom number of iterations.
|
||||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: NonZeroU32) -> Result<Self, Error> {
|
||||||
Ok(EthStore {
|
Ok(EthStore {
|
||||||
store: EthMultiStore::open_with_iterations(directory, iterations)?,
|
store: EthMultiStore::open_with_iterations(directory, iterations)?,
|
||||||
})
|
})
|
||||||
@@ -64,17 +70,17 @@ impl EthStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleSecretStore for EthStore {
|
impl SimpleSecretStore for EthStore {
|
||||||
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &str) -> Result<StoreAccountRef, Error> {
|
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result<StoreAccountRef, Error> {
|
||||||
self.store.insert_account(vault, secret, password)
|
self.store.insert_account(vault, secret, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &str, derivation: Derivation)
|
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation)
|
||||||
-> Result<StoreAccountRef, Error>
|
-> Result<StoreAccountRef, Error>
|
||||||
{
|
{
|
||||||
self.store.insert_derived(vault, account_ref, password, derivation)
|
self.store.insert_derived(vault, account_ref, password, derivation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation) -> Result<Address, Error> {
|
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<Address, Error> {
|
||||||
self.store.generate_derived(account_ref, password, derivation)
|
self.store.generate_derived(account_ref, password, derivation)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,42 +92,42 @@ impl SimpleSecretStore for EthStore {
|
|||||||
self.store.accounts()
|
self.store.accounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_password(&self, account: &StoreAccountRef, old_password: &str, new_password: &str) -> Result<(), Error> {
|
fn change_password(&self, account: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error> {
|
||||||
self.store.change_password(account, old_password, new_password)
|
self.store.change_password(account, old_password, new_password)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn export_account(&self, account: &StoreAccountRef, password: &str) -> Result<OpaqueKeyFile, Error> {
|
fn export_account(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueKeyFile, Error> {
|
||||||
self.store.export_account(account, password)
|
self.store.export_account(account, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_account(&self, account: &StoreAccountRef, password: &str) -> Result<(), Error> {
|
fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error> {
|
||||||
self.store.remove_account(account, password)
|
self.store.remove_account(account, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result<Signature, Error> {
|
fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result<Signature, Error> {
|
||||||
self.get(account)?.sign(password, message)
|
self.get(account)?.sign(password, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message)
|
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message)
|
||||||
-> Result<Signature, Error>
|
-> Result<Signature, Error>
|
||||||
{
|
{
|
||||||
self.store.sign_derived(account_ref, password, derivation, message)
|
self.store.sign_derived(account_ref, password, derivation, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result<Secret, Error> {
|
fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result<Secret, Error> {
|
||||||
self.store.agree(account, password, other)
|
self.store.agree(account, password, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let account = self.get(account)?;
|
let account = self.get(account)?;
|
||||||
account.decrypt(password, shared_mac, message)
|
account.decrypt(password, shared_mac, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_vault(&self, name: &str, password: &str) -> Result<(), Error> {
|
fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||||
self.store.create_vault(name, password)
|
self.store.create_vault(name, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_vault(&self, name: &str, password: &str) -> Result<(), Error> {
|
fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||||
self.store.open_vault(name, password)
|
self.store.open_vault(name, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +143,7 @@ impl SimpleSecretStore for EthStore {
|
|||||||
self.store.list_opened_vaults()
|
self.store.list_opened_vaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_vault_password(&self, name: &str, new_password: &str) -> Result<(), Error> {
|
fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> {
|
||||||
self.store.change_vault_password(name, new_password)
|
self.store.change_vault_password(name, new_password)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,20 +161,20 @@ impl SimpleSecretStore for EthStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SecretStore for EthStore {
|
impl SecretStore for EthStore {
|
||||||
fn raw_secret(&self, account: &StoreAccountRef, password: &str) -> Result<OpaqueSecret, Error> {
|
fn raw_secret(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueSecret, Error> {
|
||||||
Ok(OpaqueSecret(self.get(account)?.crypto.secret(password)?))
|
Ok(OpaqueSecret(self.get(account)?.crypto.secret(password)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result<StoreAccountRef, Error> {
|
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &Password) -> Result<StoreAccountRef, Error> {
|
||||||
let json_wallet = json::PresaleWallet::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?;
|
let json_wallet = json::PresaleWallet::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?;
|
||||||
let wallet = PresaleWallet::from(json_wallet);
|
let wallet = PresaleWallet::from(json_wallet);
|
||||||
let keypair = wallet.decrypt(password).map_err(|_| Error::InvalidPassword)?;
|
let keypair = wallet.decrypt(password).map_err(|_| Error::InvalidPassword)?;
|
||||||
self.insert_account(vault, keypair.secret().clone(), password)
|
self.insert_account(vault, keypair.secret().clone(), password)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &str, gen_id: bool) -> Result<StoreAccountRef, Error> {
|
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result<StoreAccountRef, Error> {
|
||||||
let json_keyfile = json::KeyFile::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?;
|
let json_keyfile = json::KeyFile::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?;
|
||||||
let mut safe_account = SafeAccount::from_file(json_keyfile, None);
|
let mut safe_account = SafeAccount::from_file(json_keyfile, None, &None)?;
|
||||||
|
|
||||||
if gen_id {
|
if gen_id {
|
||||||
safe_account.id = Random::random();
|
safe_account.id = Random::random();
|
||||||
@@ -179,19 +185,19 @@ impl SecretStore for EthStore {
|
|||||||
self.store.import(vault, safe_account)
|
self.store.import(vault, safe_account)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_password(&self, account: &StoreAccountRef, password: &str) -> Result<bool, Error> {
|
fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result<bool, Error> {
|
||||||
let account = self.get(account)?;
|
let account = self.get(account)?;
|
||||||
Ok(account.check_password(password))
|
Ok(account.check_password(password))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &str, new_password: &str) -> Result<(), Error> {
|
fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error> {
|
||||||
let account = self.get(account)?;
|
let account = self.get(account)?;
|
||||||
let secret = account.crypto.secret(password)?;
|
let secret = account.crypto.secret(password)?;
|
||||||
new_store.insert_account(new_vault, secret, new_password)?;
|
new_store.insert_account(new_vault, secret, new_password)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn public(&self, account: &StoreAccountRef, password: &str) -> Result<Public, Error> {
|
fn public(&self, account: &StoreAccountRef, password: &Password) -> Result<Public, Error> {
|
||||||
let account = self.get(account)?;
|
let account = self.get(account)?;
|
||||||
account.public(password)
|
account.public(password)
|
||||||
}
|
}
|
||||||
@@ -257,7 +263,7 @@ impl SecretStore for EthStore {
|
|||||||
/// Similar to `EthStore` but may store many accounts (with different passwords) for the same `Address`
|
/// Similar to `EthStore` but may store many accounts (with different passwords) for the same `Address`
|
||||||
pub struct EthMultiStore {
|
pub struct EthMultiStore {
|
||||||
dir: Box<KeyDirectory>,
|
dir: Box<KeyDirectory>,
|
||||||
iterations: u32,
|
iterations: NonZeroU32,
|
||||||
// order lock: cache, then vaults
|
// order lock: cache, then vaults
|
||||||
cache: RwLock<BTreeMap<StoreAccountRef, Vec<SafeAccount>>>,
|
cache: RwLock<BTreeMap<StoreAccountRef, Vec<SafeAccount>>>,
|
||||||
vaults: Mutex<HashMap<String, Box<VaultKeyDirectory>>>,
|
vaults: Mutex<HashMap<String, Box<VaultKeyDirectory>>>,
|
||||||
@@ -273,11 +279,11 @@ struct Timestamp {
|
|||||||
impl EthMultiStore {
|
impl EthMultiStore {
|
||||||
/// Open new multi-accounts store with given key directory backend.
|
/// Open new multi-accounts store with given key directory backend.
|
||||||
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
|
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
|
||||||
Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
|
Self::open_with_iterations(directory, *KEY_ITERATIONS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open new multi-accounts store with given key directory backend and custom number of iterations for new keys.
|
/// Open new multi-accounts store with given key directory backend and custom number of iterations for new keys.
|
||||||
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
|
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: NonZeroU32) -> Result<Self, Error> {
|
||||||
let store = EthMultiStore {
|
let store = EthMultiStore {
|
||||||
dir: directory,
|
dir: directory,
|
||||||
vaults: Mutex::new(HashMap::new()),
|
vaults: Mutex::new(HashMap::new()),
|
||||||
@@ -365,7 +371,7 @@ impl EthMultiStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matching(&self, account: &StoreAccountRef, password: &str) -> Result<Vec<SafeAccount>, Error> {
|
fn get_matching(&self, account: &StoreAccountRef, password: &Password) -> Result<Vec<SafeAccount>, Error> {
|
||||||
let accounts = self.get_accounts(account)?;
|
let accounts = self.get_accounts(account)?;
|
||||||
|
|
||||||
Ok(accounts.into_iter()
|
Ok(accounts.into_iter()
|
||||||
@@ -455,14 +461,14 @@ impl EthMultiStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleSecretStore for EthMultiStore {
|
impl SimpleSecretStore for EthMultiStore {
|
||||||
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &str) -> Result<StoreAccountRef, Error> {
|
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result<StoreAccountRef, Error> {
|
||||||
let keypair = KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)?;
|
let keypair = KeyPair::from_secret(secret).map_err(|_| Error::CreationFailed)?;
|
||||||
let id: [u8; 16] = Random::random();
|
let id: [u8; 16] = Random::random();
|
||||||
let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned());
|
let account = SafeAccount::create(&keypair, id, password, self.iterations, "".to_owned(), "{}".to_owned())?;
|
||||||
self.import(vault, account)
|
self.import(vault, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &str, derivation: Derivation)
|
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation)
|
||||||
-> Result<StoreAccountRef, Error>
|
-> Result<StoreAccountRef, Error>
|
||||||
{
|
{
|
||||||
let accounts = self.get_matching(account_ref, password)?;
|
let accounts = self.get_matching(account_ref, password)?;
|
||||||
@@ -473,7 +479,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
Err(Error::InvalidPassword)
|
Err(Error::InvalidPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation)
|
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation)
|
||||||
-> Result<Address, Error>
|
-> Result<Address, Error>
|
||||||
{
|
{
|
||||||
let accounts = self.get_matching(&account_ref, password)?;
|
let accounts = self.get_matching(&account_ref, password)?;
|
||||||
@@ -484,7 +490,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
Err(Error::InvalidPassword)
|
Err(Error::InvalidPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message)
|
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message)
|
||||||
-> Result<Signature, Error>
|
-> Result<Signature, Error>
|
||||||
{
|
{
|
||||||
let accounts = self.get_matching(&account_ref, password)?;
|
let accounts = self.get_matching(&account_ref, password)?;
|
||||||
@@ -518,7 +524,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
Ok(self.cache.read().keys().cloned().collect())
|
Ok(self.cache.read().keys().cloned().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_account(&self, account_ref: &StoreAccountRef, password: &str) -> Result<(), Error> {
|
fn remove_account(&self, account_ref: &StoreAccountRef, password: &Password) -> Result<(), Error> {
|
||||||
let accounts = self.get_matching(account_ref, password)?;
|
let accounts = self.get_matching(account_ref, password)?;
|
||||||
|
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
@@ -528,7 +534,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
Err(Error::InvalidPassword)
|
Err(Error::InvalidPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_password(&self, account_ref: &StoreAccountRef, old_password: &str, new_password: &str) -> Result<(), Error> {
|
fn change_password(&self, account_ref: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error> {
|
||||||
let accounts = self.get_matching(account_ref, old_password)?;
|
let accounts = self.get_matching(account_ref, old_password)?;
|
||||||
|
|
||||||
if accounts.is_empty() {
|
if accounts.is_empty() {
|
||||||
@@ -544,11 +550,11 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn export_account(&self, account_ref: &StoreAccountRef, password: &str) -> Result<OpaqueKeyFile, Error> {
|
fn export_account(&self, account_ref: &StoreAccountRef, password: &Password) -> Result<OpaqueKeyFile, Error> {
|
||||||
self.get_matching(account_ref, password)?.into_iter().nth(0).map(Into::into).ok_or(Error::InvalidPassword)
|
self.get_matching(account_ref, password)?.into_iter().nth(0).map(Into::into).ok_or(Error::InvalidPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result<Signature, Error> {
|
fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result<Signature, Error> {
|
||||||
let accounts = self.get_matching(account, password)?;
|
let accounts = self.get_matching(account, password)?;
|
||||||
match accounts.first() {
|
match accounts.first() {
|
||||||
Some(ref account) => account.sign(password, message),
|
Some(ref account) => account.sign(password, message),
|
||||||
@@ -556,7 +562,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let accounts = self.get_matching(account, password)?;
|
let accounts = self.get_matching(account, password)?;
|
||||||
match accounts.first() {
|
match accounts.first() {
|
||||||
Some(ref account) => account.decrypt(password, shared_mac, message),
|
Some(ref account) => account.decrypt(password, shared_mac, message),
|
||||||
@@ -564,7 +570,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result<Secret, Error> {
|
fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result<Secret, Error> {
|
||||||
let accounts = self.get_matching(account, password)?;
|
let accounts = self.get_matching(account, password)?;
|
||||||
match accounts.first() {
|
match accounts.first() {
|
||||||
Some(ref account) => account.agree(password, other),
|
Some(ref account) => account.agree(password, other),
|
||||||
@@ -572,7 +578,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_vault(&self, name: &str, password: &str) -> Result<(), Error> {
|
fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||||
let is_vault_created = { // lock border
|
let is_vault_created = { // lock border
|
||||||
let mut vaults = self.vaults.lock();
|
let mut vaults = self.vaults.lock();
|
||||||
if !vaults.contains_key(&name.to_owned()) {
|
if !vaults.contains_key(&name.to_owned()) {
|
||||||
@@ -592,7 +598,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_vault(&self, name: &str, password: &str) -> Result<(), Error> {
|
fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||||
let is_vault_opened = { // lock border
|
let is_vault_opened = { // lock border
|
||||||
let mut vaults = self.vaults.lock();
|
let mut vaults = self.vaults.lock();
|
||||||
if !vaults.contains_key(&name.to_owned()) {
|
if !vaults.contains_key(&name.to_owned()) {
|
||||||
@@ -629,7 +635,7 @@ impl SimpleSecretStore for EthMultiStore {
|
|||||||
Ok(self.vaults.lock().keys().cloned().collect())
|
Ok(self.vaults.lock().keys().cloned().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_vault_password(&self, name: &str, new_password: &str) -> Result<(), Error> {
|
fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> {
|
||||||
let old_key = self.vaults.lock().get(name).map(|v| v.key()).ok_or(Error::VaultNotFound)?;
|
let old_key = self.vaults.lock().get(name).map(|v| v.key()).ok_or(Error::VaultNotFound)?;
|
||||||
let vault_provider = self.dir.as_vault_provider().ok_or(Error::VaultsAreNotSupported)?;
|
let vault_provider = self.dir.as_vault_provider().ok_or(Error::VaultsAreNotSupported)?;
|
||||||
let vault = vault_provider.open(name, old_key)?;
|
let vault = vault_provider.open(name, old_key)?;
|
||||||
@@ -732,7 +738,8 @@ mod tests {
|
|||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
|
let passwd = "test".into();
|
||||||
|
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(address, StoreAccountRef::root(keypair.address()));
|
assert_eq!(address, StoreAccountRef::root(keypair.address()));
|
||||||
@@ -745,7 +752,8 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let store = store();
|
let store = store();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
|
let passwd = "test".into();
|
||||||
|
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd).unwrap();
|
||||||
assert_eq!(&store.meta(&address).unwrap(), "{}");
|
assert_eq!(&store.meta(&address).unwrap(), "{}");
|
||||||
assert_eq!(&store.name(&address).unwrap(), "");
|
assert_eq!(&store.name(&address).unwrap(), "");
|
||||||
|
|
||||||
@@ -763,11 +771,12 @@ mod tests {
|
|||||||
fn should_remove_account() {
|
fn should_remove_account() {
|
||||||
// given
|
// given
|
||||||
let store = store();
|
let store = store();
|
||||||
|
let passwd = "test".into();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
|
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.remove_account(&address, "test").unwrap();
|
store.remove_account(&address, &passwd).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(store.accounts().unwrap().len(), 0, "Should remove account.");
|
assert_eq!(store.accounts().unwrap().len(), 0, "Should remove account.");
|
||||||
@@ -777,12 +786,13 @@ mod tests {
|
|||||||
fn should_return_true_if_password_is_correct() {
|
fn should_return_true_if_password_is_correct() {
|
||||||
// given
|
// given
|
||||||
let store = store();
|
let store = store();
|
||||||
|
let passwd = "test".into();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
|
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res1 = store.test_password(&address, "x").unwrap();
|
let res1 = store.test_password(&address, &"x".into()).unwrap();
|
||||||
let res2 = store.test_password(&address, "test").unwrap();
|
let res2 = store.test_password(&address, &passwd).unwrap();
|
||||||
|
|
||||||
assert!(!res1, "First password should be invalid.");
|
assert!(!res1, "First password should be invalid.");
|
||||||
assert!(res2, "Second password should be correct.");
|
assert!(res2, "Second password should be correct.");
|
||||||
@@ -792,16 +802,18 @@ mod tests {
|
|||||||
fn multistore_should_be_able_to_have_the_same_account_twice() {
|
fn multistore_should_be_able_to_have_the_same_account_twice() {
|
||||||
// given
|
// given
|
||||||
let store = multi_store();
|
let store = multi_store();
|
||||||
|
let passwd1 = "test".into();
|
||||||
|
let passwd2 = "xyz".into();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
|
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd1).unwrap();
|
||||||
let address2 = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "xyz").unwrap();
|
let address2 = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd2).unwrap();
|
||||||
assert_eq!(address, address2);
|
assert_eq!(address, address2);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
assert!(store.remove_account(&address, "test").is_ok(), "First password should work.");
|
assert!(store.remove_account(&address, &passwd1).is_ok(), "First password should work.");
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
|
|
||||||
assert!(store.remove_account(&address, "xyz").is_ok(), "Second password should work too.");
|
assert!(store.remove_account(&address, &passwd2).is_ok(), "Second password should work too.");
|
||||||
assert_eq!(store.accounts().unwrap().len(), 0);
|
assert_eq!(store.accounts().unwrap().len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,17 +821,19 @@ mod tests {
|
|||||||
fn should_copy_account() {
|
fn should_copy_account() {
|
||||||
// given
|
// given
|
||||||
let store = store();
|
let store = store();
|
||||||
|
let passwd1 = "test".into();
|
||||||
|
let passwd2 = "xzy".into();
|
||||||
let multi_store = multi_store();
|
let multi_store = multi_store();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
|
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd1).unwrap();
|
||||||
assert_eq!(multi_store.accounts().unwrap().len(), 0);
|
assert_eq!(multi_store.accounts().unwrap().len(), 0);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.copy_account(&multi_store, SecretVaultRef::Root, &address, "test", "xyz").unwrap();
|
store.copy_account(&multi_store, SecretVaultRef::Root, &address, &passwd1, &passwd2).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(store.test_password(&address, "test").unwrap(), "First password should work for store.");
|
assert!(store.test_password(&address, &passwd1).unwrap(), "First password should work for store.");
|
||||||
assert!(multi_store.sign(&address, "xyz", &Default::default()).is_ok(), "Second password should work for second store.");
|
assert!(multi_store.sign(&address, &passwd2, &Default::default()).is_ok(), "Second password should work for second store.");
|
||||||
assert_eq!(multi_store.accounts().unwrap().len(), 1);
|
assert_eq!(multi_store.accounts().unwrap().len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -828,23 +842,23 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name1 = "vault1"; let password1 = "password1";
|
let name1 = "vault1"; let password1 = "password1".into();
|
||||||
let name2 = "vault2"; let password2 = "password2";
|
let name2 = "vault2"; let password2 = "password2".into();
|
||||||
let keypair1 = keypair();
|
let keypair1 = keypair();
|
||||||
let keypair2 = keypair();
|
let keypair2 = keypair();
|
||||||
let keypair3 = keypair(); let password3 = "password3";
|
let keypair3 = keypair(); let password3 = "password3".into();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name1, password1).unwrap();
|
store.create_vault(name1, &password1).unwrap();
|
||||||
store.create_vault(name2, password2).unwrap();
|
store.create_vault(name2, &password2).unwrap();
|
||||||
|
|
||||||
// then [can create vaults] ^^^
|
// then [can create vaults] ^^^
|
||||||
|
|
||||||
// and when
|
// and when
|
||||||
store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), password1).unwrap();
|
store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), &password1).unwrap();
|
||||||
store.insert_account(SecretVaultRef::Vault(name2.to_owned()), keypair2.secret().clone(), password2).unwrap();
|
store.insert_account(SecretVaultRef::Vault(name2.to_owned()), keypair2.secret().clone(), &password2).unwrap();
|
||||||
store.insert_account(SecretVaultRef::Root, keypair3.secret().clone(), password3).unwrap();
|
store.insert_account(SecretVaultRef::Root, keypair3.secret().clone(), &password3).unwrap();
|
||||||
store.insert_account(SecretVaultRef::Vault("vault3".to_owned()), keypair1.secret().clone(), password3).unwrap_err();
|
store.insert_account(SecretVaultRef::Vault("vault3".to_owned()), keypair1.secret().clone(), &password3).unwrap_err();
|
||||||
let accounts = store.accounts().unwrap();
|
let accounts = store.accounts().unwrap();
|
||||||
|
|
||||||
// then [can create accounts in vaults]
|
// then [can create accounts in vaults]
|
||||||
@@ -864,10 +878,10 @@ mod tests {
|
|||||||
assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root));
|
assert!(accounts.iter().any(|a| a.vault == SecretVaultRef::Root));
|
||||||
|
|
||||||
// and when
|
// and when
|
||||||
store.open_vault(name1, password2).unwrap_err();
|
store.open_vault(name1, &password2).unwrap_err();
|
||||||
store.open_vault(name2, password1).unwrap_err();
|
store.open_vault(name2, &password1).unwrap_err();
|
||||||
store.open_vault(name1, password1).unwrap();
|
store.open_vault(name1, &password1).unwrap();
|
||||||
store.open_vault(name2, password2).unwrap();
|
store.open_vault(name2, &password2).unwrap();
|
||||||
let accounts = store.accounts().unwrap();
|
let accounts = store.accounts().unwrap();
|
||||||
|
|
||||||
// then [can check vaults on open + can reopen vaults + accounts from vaults appear]
|
// then [can check vaults on open + can reopen vaults + accounts from vaults appear]
|
||||||
@@ -882,19 +896,19 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name1 = "vault1"; let password1 = "password1";
|
let name1 = "vault1"; let password1 = "password1".into();
|
||||||
let name2 = "vault2"; let password2 = "password2";
|
let name2 = "vault2"; let password2 = "password2".into();
|
||||||
let password3 = "password3";
|
let password3 = "password3".into();
|
||||||
let keypair1 = keypair();
|
let keypair1 = keypair();
|
||||||
let keypair2 = keypair();
|
let keypair2 = keypair();
|
||||||
let keypair3 = keypair();
|
let keypair3 = keypair();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name1, password1).unwrap();
|
store.create_vault(name1, &password1).unwrap();
|
||||||
store.create_vault(name2, password2).unwrap();
|
store.create_vault(name2, &password2).unwrap();
|
||||||
let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), password1).unwrap();
|
let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), &password1).unwrap();
|
||||||
let account2 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair2.secret().clone(), password1).unwrap();
|
let account2 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair2.secret().clone(), &password1).unwrap();
|
||||||
let account3 = store.insert_account(SecretVaultRef::Root, keypair3.secret().clone(), password3).unwrap();
|
let account3 = store.insert_account(SecretVaultRef::Root, keypair3.secret().clone(), &password3).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
let account1 = store.change_account_vault(SecretVaultRef::Root, account1.clone()).unwrap();
|
let account1 = store.change_account_vault(SecretVaultRef::Root, account1.clone()).unwrap();
|
||||||
@@ -917,11 +931,11 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let password1 = "password1";
|
let password1 = "password1".into();
|
||||||
let keypair1 = keypair();
|
let keypair1 = keypair();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let account1 = store.insert_account(SecretVaultRef::Root, keypair1.secret().clone(), password1).unwrap();
|
let account1 = store.insert_account(SecretVaultRef::Root, keypair1.secret().clone(), &password1).unwrap();
|
||||||
store.change_account_vault(SecretVaultRef::Root, account1).unwrap();
|
store.change_account_vault(SecretVaultRef::Root, account1).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@@ -934,16 +948,16 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name1 = "vault1"; let password1 = "password1";
|
let name1 = "vault1"; let password1 = "password1".into();
|
||||||
let keypair1 = keypair();
|
let keypair1 = keypair();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name1, password1).unwrap();
|
store.create_vault(name1, &password1).unwrap();
|
||||||
let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), password1).unwrap();
|
let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), &password1).unwrap();
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
store.remove_account(&account1, password1).unwrap();
|
store.remove_account(&account1, &password1).unwrap();
|
||||||
assert_eq!(store.accounts().unwrap().len(), 0);
|
assert_eq!(store.accounts().unwrap().len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -952,17 +966,17 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name1 = "vault1"; let password1 = "password1";
|
let name1 = "vault1"; let password1 = "password1".into();
|
||||||
let password2 = "password2";
|
let password2 = "password2".into();
|
||||||
let keypair1 = keypair();
|
let keypair1 = keypair();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name1, password1).unwrap();
|
store.create_vault(name1, &password1).unwrap();
|
||||||
let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), password1).unwrap();
|
let account1 = store.insert_account(SecretVaultRef::Vault(name1.to_owned()), keypair1.secret().clone(), &password1).unwrap();
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
store.remove_account(&account1, password2).unwrap_err();
|
store.remove_account(&account1, &password2).unwrap_err();
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -971,24 +985,24 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name = "vault"; let password = "password";
|
let name = "vault"; let password = "password".into();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name, password).unwrap();
|
store.create_vault(name, &password).unwrap();
|
||||||
store.insert_account(SecretVaultRef::Vault(name.to_owned()), keypair.secret().clone(), password).unwrap();
|
store.insert_account(SecretVaultRef::Vault(name.to_owned()), keypair.secret().clone(), &password).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
let new_password = "new_password";
|
let new_password = "new_password".into();
|
||||||
store.change_vault_password(name, new_password).unwrap();
|
store.change_vault_password(name, &new_password).unwrap();
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
|
|
||||||
// and when
|
// and when
|
||||||
store.close_vault(name).unwrap();
|
store.close_vault(name).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
store.open_vault(name, new_password).unwrap();
|
store.open_vault(name, &new_password).unwrap();
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -997,18 +1011,18 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name = "vault"; let password = "password";
|
let name = "vault"; let password = "password".into();
|
||||||
let secret_password = "sec_password";
|
let secret_password = "sec_password".into();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name, password).unwrap();
|
store.create_vault(name, &password).unwrap();
|
||||||
let account_ref = store.insert_account(SecretVaultRef::Vault(name.to_owned()), keypair.secret().clone(), secret_password).unwrap();
|
let account_ref = store.insert_account(SecretVaultRef::Vault(name.to_owned()), keypair.secret().clone(), &secret_password).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
let new_secret_password = "new_sec_password";
|
let new_secret_password = "new_sec_password".into();
|
||||||
store.change_password(&account_ref, secret_password, new_secret_password).unwrap();
|
store.change_password(&account_ref, &secret_password, &new_secret_password).unwrap();
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1017,14 +1031,14 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name1 = "vault1"; let password1 = "password1";
|
let name1 = "vault1"; let password1 = "password1".into();
|
||||||
let name2 = "vault2"; let password2 = "password2";
|
let name2 = "vault2"; let password2 = "password2".into();
|
||||||
let name3 = "vault3"; let password3 = "password3";
|
let name3 = "vault3"; let password3 = "password3".into();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name1, password1).unwrap();
|
store.create_vault(name1, &password1).unwrap();
|
||||||
store.create_vault(name2, password2).unwrap();
|
store.create_vault(name2, &password2).unwrap();
|
||||||
store.create_vault(name3, password3).unwrap();
|
store.create_vault(name3, &password3).unwrap();
|
||||||
store.close_vault(name2).unwrap();
|
store.close_vault(name2).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@@ -1039,10 +1053,10 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name1 = "vault1"; let password1 = "password1";
|
let name1 = "vault1"; let password1 = "password1".into();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name1, password1).unwrap();
|
store.create_vault(name1, &password1).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(store.get_vault_meta(name1).unwrap(), "{}".to_owned());
|
assert_eq!(store.get_vault_meta(name1).unwrap(), "{}".to_owned());
|
||||||
@@ -1051,7 +1065,7 @@ mod tests {
|
|||||||
|
|
||||||
// and when
|
// and when
|
||||||
store.close_vault(name1).unwrap();
|
store.close_vault(name1).unwrap();
|
||||||
store.open_vault(name1, password1).unwrap();
|
store.open_vault(name1, &password1).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(store.get_vault_meta(name1).unwrap(), "Hello, world!!!".to_owned());
|
assert_eq!(store.get_vault_meta(name1).unwrap(), "Hello, world!!!".to_owned());
|
||||||
@@ -1069,13 +1083,13 @@ mod tests {
|
|||||||
// given we have one account in the store
|
// given we have one account in the store
|
||||||
let store = store();
|
let store = store();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
|
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &"test".into()).unwrap();
|
||||||
|
|
||||||
// when we deriving from that account
|
// when we deriving from that account
|
||||||
let derived = store.insert_derived(
|
let derived = store.insert_derived(
|
||||||
SecretVaultRef::Root,
|
SecretVaultRef::Root,
|
||||||
&address,
|
&address,
|
||||||
"test",
|
&"test".into(),
|
||||||
Derivation::HardHash(H256::from(0)),
|
Derivation::HardHash(H256::from(0)),
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
@@ -1084,7 +1098,7 @@ mod tests {
|
|||||||
assert_eq!(accounts.len(), 2);
|
assert_eq!(accounts.len(), 2);
|
||||||
|
|
||||||
// and we can sign with the derived contract
|
// and we can sign with the derived contract
|
||||||
assert!(store.sign(&derived, "test", &Default::default()).is_ok(), "Second password should work for second store.");
|
assert!(store.sign(&derived, &"test".into(), &Default::default()).is_ok(), "Second password should work for second store.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1092,13 +1106,13 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let mut dir = RootDiskDirectoryGuard::new();
|
let mut dir = RootDiskDirectoryGuard::new();
|
||||||
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
let store = EthStore::open(dir.key_dir.take().unwrap()).unwrap();
|
||||||
let name = "vault"; let password = "password1";
|
let name = "vault"; let password = "password1".into();
|
||||||
let new_password = "password2";
|
let new_password = "password2".into();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
store.create_vault(name, password).unwrap();
|
store.create_vault(name, &password).unwrap();
|
||||||
store.set_vault_meta(name, "OldMeta").unwrap();
|
store.set_vault_meta(name, "OldMeta").unwrap();
|
||||||
store.change_vault_password(name, new_password).unwrap();
|
store.change_vault_password(name, &new_password).unwrap();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(store.get_vault_meta(name).unwrap(), "OldMeta".to_owned());
|
assert_eq!(store.get_vault_meta(name).unwrap(), "OldMeta".to_owned());
|
||||||
@@ -1109,10 +1123,10 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let store = store();
|
let store = store();
|
||||||
let keypair = keypair();
|
let keypair = keypair();
|
||||||
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
|
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &"test".into()).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let exported = store.export_account(&address, "test");
|
let exported = store.export_account(&address, &"test".into());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert!(exported.is_ok(), "Should export single account: {:?}", exported);
|
assert!(exported.is_ok(), "Should export single account: {:?}", exported);
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -25,7 +25,7 @@ use Error;
|
|||||||
|
|
||||||
/// Import an account from a file.
|
/// Import an account from a file.
|
||||||
pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error> {
|
pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error> {
|
||||||
let key_manager = DiskKeyFileManager;
|
let key_manager = DiskKeyFileManager::default();
|
||||||
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>();
|
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>();
|
||||||
let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned());
|
let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned());
|
||||||
let account = fs::File::open(&path)
|
let account = fs::File::open(&path)
|
||||||
@@ -42,7 +42,9 @@ pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error>
|
|||||||
/// Import all accounts from one directory to the other.
|
/// Import all accounts from one directory to the other.
|
||||||
pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result<Vec<Address>, Error> {
|
pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result<Vec<Address>, Error> {
|
||||||
let accounts = src.load()?;
|
let accounts = src.load()?;
|
||||||
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>();
|
let existing_accounts = dst.load()?.into_iter()
|
||||||
|
.map(|a| a.address)
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
accounts.into_iter()
|
accounts.into_iter()
|
||||||
.filter(|a| !existing_accounts.contains(&a.address))
|
.filter(|a| !existing_accounts.contains(&a.address))
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::{ops, str};
|
use std::{ops, str};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
@@ -72,4 +72,3 @@ impl From<Bytes> for Vec<u8> {
|
|||||||
b.0
|
b.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::{fmt, str};
|
use std::{fmt, str};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
@@ -52,6 +52,7 @@ enum CryptoField {
|
|||||||
Kdf,
|
Kdf,
|
||||||
KdfParams,
|
KdfParams,
|
||||||
Mac,
|
Mac,
|
||||||
|
Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Deserialize<'a> for CryptoField {
|
impl<'a> Deserialize<'a> for CryptoField {
|
||||||
@@ -81,6 +82,7 @@ impl<'a> Visitor<'a> for CryptoFieldVisitor {
|
|||||||
"kdf" => Ok(CryptoField::Kdf),
|
"kdf" => Ok(CryptoField::Kdf),
|
||||||
"kdfparams" => Ok(CryptoField::KdfParams),
|
"kdfparams" => Ok(CryptoField::KdfParams),
|
||||||
"mac" => Ok(CryptoField::Mac),
|
"mac" => Ok(CryptoField::Mac),
|
||||||
|
"version" => Ok(CryptoField::Version),
|
||||||
_ => Err(Error::custom(format!("Unknown field: '{}'", value))),
|
_ => Err(Error::custom(format!("Unknown field: '{}'", value))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,6 +124,8 @@ impl<'a> Visitor<'a> for CryptoVisitor {
|
|||||||
Some(CryptoField::Kdf) => { kdf = Some(visitor.next_value()?); }
|
Some(CryptoField::Kdf) => { kdf = Some(visitor.next_value()?); }
|
||||||
Some(CryptoField::KdfParams) => { kdfparams = Some(visitor.next_value()?); }
|
Some(CryptoField::KdfParams) => { kdfparams = Some(visitor.next_value()?); }
|
||||||
Some(CryptoField::Mac) => { mac = Some(visitor.next_value()?); }
|
Some(CryptoField::Mac) => { mac = Some(visitor.next_value()?); }
|
||||||
|
// skip not required version field (it appears in pyethereum generated keystores)
|
||||||
|
Some(CryptoField::Version) => { visitor.next_value().unwrap_or(()) }
|
||||||
None => { break; }
|
None => { break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::{ops, fmt, str};
|
use std::{ops, fmt, str};
|
||||||
use rustc_hex::{FromHex, ToHex};
|
use rustc_hex::{FromHex, ToHex};
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Universaly unique identifier.
|
//! Universaly unique identifier.
|
||||||
use std::{fmt, str};
|
use std::{fmt, str};
|
||||||
@@ -1,23 +1,24 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||||
use serde::de::{Visitor, Error as SerdeError};
|
use serde::de::{Visitor, Error as SerdeError};
|
||||||
use super::{Error, H256};
|
use super::{Error, Bytes};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum KdfSer {
|
pub enum KdfSer {
|
||||||
@@ -108,10 +109,10 @@ impl<'a> Visitor<'a> for PrfVisitor {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Pbkdf2 {
|
pub struct Pbkdf2 {
|
||||||
pub c: u32,
|
pub c: NonZeroU32,
|
||||||
pub dklen: u32,
|
pub dklen: u32,
|
||||||
pub prf: Prf,
|
pub prf: Prf,
|
||||||
pub salt: H256,
|
pub salt: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
@@ -120,7 +121,7 @@ pub struct Scrypt {
|
|||||||
pub p: u32,
|
pub p: u32,
|
||||||
pub n: u32,
|
pub n: u32,
|
||||||
pub r: u32,
|
pub r: u32,
|
||||||
pub salt: H256,
|
pub salt: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
@@ -46,7 +46,7 @@ pub struct KeyFile {
|
|||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
pub crypto: Crypto,
|
pub crypto: Crypto,
|
||||||
pub address: H160,
|
pub address: Option<H160>,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub meta: Option<String>,
|
pub meta: Option<String>,
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,6 @@ impl<'a> Deserialize<'a> for KeyFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T> where
|
fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T> where
|
||||||
T: DeserializeOwned
|
T: DeserializeOwned
|
||||||
{
|
{
|
||||||
@@ -159,11 +158,6 @@ impl<'a> Visitor<'a> for KeyFileVisitor {
|
|||||||
None => return Err(V::Error::missing_field("crypto")),
|
None => return Err(V::Error::missing_field("crypto")),
|
||||||
};
|
};
|
||||||
|
|
||||||
let address = match address {
|
|
||||||
Some(address) => address,
|
|
||||||
None => return Err(V::Error::missing_field("address")),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = KeyFile {
|
let result = KeyFile {
|
||||||
id: id,
|
id: id,
|
||||||
version: version,
|
version: version,
|
||||||
@@ -223,7 +217,7 @@ mod tests {
|
|||||||
let expected = KeyFile {
|
let expected = KeyFile {
|
||||||
id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
|
||||||
version: Version::V3,
|
version: Version::V3,
|
||||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||||
crypto: Crypto {
|
crypto: Crypto {
|
||||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||||
@@ -274,7 +268,7 @@ mod tests {
|
|||||||
let expected = KeyFile {
|
let expected = KeyFile {
|
||||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||||
version: Version::V3,
|
version: Version::V3,
|
||||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||||
crypto: Crypto {
|
crypto: Crypto {
|
||||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||||
@@ -302,7 +296,7 @@ mod tests {
|
|||||||
let file = KeyFile {
|
let file = KeyFile {
|
||||||
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
|
||||||
version: Version::V3,
|
version: Version::V3,
|
||||||
address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(),
|
address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
|
||||||
crypto: Crypto {
|
crypto: Crypto {
|
||||||
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
cipher: Cipher::Aes128Ctr(Aes128Ctr {
|
||||||
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Contract interface specification.
|
//! Contract interface specification.
|
||||||
|
|
||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use super::{H160, Bytes};
|
use super::{H160, Bytes};
|
||||||
19
ethstore/src/json/vault_file.rs → accounts/ethstore/src/json/vault_file.rs
Executable file → Normal file
19
ethstore/src/json/vault_file.rs → accounts/ethstore/src/json/vault_file.rs
Executable file → Normal file
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
@@ -41,6 +41,11 @@ impl VaultFile {
|
|||||||
mod test {
|
mod test {
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use json::{VaultFile, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf};
|
use json::{VaultFile, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf};
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_and_from_json() {
|
fn to_and_from_json() {
|
||||||
@@ -51,7 +56,7 @@ mod test {
|
|||||||
}),
|
}),
|
||||||
ciphertext: "4d6938a1f49b7782".into(),
|
ciphertext: "4d6938a1f49b7782".into(),
|
||||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||||
c: 1024,
|
c: *ITERATIONS,
|
||||||
dklen: 32,
|
dklen: 32,
|
||||||
prf: Prf::HmacSha256,
|
prf: Prf::HmacSha256,
|
||||||
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
||||||
@@ -76,7 +81,7 @@ mod test {
|
|||||||
}),
|
}),
|
||||||
ciphertext: "4d6938a1f49b7782".into(),
|
ciphertext: "4d6938a1f49b7782".into(),
|
||||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||||
c: 1024,
|
c: *ITERATIONS,
|
||||||
dklen: 32,
|
dklen: 32,
|
||||||
prf: Prf::HmacSha256,
|
prf: Prf::HmacSha256,
|
||||||
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
salt: "b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5".into(),
|
||||||
19
ethstore/src/json/vault_key_file.rs → accounts/ethstore/src/json/vault_key_file.rs
Executable file → Normal file
19
ethstore/src/json/vault_key_file.rs → accounts/ethstore/src/json/vault_key_file.rs
Executable file → Normal file
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
@@ -106,6 +106,11 @@ mod test {
|
|||||||
use serde_json;
|
use serde_json;
|
||||||
use json::{VaultKeyFile, Version, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf,
|
use json::{VaultKeyFile, Version, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf,
|
||||||
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
|
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_and_from_json() {
|
fn to_and_from_json() {
|
||||||
@@ -118,7 +123,7 @@ mod test {
|
|||||||
}),
|
}),
|
||||||
ciphertext: "4befe0a66d9a4b6fec8e39eb5c90ac5dafdeaab005fff1af665fd1f9af925c91".into(),
|
ciphertext: "4befe0a66d9a4b6fec8e39eb5c90ac5dafdeaab005fff1af665fd1f9af925c91".into(),
|
||||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||||
c: 10240,
|
c: *ITERATIONS,
|
||||||
dklen: 32,
|
dklen: 32,
|
||||||
prf: Prf::HmacSha256,
|
prf: Prf::HmacSha256,
|
||||||
salt: "f17731e84ecac390546692dbd4ccf6a3a2720dc9652984978381e61c28a471b2".into(),
|
salt: "f17731e84ecac390546692dbd4ccf6a3a2720dc9652984978381e61c28a471b2".into(),
|
||||||
@@ -131,7 +136,7 @@ mod test {
|
|||||||
}),
|
}),
|
||||||
ciphertext: "fef0d113d7576c1702daf380ad6f4c5408389e57991cae2a174facd74bd549338e1014850bddbab7eb486ff5f5c9c5532800c6a6d4db2be2212cd5cd3769244ab230e1f369e8382a9e6d7c0a".into(),
|
ciphertext: "fef0d113d7576c1702daf380ad6f4c5408389e57991cae2a174facd74bd549338e1014850bddbab7eb486ff5f5c9c5532800c6a6d4db2be2212cd5cd3769244ab230e1f369e8382a9e6d7c0a".into(),
|
||||||
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
kdf: Kdf::Pbkdf2(Pbkdf2 {
|
||||||
c: 10240,
|
c: *ITERATIONS,
|
||||||
dklen: 32,
|
dklen: 32,
|
||||||
prf: Prf::HmacSha256,
|
prf: Prf::HmacSha256,
|
||||||
salt: "aca82865174a82249a198814b263f43a631f272cbf7ed329d0f0839d259c652a".into(),
|
salt: "aca82865174a82249a198814b263f43a631f272cbf7ed329d0f0839d259c652a".into(),
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||||
@@ -56,4 +56,3 @@ impl<'a> Visitor<'a> for VersionVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
19
ethstore/src/lib.rs → accounts/ethstore/src/lib.rs
Executable file → Normal file
19
ethstore/src/lib.rs → accounts/ethstore/src/lib.rs
Executable file → Normal file
@@ -1,24 +1,23 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Ethereum key-management.
|
//! Ethereum key-management.
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
extern crate crypto as rcrypto;
|
|
||||||
extern crate dir;
|
extern crate dir;
|
||||||
extern crate itertools;
|
extern crate itertools;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
@@ -32,16 +31,22 @@ extern crate time;
|
|||||||
extern crate tiny_keccak;
|
extern crate tiny_keccak;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
extern crate ethcrypto as crypto;
|
extern crate parity_crypto as crypto;
|
||||||
extern crate ethereum_types;
|
extern crate ethereum_types;
|
||||||
extern crate ethkey as _ethkey;
|
extern crate ethkey as _ethkey;
|
||||||
extern crate parity_wordlist;
|
extern crate parity_wordlist;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate matches;
|
||||||
|
|
||||||
pub mod accounts_dir;
|
pub mod accounts_dir;
|
||||||
pub mod ethkey;
|
pub mod ethkey;
|
||||||
|
|
||||||
@@ -1,11 +1,25 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::num::NonZeroU32;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use rcrypto::pbkdf2::pbkdf2;
|
|
||||||
use rcrypto::sha2::Sha256;
|
|
||||||
use rcrypto::hmac::Hmac;
|
|
||||||
use json;
|
use json;
|
||||||
use ethkey::{Address, Secret, KeyPair};
|
use ethkey::{Address, Secret, KeyPair, Password};
|
||||||
use crypto::Keccak256;
|
use crypto::{Keccak256, pbkdf2};
|
||||||
use {crypto, Error};
|
use {crypto, Error};
|
||||||
|
|
||||||
/// Pre-sale wallet.
|
/// Pre-sale wallet.
|
||||||
@@ -41,13 +55,16 @@ impl PresaleWallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt the wallet.
|
/// Decrypt the wallet.
|
||||||
pub fn decrypt(&self, password: &str) -> Result<KeyPair, Error> {
|
pub fn decrypt(&self, password: &Password) -> Result<KeyPair, Error> {
|
||||||
let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes());
|
let mut derived_key = [0u8; 32];
|
||||||
let mut derived_key = vec![0u8; 16];
|
let salt = pbkdf2::Salt(password.as_bytes());
|
||||||
pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key);
|
let sec = pbkdf2::Secret(password.as_bytes());
|
||||||
|
let iter = NonZeroU32::new(2000).expect("2000 > 0; qed");
|
||||||
|
pbkdf2::sha256(iter, salt, sec, &mut derived_key);
|
||||||
|
|
||||||
let mut key = vec![0; self.ciphertext.len()];
|
let mut key = vec![0; self.ciphertext.len()];
|
||||||
let len = crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword)?;
|
let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key)
|
||||||
|
.map_err(|_| Error::InvalidPassword)?;
|
||||||
let unpadded = &key[..len];
|
let unpadded = &key[..len];
|
||||||
|
|
||||||
let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?;
|
let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?;
|
||||||
@@ -78,7 +95,7 @@ mod tests {
|
|||||||
|
|
||||||
let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap();
|
let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap();
|
||||||
let wallet = PresaleWallet::from(wallet);
|
let wallet = PresaleWallet::from(wallet);
|
||||||
assert!(wallet.decrypt("123").is_ok());
|
assert!(wallet.decrypt(&"123".into()).is_ok());
|
||||||
assert!(wallet.decrypt("124").is_err());
|
assert!(wallet.decrypt(&"124".into()).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use rand::{Rng, OsRng};
|
use rand::{Rng, OsRng};
|
||||||
|
|
||||||
@@ -43,4 +43,3 @@ pub fn random_string(length: usize) -> String {
|
|||||||
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
let mut rng = OsRng::new().expect("Not able to operate without random source.");
|
||||||
rng.gen_ascii_chars().take(length).collect()
|
rng.gen_ascii_chars().take(length).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
50
ethstore/src/secret_store.rs → accounts/ethstore/src/secret_store.rs
Executable file → Normal file
50
ethstore/src/secret_store.rs → accounts/ethstore/src/secret_store.rs
Executable file → Normal file
@@ -1,23 +1,23 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use ethkey::{Address, Message, Signature, Secret, Public};
|
use ethkey::{Address, Message, Signature, Secret, Password, Public};
|
||||||
use Error;
|
use Error;
|
||||||
use json::{Uuid, OpaqueKeyFile};
|
use json::{Uuid, OpaqueKeyFile};
|
||||||
use ethereum_types::H256;
|
use ethereum_types::H256;
|
||||||
@@ -56,25 +56,25 @@ impl ::std::borrow::Borrow<Address> for StoreAccountRef {
|
|||||||
/// Simple Secret Store API
|
/// Simple Secret Store API
|
||||||
pub trait SimpleSecretStore: Send + Sync {
|
pub trait SimpleSecretStore: Send + Sync {
|
||||||
/// Inserts new accounts to the store (or vault) with given password.
|
/// Inserts new accounts to the store (or vault) with given password.
|
||||||
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &str) -> Result<StoreAccountRef, Error>;
|
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result<StoreAccountRef, Error>;
|
||||||
/// Inserts new derived account to the store (or vault) with given password.
|
/// Inserts new derived account to the store (or vault) with given password.
|
||||||
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &str, derivation: Derivation) -> Result<StoreAccountRef, Error>;
|
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<StoreAccountRef, Error>;
|
||||||
/// Changes accounts password.
|
/// Changes accounts password.
|
||||||
fn change_password(&self, account: &StoreAccountRef, old_password: &str, new_password: &str) -> Result<(), Error>;
|
fn change_password(&self, account: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error>;
|
||||||
/// Exports key details for account.
|
/// Exports key details for account.
|
||||||
fn export_account(&self, account: &StoreAccountRef, password: &str) -> Result<OpaqueKeyFile, Error>;
|
fn export_account(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueKeyFile, Error>;
|
||||||
/// Entirely removes account from the store and underlying storage.
|
/// Entirely removes account from the store and underlying storage.
|
||||||
fn remove_account(&self, account: &StoreAccountRef, password: &str) -> Result<(), Error>;
|
fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>;
|
||||||
/// Generates new derived account.
|
/// Generates new derived account.
|
||||||
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation) -> Result<Address, Error>;
|
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<Address, Error>;
|
||||||
/// Sign a message with given account.
|
/// Sign a message with given account.
|
||||||
fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result<Signature, Error>;
|
fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result<Signature, Error>;
|
||||||
/// Sign a message with derived account.
|
/// Sign a message with derived account.
|
||||||
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message) -> Result<Signature, Error>;
|
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message) -> Result<Signature, Error>;
|
||||||
/// Decrypt a messages with given account.
|
/// Decrypt a messages with given account.
|
||||||
fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
|
fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
|
||||||
/// Agree on shared key.
|
/// Agree on shared key.
|
||||||
fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result<Secret, Error>;
|
fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result<Secret, Error>;
|
||||||
|
|
||||||
/// Returns all accounts in this secret store.
|
/// Returns all accounts in this secret store.
|
||||||
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>;
|
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>;
|
||||||
@@ -83,9 +83,9 @@ pub trait SimpleSecretStore: Send + Sync {
|
|||||||
fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error>;
|
fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error>;
|
||||||
|
|
||||||
/// Create new vault with given password
|
/// Create new vault with given password
|
||||||
fn create_vault(&self, name: &str, password: &str) -> Result<(), Error>;
|
fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error>;
|
||||||
/// Open vault with given password
|
/// Open vault with given password
|
||||||
fn open_vault(&self, name: &str, password: &str) -> Result<(), Error>;
|
fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error>;
|
||||||
/// Close vault
|
/// Close vault
|
||||||
fn close_vault(&self, name: &str) -> Result<(), Error>;
|
fn close_vault(&self, name: &str) -> Result<(), Error>;
|
||||||
/// List all vaults
|
/// List all vaults
|
||||||
@@ -93,7 +93,7 @@ pub trait SimpleSecretStore: Send + Sync {
|
|||||||
/// List all currently opened vaults
|
/// List all currently opened vaults
|
||||||
fn list_opened_vaults(&self) -> Result<Vec<String>, Error>;
|
fn list_opened_vaults(&self) -> Result<Vec<String>, Error>;
|
||||||
/// Change vault password
|
/// Change vault password
|
||||||
fn change_vault_password(&self, name: &str, new_password: &str) -> Result<(), Error>;
|
fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>;
|
||||||
/// Cnage account' vault
|
/// Cnage account' vault
|
||||||
fn change_account_vault(&self, vault: SecretVaultRef, account: StoreAccountRef) -> Result<StoreAccountRef, Error>;
|
fn change_account_vault(&self, vault: SecretVaultRef, account: StoreAccountRef) -> Result<StoreAccountRef, Error>;
|
||||||
/// Get vault metadata string.
|
/// Get vault metadata string.
|
||||||
@@ -106,7 +106,7 @@ pub trait SimpleSecretStore: Send + Sync {
|
|||||||
pub trait SecretStore: SimpleSecretStore {
|
pub trait SecretStore: SimpleSecretStore {
|
||||||
|
|
||||||
/// Returns a raw opaque Secret that can be later used to sign a message.
|
/// Returns a raw opaque Secret that can be later used to sign a message.
|
||||||
fn raw_secret(&self, account: &StoreAccountRef, password: &str) -> Result<OpaqueSecret, Error>;
|
fn raw_secret(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueSecret, Error>;
|
||||||
|
|
||||||
/// Signs a message with raw secret.
|
/// Signs a message with raw secret.
|
||||||
fn sign_with_secret(&self, secret: &OpaqueSecret, message: &Message) -> Result<Signature, Error> {
|
fn sign_with_secret(&self, secret: &OpaqueSecret, message: &Message) -> Result<Signature, Error> {
|
||||||
@@ -114,16 +114,16 @@ pub trait SecretStore: SimpleSecretStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Imports presale wallet
|
/// Imports presale wallet
|
||||||
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result<StoreAccountRef, Error>;
|
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &Password) -> Result<StoreAccountRef, Error>;
|
||||||
/// Imports existing JSON wallet
|
/// Imports existing JSON wallet
|
||||||
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &str, gen_id: bool) -> Result<StoreAccountRef, Error>;
|
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result<StoreAccountRef, Error>;
|
||||||
/// Copies account between stores and vaults.
|
/// Copies account between stores and vaults.
|
||||||
fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &str, new_password: &str) -> Result<(), Error>;
|
fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error>;
|
||||||
/// Checks if password matches given account.
|
/// Checks if password matches given account.
|
||||||
fn test_password(&self, account: &StoreAccountRef, password: &str) -> Result<bool, Error>;
|
fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result<bool, Error>;
|
||||||
|
|
||||||
/// Returns a public key for given account.
|
/// Returns a public key for given account.
|
||||||
fn public(&self, account: &StoreAccountRef, password: &str) -> Result<Public, Error>;
|
fn public(&self, account: &StoreAccountRef, password: &Password) -> Result<Public, Error>;
|
||||||
|
|
||||||
/// Returns uuid of an account.
|
/// Returns uuid of an account.
|
||||||
fn uuid(&self, account: &StoreAccountRef) -> Result<Uuid, Error>;
|
fn uuid(&self, account: &StoreAccountRef) -> Result<Uuid, Error>;
|
||||||
40
ethstore/tests/api.rs → accounts/ethstore/tests/api.rs
Executable file → Normal file
40
ethstore/tests/api.rs → accounts/ethstore/tests/api.rs
Executable file → Normal file
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate ethstore;
|
extern crate ethstore;
|
||||||
@@ -46,9 +46,9 @@ fn secret_store_create_account() {
|
|||||||
let dir = TransientDir::create().unwrap();
|
let dir = TransientDir::create().unwrap();
|
||||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||||
assert_eq!(store.accounts().unwrap().len(), 0);
|
assert_eq!(store.accounts().unwrap().len(), 0);
|
||||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), "").is_ok());
|
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||||
assert_eq!(store.accounts().unwrap().len(), 1);
|
assert_eq!(store.accounts().unwrap().len(), 1);
|
||||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), "").is_ok());
|
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||||
assert_eq!(store.accounts().unwrap().len(), 2);
|
assert_eq!(store.accounts().unwrap().len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,36 +56,36 @@ fn secret_store_create_account() {
|
|||||||
fn secret_store_sign() {
|
fn secret_store_sign() {
|
||||||
let dir = TransientDir::create().unwrap();
|
let dir = TransientDir::create().unwrap();
|
||||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), "").is_ok());
|
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||||
let accounts = store.accounts().unwrap();
|
let accounts = store.accounts().unwrap();
|
||||||
assert_eq!(accounts.len(), 1);
|
assert_eq!(accounts.len(), 1);
|
||||||
assert!(store.sign(&accounts[0], "", &Default::default()).is_ok());
|
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok());
|
||||||
assert!(store.sign(&accounts[0], "1", &Default::default()).is_err());
|
assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn secret_store_change_password() {
|
fn secret_store_change_password() {
|
||||||
let dir = TransientDir::create().unwrap();
|
let dir = TransientDir::create().unwrap();
|
||||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), "").is_ok());
|
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||||
let accounts = store.accounts().unwrap();
|
let accounts = store.accounts().unwrap();
|
||||||
assert_eq!(accounts.len(), 1);
|
assert_eq!(accounts.len(), 1);
|
||||||
assert!(store.sign(&accounts[0], "", &Default::default()).is_ok());
|
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok());
|
||||||
assert!(store.change_password(&accounts[0], "", "1").is_ok());
|
assert!(store.change_password(&accounts[0], &"".into(), &"1".into()).is_ok());
|
||||||
assert!(store.sign(&accounts[0], "", &Default::default()).is_err());
|
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_err());
|
||||||
assert!(store.sign(&accounts[0], "1", &Default::default()).is_ok());
|
assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn secret_store_remove_account() {
|
fn secret_store_remove_account() {
|
||||||
let dir = TransientDir::create().unwrap();
|
let dir = TransientDir::create().unwrap();
|
||||||
let store = EthStore::open(Box::new(dir)).unwrap();
|
let store = EthStore::open(Box::new(dir)).unwrap();
|
||||||
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), "").is_ok());
|
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok());
|
||||||
let accounts = store.accounts().unwrap();
|
let accounts = store.accounts().unwrap();
|
||||||
assert_eq!(accounts.len(), 1);
|
assert_eq!(accounts.len(), 1);
|
||||||
assert!(store.remove_account(&accounts[0], "").is_ok());
|
assert!(store.remove_account(&accounts[0], &"".into()).is_ok());
|
||||||
assert_eq!(store.accounts().unwrap().len(), 0);
|
assert_eq!(store.accounts().unwrap().len(), 0);
|
||||||
assert!(store.remove_account(&accounts[0], "").is_err());
|
assert!(store.remove_account(&accounts[0], &"".into()).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_path() -> &'static str {
|
fn test_path() -> &'static str {
|
||||||
@@ -146,8 +146,8 @@ fn test_decrypting_files_with_short_ciphertext() {
|
|||||||
|
|
||||||
let message = Default::default();
|
let message = Default::default();
|
||||||
|
|
||||||
let s1 = store.sign(&accounts[0], "foo", &message).unwrap();
|
let s1 = store.sign(&accounts[0], &"foo".into(), &message).unwrap();
|
||||||
let s2 = store.sign(&accounts[1], "foo", &message).unwrap();
|
let s2 = store.sign(&accounts[1], &"foo".into(), &message).unwrap();
|
||||||
assert!(verify_address(&accounts[0].address, &s1, &message).unwrap());
|
assert!(verify_address(&accounts[0].address, &s1, &message).unwrap());
|
||||||
assert!(verify_address(&kp1.address(), &s1, &message).unwrap());
|
assert!(verify_address(&kp1.address(), &s1, &message).unwrap());
|
||||||
assert!(verify_address(&kp2.address(), &s2, &message).unwrap());
|
assert!(verify_address(&kp2.address(), &s2, &message).unwrap());
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
mod transient_dir;
|
mod transient_dir;
|
||||||
|
|
||||||
10
ethstore/tests/util/transient_dir.rs → accounts/ethstore/tests/util/transient_dir.rs
Executable file → Normal file
10
ethstore/tests/util/transient_dir.rs → accounts/ethstore/tests/util/transient_dir.rs
Executable file → Normal file
@@ -1,18 +1,18 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
10
accounts/fake-hardware-wallet/Cargo.toml
Normal file
10
accounts/fake-hardware-wallet/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
description = "Fake hardware-wallet, for OS' that don't support libusb"
|
||||||
|
name = "fake-hardware-wallet"
|
||||||
|
version = "0.0.1"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ethereum-types = "0.4"
|
||||||
|
ethkey = { path = "../../accounts/ethkey" }
|
||||||
101
accounts/fake-hardware-wallet/src/lib.rs
Normal file
101
accounts/fake-hardware-wallet/src/lib.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Dummy module for platforms that does not provide support for hardware wallets (libusb)
|
||||||
|
|
||||||
|
extern crate ethereum_types;
|
||||||
|
extern crate ethkey;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use ethereum_types::U256;
|
||||||
|
use ethkey::{Address, Signature};
|
||||||
|
|
||||||
|
pub struct WalletInfo {
|
||||||
|
pub address: Address,
|
||||||
|
pub name: String,
|
||||||
|
pub manufacturer: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// `ErrorType` for devices with no `hardware wallet`
|
||||||
|
pub enum Error {
|
||||||
|
NoWallet,
|
||||||
|
KeyNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TransactionInfo {
|
||||||
|
/// Nonce
|
||||||
|
pub nonce: U256,
|
||||||
|
/// Gas price
|
||||||
|
pub gas_price: U256,
|
||||||
|
/// Gas limit
|
||||||
|
pub gas_limit: U256,
|
||||||
|
/// Receiver
|
||||||
|
pub to: Option<Address>,
|
||||||
|
/// Value
|
||||||
|
pub value: U256,
|
||||||
|
/// Data
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
/// Chain ID
|
||||||
|
pub chain_id: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum KeyPath {
|
||||||
|
/// Ethereum.
|
||||||
|
Ethereum,
|
||||||
|
/// Ethereum classic.
|
||||||
|
EthereumClassic,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `HardwareWalletManager` for devices with no `hardware wallet`
|
||||||
|
pub struct HardwareWalletManager;
|
||||||
|
|
||||||
|
impl HardwareWalletManager {
|
||||||
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
Err(Error::NoWallet)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_key_path(&self, _key_path: KeyPath) {}
|
||||||
|
|
||||||
|
pub fn wallet_info(&self, _: &Address) -> Option<WalletInfo> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_wallets(&self) -> Vec<WalletInfo> {
|
||||||
|
Vec::with_capacity(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
|
||||||
|
Err(Error::NoWallet)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pin_matrix_ack(&self, _: &str, _: &str) -> Result<bool, Error> {
|
||||||
|
Err(Error::NoWallet)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign_transaction(&self, _address: &Address, _transaction: &TransactionInfo, _rlp_transaction: &[u8]) -> Result<Signature, Error> {
|
||||||
|
Err(Error::NoWallet) }
|
||||||
|
|
||||||
|
pub fn sign_message(&self, _address: &Address, _msg: &[u8]) -> Result<Signature, Error> {
|
||||||
|
Err(Error::NoWallet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "No hardware wallet!!")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,18 +3,19 @@ description = "Hardware wallet support."
|
|||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "hardware-wallet"
|
name = "hardware-wallet"
|
||||||
version = "1.9.0"
|
version = "1.12.0"
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.3"
|
log = "0.4"
|
||||||
parking_lot = "0.5"
|
parking_lot = "0.7"
|
||||||
protobuf = "1.4"
|
protobuf = "1.4"
|
||||||
hidapi = { git = "https://github.com/paritytech/hidapi-rs" }
|
hidapi = { git = "https://github.com/paritytech/hidapi-rs" }
|
||||||
libusb = { git = "https://github.com/paritytech/libusb-rs" }
|
libusb = { git = "https://github.com/paritytech/libusb-rs" }
|
||||||
trezor-sys = { git = "https://github.com/paritytech/trezor-sys" }
|
trezor-sys = { git = "https://github.com/paritytech/trezor-sys" }
|
||||||
ethkey = { path = "../ethkey" }
|
ethkey = { path = "../ethkey" }
|
||||||
ethereum-types = "0.2"
|
ethereum-types = "0.4"
|
||||||
|
semver = "0.9"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
534
accounts/hw/src/ledger.rs
Normal file
534
accounts/hw/src/ledger.rs
Normal file
File diff suppressed because one or more lines are too long
402
accounts/hw/src/lib.rs
Normal file
402
accounts/hw/src/lib.rs
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Hardware wallet management.
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![warn(warnings)]
|
||||||
|
|
||||||
|
extern crate ethereum_types;
|
||||||
|
extern crate ethkey;
|
||||||
|
extern crate hidapi;
|
||||||
|
extern crate libusb;
|
||||||
|
extern crate parking_lot;
|
||||||
|
extern crate protobuf;
|
||||||
|
extern crate semver;
|
||||||
|
extern crate trezor_sys;
|
||||||
|
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[cfg(test)] extern crate rustc_hex;
|
||||||
|
|
||||||
|
mod ledger;
|
||||||
|
mod trezor;
|
||||||
|
|
||||||
|
use std::sync::{Arc, atomic, atomic::AtomicBool, Weak};
|
||||||
|
use std::{fmt, time::Duration};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use ethereum_types::U256;
|
||||||
|
use ethkey::{Address, Signature};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
const HID_GLOBAL_USAGE_PAGE: u16 = 0xFF00;
|
||||||
|
const HID_USB_DEVICE_CLASS: u8 = 0;
|
||||||
|
const MAX_POLLING_DURATION: Duration = Duration::from_millis(500);
|
||||||
|
const USB_EVENT_POLLING_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
|
|
||||||
|
/// `HardwareWallet` device
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Device {
|
||||||
|
path: String,
|
||||||
|
info: WalletInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Wallet` trait
|
||||||
|
pub trait Wallet<'a> {
|
||||||
|
/// Error
|
||||||
|
type Error;
|
||||||
|
/// Transaction data format
|
||||||
|
type Transaction;
|
||||||
|
|
||||||
|
/// Sign transaction data with wallet managing `address`.
|
||||||
|
fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result<Signature, Self::Error>;
|
||||||
|
|
||||||
|
/// Set key derivation path for a chain.
|
||||||
|
fn set_key_path(&self, key_path: KeyPath);
|
||||||
|
|
||||||
|
/// Re-populate device list
|
||||||
|
/// Note, this assumes all devices are iterated over and updated
|
||||||
|
fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Self::Error>;
|
||||||
|
|
||||||
|
/// Read device info
|
||||||
|
fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Self::Error>;
|
||||||
|
|
||||||
|
/// List connected and acknowledged wallets
|
||||||
|
fn list_devices(&self) -> Vec<WalletInfo>;
|
||||||
|
|
||||||
|
/// List locked wallets
|
||||||
|
/// This may be moved if it is the wrong assumption, for example this is not supported by Ledger
|
||||||
|
/// Then this method return a empty vector
|
||||||
|
fn list_locked_devices(&self) -> Vec<String>;
|
||||||
|
|
||||||
|
/// Get wallet info.
|
||||||
|
fn get_wallet(&self, address: &Address) -> Option<WalletInfo>;
|
||||||
|
|
||||||
|
/// Generate ethereum address for a Wallet
|
||||||
|
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Self::Error>;
|
||||||
|
|
||||||
|
/// Open a device using `device path`
|
||||||
|
/// Note, f - is a closure that borrows HidResult<HidDevice>
|
||||||
|
/// HidDevice is in turn a type alias for a `c_void function pointer`
|
||||||
|
/// For further information see:
|
||||||
|
/// * <https://github.com/paritytech/hidapi-rs>
|
||||||
|
/// * <https://github.com/rust-lang/libc>
|
||||||
|
fn open_path<R, F>(&self, f: F) -> Result<R, Self::Error>
|
||||||
|
where F: Fn() -> Result<R, &'static str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardware wallet error.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Ledger device error.
|
||||||
|
LedgerDevice(ledger::Error),
|
||||||
|
/// Trezor device error
|
||||||
|
TrezorDevice(trezor::Error),
|
||||||
|
/// USB error.
|
||||||
|
Usb(libusb::Error),
|
||||||
|
/// HID error
|
||||||
|
Hid(String),
|
||||||
|
/// Hardware wallet not found for specified key.
|
||||||
|
KeyNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is the transaction info we need to supply to Trezor message. It's more
|
||||||
|
/// or less a duplicate of `ethcore::transaction::Transaction`, but we can't
|
||||||
|
/// import ethcore here as that would be a circular dependency.
|
||||||
|
pub struct TransactionInfo {
|
||||||
|
/// Nonce
|
||||||
|
pub nonce: U256,
|
||||||
|
/// Gas price
|
||||||
|
pub gas_price: U256,
|
||||||
|
/// Gas limit
|
||||||
|
pub gas_limit: U256,
|
||||||
|
/// Receiver
|
||||||
|
pub to: Option<Address>,
|
||||||
|
/// Value
|
||||||
|
pub value: U256,
|
||||||
|
/// Data
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
/// Chain ID
|
||||||
|
pub chain_id: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardware wallet information.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct WalletInfo {
|
||||||
|
/// Wallet device name.
|
||||||
|
pub name: String,
|
||||||
|
/// Wallet device manufacturer.
|
||||||
|
pub manufacturer: String,
|
||||||
|
/// Wallet device serial number.
|
||||||
|
pub serial: String,
|
||||||
|
/// Ethereum address.
|
||||||
|
pub address: Address,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Key derivation paths used on hardware wallets.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum KeyPath {
|
||||||
|
/// Ethereum.
|
||||||
|
Ethereum,
|
||||||
|
/// Ethereum classic.
|
||||||
|
EthereumClassic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
match *self {
|
||||||
|
Error::KeyNotFound => write!(f, "Key not found for given address."),
|
||||||
|
Error::LedgerDevice(ref e) => write!(f, "{}", e),
|
||||||
|
Error::TrezorDevice(ref e) => write!(f, "{}", e),
|
||||||
|
Error::Usb(ref e) => write!(f, "{}", e),
|
||||||
|
Error::Hid(ref e) => write!(f, "{}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ledger::Error> for Error {
|
||||||
|
fn from(err: ledger::Error) -> Self {
|
||||||
|
match err {
|
||||||
|
ledger::Error::KeyNotFound => Error::KeyNotFound,
|
||||||
|
_ => Error::LedgerDevice(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<trezor::Error> for Error {
|
||||||
|
fn from(err: trezor::Error) -> Self {
|
||||||
|
match err {
|
||||||
|
trezor::Error::KeyNotFound => Error::KeyNotFound,
|
||||||
|
_ => Error::TrezorDevice(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<libusb::Error> for Error {
|
||||||
|
fn from(err: libusb::Error) -> Self {
|
||||||
|
Error::Usb(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies the direction of the `HardwareWallet` i.e, whether it arrived or left
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum DeviceDirection {
|
||||||
|
/// Device arrived
|
||||||
|
Arrived,
|
||||||
|
/// Device left
|
||||||
|
Left,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DeviceDirection {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DeviceDirection::Arrived => write!(f, "arrived"),
|
||||||
|
DeviceDirection::Left => write!(f, "left"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardware wallet management interface.
|
||||||
|
pub struct HardwareWalletManager {
|
||||||
|
exiting: Arc<AtomicBool>,
|
||||||
|
ledger: Arc<ledger::Manager>,
|
||||||
|
trezor: Arc<trezor::Manager>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HardwareWalletManager {
|
||||||
|
/// Hardware wallet constructor
|
||||||
|
pub fn new() -> Result<Self, Error> {
|
||||||
|
let exiting = Arc::new(AtomicBool::new(false));
|
||||||
|
let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().map_err(|e| Error::Hid(e.to_string().clone()))?));
|
||||||
|
let ledger = ledger::Manager::new(hidapi.clone());
|
||||||
|
let trezor = trezor::Manager::new(hidapi.clone());
|
||||||
|
let usb_context = Arc::new(libusb::Context::new()?);
|
||||||
|
|
||||||
|
let l = ledger.clone();
|
||||||
|
let t = trezor.clone();
|
||||||
|
let exit = exiting.clone();
|
||||||
|
|
||||||
|
// Subscribe to all vendor IDs (VIDs) and product IDs (PIDs)
|
||||||
|
// This means that the `HardwareWalletManager` is responsible to validate the detected device
|
||||||
|
usb_context.register_callback(
|
||||||
|
None, None, Some(HID_USB_DEVICE_CLASS),
|
||||||
|
Box::new(EventHandler::new(
|
||||||
|
Arc::downgrade(&ledger),
|
||||||
|
Arc::downgrade(&trezor)
|
||||||
|
))
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Hardware event subscriber thread
|
||||||
|
thread::Builder::new()
|
||||||
|
.name("hw_wallet_manager".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
if let Err(e) = l.update_devices(DeviceDirection::Arrived) {
|
||||||
|
debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e);
|
||||||
|
}
|
||||||
|
if let Err(e) = t.update_devices(DeviceDirection::Arrived) {
|
||||||
|
debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
while !exit.load(atomic::Ordering::Acquire) {
|
||||||
|
if let Err(e) = usb_context.handle_events(Some(USB_EVENT_POLLING_INTERVAL)) {
|
||||||
|
debug!(target: "hw", "HardwareWalletManager event handler error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
exiting,
|
||||||
|
trezor,
|
||||||
|
ledger,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Select key derivation path for a chain.
|
||||||
|
/// Currently, only one hard-coded keypath is supported
|
||||||
|
/// It is managed by `ethcore/account_provider`
|
||||||
|
pub fn set_key_path(&self, key_path: KeyPath) {
|
||||||
|
self.ledger.set_key_path(key_path);
|
||||||
|
self.trezor.set_key_path(key_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List connected wallets. This only returns wallets that are ready to be used.
|
||||||
|
pub fn list_wallets(&self) -> Vec<WalletInfo> {
|
||||||
|
let mut wallets = Vec::new();
|
||||||
|
wallets.extend(self.ledger.list_devices());
|
||||||
|
wallets.extend(self.trezor.list_devices());
|
||||||
|
wallets
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a list of paths to locked hardware wallets
|
||||||
|
/// This is only applicable to Trezor because Ledger only appears as
|
||||||
|
/// a device when it is unlocked
|
||||||
|
pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
|
||||||
|
Ok(self.trezor.list_locked_devices())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get connected wallet info.
|
||||||
|
pub fn wallet_info(&self, address: &Address) -> Option<WalletInfo> {
|
||||||
|
if let Some(info) = self.ledger.get_wallet(address) {
|
||||||
|
Some(info)
|
||||||
|
} else {
|
||||||
|
self.trezor.get_wallet(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign a message with the wallet (only supported by Ledger)
|
||||||
|
pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result<Signature, Error> {
|
||||||
|
if self.ledger.get_wallet(address).is_some() {
|
||||||
|
Ok(self.ledger.sign_message(address, msg)?)
|
||||||
|
} else if self.trezor.get_wallet(address).is_some() {
|
||||||
|
Err(Error::TrezorDevice(trezor::Error::NoSigningMessage))
|
||||||
|
} else {
|
||||||
|
Err(Error::KeyNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign transaction data with wallet managing `address`.
|
||||||
|
pub fn sign_transaction(&self, address: &Address, t_info: &TransactionInfo, encoded_transaction: &[u8]) -> Result<Signature, Error> {
|
||||||
|
if self.ledger.get_wallet(address).is_some() {
|
||||||
|
Ok(self.ledger.sign_transaction(address, encoded_transaction)?)
|
||||||
|
} else if self.trezor.get_wallet(address).is_some() {
|
||||||
|
Ok(self.trezor.sign_transaction(address, t_info)?)
|
||||||
|
} else {
|
||||||
|
Err(Error::KeyNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a pin to a device at a certain path to unlock it
|
||||||
|
/// This is only applicable to Trezor because Ledger only appears as
|
||||||
|
/// a device when it is unlocked
|
||||||
|
pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, Error> {
|
||||||
|
self.trezor.pin_matrix_ack(path, pin).map_err(Error::TrezorDevice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for HardwareWalletManager {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Indicate to the USB Hotplug handler that it
|
||||||
|
// shall terminate but don't wait for it to terminate.
|
||||||
|
// If it doesn't terminate for some reason USB Hotplug events will be handled
|
||||||
|
// even if the HardwareWalletManger has been dropped
|
||||||
|
self.exiting.store(true, atomic::Ordering::Release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardware wallet event handler
|
||||||
|
///
|
||||||
|
/// Note, that this runs to completion and race-conditions can't occur but it can
|
||||||
|
/// stop other events for being processed with an infinite loop or similar
|
||||||
|
struct EventHandler {
|
||||||
|
ledger: Weak<ledger::Manager>,
|
||||||
|
trezor: Weak<trezor::Manager>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventHandler {
|
||||||
|
/// Trezor event handler constructor
|
||||||
|
pub fn new(ledger: Weak<ledger::Manager>, trezor: Weak<trezor::Manager>) -> Self {
|
||||||
|
Self { ledger, trezor }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_device_info(device: &libusb::Device) -> Result<(u16, u16), Error> {
|
||||||
|
let desc = device.device_descriptor()?;
|
||||||
|
Ok((desc.vendor_id(), desc.product_id()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl libusb::Hotplug for EventHandler {
|
||||||
|
fn device_arrived(&mut self, device: libusb::Device) {
|
||||||
|
// Upgrade reference to an Arc
|
||||||
|
if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
|
||||||
|
// Version ID and Product ID are available
|
||||||
|
if let Ok((vid, pid)) = Self::extract_device_info(&device) {
|
||||||
|
if trezor::is_valid_trezor(vid, pid) {
|
||||||
|
if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived) {
|
||||||
|
trace!(target: "hw", "Trezor device was detected but connection failed");
|
||||||
|
}
|
||||||
|
} else if ledger::is_valid_ledger(vid, pid) {
|
||||||
|
if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Arrived) {
|
||||||
|
trace!(target: "hw", "Ledger device was detected but connection failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_left(&mut self, device: libusb::Device) {
|
||||||
|
// Upgrade reference to an Arc
|
||||||
|
if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
|
||||||
|
// Version ID and Product ID are available
|
||||||
|
if let Ok((vid, pid)) = Self::extract_device_info(&device) {
|
||||||
|
if trezor::is_valid_trezor(vid, pid) {
|
||||||
|
if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Left) {
|
||||||
|
trace!(target: "hw", "Trezor device was detected but disconnection failed");
|
||||||
|
}
|
||||||
|
} else if ledger::is_valid_ledger(vid, pid) {
|
||||||
|
if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Left) {
|
||||||
|
trace!(target: "hw", "Ledger device was detected but disconnection failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to determine if a device is a valid HID
|
||||||
|
pub fn is_valid_hid_device(usage_page: u16, interface_number: i32) -> bool {
|
||||||
|
usage_page == HID_GLOBAL_USAGE_PAGE || interface_number == HID_USB_DEVICE_CLASS as i32
|
||||||
|
}
|
||||||
@@ -1,50 +1,45 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Trezor hardware wallet module. Supports Trezor v1.
|
//! Trezor hardware wallet module. Supports Trezor v1.
|
||||||
//! See http://doc.satoshilabs.com/trezor-tech/api-protobuf.html
|
//! See <http://doc.satoshilabs.com/trezor-tech/api-protobuf.html>
|
||||||
//! and https://github.com/trezor/trezor-common/blob/master/protob/protocol.md
|
//! and <https://github.com/trezor/trezor-common/blob/master/protob/protocol.md>
|
||||||
//! for protocol details.
|
//! for protocol details.
|
||||||
|
|
||||||
use super::{WalletInfo, TransactionInfo, KeyPath};
|
|
||||||
|
|
||||||
use std::cmp::{min, max};
|
use std::cmp::{min, max};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::{Arc, Weak};
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
use ethereum_types::{U256, H256, Address};
|
use ethereum_types::{U256, H256, Address};
|
||||||
|
|
||||||
use ethkey::Signature;
|
use ethkey::Signature;
|
||||||
use hidapi;
|
use hidapi;
|
||||||
use libusb;
|
use libusb;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use protobuf;
|
use protobuf::{self, Message, ProtobufEnum};
|
||||||
use protobuf::{Message, ProtobufEnum};
|
use super::{DeviceDirection, WalletInfo, TransactionInfo, KeyPath, Wallet, Device, is_valid_hid_device};
|
||||||
|
|
||||||
use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck};
|
use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck};
|
||||||
|
|
||||||
/// Trezor v1 vendor ID
|
/// Trezor v1 vendor ID
|
||||||
pub const TREZOR_VID: u16 = 0x534c;
|
const TREZOR_VID: u16 = 0x534c;
|
||||||
/// Trezor product IDs
|
/// Trezor product IDs
|
||||||
pub const TREZOR_PIDS: [u16; 1] = [0x0001];
|
const TREZOR_PIDS: [u16; 1] = [0x0001];
|
||||||
|
|
||||||
const ETH_DERIVATION_PATH: [u32; 5] = [0x8000002C, 0x8000003C, 0x80000000, 0, 0]; // m/44'/60'/0'/0/0
|
const ETH_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003C, 0x8000_0000, 0, 0]; // m/44'/60'/0'/0/0
|
||||||
const ETC_DERIVATION_PATH: [u32; 5] = [0x8000002C, 0x8000003D, 0x80000000, 0, 0]; // m/44'/61'/0'/0/0
|
const ETC_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003D, 0x8000_0000, 0, 0]; // m/44'/61'/0'/0/0
|
||||||
|
|
||||||
/// Hardware wallet error.
|
/// Hardware wallet error.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -53,6 +48,8 @@ pub enum Error {
|
|||||||
Protocol(&'static str),
|
Protocol(&'static str),
|
||||||
/// Hidapi error.
|
/// Hidapi error.
|
||||||
Usb(hidapi::HidError),
|
Usb(hidapi::HidError),
|
||||||
|
/// Libusb error
|
||||||
|
LibUsb(libusb::Error),
|
||||||
/// Device with request key is not available.
|
/// Device with request key is not available.
|
||||||
KeyNotFound,
|
KeyNotFound,
|
||||||
/// Signing has been cancelled by user.
|
/// Signing has been cancelled by user.
|
||||||
@@ -61,6 +58,14 @@ pub enum Error {
|
|||||||
BadMessageType,
|
BadMessageType,
|
||||||
/// Trying to read from a closed device at the given path
|
/// Trying to read from a closed device at the given path
|
||||||
LockedDevice(String),
|
LockedDevice(String),
|
||||||
|
/// Signing messages are not supported by Trezor
|
||||||
|
NoSigningMessage,
|
||||||
|
/// No device arrived
|
||||||
|
NoDeviceArrived,
|
||||||
|
/// No device left
|
||||||
|
NoDeviceLeft,
|
||||||
|
/// Invalid PID or VID
|
||||||
|
InvalidDevice,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
@@ -68,27 +73,38 @@ impl fmt::Display for Error {
|
|||||||
match *self {
|
match *self {
|
||||||
Error::Protocol(ref s) => write!(f, "Trezor protocol error: {}", s),
|
Error::Protocol(ref s) => write!(f, "Trezor protocol error: {}", s),
|
||||||
Error::Usb(ref e) => write!(f, "USB communication error: {}", e),
|
Error::Usb(ref e) => write!(f, "USB communication error: {}", e),
|
||||||
|
Error::LibUsb(ref e) => write!(f, "LibUSB communication error: {}", e),
|
||||||
Error::KeyNotFound => write!(f, "Key not found"),
|
Error::KeyNotFound => write!(f, "Key not found"),
|
||||||
Error::UserCancel => write!(f, "Operation has been cancelled"),
|
Error::UserCancel => write!(f, "Operation has been cancelled"),
|
||||||
Error::BadMessageType => write!(f, "Bad Message Type in RPC call"),
|
Error::BadMessageType => write!(f, "Bad Message Type in RPC call"),
|
||||||
Error::LockedDevice(ref s) => write!(f, "Device is locked, needs PIN to perform operations: {}", s),
|
Error::LockedDevice(ref s) => write!(f, "Device is locked, needs PIN to perform operations: {}", s),
|
||||||
|
Error::NoSigningMessage=> write!(f, "Signing messages are not supported by Trezor"),
|
||||||
|
Error::NoDeviceArrived => write!(f, "No device arrived"),
|
||||||
|
Error::NoDeviceLeft => write!(f, "No device left"),
|
||||||
|
Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<hidapi::HidError> for Error {
|
impl From<hidapi::HidError> for Error {
|
||||||
fn from(err: hidapi::HidError) -> Error {
|
fn from(err: hidapi::HidError) -> Self {
|
||||||
Error::Usb(err)
|
Error::Usb(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<libusb::Error> for Error {
|
||||||
|
fn from(err: libusb::Error) -> Self {
|
||||||
|
Error::LibUsb(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<protobuf::ProtobufError> for Error {
|
impl From<protobuf::ProtobufError> for Error {
|
||||||
fn from(_: protobuf::ProtobufError) -> Error {
|
fn from(_: protobuf::ProtobufError) -> Self {
|
||||||
Error::Protocol(&"Could not read response from Trezor Device")
|
Error::Protocol(&"Could not read response from Trezor Device")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ledger device manager
|
/// Trezor device manager
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
usb: Arc<Mutex<hidapi::HidApi>>,
|
usb: Arc<Mutex<hidapi::HidApi>>,
|
||||||
devices: RwLock<Vec<Device>>,
|
devices: RwLock<Vec<Device>>,
|
||||||
@@ -96,12 +112,6 @@ pub struct Manager {
|
|||||||
key_path: RwLock<KeyPath>,
|
key_path: RwLock<KeyPath>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Device {
|
|
||||||
path: String,
|
|
||||||
info: WalletInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// HID Version used for the Trezor device
|
/// HID Version used for the Trezor device
|
||||||
enum HidVersion {
|
enum HidVersion {
|
||||||
V1,
|
V1,
|
||||||
@@ -110,102 +120,13 @@ enum HidVersion {
|
|||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
/// Create a new instance.
|
/// Create a new instance.
|
||||||
pub fn new(hidapi: Arc<Mutex<hidapi::HidApi>>) -> Manager {
|
pub fn new(usb: Arc<Mutex<hidapi::HidApi>>) -> Arc<Self> {
|
||||||
Manager {
|
Arc::new(Self {
|
||||||
usb: hidapi,
|
usb,
|
||||||
devices: RwLock::new(Vec::new()),
|
devices: RwLock::new(Vec::new()),
|
||||||
locked_devices: RwLock::new(Vec::new()),
|
locked_devices: RwLock::new(Vec::new()),
|
||||||
key_path: RwLock::new(KeyPath::Ethereum),
|
key_path: RwLock::new(KeyPath::Ethereum),
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
/// Re-populate device list
|
|
||||||
pub fn update_devices(&self) -> Result<usize, Error> {
|
|
||||||
let mut usb = self.usb.lock();
|
|
||||||
usb.refresh_devices();
|
|
||||||
let devices = usb.devices();
|
|
||||||
let mut new_devices = Vec::new();
|
|
||||||
let mut locked_devices = Vec::new();
|
|
||||||
let mut error = None;
|
|
||||||
for usb_device in devices {
|
|
||||||
let is_trezor = usb_device.vendor_id == TREZOR_VID;
|
|
||||||
let is_supported_product = TREZOR_PIDS.contains(&usb_device.product_id);
|
|
||||||
let is_valid = usb_device.usage_page == 0xFF00 || usb_device.interface_number == 0;
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
"Checking device: {:?}, trezor: {:?}, prod: {:?}, valid: {:?}",
|
|
||||||
usb_device,
|
|
||||||
is_trezor,
|
|
||||||
is_supported_product,
|
|
||||||
is_valid,
|
|
||||||
);
|
|
||||||
if !is_trezor || !is_supported_product || !is_valid {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match self.read_device_info(&usb, &usb_device) {
|
|
||||||
Ok(device) => new_devices.push(device),
|
|
||||||
Err(Error::LockedDevice(path)) => locked_devices.push(path.to_string()),
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error reading device: {:?}", e);
|
|
||||||
error = Some(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let count = new_devices.len();
|
|
||||||
trace!("Got devices: {:?}, closed: {:?}", new_devices, locked_devices);
|
|
||||||
*self.devices.write() = new_devices;
|
|
||||||
*self.locked_devices.write() = locked_devices;
|
|
||||||
match error {
|
|
||||||
Some(e) => Err(e),
|
|
||||||
None => Ok(count),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_device_info(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Error> {
|
|
||||||
let handle = self.open_path(|| usb.open_path(&dev_info.path))?;
|
|
||||||
let manufacturer = dev_info.manufacturer_string.clone().unwrap_or("Unknown".to_owned());
|
|
||||||
let name = dev_info.product_string.clone().unwrap_or("Unknown".to_owned());
|
|
||||||
let serial = dev_info.serial_number.clone().unwrap_or("Unknown".to_owned());
|
|
||||||
match self.get_address(&handle) {
|
|
||||||
Ok(Some(addr)) => {
|
|
||||||
Ok(Device {
|
|
||||||
path: dev_info.path.clone(),
|
|
||||||
info: WalletInfo {
|
|
||||||
name: name,
|
|
||||||
manufacturer: manufacturer,
|
|
||||||
serial: serial,
|
|
||||||
address: addr,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select key derivation path for a known chain.
|
|
||||||
pub fn set_key_path(&self, key_path: KeyPath) {
|
|
||||||
*self.key_path.write() = key_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List connected wallets. This only returns wallets that are ready to be used.
|
|
||||||
pub fn list_devices(&self) -> Vec<WalletInfo> {
|
|
||||||
self.devices.read().iter().map(|d| d.info.clone()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list_locked_devices(&self) -> Vec<String> {
|
|
||||||
(*self.locked_devices.read()).clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get wallet info.
|
|
||||||
pub fn device_info(&self, address: &Address) -> Option<WalletInfo> {
|
|
||||||
self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_path<R, F>(&self, f: F) -> Result<R, Error>
|
|
||||||
where F: Fn() -> Result<R, &'static str>
|
|
||||||
{
|
|
||||||
f().map_err(Into::into)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pin_matrix_ack(&self, device_path: &str, pin: &str) -> Result<bool, Error> {
|
pub fn pin_matrix_ack(&self, device_path: &str, pin: &str) -> Result<bool, Error> {
|
||||||
@@ -215,7 +136,7 @@ impl Manager {
|
|||||||
let t = MessageType::MessageType_PinMatrixAck;
|
let t = MessageType::MessageType_PinMatrixAck;
|
||||||
let mut m = PinMatrixAck::new();
|
let mut m = PinMatrixAck::new();
|
||||||
m.set_pin(pin.to_string());
|
m.set_pin(pin.to_string());
|
||||||
self.send_device_message(&device, &t, &m)?;
|
self.send_device_message(&device, t, &m)?;
|
||||||
let (resp_type, _) = self.read_device_response(&device)?;
|
let (resp_type, _) = self.read_device_response(&device)?;
|
||||||
match resp_type {
|
match resp_type {
|
||||||
// Getting an Address back means it's unlocked, this is undocumented behavior
|
// Getting an Address back means it's unlocked, this is undocumented behavior
|
||||||
@@ -225,68 +146,12 @@ impl Manager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.update_devices()?;
|
self.update_devices(DeviceDirection::Arrived)?;
|
||||||
unlocked
|
unlocked
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Error> {
|
|
||||||
let typ = MessageType::MessageType_EthereumGetAddress;
|
|
||||||
let mut message = EthereumGetAddress::new();
|
|
||||||
match *self.key_path.read() {
|
|
||||||
KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
|
|
||||||
KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
|
|
||||||
}
|
|
||||||
message.set_show_display(false);
|
|
||||||
self.send_device_message(&device, &typ, &message)?;
|
|
||||||
|
|
||||||
let (resp_type, bytes) = self.read_device_response(&device)?;
|
|
||||||
match resp_type {
|
|
||||||
MessageType::MessageType_EthereumAddress => {
|
|
||||||
let response: EthereumAddress = protobuf::core::parse_from_bytes(&bytes)?;
|
|
||||||
Ok(Some(From::from(response.get_address())))
|
|
||||||
}
|
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sign transaction data with wallet managing `address`.
|
|
||||||
pub fn sign_transaction(&self, address: &Address, t_info: &TransactionInfo) -> Result<Signature, Error> {
|
|
||||||
let usb = self.usb.lock();
|
|
||||||
let devices = self.devices.read();
|
|
||||||
let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?;
|
|
||||||
let handle = self.open_path(|| usb.open_path(&device.path))?;
|
|
||||||
let msg_type = MessageType::MessageType_EthereumSignTx;
|
|
||||||
let mut message = EthereumSignTx::new();
|
|
||||||
match *self.key_path.read() {
|
|
||||||
KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
|
|
||||||
KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
|
|
||||||
}
|
|
||||||
message.set_nonce(self.u256_to_be_vec(&t_info.nonce));
|
|
||||||
message.set_gas_limit(self.u256_to_be_vec(&t_info.gas_limit));
|
|
||||||
message.set_gas_price(self.u256_to_be_vec(&t_info.gas_price));
|
|
||||||
message.set_value(self.u256_to_be_vec(&t_info.value));
|
|
||||||
|
|
||||||
match t_info.to {
|
|
||||||
Some(addr) => {
|
|
||||||
message.set_to(addr.to_vec())
|
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
let first_chunk_length = min(t_info.data.len(), 1024);
|
|
||||||
let chunk = &t_info.data[0..first_chunk_length];
|
|
||||||
message.set_data_initial_chunk(chunk.to_vec());
|
|
||||||
message.set_data_length(t_info.data.len() as u32);
|
|
||||||
if let Some(c_id) = t_info.chain_id {
|
|
||||||
message.set_chain_id(c_id as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.send_device_message(&handle, &msg_type, &message)?;
|
|
||||||
|
|
||||||
self.signing_loop(&handle, &t_info.chain_id, &t_info.data[first_chunk_length..])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn u256_to_be_vec(&self, val: &U256) -> Vec<u8> {
|
fn u256_to_be_vec(&self, val: &U256) -> Vec<u8> {
|
||||||
let mut buf = [0u8; 32];
|
let mut buf = [0_u8; 32];
|
||||||
val.to_big_endian(&mut buf);
|
val.to_big_endian(&mut buf);
|
||||||
buf.iter().skip_while(|x| **x == 0).cloned().collect()
|
buf.iter().skip_while(|x| **x == 0).cloned().collect()
|
||||||
}
|
}
|
||||||
@@ -296,7 +161,7 @@ impl Manager {
|
|||||||
match resp_type {
|
match resp_type {
|
||||||
MessageType::MessageType_Cancel => Err(Error::UserCancel),
|
MessageType::MessageType_Cancel => Err(Error::UserCancel),
|
||||||
MessageType::MessageType_ButtonRequest => {
|
MessageType::MessageType_ButtonRequest => {
|
||||||
self.send_device_message(handle, &MessageType::MessageType_ButtonAck, &ButtonAck::new())?;
|
self.send_device_message(handle, MessageType::MessageType_ButtonAck, &ButtonAck::new())?;
|
||||||
// Signing loop goes back to the top and reading blocks
|
// Signing loop goes back to the top and reading blocks
|
||||||
// for up to 5 minutes waiting for response from the device
|
// for up to 5 minutes waiting for response from the device
|
||||||
// if the user doesn't click any button within 5 minutes you
|
// if the user doesn't click any button within 5 minutes you
|
||||||
@@ -309,7 +174,7 @@ impl Manager {
|
|||||||
let mut msg = EthereumTxAck::new();
|
let mut msg = EthereumTxAck::new();
|
||||||
let len = resp.get_data_length() as usize;
|
let len = resp.get_data_length() as usize;
|
||||||
msg.set_data_chunk(data[..len].to_vec());
|
msg.set_data_chunk(data[..len].to_vec());
|
||||||
self.send_device_message(handle, &MessageType::MessageType_EthereumTxAck, &msg)?;
|
self.send_device_message(handle, MessageType::MessageType_EthereumTxAck, &msg)?;
|
||||||
self.signing_loop(handle, chain_id, &data[len..])
|
self.signing_loop(handle, chain_id, &data[len..])
|
||||||
} else {
|
} else {
|
||||||
let v = resp.get_signature_v();
|
let v = resp.get_signature_v();
|
||||||
@@ -334,15 +199,15 @@ impl Manager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_device_message(&self, device: &hidapi::HidDevice, msg_type: &MessageType, msg: &Message) -> Result<usize, Error> {
|
fn send_device_message(&self, device: &hidapi::HidDevice, msg_type: MessageType, msg: &Message) -> Result<usize, Error> {
|
||||||
let msg_id = *msg_type as u16;
|
let msg_id = msg_type as u16;
|
||||||
let mut message = msg.write_to_bytes()?;
|
let mut message = msg.write_to_bytes()?;
|
||||||
let msg_size = message.len();
|
let msg_size = message.len();
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let hid_version = self.probe_hid_version(device)?;
|
let hid_version = self.probe_hid_version(device)?;
|
||||||
// Magic constants
|
// Magic constants
|
||||||
data.push('#' as u8);
|
data.push(b'#');
|
||||||
data.push('#' as u8);
|
data.push(b'#');
|
||||||
// Convert msg_id to BE and split into bytes
|
// Convert msg_id to BE and split into bytes
|
||||||
data.push(((msg_id >> 8) & 0xFF) as u8);
|
data.push(((msg_id >> 8) & 0xFF) as u8);
|
||||||
data.push((msg_id & 0xFF) as u8);
|
data.push((msg_id & 0xFF) as u8);
|
||||||
@@ -358,8 +223,8 @@ impl Manager {
|
|||||||
let mut total_written = 0;
|
let mut total_written = 0;
|
||||||
for chunk in data.chunks(63) {
|
for chunk in data.chunks(63) {
|
||||||
let mut padded_chunk = match hid_version {
|
let mut padded_chunk = match hid_version {
|
||||||
HidVersion::V1 => vec!['?' as u8],
|
HidVersion::V1 => vec![b'?'],
|
||||||
HidVersion::V2 => vec![0, '?' as u8],
|
HidVersion::V2 => vec![0, b'?'],
|
||||||
};
|
};
|
||||||
padded_chunk.extend_from_slice(&chunk);
|
padded_chunk.extend_from_slice(&chunk);
|
||||||
total_written += device.write(&padded_chunk)?;
|
total_written += device.write(&padded_chunk)?;
|
||||||
@@ -368,10 +233,10 @@ impl Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn probe_hid_version(&self, device: &hidapi::HidDevice) -> Result<HidVersion, Error> {
|
fn probe_hid_version(&self, device: &hidapi::HidDevice) -> Result<HidVersion, Error> {
|
||||||
let mut buf2 = [0xFFu8; 65];
|
let mut buf2 = [0xFF_u8; 65];
|
||||||
buf2[0] = 0;
|
buf2[0] = 0;
|
||||||
buf2[1] = 63;
|
buf2[1] = 63;
|
||||||
let mut buf1 = [0xFFu8; 64];
|
let mut buf1 = [0xFF_u8; 64];
|
||||||
buf1[0] = 63;
|
buf1[0] = 63;
|
||||||
if device.write(&buf2)? == 65 {
|
if device.write(&buf2)? == 65 {
|
||||||
Ok(HidVersion::V2)
|
Ok(HidVersion::V2)
|
||||||
@@ -387,7 +252,7 @@ impl Manager {
|
|||||||
let mut buf = vec![0; 64];
|
let mut buf = vec![0; 64];
|
||||||
|
|
||||||
let first_chunk = device.read_timeout(&mut buf, 300_000)?;
|
let first_chunk = device.read_timeout(&mut buf, 300_000)?;
|
||||||
if first_chunk < 9 || buf[0] != '?' as u8 || buf[1] != '#' as u8 || buf[2] != '#' as u8 {
|
if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' {
|
||||||
return Err(protocol_err);
|
return Err(protocol_err);
|
||||||
}
|
}
|
||||||
let msg_type = MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF)).ok_or(protocol_err)?;
|
let msg_type = MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF)).ok_or(protocol_err)?;
|
||||||
@@ -402,40 +267,165 @@ impl Manager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trezor event handler
|
impl<'a> Wallet<'a> for Manager {
|
||||||
/// A separate thread is handeling incoming events
|
type Error = Error;
|
||||||
pub struct EventHandler {
|
type Transaction = &'a TransactionInfo;
|
||||||
trezor: Weak<Manager>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventHandler {
|
|
||||||
// Trezor event handler constructor
|
|
||||||
pub fn new(trezor: Weak<Manager>) -> Self {
|
|
||||||
Self { trezor: trezor }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl libusb::Hotplug for EventHandler {
|
|
||||||
fn device_arrived(&mut self, _device: libusb::Device) {
|
|
||||||
debug!(target: "hw", "Trezor V1 arrived");
|
|
||||||
if let Some(trezor) = self.trezor.upgrade() {
|
|
||||||
// Wait for the device to boot up
|
|
||||||
thread::sleep(Duration::from_millis(1000));
|
|
||||||
if let Err(e) = trezor.update_devices() {
|
|
||||||
debug!(target: "hw", "Trezor V1 connect error: {:?}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fn sign_transaction(&self, address: &Address, t_info: Self::Transaction) ->
|
||||||
|
Result<Signature, Error> {
|
||||||
|
let usb = self.usb.lock();
|
||||||
|
let devices = self.devices.read();
|
||||||
|
let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?;
|
||||||
|
let handle = self.open_path(|| usb.open_path(&device.path))?;
|
||||||
|
let msg_type = MessageType::MessageType_EthereumSignTx;
|
||||||
|
let mut message = EthereumSignTx::new();
|
||||||
|
match *self.key_path.read() {
|
||||||
|
KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
|
||||||
|
KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
|
||||||
}
|
}
|
||||||
|
message.set_nonce(self.u256_to_be_vec(&t_info.nonce));
|
||||||
|
message.set_gas_limit(self.u256_to_be_vec(&t_info.gas_limit));
|
||||||
|
message.set_gas_price(self.u256_to_be_vec(&t_info.gas_price));
|
||||||
|
message.set_value(self.u256_to_be_vec(&t_info.value));
|
||||||
|
|
||||||
|
if let Some(addr) = t_info.to {
|
||||||
|
message.set_to(addr.to_vec())
|
||||||
|
}
|
||||||
|
let first_chunk_length = min(t_info.data.len(), 1024);
|
||||||
|
let chunk = &t_info.data[0..first_chunk_length];
|
||||||
|
message.set_data_initial_chunk(chunk.to_vec());
|
||||||
|
message.set_data_length(t_info.data.len() as u32);
|
||||||
|
if let Some(c_id) = t_info.chain_id {
|
||||||
|
message.set_chain_id(c_id as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_device_message(&handle, msg_type, &message)?;
|
||||||
|
|
||||||
|
self.signing_loop(&handle, &t_info.chain_id, &t_info.data[first_chunk_length..])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_left(&mut self, _device: libusb::Device) {
|
fn set_key_path(&self, key_path: KeyPath) {
|
||||||
debug!(target: "hw", "Trezor V1 left");
|
*self.key_path.write() = key_path;
|
||||||
if let Some(trezor) = self.trezor.upgrade() {
|
}
|
||||||
if let Err(e) = trezor.update_devices() {
|
|
||||||
debug!(target: "hw", "Trezor V1 disconnect error: {:?}", e);
|
fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Error> {
|
||||||
|
let mut usb = self.usb.lock();
|
||||||
|
usb.refresh_devices();
|
||||||
|
let devices = usb.devices();
|
||||||
|
let num_prev_devices = self.devices.read().len();
|
||||||
|
|
||||||
|
let detected_devices = devices.iter()
|
||||||
|
.filter(|&d| is_valid_trezor(d.vendor_id, d.product_id) &&
|
||||||
|
is_valid_hid_device(d.usage_page, d.interface_number)
|
||||||
|
)
|
||||||
|
.fold(Vec::new(), |mut v, d| {
|
||||||
|
match self.read_device(&usb, &d) {
|
||||||
|
Ok(info) => {
|
||||||
|
trace!(target: "hw", "Found device: {:?}", info);
|
||||||
|
v.push(info);
|
||||||
|
}
|
||||||
|
Err(e) => trace!(target: "hw", "Error reading device info: {}", e),
|
||||||
|
};
|
||||||
|
v
|
||||||
|
});
|
||||||
|
|
||||||
|
let num_curr_devices = detected_devices.len();
|
||||||
|
*self.devices.write() = detected_devices;
|
||||||
|
|
||||||
|
match device_direction {
|
||||||
|
DeviceDirection::Arrived => {
|
||||||
|
if num_curr_devices > num_prev_devices {
|
||||||
|
Ok(num_curr_devices - num_prev_devices)
|
||||||
|
} else {
|
||||||
|
Err(Error::NoDeviceArrived)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeviceDirection::Left => {
|
||||||
|
if num_prev_devices > num_curr_devices {
|
||||||
|
Ok(num_prev_devices - num_curr_devices)
|
||||||
|
} else {
|
||||||
|
Err(Error::NoDeviceLeft)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Error> {
|
||||||
|
let handle = self.open_path(|| usb.open_path(&dev_info.path))?;
|
||||||
|
let manufacturer = dev_info.manufacturer_string.clone().unwrap_or_else(|| "Unknown".to_owned());
|
||||||
|
let name = dev_info.product_string.clone().unwrap_or_else(|| "Unknown".to_owned());
|
||||||
|
let serial = dev_info.serial_number.clone().unwrap_or_else(|| "Unknown".to_owned());
|
||||||
|
match self.get_address(&handle) {
|
||||||
|
Ok(Some(addr)) => {
|
||||||
|
Ok(Device {
|
||||||
|
path: dev_info.path.clone(),
|
||||||
|
info: WalletInfo {
|
||||||
|
name,
|
||||||
|
manufacturer,
|
||||||
|
serial,
|
||||||
|
address: addr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_devices(&self) -> Vec<WalletInfo> {
|
||||||
|
self.devices.read().iter().map(|d| d.info.clone()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_locked_devices(&self) -> Vec<String> {
|
||||||
|
(*self.locked_devices.read()).clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_wallet(&self, address: &Address) -> Option<WalletInfo> {
|
||||||
|
self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Error> {
|
||||||
|
let typ = MessageType::MessageType_EthereumGetAddress;
|
||||||
|
let mut message = EthereumGetAddress::new();
|
||||||
|
match *self.key_path.read() {
|
||||||
|
KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
|
||||||
|
KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
|
||||||
|
}
|
||||||
|
message.set_show_display(false);
|
||||||
|
self.send_device_message(&device, typ, &message)?;
|
||||||
|
|
||||||
|
let (resp_type, bytes) = self.read_device_response(&device)?;
|
||||||
|
match resp_type {
|
||||||
|
MessageType::MessageType_EthereumAddress => {
|
||||||
|
let response: EthereumAddress = protobuf::core::parse_from_bytes(&bytes)?;
|
||||||
|
Ok(Some(From::from(response.get_address())))
|
||||||
|
}
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_path<R, F>(&self, f: F) -> Result<R, Error>
|
||||||
|
where F: Fn() -> Result<R, &'static str>
|
||||||
|
{
|
||||||
|
f().map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poll the device in maximum `max_polling_duration` if it doesn't succeed
|
||||||
|
pub fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDirection) -> bool {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
while start_time.elapsed() <= *duration {
|
||||||
|
if let Ok(num_devices) = trezor.update_devices(dir) {
|
||||||
|
trace!(target: "hw", "{} Trezor devices {}", num_devices, dir);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the detected device is a Trezor device by checking both the product ID and the vendor ID
|
||||||
|
pub fn is_valid_trezor(vid: u16, pid: u16) -> bool {
|
||||||
|
vid == TREZOR_VID && TREZOR_PIDS.contains(&pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -443,29 +433,31 @@ impl libusb::Hotplug for EventHandler {
|
|||||||
/// This test can't be run without an actual trezor device connected
|
/// This test can't be run without an actual trezor device connected
|
||||||
/// (and unlocked) attached to the machine that's running the test
|
/// (and unlocked) attached to the machine that's running the test
|
||||||
fn test_signature() {
|
fn test_signature() {
|
||||||
use ethereum_types::{H160, H256, U256};
|
use ethereum_types::Address;
|
||||||
|
use MAX_POLLING_DURATION;
|
||||||
|
use super::HardwareWalletManager;
|
||||||
|
|
||||||
let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().unwrap()));
|
let manager = HardwareWalletManager::new().unwrap();
|
||||||
let manager = Manager::new(hidapi.clone());
|
|
||||||
let addr: Address = H160::from("some_addr");
|
|
||||||
|
|
||||||
manager.update_devices().unwrap();
|
assert_eq!(try_connect_polling(&manager.trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived), true);
|
||||||
|
|
||||||
|
let addr: Address = manager.list_wallets()
|
||||||
|
.iter()
|
||||||
|
.filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string())
|
||||||
|
.nth(0)
|
||||||
|
.map(|d| d.address)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let t_info = TransactionInfo {
|
let t_info = TransactionInfo {
|
||||||
nonce: U256::from(1),
|
nonce: U256::from(1),
|
||||||
gas_price: U256::from(100),
|
gas_price: U256::from(100),
|
||||||
gas_limit: U256::from(21_000),
|
gas_limit: U256::from(21_000),
|
||||||
to: Some(H160::from("some_other_addr")),
|
to: Some(Address::from(1337)),
|
||||||
chain_id: Some(17),
|
chain_id: Some(1),
|
||||||
value: U256::from(1_000_000),
|
value: U256::from(1_000_000),
|
||||||
data: (&[1u8; 3000]).to_vec(),
|
data: (&[1u8; 3000]).to_vec(),
|
||||||
};
|
};
|
||||||
let signature = manager.sign_transaction(&addr, &t_info).unwrap();
|
|
||||||
let expected = Signature::from_rsv(
|
|
||||||
&H256::from("device_specific_r"),
|
|
||||||
&H256::from("device_specific_s"),
|
|
||||||
0x01
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(signature, expected)
|
let signature = manager.trezor.sign_transaction(&addr, &t_info);
|
||||||
|
assert!(signature.is_ok());
|
||||||
}
|
}
|
||||||
73
accounts/src/account_data.rs
Normal file
73
accounts/src/account_data.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Account Metadata
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
|
||||||
|
use ethkey::{Address, Password};
|
||||||
|
use serde_derive::{Serialize, Deserialize};
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
/// Type of unlock.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum Unlock {
|
||||||
|
/// If account is unlocked temporarily, it should be locked after first usage.
|
||||||
|
OneTime,
|
||||||
|
/// Account unlocked permanently can always sign message.
|
||||||
|
/// Use with caution.
|
||||||
|
Perm,
|
||||||
|
/// Account unlocked with a timeout
|
||||||
|
Timed(Instant),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data associated with account.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AccountData {
|
||||||
|
pub unlock: Unlock,
|
||||||
|
pub password: Password,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collected account metadata
|
||||||
|
#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct AccountMeta {
|
||||||
|
/// The name of the account.
|
||||||
|
pub name: String,
|
||||||
|
/// The rest of the metadata of the account.
|
||||||
|
pub meta: String,
|
||||||
|
/// The 128-bit Uuid of the account, if it has one (brain-wallets don't).
|
||||||
|
pub uuid: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountMeta {
|
||||||
|
/// Read a hash map of Address -> AccountMeta
|
||||||
|
pub fn read<R>(reader: R) -> Result<HashMap<Address, Self>, serde_json::Error> where
|
||||||
|
R: ::std::io::Read,
|
||||||
|
{
|
||||||
|
serde_json::from_reader(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a hash map of Address -> AccountMeta
|
||||||
|
pub fn write<W>(m: &HashMap<Address, Self>, writer: &mut W) -> Result<(), serde_json::Error> where
|
||||||
|
W: ::std::io::Write,
|
||||||
|
{
|
||||||
|
serde_json::to_writer(writer, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
56
accounts/src/error.rs
Normal file
56
accounts/src/error.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use ethstore::{Error as SSError};
|
||||||
|
use hardware_wallet::{Error as HardwareError};
|
||||||
|
|
||||||
|
/// Signing error
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SignError {
|
||||||
|
/// Account is not unlocked
|
||||||
|
NotUnlocked,
|
||||||
|
/// Account does not exist.
|
||||||
|
NotFound,
|
||||||
|
/// Low-level hardware device error.
|
||||||
|
Hardware(HardwareError),
|
||||||
|
/// Low-level error from store
|
||||||
|
SStore(SSError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SignError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
match *self {
|
||||||
|
SignError::NotUnlocked => write!(f, "Account is locked"),
|
||||||
|
SignError::NotFound => write!(f, "Account does not exist"),
|
||||||
|
SignError::Hardware(ref e) => write!(f, "{}", e),
|
||||||
|
SignError::SStore(ref e) => write!(f, "{}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HardwareError> for SignError {
|
||||||
|
fn from(e: HardwareError) -> Self {
|
||||||
|
SignError::Hardware(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SSError> for SignError {
|
||||||
|
fn from(e: SSError) -> Self {
|
||||||
|
SignError::SStore(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
549
ethcore/src/account_provider/mod.rs → accounts/src/lib.rs
Executable file → Normal file
549
ethcore/src/account_provider/mod.rs → accounts/src/lib.rs
Executable file → Normal file
@@ -1,122 +1,68 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Parity.
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
// Parity is free software: you can redistribute it and/or modify
|
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// Parity is distributed in the hope that it will be useful,
|
// Parity Ethereum 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
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU 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 General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! Account management.
|
//! Account management.
|
||||||
|
|
||||||
|
mod account_data;
|
||||||
|
mod error;
|
||||||
mod stores;
|
mod stores;
|
||||||
|
|
||||||
use self::stores::{AddressBook, DappsSettingsStore, NewDappsPolicy};
|
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||||
|
extern crate fake_hardware_wallet as hardware_wallet;
|
||||||
|
|
||||||
use std::fmt;
|
use self::account_data::{Unlock, AccountData};
|
||||||
use std::collections::{HashMap, HashSet};
|
use self::stores::AddressBook;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use parking_lot::RwLock;
|
|
||||||
|
use common_types::transaction::{Action, Transaction};
|
||||||
|
use ethkey::{Address, Message, Public, Secret, Password, Random, Generator};
|
||||||
|
use ethstore::accounts_dir::MemoryDirectory;
|
||||||
use ethstore::{
|
use ethstore::{
|
||||||
SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore,
|
SimpleSecretStore, SecretStore, EthStore, EthMultiStore,
|
||||||
random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret,
|
random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret,
|
||||||
};
|
};
|
||||||
use ethstore::accounts_dir::MemoryDirectory;
|
use log::{warn, debug};
|
||||||
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
|
use parking_lot::RwLock;
|
||||||
use ethjson::misc::AccountMeta;
|
|
||||||
use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo};
|
|
||||||
use super::transaction::{Action, Transaction};
|
|
||||||
pub use ethstore::ethkey::Signature;
|
|
||||||
pub use ethstore::{Derivation, IndexDerivation, KeyFile};
|
|
||||||
|
|
||||||
/// Type of unlock.
|
pub use ethkey::Signature;
|
||||||
#[derive(Clone, PartialEq)]
|
pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error};
|
||||||
enum Unlock {
|
pub use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo};
|
||||||
/// If account is unlocked temporarily, it should be locked after first usage.
|
|
||||||
OneTime,
|
pub use self::account_data::AccountMeta;
|
||||||
/// Account unlocked permantently can always sign message.
|
pub use self::error::SignError;
|
||||||
/// Use with caution.
|
|
||||||
Perm,
|
type AccountToken = Password;
|
||||||
/// Account unlocked with a timeout
|
|
||||||
Timed(Instant),
|
/// Account management settings.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct AccountProviderSettings {
|
||||||
|
/// Enable hardware wallet support.
|
||||||
|
pub enable_hardware_wallets: bool,
|
||||||
|
/// Use the classic chain key on the hardware wallet.
|
||||||
|
pub hardware_wallet_classic_key: bool,
|
||||||
|
/// Store raw account secret when unlocking the account permanently.
|
||||||
|
pub unlock_keep_secret: bool,
|
||||||
|
/// Disallowed accounts.
|
||||||
|
pub blacklisted_accounts: Vec<Address>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data associated with account.
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct AccountData {
|
|
||||||
unlock: Unlock,
|
|
||||||
password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Signing error
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum SignError {
|
|
||||||
/// Account is not unlocked
|
|
||||||
NotUnlocked,
|
|
||||||
/// Account does not exist.
|
|
||||||
NotFound,
|
|
||||||
/// Low-level hardware device error.
|
|
||||||
Hardware(HardwareError),
|
|
||||||
/// Low-level error from store
|
|
||||||
SStore(SSError),
|
|
||||||
/// Inappropriate chain
|
|
||||||
InappropriateChain,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for SignError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
||||||
match *self {
|
|
||||||
SignError::NotUnlocked => write!(f, "Account is locked"),
|
|
||||||
SignError::NotFound => write!(f, "Account does not exist"),
|
|
||||||
SignError::Hardware(ref e) => write!(f, "{}", e),
|
|
||||||
SignError::SStore(ref e) => write!(f, "{}", e),
|
|
||||||
SignError::InappropriateChain => write!(f, "Inappropriate chain"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<HardwareError> for SignError {
|
|
||||||
fn from(e: HardwareError) -> Self {
|
|
||||||
SignError::Hardware(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SSError> for SignError {
|
|
||||||
fn from(e: SSError) -> Self {
|
|
||||||
SignError::SStore(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `AccountProvider` errors.
|
|
||||||
pub type Error = SSError;
|
|
||||||
|
|
||||||
/// Dapp identifier
|
|
||||||
#[derive(Default, Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct DappId(String);
|
|
||||||
|
|
||||||
impl From<DappId> for String {
|
|
||||||
fn from(id: DappId) -> String { id.0 }
|
|
||||||
}
|
|
||||||
impl From<String> for DappId {
|
|
||||||
fn from(id: String) -> DappId { DappId(id) }
|
|
||||||
}
|
|
||||||
impl<'a> From<&'a str> for DappId {
|
|
||||||
fn from(id: &'a str) -> DappId { DappId(id.to_owned()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transient_sstore() -> EthMultiStore {
|
|
||||||
EthMultiStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccountToken = String;
|
|
||||||
|
|
||||||
/// Account management.
|
/// Account management.
|
||||||
/// Responsible for unlocking accounts.
|
/// Responsible for unlocking accounts.
|
||||||
pub struct AccountProvider {
|
pub struct AccountProvider {
|
||||||
@@ -126,8 +72,6 @@ pub struct AccountProvider {
|
|||||||
unlocked: RwLock<HashMap<StoreAccountRef, AccountData>>,
|
unlocked: RwLock<HashMap<StoreAccountRef, AccountData>>,
|
||||||
/// Address book.
|
/// Address book.
|
||||||
address_book: RwLock<AddressBook>,
|
address_book: RwLock<AddressBook>,
|
||||||
/// Dapps settings.
|
|
||||||
dapps_settings: RwLock<DappsSettingsStore>,
|
|
||||||
/// Accounts on disk
|
/// Accounts on disk
|
||||||
sstore: Box<SecretStore>,
|
sstore: Box<SecretStore>,
|
||||||
/// Accounts unlocked with rolling tokens
|
/// Accounts unlocked with rolling tokens
|
||||||
@@ -141,33 +85,15 @@ pub struct AccountProvider {
|
|||||||
blacklisted_accounts: Vec<Address>,
|
blacklisted_accounts: Vec<Address>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Account management settings.
|
fn transient_sstore() -> EthMultiStore {
|
||||||
pub struct AccountProviderSettings {
|
EthMultiStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")
|
||||||
/// Enable hardware wallet support.
|
|
||||||
pub enable_hardware_wallets: bool,
|
|
||||||
/// Use the classic chain key on the hardware wallet.
|
|
||||||
pub hardware_wallet_classic_key: bool,
|
|
||||||
/// Store raw account secret when unlocking the account permanently.
|
|
||||||
pub unlock_keep_secret: bool,
|
|
||||||
/// Disallowed accounts.
|
|
||||||
pub blacklisted_accounts: Vec<Address>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AccountProviderSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
AccountProviderSettings {
|
|
||||||
enable_hardware_wallets: false,
|
|
||||||
hardware_wallet_classic_key: false,
|
|
||||||
unlock_keep_secret: false,
|
|
||||||
blacklisted_accounts: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountProvider {
|
impl AccountProvider {
|
||||||
/// Creates new account provider.
|
/// Creates new account provider.
|
||||||
pub fn new(sstore: Box<SecretStore>, settings: AccountProviderSettings) -> Self {
|
pub fn new(sstore: Box<SecretStore>, settings: AccountProviderSettings) -> Self {
|
||||||
let mut hardware_store = None;
|
let mut hardware_store = None;
|
||||||
|
|
||||||
if settings.enable_hardware_wallets {
|
if settings.enable_hardware_wallets {
|
||||||
match HardwareWalletManager::new() {
|
match HardwareWalletManager::new() {
|
||||||
Ok(manager) => {
|
Ok(manager) => {
|
||||||
@@ -195,7 +121,6 @@ impl AccountProvider {
|
|||||||
unlocked_secrets: RwLock::new(HashMap::new()),
|
unlocked_secrets: RwLock::new(HashMap::new()),
|
||||||
unlocked: RwLock::new(HashMap::new()),
|
unlocked: RwLock::new(HashMap::new()),
|
||||||
address_book: RwLock::new(address_book),
|
address_book: RwLock::new(address_book),
|
||||||
dapps_settings: RwLock::new(DappsSettingsStore::new(&sstore.local_path())),
|
|
||||||
sstore: sstore,
|
sstore: sstore,
|
||||||
transient_sstore: transient_sstore(),
|
transient_sstore: transient_sstore(),
|
||||||
hardware_store: hardware_store,
|
hardware_store: hardware_store,
|
||||||
@@ -210,7 +135,6 @@ impl AccountProvider {
|
|||||||
unlocked_secrets: RwLock::new(HashMap::new()),
|
unlocked_secrets: RwLock::new(HashMap::new()),
|
||||||
unlocked: RwLock::new(HashMap::new()),
|
unlocked: RwLock::new(HashMap::new()),
|
||||||
address_book: RwLock::new(AddressBook::transient()),
|
address_book: RwLock::new(AddressBook::transient()),
|
||||||
dapps_settings: RwLock::new(DappsSettingsStore::transient()),
|
|
||||||
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
|
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
|
||||||
transient_sstore: transient_sstore(),
|
transient_sstore: transient_sstore(),
|
||||||
hardware_store: None,
|
hardware_store: None,
|
||||||
@@ -220,12 +144,12 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new random account.
|
/// Creates new random account.
|
||||||
pub fn new_account(&self, password: &str) -> Result<Address, Error> {
|
pub fn new_account(&self, password: &Password) -> Result<Address, Error> {
|
||||||
self.new_account_and_public(password).map(|d| d.0)
|
self.new_account_and_public(password).map(|d| d.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new random account and returns address and public key
|
/// Creates new random account and returns address and public key
|
||||||
pub fn new_account_and_public(&self, password: &str) -> Result<(Address, Public), Error> {
|
pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> {
|
||||||
let acc = Random.generate().expect("secp context has generation capabilities; qed");
|
let acc = Random.generate().expect("secp context has generation capabilities; qed");
|
||||||
let public = acc.public().clone();
|
let public = acc.public().clone();
|
||||||
let secret = acc.secret().clone();
|
let secret = acc.secret().clone();
|
||||||
@@ -235,11 +159,11 @@ impl AccountProvider {
|
|||||||
|
|
||||||
/// Inserts new account into underlying store.
|
/// Inserts new account into underlying store.
|
||||||
/// Does not unlock account!
|
/// Does not unlock account!
|
||||||
pub fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error> {
|
pub fn insert_account(&self, secret: Secret, password: &Password) -> Result<Address, Error> {
|
||||||
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?;
|
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?;
|
||||||
if self.blacklisted_accounts.contains(&account.address) {
|
if self.blacklisted_accounts.contains(&account.address) {
|
||||||
self.sstore.remove_account(&account, password)?;
|
self.sstore.remove_account(&account, password)?;
|
||||||
return Err(SSError::InvalidAccount.into());
|
return Err(Error::InvalidAccount.into());
|
||||||
}
|
}
|
||||||
Ok(account.address)
|
Ok(account.address)
|
||||||
}
|
}
|
||||||
@@ -247,7 +171,7 @@ impl AccountProvider {
|
|||||||
/// Generates new derived account based on the existing one
|
/// Generates new derived account based on the existing one
|
||||||
/// If password is not provided, account must be unlocked
|
/// If password is not provided, account must be unlocked
|
||||||
/// New account will be created with the same password (if save: true)
|
/// New account will be created with the same password (if save: true)
|
||||||
pub fn derive_account(&self, address: &Address, password: Option<String>, derivation: Derivation, save: bool)
|
pub fn derive_account(&self, address: &Address, password: Option<Password>, derivation: Derivation, save: bool)
|
||||||
-> Result<Address, SignError>
|
-> Result<Address, SignError>
|
||||||
{
|
{
|
||||||
let account = self.sstore.account_ref(&address)?;
|
let account = self.sstore.account_ref(&address)?;
|
||||||
@@ -259,41 +183,50 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Import a new presale wallet.
|
/// Import a new presale wallet.
|
||||||
pub fn import_presale(&self, presale_json: &[u8], password: &str) -> Result<Address, Error> {
|
pub fn import_presale(&self, presale_json: &[u8], password: &Password) -> Result<Address, Error> {
|
||||||
let account = self.sstore.import_presale(SecretVaultRef::Root, presale_json, password)?;
|
let account = self.sstore.import_presale(SecretVaultRef::Root, presale_json, password)?;
|
||||||
Ok(Address::from(account.address).into())
|
Ok(Address::from(account.address).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import a new wallet.
|
/// Import a new wallet.
|
||||||
pub fn import_wallet(&self, json: &[u8], password: &str, gen_id: bool) -> Result<Address, Error> {
|
pub fn import_wallet(&self, json: &[u8], password: &Password, gen_id: bool) -> Result<Address, Error> {
|
||||||
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password, gen_id)?;
|
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password, gen_id)?;
|
||||||
if self.blacklisted_accounts.contains(&account.address) {
|
if self.blacklisted_accounts.contains(&account.address) {
|
||||||
self.sstore.remove_account(&account, password)?;
|
self.sstore.remove_account(&account, password)?;
|
||||||
return Err(SSError::InvalidAccount.into());
|
return Err(Error::InvalidAccount.into());
|
||||||
}
|
}
|
||||||
Ok(Address::from(account.address).into())
|
Ok(Address::from(account.address).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether an account with a given address is present.
|
/// Checks whether an account with a given address is present.
|
||||||
pub fn has_account(&self, address: Address) -> Result<bool, Error> {
|
pub fn has_account(&self, address: Address) -> bool {
|
||||||
Ok(self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address))
|
self.sstore.account_ref(&address).is_ok() && !self.blacklisted_accounts.contains(&address)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns addresses of all accounts.
|
/// Returns addresses of all accounts.
|
||||||
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
|
pub fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||||
let accounts = self.sstore.accounts()?;
|
let accounts = self.sstore.accounts()?;
|
||||||
Ok(accounts
|
Ok(accounts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|a| a.address)
|
.map(|a| a.address)
|
||||||
.filter(|address| !self.blacklisted_accounts.contains(address))
|
.filter(|address| !self.blacklisted_accounts.contains(address))
|
||||||
.collect()
|
.collect()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the address of default account.
|
||||||
|
pub fn default_account(&self) -> Result<Address, Error> {
|
||||||
|
Ok(self.accounts()?.first().cloned().unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns addresses of hardware accounts.
|
/// Returns addresses of hardware accounts.
|
||||||
pub fn hardware_accounts(&self) -> Result<Vec<Address>, Error> {
|
pub fn hardware_accounts(&self) -> Result<Vec<Address>, Error> {
|
||||||
let accounts = self.hardware_store.as_ref().map_or(Vec::new(), |h| h.list_wallets());
|
if let Some(accounts) = self.hardware_store.as_ref().map(|h| h.list_wallets()) {
|
||||||
Ok(accounts.into_iter().map(|a| a.address).collect())
|
if !accounts.is_empty() {
|
||||||
|
return Ok(accounts.into_iter().map(|a| a.address).collect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::Custom("No hardware wallet accounts were found".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a list of paths to locked hardware wallets
|
/// Get a list of paths to locked hardware wallets
|
||||||
@@ -314,175 +247,6 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets addresses of accounts exposed for unknown dapps.
|
|
||||||
/// `None` means that all accounts will be visible.
|
|
||||||
/// If not `None` or empty it will also override default account.
|
|
||||||
pub fn set_new_dapps_addresses(&self, accounts: Option<Vec<Address>>) -> Result<(), Error> {
|
|
||||||
let current_default = self.new_dapps_default_address()?;
|
|
||||||
|
|
||||||
self.dapps_settings.write().set_policy(match accounts {
|
|
||||||
None => NewDappsPolicy::AllAccounts {
|
|
||||||
default: current_default,
|
|
||||||
},
|
|
||||||
Some(accounts) => NewDappsPolicy::Whitelist(accounts),
|
|
||||||
});
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets addresses of accounts exposed for unknown dapps.
|
|
||||||
/// `None` means that all accounts will be visible.
|
|
||||||
pub fn new_dapps_addresses(&self) -> Result<Option<Vec<Address>>, Error> {
|
|
||||||
Ok(match self.dapps_settings.read().policy() {
|
|
||||||
NewDappsPolicy::AllAccounts { .. } => None,
|
|
||||||
NewDappsPolicy::Whitelist(accounts) => Some(accounts),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets a default account for unknown dapps.
|
|
||||||
/// This account will always be returned as the first one.
|
|
||||||
pub fn set_new_dapps_default_address(&self, address: Address) -> Result<(), Error> {
|
|
||||||
if !self.valid_addresses()?.contains(&address) {
|
|
||||||
return Err(SSError::InvalidAccount.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut settings = self.dapps_settings.write();
|
|
||||||
let new_policy = match settings.policy() {
|
|
||||||
NewDappsPolicy::AllAccounts { .. } => NewDappsPolicy::AllAccounts { default: address },
|
|
||||||
NewDappsPolicy::Whitelist(list) => NewDappsPolicy::Whitelist(Self::insert_default(list, address)),
|
|
||||||
};
|
|
||||||
settings.set_policy(new_policy);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts given address as first in the vector, preventing duplicates.
|
|
||||||
fn insert_default(mut addresses: Vec<Address>, default: Address) -> Vec<Address> {
|
|
||||||
if let Some(position) = addresses.iter().position(|address| address == &default) {
|
|
||||||
addresses.swap(0, position);
|
|
||||||
} else {
|
|
||||||
addresses.insert(0, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
addresses
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a list of accounts that new dapp should see.
|
|
||||||
/// First account is always the default account.
|
|
||||||
fn new_dapps_addresses_list(&self) -> Result<Vec<Address>, Error> {
|
|
||||||
match self.dapps_settings.read().policy() {
|
|
||||||
NewDappsPolicy::AllAccounts { default } => if default.is_zero() {
|
|
||||||
self.accounts()
|
|
||||||
} else {
|
|
||||||
Ok(Self::insert_default(self.accounts()?, default))
|
|
||||||
},
|
|
||||||
NewDappsPolicy::Whitelist(accounts) => {
|
|
||||||
let addresses = self.filter_addresses(accounts)?;
|
|
||||||
if addresses.is_empty() {
|
|
||||||
Ok(vec![self.accounts()?.get(0).cloned().unwrap_or(0.into())])
|
|
||||||
} else {
|
|
||||||
Ok(addresses)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a default account for new dapps
|
|
||||||
/// Will return zero address in case the default is not set and there are no accounts configured.
|
|
||||||
pub fn new_dapps_default_address(&self) -> Result<Address, Error> {
|
|
||||||
Ok(self.new_dapps_addresses_list()?
|
|
||||||
.get(0)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or(0.into())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a list of dapps recently requesting accounts.
|
|
||||||
pub fn recent_dapps(&self) -> Result<HashMap<DappId, u64>, Error> {
|
|
||||||
Ok(self.dapps_settings.read().recent_dapps())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marks dapp as recently used.
|
|
||||||
pub fn note_dapp_used(&self, dapp: DappId) -> Result<(), Error> {
|
|
||||||
let mut dapps = self.dapps_settings.write();
|
|
||||||
dapps.mark_dapp_used(dapp.clone());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets addresses visible for given dapp.
|
|
||||||
pub fn dapp_addresses(&self, dapp: DappId) -> Result<Vec<Address>, Error> {
|
|
||||||
let accounts = self.dapps_settings.read().settings().get(&dapp).map(|settings| {
|
|
||||||
(settings.accounts.clone(), settings.default.clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
match accounts {
|
|
||||||
Some((Some(accounts), Some(default))) => self.filter_addresses(Self::insert_default(accounts, default)),
|
|
||||||
Some((Some(accounts), None)) => self.filter_addresses(accounts),
|
|
||||||
Some((None, Some(default))) => self.filter_addresses(Self::insert_default(self.new_dapps_addresses_list()?, default)),
|
|
||||||
_ => self.new_dapps_addresses_list(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns default account for particular dapp falling back to other allowed accounts if necessary.
|
|
||||||
pub fn dapp_default_address(&self, dapp: DappId) -> Result<Address, Error> {
|
|
||||||
let dapp_default = self.dapp_addresses(dapp)?
|
|
||||||
.get(0)
|
|
||||||
.cloned();
|
|
||||||
|
|
||||||
match dapp_default {
|
|
||||||
Some(default) => Ok(default),
|
|
||||||
None => self.new_dapps_default_address(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets default address for given dapp.
|
|
||||||
/// Does not alter dapp addresses, but this account will always be returned as the first one.
|
|
||||||
pub fn set_dapp_default_address(&self, dapp: DappId, address: Address) -> Result<(), Error> {
|
|
||||||
if !self.valid_addresses()?.contains(&address) {
|
|
||||||
return Err(SSError::InvalidAccount.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dapps_settings.write().set_default(dapp, address);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets addresses visible for given dapp.
|
|
||||||
/// If `None` - falls back to dapps addresses
|
|
||||||
/// If not `None` and not empty it will also override default account.
|
|
||||||
pub fn set_dapp_addresses(&self, dapp: DappId, addresses: Option<Vec<Address>>) -> Result<(), Error> {
|
|
||||||
let (addresses, default) = match addresses {
|
|
||||||
Some(addresses) => {
|
|
||||||
let addresses = self.filter_addresses(addresses)?;
|
|
||||||
let default = addresses.get(0).cloned();
|
|
||||||
(Some(addresses), default)
|
|
||||||
},
|
|
||||||
None => (None, None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut settings = self.dapps_settings.write();
|
|
||||||
if let Some(default) = default {
|
|
||||||
settings.set_default(dapp.clone(), default);
|
|
||||||
}
|
|
||||||
settings.set_accounts(dapp, addresses);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn valid_addresses(&self) -> Result<HashSet<Address>, Error> {
|
|
||||||
Ok(self.addresses_info().into_iter()
|
|
||||||
.map(|(address, _)| address)
|
|
||||||
.chain(self.accounts()?)
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes addresses that are neither accounts nor in address book.
|
|
||||||
fn filter_addresses(&self, addresses: Vec<Address>) -> Result<Vec<Address>, Error> {
|
|
||||||
let valid = self.valid_addresses()?;
|
|
||||||
|
|
||||||
Ok(addresses.into_iter()
|
|
||||||
.filter(|a| valid.contains(&a))
|
|
||||||
.collect()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns each address along with metadata.
|
/// Returns each address along with metadata.
|
||||||
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
||||||
self.address_book.read().get()
|
self.address_book.read().get()
|
||||||
@@ -498,7 +262,7 @@ impl AccountProvider {
|
|||||||
self.address_book.write().set_meta(account, meta)
|
self.address_book.write().set_meta(account, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes and address from the addressbook
|
/// Removes and address from the address book
|
||||||
pub fn remove_address(&self, addr: Address) {
|
pub fn remove_address(&self, addr: Address) {
|
||||||
self.address_book.write().remove(addr)
|
self.address_book.write().remove(addr)
|
||||||
}
|
}
|
||||||
@@ -546,7 +310,7 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns account public key.
|
/// Returns account public key.
|
||||||
pub fn account_public(&self, address: Address, password: &str) -> Result<Public, Error> {
|
pub fn account_public(&self, address: Address, password: &Password) -> Result<Public, Error> {
|
||||||
self.sstore.public(&self.sstore.account_ref(&address)?, password)
|
self.sstore.public(&self.sstore.account_ref(&address)?, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,32 +327,32 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
||||||
pub fn test_password(&self, address: &Address, password: &str) -> Result<bool, Error> {
|
pub fn test_password(&self, address: &Address, password: &Password) -> Result<bool, Error> {
|
||||||
self.sstore.test_password(&self.sstore.account_ref(&address)?, password)
|
self.sstore.test_password(&self.sstore.account_ref(&address)?, password)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Permanently removes an account.
|
/// Permanently removes an account.
|
||||||
pub fn kill_account(&self, address: &Address, password: &str) -> Result<(), Error> {
|
pub fn kill_account(&self, address: &Address, password: &Password) -> Result<(), Error> {
|
||||||
self.sstore.remove_account(&self.sstore.account_ref(&address)?, &password)?;
|
self.sstore.remove_account(&self.sstore.account_ref(&address)?, &password)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
|
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
|
||||||
pub fn change_password(&self, address: &Address, password: String, new_password: String) -> Result<(), Error> {
|
pub fn change_password(&self, address: &Address, password: Password, new_password: Password) -> Result<(), Error> {
|
||||||
self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password)
|
self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exports an account for given address.
|
/// Exports an account for given address.
|
||||||
pub fn export_account(&self, address: &Address, password: String) -> Result<KeyFile, Error> {
|
pub fn export_account(&self, address: &Address, password: Password) -> Result<KeyFile, Error> {
|
||||||
self.sstore.export_account(&self.sstore.account_ref(address)?, &password)
|
self.sstore.export_account(&self.sstore.account_ref(address)?, &password)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper method used for unlocking accounts.
|
/// Helper method used for unlocking accounts.
|
||||||
fn unlock_account(&self, address: Address, password: String, unlock: Unlock) -> Result<(), Error> {
|
fn unlock_account(&self, address: Address, password: Password, unlock: Unlock) -> Result<(), Error> {
|
||||||
let account = self.sstore.account_ref(&address)?;
|
let account = self.sstore.account_ref(&address)?;
|
||||||
|
|
||||||
// check if account is already unlocked pernamently, if it is, do nothing
|
// check if account is already unlocked permanently, if it is, do nothing
|
||||||
let mut unlocked = self.unlocked.write();
|
let mut unlocked = self.unlocked.write();
|
||||||
if let Some(data) = unlocked.get(&account) {
|
if let Some(data) = unlocked.get(&account) {
|
||||||
if let Unlock::Perm = data.unlock {
|
if let Unlock::Perm = data.unlock {
|
||||||
@@ -615,7 +379,7 @@ impl AccountProvider {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn password(&self, account: &StoreAccountRef) -> Result<String, SignError> {
|
fn password(&self, account: &StoreAccountRef) -> Result<Password, SignError> {
|
||||||
let mut unlocked = self.unlocked.write();
|
let mut unlocked = self.unlocked.write();
|
||||||
let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
|
let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
|
||||||
if let Unlock::OneTime = data.unlock {
|
if let Unlock::OneTime = data.unlock {
|
||||||
@@ -627,22 +391,22 @@ impl AccountProvider {
|
|||||||
return Err(SignError::NotUnlocked);
|
return Err(SignError::NotUnlocked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(data.password.clone())
|
Ok(data.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unlocks account permanently.
|
/// Unlocks account permanently.
|
||||||
pub fn unlock_account_permanently(&self, account: Address, password: String) -> Result<(), Error> {
|
pub fn unlock_account_permanently(&self, account: Address, password: Password) -> Result<(), Error> {
|
||||||
self.unlock_account(account, password, Unlock::Perm)
|
self.unlock_account(account, password, Unlock::Perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unlocks account temporarily (for one signing).
|
/// Unlocks account temporarily (for one signing).
|
||||||
pub fn unlock_account_temporarily(&self, account: Address, password: String) -> Result<(), Error> {
|
pub fn unlock_account_temporarily(&self, account: Address, password: Password) -> Result<(), Error> {
|
||||||
self.unlock_account(account, password, Unlock::OneTime)
|
self.unlock_account(account, password, Unlock::OneTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unlocks account temporarily with a timeout.
|
/// Unlocks account temporarily with a timeout.
|
||||||
pub fn unlock_account_timed(&self, account: Address, password: String, duration_ms: u32) -> Result<(), Error> {
|
pub fn unlock_account_timed(&self, account: Address, password: Password, duration: Duration) -> Result<(), Error> {
|
||||||
self.unlock_account(account, password, Unlock::Timed(Instant::now() + Duration::from_millis(duration_ms as u64)))
|
self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if given account is unlocked
|
/// Checks if given account is unlocked
|
||||||
@@ -663,7 +427,7 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Signs the message. If password is not provided the account must be unlocked.
|
/// Signs the message. If password is not provided the account must be unlocked.
|
||||||
pub fn sign(&self, address: Address, password: Option<String>, message: Message) -> Result<Signature, SignError> {
|
pub fn sign(&self, address: Address, password: Option<Password>, message: Message) -> Result<Signature, SignError> {
|
||||||
let account = self.sstore.account_ref(&address)?;
|
let account = self.sstore.account_ref(&address)?;
|
||||||
match self.unlocked_secrets.read().get(&account) {
|
match self.unlocked_secrets.read().get(&account) {
|
||||||
Some(secret) => {
|
Some(secret) => {
|
||||||
@@ -677,7 +441,7 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Signs message using the derived secret. If password is not provided the account must be unlocked.
|
/// Signs message using the derived secret. If password is not provided the account must be unlocked.
|
||||||
pub fn sign_derived(&self, address: &Address, password: Option<String>, derivation: Derivation, message: Message)
|
pub fn sign_derived(&self, address: &Address, password: Option<Password>, derivation: Derivation, message: Message)
|
||||||
-> Result<Signature, SignError>
|
-> Result<Signature, SignError>
|
||||||
{
|
{
|
||||||
let account = self.sstore.account_ref(address)?;
|
let account = self.sstore.account_ref(address)?;
|
||||||
@@ -690,7 +454,7 @@ impl AccountProvider {
|
|||||||
let account = self.sstore.account_ref(&address)?;
|
let account = self.sstore.account_ref(&address)?;
|
||||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||||
|
|
||||||
let new_token = random_string(16);
|
let new_token = Password::from(random_string(16));
|
||||||
let signature = if is_std_password {
|
let signature = if is_std_password {
|
||||||
// Insert to transient store
|
// Insert to transient store
|
||||||
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
|
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
|
||||||
@@ -713,7 +477,7 @@ impl AccountProvider {
|
|||||||
let account = self.sstore.account_ref(&address)?;
|
let account = self.sstore.account_ref(&address)?;
|
||||||
let is_std_password = self.sstore.test_password(&account, &token)?;
|
let is_std_password = self.sstore.test_password(&account, &token)?;
|
||||||
|
|
||||||
let new_token = random_string(16);
|
let new_token = Password::from(random_string(16));
|
||||||
let message = if is_std_password {
|
let message = if is_std_password {
|
||||||
// Insert to transient store
|
// Insert to transient store
|
||||||
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
|
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?;
|
||||||
@@ -730,14 +494,14 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypts a message. If password is not provided the account must be unlocked.
|
/// Decrypts a message. If password is not provided the account must be unlocked.
|
||||||
pub fn decrypt(&self, address: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, SignError> {
|
pub fn decrypt(&self, address: Address, password: Option<Password>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, SignError> {
|
||||||
let account = self.sstore.account_ref(&address)?;
|
let account = self.sstore.account_ref(&address)?;
|
||||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||||
Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?)
|
Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Agree on shared key.
|
/// Agree on shared key.
|
||||||
pub fn agree(&self, address: Address, password: Option<String>, other_public: &Public) -> Result<Secret, SignError> {
|
pub fn agree(&self, address: Address, password: Option<Password>, other_public: &Public) -> Result<Secret, SignError> {
|
||||||
let account = self.sstore.account_ref(&address)?;
|
let account = self.sstore.account_ref(&address)?;
|
||||||
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
|
||||||
Ok(self.sstore.agree(&account, &password, other_public)?)
|
Ok(self.sstore.agree(&account, &password, other_public)?)
|
||||||
@@ -756,13 +520,13 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create new vault.
|
/// Create new vault.
|
||||||
pub fn create_vault(&self, name: &str, password: &str) -> Result<(), Error> {
|
pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||||
self.sstore.create_vault(name, password)
|
self.sstore.create_vault(name, password)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open existing vault.
|
/// Open existing vault.
|
||||||
pub fn open_vault(&self, name: &str, password: &str) -> Result<(), Error> {
|
pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
|
||||||
self.sstore.open_vault(name, password)
|
self.sstore.open_vault(name, password)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
@@ -786,7 +550,7 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Change vault password.
|
/// Change vault password.
|
||||||
pub fn change_vault_password(&self, name: &str, new_password: &str) -> Result<(), Error> {
|
pub fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> {
|
||||||
self.sstore.change_vault_password(name, new_password)
|
self.sstore.change_vault_password(name, new_password)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
@@ -812,8 +576,17 @@ impl AccountProvider {
|
|||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sign message with hardware wallet.
|
||||||
|
pub fn sign_message_with_hardware(&self, address: &Address, message: &[u8]) -> Result<Signature, SignError> {
|
||||||
|
match self.hardware_store.as_ref().map(|s| s.sign_message(address, message)) {
|
||||||
|
None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
|
||||||
|
Some(Err(e)) => Err(From::from(e)),
|
||||||
|
Some(Ok(s)) => Ok(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sign transaction with hardware wallet.
|
/// Sign transaction with hardware wallet.
|
||||||
pub fn sign_with_hardware(&self, address: Address, transaction: &Transaction, chain_id: Option<u64>, rlp_encoded_transaction: &[u8]) -> Result<Signature, SignError> {
|
pub fn sign_transaction_with_hardware(&self, address: &Address, transaction: &Transaction, chain_id: Option<u64>, rlp_encoded_transaction: &[u8]) -> Result<Signature, SignError> {
|
||||||
let t_info = TransactionInfo {
|
let t_info = TransactionInfo {
|
||||||
nonce: transaction.nonce,
|
nonce: transaction.nonce,
|
||||||
gas_price: transaction.gas_price,
|
gas_price: transaction.gas_price,
|
||||||
@@ -836,9 +609,9 @@ impl AccountProvider {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{AccountProvider, Unlock, DappId};
|
use super::{AccountProvider, Unlock};
|
||||||
use std::time::Instant;
|
use std::time::{Duration, Instant};
|
||||||
use ethstore::ethkey::{Generator, Random, Address};
|
use ethkey::{Generator, Random, Address};
|
||||||
use ethstore::{StoreAccountRef, Derivation};
|
use ethstore::{StoreAccountRef, Derivation};
|
||||||
use ethereum_types::H256;
|
use ethereum_types::H256;
|
||||||
|
|
||||||
@@ -846,7 +619,7 @@ mod tests {
|
|||||||
fn unlock_account_temp() {
|
fn unlock_account_temp() {
|
||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
|
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err());
|
||||||
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
@@ -857,7 +630,7 @@ mod tests {
|
|||||||
fn derived_account_nosave() {
|
fn derived_account_nosave() {
|
||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "base").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
||||||
|
|
||||||
let derived_addr = ap.derive_account(
|
let derived_addr = ap.derive_account(
|
||||||
@@ -875,7 +648,7 @@ mod tests {
|
|||||||
fn derived_account_save() {
|
fn derived_account_save() {
|
||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "base").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
||||||
|
|
||||||
let derived_addr = ap.derive_account(
|
let derived_addr = ap.derive_account(
|
||||||
@@ -896,7 +669,7 @@ mod tests {
|
|||||||
fn derived_account_sign() {
|
fn derived_account_sign() {
|
||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "base").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
|
||||||
|
|
||||||
let derived_addr = ap.derive_account(
|
let derived_addr = ap.derive_account(
|
||||||
@@ -926,7 +699,7 @@ mod tests {
|
|||||||
fn unlock_account_perm() {
|
fn unlock_account_perm() {
|
||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
|
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err());
|
||||||
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok());
|
||||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
@@ -940,9 +713,9 @@ mod tests {
|
|||||||
fn unlock_account_timer() {
|
fn unlock_account_timer() {
|
||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
|
||||||
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), 60000).is_err());
|
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60)).is_err());
|
||||||
assert!(ap.unlock_account_timed(kp.address(), "test".into(), 60000).is_ok());
|
assert!(ap.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60)).is_ok());
|
||||||
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
|
||||||
ap.unlocked.write().get_mut(&StoreAccountRef::root(kp.address())).unwrap().unlock = Unlock::Timed(Instant::now());
|
ap.unlocked.write().get_mut(&StoreAccountRef::root(kp.address())).unwrap().unlock = Unlock::Timed(Instant::now());
|
||||||
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
assert!(ap.sign(kp.address(), None, Default::default()).is_err());
|
||||||
@@ -953,7 +726,7 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let kp = Random.generate().unwrap();
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), Default::default()).unwrap();
|
let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), Default::default()).unwrap();
|
||||||
@@ -964,101 +737,11 @@ mod tests {
|
|||||||
assert!(ap.sign_with_token(kp.address(), token, Default::default()).is_err(), "Second usage of the same token should fail.");
|
assert!(ap.sign_with_token(kp.address(), token, Default::default()).is_err(), "Second usage of the same token should fail.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_reset_dapp_addresses_to_default() {
|
|
||||||
// given
|
|
||||||
let ap = AccountProvider::transient_provider();
|
|
||||||
let app = DappId("app1".into());
|
|
||||||
// add accounts to address book
|
|
||||||
ap.set_address_name(1.into(), "1".into());
|
|
||||||
ap.set_address_name(2.into(), "2".into());
|
|
||||||
// set `AllAccounts` policy
|
|
||||||
ap.set_new_dapps_addresses(Some(vec![1.into(), 2.into()])).unwrap();
|
|
||||||
assert_eq!(ap.dapp_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]);
|
|
||||||
|
|
||||||
// Alter and check
|
|
||||||
ap.set_dapp_addresses(app.clone(), Some(vec![1.into(), 3.into()])).unwrap();
|
|
||||||
assert_eq!(ap.dapp_addresses(app.clone()).unwrap(), vec![1.into()]);
|
|
||||||
|
|
||||||
// Reset back to default
|
|
||||||
ap.set_dapp_addresses(app.clone(), None).unwrap();
|
|
||||||
assert_eq!(ap.dapp_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_set_dapps_default_address() {
|
|
||||||
// given
|
|
||||||
let ap = AccountProvider::transient_provider();
|
|
||||||
let app = DappId("app1".into());
|
|
||||||
// set `AllAccounts` policy
|
|
||||||
ap.set_new_dapps_addresses(None).unwrap();
|
|
||||||
// add accounts to address book
|
|
||||||
ap.set_address_name(1.into(), "1".into());
|
|
||||||
ap.set_address_name(2.into(), "2".into());
|
|
||||||
|
|
||||||
ap.set_dapp_addresses(app.clone(), Some(vec![1.into(), 2.into(), 3.into()])).unwrap();
|
|
||||||
assert_eq!(ap.dapp_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]);
|
|
||||||
assert_eq!(ap.dapp_default_address("app1".into()).unwrap(), 1.into());
|
|
||||||
|
|
||||||
// when setting empty list
|
|
||||||
ap.set_dapp_addresses(app.clone(), Some(vec![])).unwrap();
|
|
||||||
|
|
||||||
// then default account is intact
|
|
||||||
assert_eq!(ap.dapp_addresses(app.clone()).unwrap(), vec![1.into()]);
|
|
||||||
assert_eq!(ap.dapp_default_address("app1".into()).unwrap(), 1.into());
|
|
||||||
|
|
||||||
// alter default account
|
|
||||||
ap.set_dapp_default_address("app1".into(), 2.into()).unwrap();
|
|
||||||
assert_eq!(ap.dapp_addresses(app.clone()).unwrap(), vec![2.into()]);
|
|
||||||
assert_eq!(ap.dapp_default_address("app1".into()).unwrap(), 2.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_set_dapps_policy_and_default_account() {
|
|
||||||
// given
|
|
||||||
let ap = AccountProvider::transient_provider();
|
|
||||||
|
|
||||||
// default_account should be always available
|
|
||||||
assert_eq!(ap.new_dapps_default_address().unwrap(), 0.into());
|
|
||||||
|
|
||||||
let address = ap.new_account("test").unwrap();
|
|
||||||
ap.set_address_name(1.into(), "1".into());
|
|
||||||
|
|
||||||
// Default account set to first account by default
|
|
||||||
assert_eq!(ap.new_dapps_default_address().unwrap(), address);
|
|
||||||
assert_eq!(ap.dapp_default_address("app1".into()).unwrap(), address);
|
|
||||||
|
|
||||||
// Even when returning nothing
|
|
||||||
ap.set_new_dapps_addresses(Some(vec![])).unwrap();
|
|
||||||
// Default account is still returned
|
|
||||||
assert_eq!(ap.dapp_addresses("app1".into()).unwrap(), vec![address]);
|
|
||||||
|
|
||||||
// change to all
|
|
||||||
ap.set_new_dapps_addresses(None).unwrap();
|
|
||||||
assert_eq!(ap.dapp_addresses("app1".into()).unwrap(), vec![address]);
|
|
||||||
|
|
||||||
// change to non-existent account
|
|
||||||
ap.set_new_dapps_addresses(Some(vec![2.into()])).unwrap();
|
|
||||||
assert_eq!(ap.dapp_addresses("app1".into()).unwrap(), vec![address]);
|
|
||||||
|
|
||||||
// change to a addresses
|
|
||||||
ap.set_new_dapps_addresses(Some(vec![1.into()])).unwrap();
|
|
||||||
assert_eq!(ap.dapp_addresses("app1".into()).unwrap(), vec![1.into()]);
|
|
||||||
|
|
||||||
// it overrides default account
|
|
||||||
assert_eq!(ap.new_dapps_default_address().unwrap(), 1.into());
|
|
||||||
assert_eq!(ap.dapp_default_address("app1".into()).unwrap(), 1.into());
|
|
||||||
|
|
||||||
ap.set_new_dapps_default_address(address).unwrap();
|
|
||||||
assert_eq!(ap.new_dapps_default_address().unwrap(), address);
|
|
||||||
assert_eq!(ap.dapp_default_address("app1".into()).unwrap(), address);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_not_return_blacklisted_account() {
|
fn should_not_return_blacklisted_account() {
|
||||||
// given
|
// given
|
||||||
let mut ap = AccountProvider::transient_provider();
|
let mut ap = AccountProvider::transient_provider();
|
||||||
let acc = ap.new_account("test").unwrap();
|
let acc = ap.new_account(&"test".into()).unwrap();
|
||||||
ap.blacklisted_accounts = vec![acc];
|
ap.blacklisted_accounts = vec![acc];
|
||||||
|
|
||||||
// then
|
// then
|
||||||
189
accounts/src/stores.rs
Normal file
189
accounts/src/stores.rs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Address Book Store
|
||||||
|
|
||||||
|
use std::{fs, fmt, hash, ops};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use ethkey::Address;
|
||||||
|
use log::{trace, warn};
|
||||||
|
|
||||||
|
use crate::AccountMeta;
|
||||||
|
|
||||||
|
/// Disk-backed map from Address to String. Uses JSON.
|
||||||
|
pub struct AddressBook {
|
||||||
|
cache: DiskMap<Address, AccountMeta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressBook {
|
||||||
|
/// Creates new address book at given directory.
|
||||||
|
pub fn new(path: &Path) -> Self {
|
||||||
|
let mut r = AddressBook {
|
||||||
|
cache: DiskMap::new(path, "address_book.json")
|
||||||
|
};
|
||||||
|
r.cache.revert(AccountMeta::read);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates transient address book (no changes are saved to disk).
|
||||||
|
pub fn transient() -> Self {
|
||||||
|
AddressBook {
|
||||||
|
cache: DiskMap::transient()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the address book.
|
||||||
|
pub fn get(&self) -> HashMap<Address, AccountMeta> {
|
||||||
|
self.cache.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save(&self) {
|
||||||
|
self.cache.save(AccountMeta::write)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets new name for given address.
|
||||||
|
pub fn set_name(&mut self, a: Address, name: String) {
|
||||||
|
{
|
||||||
|
let x = self.cache.entry(a)
|
||||||
|
.or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None});
|
||||||
|
x.name = name;
|
||||||
|
}
|
||||||
|
self.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets new meta for given address.
|
||||||
|
pub fn set_meta(&mut self, a: Address, meta: String) {
|
||||||
|
{
|
||||||
|
let x = self.cache.entry(a)
|
||||||
|
.or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None});
|
||||||
|
x.meta = meta;
|
||||||
|
}
|
||||||
|
self.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes an entry
|
||||||
|
pub fn remove(&mut self, a: Address) {
|
||||||
|
self.cache.remove(&a);
|
||||||
|
self.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disk-serializable HashMap
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DiskMap<K: hash::Hash + Eq, V> {
|
||||||
|
path: PathBuf,
|
||||||
|
cache: HashMap<K, V>,
|
||||||
|
transient: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: hash::Hash + Eq, V> ops::Deref for DiskMap<K, V> {
|
||||||
|
type Target = HashMap<K, V>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.cache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: hash::Hash + Eq, V> ops::DerefMut for DiskMap<K, V> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.cache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
|
||||||
|
pub fn new(path: &Path, file_name: &str) -> Self {
|
||||||
|
let mut path = path.to_owned();
|
||||||
|
path.push(file_name);
|
||||||
|
trace!(target: "diskmap", "path={:?}", path);
|
||||||
|
DiskMap {
|
||||||
|
path: path,
|
||||||
|
cache: HashMap::new(),
|
||||||
|
transient: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transient() -> Self {
|
||||||
|
let mut map = DiskMap::new(&PathBuf::new(), "diskmap.json".into());
|
||||||
|
map.transient = true;
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn revert<F, E>(&mut self, read: F) where
|
||||||
|
F: Fn(fs::File) -> Result<HashMap<K, V>, E>,
|
||||||
|
E: fmt::Display,
|
||||||
|
{
|
||||||
|
if self.transient { return; }
|
||||||
|
trace!(target: "diskmap", "revert {:?}", self.path);
|
||||||
|
let _ = fs::File::open(self.path.clone())
|
||||||
|
.map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e))
|
||||||
|
.and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e)))
|
||||||
|
.and_then(|m| {
|
||||||
|
self.cache = m;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save<F, E>(&self, write: F) where
|
||||||
|
F: Fn(&HashMap<K, V>, &mut fs::File) -> Result<(), E>,
|
||||||
|
E: fmt::Display,
|
||||||
|
{
|
||||||
|
if self.transient { return; }
|
||||||
|
trace!(target: "diskmap", "save {:?}", self.path);
|
||||||
|
let _ = fs::File::create(self.path.clone())
|
||||||
|
.map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e))
|
||||||
|
.and_then(|mut f| {
|
||||||
|
write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::AddressBook;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
use crate::account_data::AccountMeta;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_save_and_reload_address_book() {
|
||||||
|
let tempdir = TempDir::new("").unwrap();
|
||||||
|
let mut b = AddressBook::new(tempdir.path());
|
||||||
|
b.set_name(1.into(), "One".to_owned());
|
||||||
|
b.set_meta(1.into(), "{1:1}".to_owned());
|
||||||
|
let b = AddressBook::new(tempdir.path());
|
||||||
|
assert_eq!(b.get(), vec![
|
||||||
|
(1, AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None})
|
||||||
|
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_remove_address() {
|
||||||
|
let tempdir = TempDir::new("").unwrap();
|
||||||
|
let mut b = AddressBook::new(tempdir.path());
|
||||||
|
|
||||||
|
b.set_name(1.into(), "One".to_owned());
|
||||||
|
b.set_name(2.into(), "Two".to_owned());
|
||||||
|
b.set_name(3.into(), "Three".to_owned());
|
||||||
|
b.remove(2.into());
|
||||||
|
|
||||||
|
let b = AddressBook::new(tempdir.path());
|
||||||
|
assert_eq!(b.get(), vec![
|
||||||
|
(1, AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||||
|
(3, AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}),
|
||||||
|
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "chainspec"
|
name = "chainspec"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["debris <marek.kotewicz@gmail.com>"]
|
authors = ["Marek Kotewicz <marek@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethjson = { path = "../json" }
|
ethjson = { path = "../json" }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_ignored = "0.0.4"
|
|
||||||
|
|||||||
@@ -1,8 +1,22 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate serde_ignored;
|
|
||||||
extern crate ethjson;
|
extern crate ethjson;
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use std::{fs, env, process};
|
use std::{fs, env, process};
|
||||||
use ethjson::spec::Spec;
|
use ethjson::spec::Spec;
|
||||||
|
|
||||||
@@ -25,24 +39,11 @@ fn main() {
|
|||||||
Err(_) => quit(&format!("{} could not be opened", path)),
|
Err(_) => quit(&format!("{} could not be opened", path)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut unused = BTreeSet::new();
|
let spec: Result<Spec, _> = serde_json::from_reader(file);
|
||||||
let mut deserializer = serde_json::Deserializer::from_reader(file);
|
|
||||||
|
|
||||||
let spec: Result<Spec, _> = serde_ignored::deserialize(&mut deserializer, |field| {
|
|
||||||
unused.insert(field.to_string());
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Err(err) = spec {
|
if let Err(err) = spec {
|
||||||
quit(&format!("{} {}", path, err.to_string()));
|
quit(&format!("{} {}", path, err.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !unused.is_empty() {
|
|
||||||
let err = unused.into_iter()
|
|
||||||
.map(|field| format!("{} unexpected field `{}`", path, field))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n");
|
|
||||||
quit(&err);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{} is valid", path);
|
println!("{} is valid", path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ authors = ["Parity <admin@parity.io>"]
|
|||||||
description = "Parity Cli Tool"
|
description = "Parity Cli Tool"
|
||||||
homepage = "http://parity.io"
|
homepage = "http://parity.io"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
name = "rpc-cli"
|
name = "cli-signer"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
rpassword = "1.0"
|
rpassword = "1.0"
|
||||||
bigint = "4.0"
|
|
||||||
parity-rpc = { path = "../rpc" }
|
parity-rpc = { path = "../rpc" }
|
||||||
parity-rpc-client = { path = "../rpc_client" }
|
parity-rpc-client = { path = "rpc-client" }
|
||||||
20
cli-signer/rpc-client/Cargo.toml
Normal file
20
cli-signer/rpc-client/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Parity <admin@parity.io>"]
|
||||||
|
description = "Parity Rpc Client"
|
||||||
|
homepage = "http://parity.io"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
name = "parity-rpc-client"
|
||||||
|
version = "1.4.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
futures = "0.1"
|
||||||
|
log = "0.4"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_json = "1.0"
|
||||||
|
url = "1.2.0"
|
||||||
|
matches = "0.1"
|
||||||
|
parking_lot = "0.7"
|
||||||
|
jsonrpc-core = "10.0.1"
|
||||||
|
jsonrpc-ws-server = "10.0.1"
|
||||||
|
parity-rpc = { path = "../../rpc" }
|
||||||
|
keccak-hash = "0.1"
|
||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt::{Debug, Formatter, Error as FmtError};
|
use std::fmt::{Debug, Formatter, Error as FmtError};
|
||||||
use std::io::{BufReader, BufRead};
|
use std::io::{BufReader, BufRead};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -74,7 +90,7 @@ impl Handler for RpcHandler {
|
|||||||
})?;
|
})?;
|
||||||
let secs = timestamp.as_secs();
|
let secs = timestamp.as_secs();
|
||||||
let hashed = keccak(format!("{}:{}", self.auth_code, secs));
|
let hashed = keccak(format!("{}:{}", self.auth_code, secs));
|
||||||
let proto = format!("{:?}_{}", hashed, secs);
|
let proto = format!("{:x}_{}", hashed, secs);
|
||||||
r.add_protocol(&proto);
|
r.add_protocol(&proto);
|
||||||
Ok(r)
|
Ok(r)
|
||||||
},
|
},
|
||||||
@@ -258,7 +274,7 @@ impl Rpc {
|
|||||||
let request = MethodCall {
|
let request = MethodCall {
|
||||||
jsonrpc: Some(Version::V2),
|
jsonrpc: Some(Version::V2),
|
||||||
method: method.to_owned(),
|
method: method.to_owned(),
|
||||||
params: Some(Params::Array(params)),
|
params: Params::Array(params),
|
||||||
id: Id::Num(id as u64),
|
id: Id::Num(id as u64),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod signer_client;
|
pub mod signer_client;
|
||||||
|
|
||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use client::{Rpc, RpcError};
|
use client::{Rpc, RpcError};
|
||||||
use rpc::signer::{ConfirmationRequest, TransactionModification, U256, TransactionCondition};
|
use rpc::signer::{ConfirmationRequest, TransactionModification, U256, TransactionCondition};
|
||||||
use serde;
|
use serde;
|
||||||
@@ -1,3 +1,19 @@
|
|||||||
|
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity Ethereum.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate rpassword;
|
extern crate rpassword;
|
||||||
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
[package]
|
|
||||||
description = "Parity Dapps crate"
|
|
||||||
name = "parity-dapps"
|
|
||||||
version = "1.9.0"
|
|
||||||
license = "GPL-3.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
base32 = "0.3"
|
|
||||||
futures = "0.1"
|
|
||||||
futures-cpupool = "0.1"
|
|
||||||
linked-hash-map = "0.5"
|
|
||||||
log = "0.3"
|
|
||||||
parity-dapps-glue = "1.9"
|
|
||||||
parking_lot = "0.5"
|
|
||||||
mime_guess = "2.0.0-alpha.2"
|
|
||||||
rand = "0.4"
|
|
||||||
rustc-hex = "1.0"
|
|
||||||
serde = "1.0"
|
|
||||||
serde_derive = "1.0"
|
|
||||||
serde_json = "1.0"
|
|
||||||
unicase = "1.4"
|
|
||||||
zip = { version = "0.1", default-features = false }
|
|
||||||
itertools = "0.5"
|
|
||||||
|
|
||||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.10" }
|
|
||||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.10" }
|
|
||||||
|
|
||||||
ethcore-bytes = { path = "../util/bytes" }
|
|
||||||
ethereum-types = "0.2"
|
|
||||||
fetch = { path = "../util/fetch" }
|
|
||||||
node-health = { path = "./node-health" }
|
|
||||||
parity-hash-fetch = { path = "../hash-fetch" }
|
|
||||||
parity-reactor = { path = "../util/reactor" }
|
|
||||||
parity-ui = { path = "./ui" }
|
|
||||||
keccak-hash = { path = "../util/hash" }
|
|
||||||
parity-version = { path = "../util/version" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
env_logger = "0.4"
|
|
||||||
ethcore-devtools = { path = "../devtools" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
ui = ["parity-ui/no-precompiled-js"]
|
|
||||||
ui-precompiled = ["parity-ui/use-precompiled-js"]
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user