Compare commits
2082
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
613226dec5 | ||
|
|
366359803a | ||
|
|
3b13397165 | ||
|
|
163c2b2544 | ||
|
|
9e2603c1bf | ||
|
|
d27750590a | ||
|
|
33a00ab9e5 | ||
|
|
f765fe36d7 | ||
|
|
be7a95c9a5 | ||
|
|
2571480bb6 | ||
|
|
73d928751d | ||
|
|
0c19544d94 | ||
|
|
a80dce2e3b | ||
|
|
fb09d1ad2b | ||
|
|
963e4dd2fc | ||
|
|
a5d81c6cc9 | ||
|
|
b167e73acb | ||
|
|
e7af464033 | ||
|
|
28e454fd4a | ||
|
|
55a9c9ae0a | ||
|
|
b3eef76cdd | ||
|
|
3f81ff3507 | ||
|
|
2347ba6f39 | ||
|
|
b9d3c26eae | ||
|
|
cd6670205c | ||
|
|
9f4ac4f4f3 | ||
|
|
e4bed4d963 | ||
|
|
76b5c8b145 | ||
|
|
9836c45e03 | ||
|
|
51f1712564 | ||
|
|
e9eff8be5c | ||
|
|
4855045ea2 | ||
|
|
6b06ae528c | ||
|
|
813961b458 | ||
|
|
7ae9d26cd1 | ||
|
|
9e5cc7c910 | ||
|
|
7d166fd730 | ||
|
|
60101fd97b | ||
|
|
0bc174697a | ||
|
|
7bee5b3abc | ||
|
|
656b285195 | ||
|
|
86083531c6 | ||
|
|
c01f70105f | ||
|
|
5aafa91e22 | ||
|
|
bada564c10 | ||
|
|
f9f0d86ddf | ||
|
|
6beabb5e8a | ||
|
|
3719938849 | ||
|
|
6420990007 | ||
|
|
1fab24b9bc | ||
|
|
8bbe63bd9d | ||
|
|
fa3b1552cf | ||
|
|
c625b30781 | ||
|
|
b339750757 | ||
|
|
da960a9de4 | ||
|
|
954a20ff4c | ||
|
|
8057b994cf | ||
|
|
ea111b058e | ||
|
|
ce33074626 | ||
|
|
5f95a92135 | ||
|
|
cd817906b2 | ||
|
|
29dbc26dbd | ||
|
|
04f0130530 | ||
|
|
f210a6e91a | ||
|
|
aded0706be | ||
|
|
57567db017 | ||
|
|
5574e4f11b | ||
|
|
4b01b666f6 | ||
|
|
35740895ed | ||
|
|
4c5d84d106 | ||
|
|
a8915f0331 | ||
|
|
7f962e19c2 | ||
|
|
186fd4da74 | ||
|
|
2fdcf5347b | ||
|
|
4a983995a8 | ||
|
|
263bdc9687 | ||
|
|
42c76aaebc | ||
|
|
9326cb5411 | ||
|
|
415b69155b | ||
|
|
3423d29815 | ||
|
|
673a972fe9 | ||
|
|
1fd20b636b | ||
|
|
d7a2556292 | ||
|
|
8aa158d4e6 | ||
|
|
3595b1f8e9 | ||
|
|
9b867a221a | ||
|
|
46a016cdce | ||
|
|
bea9cd2fe2 | ||
|
|
ee9d9a52a9 | ||
|
|
1e8a875edb | ||
|
|
36a93c90eb | ||
|
|
38d714a28a | ||
|
|
bbb954afc5 | ||
|
|
de5d16eeac | ||
|
|
1968195da9 | ||
|
|
22851df145 | ||
|
|
96ade5f2dd | ||
|
|
5e727819ad | ||
|
|
5605aae269 | ||
|
|
c6b6c2990c | ||
|
|
a49dc90386 | ||
|
|
64f4e80446 | ||
|
|
e67253b6a8 | ||
|
|
c73326e96d | ||
|
|
7003960fdd | ||
|
|
0acc2a2a63 | ||
|
|
596f78f9e6 | ||
|
|
17f1eec81f | ||
|
|
f0d44ea396 | ||
|
|
ad4712b593 | ||
|
|
92b8d9ad17 | ||
|
|
12ff9fd76b | ||
|
|
e26cf4e003 | ||
|
|
b753cd4503 | ||
|
|
ee29a92021 | ||
|
|
c3916e3a7f | ||
|
|
ec0baefc48 | ||
|
|
2a3d79f055 | ||
|
|
1c01799a4b | ||
|
|
4ef330ba8b | ||
|
|
cd2bf04a6b | ||
|
|
f7a71f4be7 | ||
|
|
e5c24ff32b | ||
|
|
f87054bde4 | ||
|
|
e954cc0d67 | ||
|
|
43c887f2b6 | ||
|
|
1cf9b9a67f | ||
|
|
7dd25c3ac5 | ||
|
|
241c3fa673 | ||
|
|
785eeb1eee | ||
|
|
30d82ba962 | ||
|
|
002d021eb7 | ||
|
|
71eb41b9aa | ||
|
|
f06369166e | ||
|
|
5129fed7c8 | ||
|
|
f06794ae84 | ||
|
|
1cabecedee | ||
|
|
f17c32c593 | ||
|
|
b59f88324c | ||
|
|
0c4db8cf6b | ||
|
|
9c020e54b5 | ||
|
|
9811375d18 | ||
|
|
2632621f7e | ||
|
|
d742efe165 | ||
|
|
492ffac913 | ||
|
|
4acf0c7ec0 | ||
|
|
fa69adf45c | ||
|
|
fa04da2995 | ||
|
|
e2a554c2e6 | ||
|
|
91da81c8bc | ||
|
|
7bd18290db | ||
|
|
8e55c91cd2 | ||
|
|
514339e855 | ||
|
|
9f8ecb0ac4 | ||
|
|
be1bbe88b0 | ||
|
|
f1ccbe496a | ||
|
|
495554e565 | ||
|
|
17c378c18a | ||
|
|
6e63ea6cce | ||
|
|
10753f14d0 | ||
|
|
0af7cd1448 | ||
|
|
de3edc2ec7 | ||
|
|
d0eb4278ae | ||
|
|
b807bdbea0 | ||
|
|
386b818d16 | ||
|
|
8639409f74 | ||
|
|
b372881e91 | ||
|
|
fc351d08b0 | ||
|
|
eb325e6814 | ||
|
|
7232e5481c | ||
|
|
f26a336afb | ||
|
|
70e690dac0 | ||
|
|
78e0c96f22 | ||
|
|
d6615b6286 | ||
|
|
4604b60fb1 | ||
|
|
75041b96e4 | ||
|
|
8544bda14a | ||
|
|
745fba7d1f | ||
|
|
6986204657 | ||
|
|
3f9cde28fc | ||
|
|
aa682a377d | ||
|
|
af1d54501f | ||
|
|
009cd92010 | ||
|
|
6386107b80 | ||
|
|
218b2b82c1 | ||
|
|
b06b0147a9 | ||
|
|
b74e8f5922 | ||
|
|
94a7ef1c10 | ||
|
|
02afd7d71f | ||
|
|
a10690359c | ||
|
|
fa4ff89dca | ||
|
|
bcd84cbe9d | ||
|
|
61daaa774b | ||
|
|
d5346f8326 | ||
|
|
8ce6921b18 | ||
|
|
c8f28103f3 | ||
|
|
3cd3785385 | ||
|
|
9089aadbbf | ||
|
|
02ca1937f8 | ||
|
|
6897110fe5 | ||
|
|
b799af3e9a | ||
|
|
9641d93615 | ||
|
|
da6768c94d | ||
|
|
54a8b88d6c | ||
|
|
ba6217cc2c | ||
|
|
314da26da3 | ||
|
|
646800b8a8 | ||
|
|
92d58ab191 | ||
|
|
5531f27798 | ||
|
|
cc98117df2 | ||
|
|
ffd340086e | ||
|
|
805686be2e | ||
|
|
0d3f602d58 | ||
|
|
83436842c5 | ||
|
|
bafb922ff7 | ||
|
|
6e024d9994 | ||
|
|
d597c3fb0d | ||
|
|
24cebb8b2f | ||
|
|
0cc8446a79 | ||
|
|
3e383d909a | ||
|
|
3264a9faad | ||
|
|
c52b75d59e | ||
|
|
652c257763 | ||
|
|
060f23d748 | ||
|
|
e68ab286f6 | ||
|
|
92e5cc5ad7 | ||
|
|
6831ca9e03 | ||
|
|
f7325a69c1 | ||
|
|
bb78292faf | ||
|
|
eeca031525 | ||
|
|
f82e0e61e1 | ||
|
|
75e47db520 | ||
|
|
3da1a0d8cc | ||
|
|
a293bd798f | ||
|
|
ae60001dcc | ||
|
|
cea7c41023 | ||
|
|
0fe7a0b2e3 | ||
|
|
267368d56c | ||
|
|
f5451ad455 | ||
|
|
555537c528 | ||
|
|
cc5875c6bd | ||
|
|
c54d050cb7 | ||
|
|
cb88237305 | ||
|
|
cc44167f2c | ||
|
|
78c656e4c1 | ||
|
|
607bcc401c | ||
|
|
7fd481ea6f | ||
|
|
9c91daf19a | ||
|
|
c1ef9d1731 | ||
|
|
eaf69b160c | ||
|
|
269286e587 | ||
|
|
f99c5416ce | ||
|
|
8f6c3c2cc7 | ||
|
|
a2e25897e9 | ||
|
|
fc9e046ca7 | ||
|
|
5730d46179 | ||
|
|
9165f14693 | ||
|
|
9305eca97c | ||
|
|
0c2e4e2d4f | ||
|
|
59725b3af3 | ||
|
|
a0ab3483ed | ||
|
|
e6f3b79b23 | ||
|
|
1ed6183344 | ||
|
|
29b1ac964b | ||
|
|
8d637c515a | ||
|
|
5220a61fc5 | ||
|
|
d9502693f0 | ||
|
|
9a0d0511c7 | ||
|
|
72eb32ae3c | ||
|
|
cf9e576028 | ||
|
|
9804bd3c4c | ||
|
|
30e2642f4d | ||
|
|
54b3eafd6d | ||
|
|
f6589407c1 | ||
|
|
5c73602d24 | ||
|
|
70657e35ba | ||
|
|
908053015b | ||
|
|
8bae22ef61 | ||
|
|
1012a64110 | ||
|
|
9eb2399539 | ||
|
|
d539be4875 | ||
|
|
13fc3f2f54 | ||
|
|
33ec4d332b | ||
|
|
d6bb0500df | ||
|
|
b90666b293 | ||
|
|
173a72f374 | ||
|
|
0ae0e89ddd | ||
|
|
c4d01c04a4 | ||
|
|
0b56ca3c21 | ||
|
|
022348f426 | ||
|
|
ad108d6051 | ||
|
|
dbe2a1e2cd | ||
|
|
f86a2ced06 | ||
|
|
f7a45f6c12 | ||
|
|
658f78d75f | ||
|
|
e9eaee6ef8 | ||
|
|
505e30c7b2 | ||
|
|
1ac6f3fa33 | ||
|
|
88fa9726cc | ||
|
|
af87920b6f | ||
|
|
881b0517b1 | ||
|
|
9b2b72819b | ||
|
|
2e74e1d2ae | ||
|
|
edae6a3ae8 | ||
|
|
987571dd8c | ||
|
|
934b2df763 | ||
|
|
81bc159b3e | ||
|
|
50e05ae1ba | ||
|
|
432c69c616 | ||
|
|
e714294273 | ||
|
|
c3a144bcb6 | ||
|
|
dd86a289ce | ||
|
|
6e0dac06f4 | ||
|
|
5bd0e92068 | ||
|
|
b032d3524b | ||
|
|
6933df918a | ||
|
|
d1997b5857 | ||
|
|
aafa00abe4 | ||
|
|
d3eb808ff4 | ||
|
|
d009b70171 | ||
|
|
d358da43f7 | ||
|
|
6cfc7e3fa0 | ||
|
|
b5cb1fe1e0 | ||
|
|
17dace9086 | ||
|
|
50fae56baf | ||
|
|
42269e17cc | ||
|
|
a9689d8345 | ||
|
|
32dad4d43b | ||
|
|
a88700bef4 | ||
|
|
8d85aedeff | ||
|
|
744ae23c2c | ||
|
|
4e13d284b7 | ||
|
|
0533cf22e7 | ||
|
|
17e969bc4a | ||
|
|
8b0c0a9439 | ||
|
|
7d2462ce6b | ||
|
|
e34b20b839 | ||
|
|
363f575e6c | ||
|
|
2f272d2703 | ||
|
|
9a21b75c66 | ||
|
|
bbd2fbc434 | ||
|
|
b3fc18c988 | ||
|
|
4a8e45a88c | ||
|
|
e12e2b4e4e | ||
|
|
40972762df | ||
|
|
4f624465c2 | ||
|
|
41c019b3d2 | ||
|
|
ea6121a951 | ||
|
|
1d8c8b9a8e | ||
|
|
41ee20013e | ||
|
|
01192105b1 | ||
|
|
29a8259b2a | ||
|
|
427cb1b14d | ||
|
|
27fd150491 | ||
|
|
c2acfb5c31 | ||
|
|
c642dce97f | ||
|
|
4c64f0ca11 | ||
|
|
29b592f62f | ||
|
|
d124d10f7c | ||
|
|
b857c8f9ba | ||
|
|
aec168e05f | ||
|
|
686c92ba02 | ||
|
|
141c77e17e | ||
|
|
3c673f0086 | ||
|
|
5726c4bd30 | ||
|
|
12e373fd43 | ||
|
|
e3e86ec983 | ||
|
|
3a3e390e90 | ||
|
|
b462d7eb0b | ||
|
|
8b326c9dfe | ||
|
|
150346ae51 | ||
|
|
ed67e4c5a9 | ||
|
|
dcd42eec11 | ||
|
|
6447198e0e | ||
|
|
3003bb5731 | ||
|
|
d7b8cc6ef9 | ||
|
|
9597e4c501 | ||
|
|
f7314646ec | ||
|
|
46fb5f8602 | ||
|
|
ba389598ee | ||
|
|
e8d92a5f31 | ||
|
|
741cfb7f00 | ||
|
|
6056a52978 | ||
|
|
4ea021d151 | ||
|
|
6712560779 | ||
|
|
6bf2dcd97c | ||
|
|
d3a99456ae | ||
|
|
ecd180ecf4 | ||
|
|
cab07f61c5 | ||
|
|
ec83b9cf7c | ||
|
|
0704ac4636 | ||
|
|
6002e359ae | ||
|
|
9a1d8cecfe | ||
|
|
f73366630f | ||
|
|
728ae01012 | ||
|
|
6c2c33db56 | ||
|
|
e7065df2d4 | ||
|
|
fc2d61a55b | ||
|
|
581e7e6fde | ||
|
|
66d67f66a7 | ||
|
|
85dc2eb36f | ||
|
|
4cd861eb87 | ||
|
|
7747c3eb40 | ||
|
|
00c107548e | ||
|
|
e4cb28a310 | ||
|
|
da73b62074 | ||
|
|
d415aa779c | ||
|
|
f8c15e2d2d | ||
|
|
b74e487af2 | ||
|
|
8a69467982 | ||
|
|
1ade0e7b92 | ||
|
|
e5a0252126 | ||
|
|
c36bfb36e5 | ||
|
|
fbfab94114 | ||
|
|
b23cef8d1e | ||
|
|
85240c938e | ||
|
|
27c4cd6360 | ||
|
|
5d1b9f854e | ||
|
|
14bbbe4feb | ||
|
|
34deb5e194 | ||
|
|
0f75cb4dfd | ||
|
|
dd79c66d41 | ||
|
|
75642f393d | ||
|
|
97130684a2 | ||
|
|
981b449a3d | ||
|
|
187e386740 | ||
|
|
b76e229f8e | ||
|
|
fa59cdc2ad | ||
|
|
523a7a9d8d | ||
|
|
51142f45c3 | ||
|
|
5a7fdfe3ed | ||
|
|
ed117719aa | ||
|
|
66cafc7c71 | ||
|
|
397efddbc6 | ||
|
|
749db0bf45 | ||
|
|
60b78a74f7 | ||
|
|
31c14dc908 | ||
|
|
368fff246b | ||
|
|
8a9862bdb4 | ||
|
|
8b58f22c2e | ||
|
|
25c10e4b36 | ||
|
|
486fda17a2 | ||
|
|
1cd932fedd | ||
|
|
50291408c7 | ||
|
|
74510adaaa | ||
|
|
479a456279 | ||
|
|
eb24baa70c | ||
|
|
4e5179557d | ||
|
|
5824714cdc | ||
|
|
a9866c757e | ||
|
|
299a017ef5 | ||
|
|
a553d438c0 | ||
|
|
f865ae9f10 | ||
|
|
1bd88b24d4 | ||
|
|
d7af5c67e9 | ||
|
|
ecd85c3b02 | ||
|
|
4a68d02a4e | ||
|
|
cf2e8b2cab | ||
|
|
da72dfae3d | ||
|
|
80f7492d06 | ||
|
|
65752a9cc4 | ||
|
|
f7e6e45442 | ||
|
|
8770cf2f4b | ||
|
|
51f92b6ed0 | ||
|
|
2e0a3fca1a | ||
|
|
4f959adbc8 | ||
|
|
de701a06de | ||
|
|
e3f6e586ee | ||
|
|
1b6f6b295b | ||
|
|
2a2b63086a | ||
|
|
805d07bca6 | ||
|
|
112aca7896 | ||
|
|
cfcf946556 | ||
|
|
c649fbf43b | ||
|
|
c196570dd8 | ||
|
|
63bc34a1a0 | ||
|
|
584a4aba4c | ||
|
|
35b1963eeb | ||
|
|
4014cbdb44 | ||
|
|
be2a357102 | ||
|
|
971282a9a5 | ||
|
|
f9be73c73e | ||
|
|
c616e5e14e | ||
|
|
bc1fa74d0c | ||
|
|
9f2df68906 | ||
|
|
6077edb3d0 | ||
|
|
d9acd484c0 | ||
|
|
b535a801bc | ||
|
|
d096c75c6e | ||
|
|
897b269139 | ||
|
|
a6bb8ec688 | ||
|
|
72c27a9f73 | ||
|
|
2e1441cde7 | ||
|
|
414dd9f74e | ||
|
|
bee25d221f | ||
|
|
75076ba422 | ||
|
|
de8499a69b | ||
|
|
4d51f9f700 | ||
|
|
15847756cc | ||
|
|
c4852e04c9 | ||
|
|
441734647d | ||
|
|
847a916cd7 | ||
|
|
a1a2bf1a0e | ||
|
|
ed500dbbdf | ||
|
|
02c936c2ff | ||
|
|
6caf725e14 | ||
|
|
5026091623 | ||
|
|
6d1914ce43 | ||
|
|
f18f6bb994 | ||
|
|
a1bb005b7c | ||
|
|
931a862abc | ||
|
|
4b3cc2ca7e | ||
|
|
6cd3e7f7cb | ||
|
|
a047e158ed | ||
|
|
bd1ac4d91b | ||
|
|
59dcbe93a4 | ||
|
|
e345525672 | ||
|
|
d9a1d912fd | ||
|
|
d7d2b12a32 | ||
|
|
f16ae9dffb | ||
|
|
ae420fc9f4 | ||
|
|
f6c4c2b72e | ||
|
|
a0bdf95845 | ||
|
|
0d4bf2e980 | ||
|
|
be439d3b13 | ||
|
|
81e3b714a2 | ||
|
|
e2b0e00547 | ||
|
|
a29f19e482 | ||
|
|
fce2ec0470 | ||
|
|
5ec76d4b7b | ||
|
|
d73b369fa5 | ||
|
|
c66415ae4c | ||
|
|
639b800505 | ||
|
|
35fcf04d3d | ||
|
|
e4bb94e939 | ||
|
|
43aca717d1 | ||
|
|
cb5e5a1325 | ||
|
|
7de882b4d0 | ||
|
|
426bd4cfed | ||
|
|
1e38e02456 | ||
|
|
89b19af43f | ||
|
|
ba12b9ac3f | ||
|
|
f8f6e28985 | ||
|
|
9570ebfe0a | ||
|
|
667fb17b43 | ||
|
|
7d7c83ef79 | ||
|
|
6b56652768 | ||
|
|
c7939ff580 | ||
|
|
068e9a29d2 | ||
|
|
014092443f | ||
|
|
cf8691ed87 | ||
|
|
eb4f4675a5 | ||
|
|
41d925ab8a | ||
|
|
2ead8c089e | ||
|
|
1b0c0e10c6 | ||
|
|
f186e1a4f7 | ||
|
|
fb1788a3e9 | ||
|
|
ac6573cca7 | ||
|
|
6a27c65201 | ||
|
|
5c6cf8b619 | ||
|
|
6014530145 | ||
|
|
160bc0676f | ||
|
|
cd5cbcfe4f | ||
|
|
ae47bc15ff | ||
|
|
33076d5b43 | ||
|
|
9dcb42328e | ||
|
|
3ec736bc04 | ||
|
|
c760a88c22 | ||
|
|
8d35aacd31 | ||
|
|
4579e9bb84 | ||
|
|
5900d765fd | ||
|
|
e65d293ba4 | ||
|
|
b2dda08368 | ||
|
|
c807469a5a | ||
|
|
bfa93bb933 | ||
|
|
d3796abac7 | ||
|
|
bd3a43a367 | ||
|
|
b7c6cc5321 | ||
|
|
939d7a7a0b | ||
|
|
33a419fbdb | ||
|
|
fa1b559b2c | ||
|
|
e46c4652a5 | ||
|
|
832657edfb | ||
|
|
c3b1cf19a4 | ||
|
|
1565e10fff | ||
|
|
e38484a109 | ||
|
|
0522af155d | ||
|
|
1947f26cdb | ||
|
|
0f0589bcf5 | ||
|
|
e8e2b3a6f9 | ||
|
|
5287119f4e | ||
|
|
fd7af33d4b | ||
|
|
af794a6281 | ||
|
|
360d87cbab | ||
|
|
bf0e50d717 | ||
|
|
55f7c81aaf | ||
|
|
a02bf83332 | ||
|
|
36cd9826aa | ||
|
|
7c22dabb63 | ||
|
|
45722772fc | ||
|
|
1a708b2e2a | ||
|
|
ab776352b1 | ||
|
|
15e07b38b6 | ||
|
|
e34ae07b9a | ||
|
|
a28e64227b | ||
|
|
1b6aa537d3 | ||
|
|
88ccd73139 | ||
|
|
db2e1fc2c4 | ||
|
|
fbd346a36b | ||
|
|
7ee42e61d5 | ||
|
|
9b55f4b471 | ||
|
|
86ccaa1981 | ||
|
|
a9b62bcc5c | ||
|
|
016d0d15c3 | ||
|
|
da7db5fa98 | ||
|
|
322ab8fd6f | ||
|
|
8ee296d130 | ||
|
|
e6ce6211de | ||
|
|
96f9d58066 | ||
|
|
f4948dcba0 | ||
|
|
309f54aa0c | ||
|
|
ef1d485c6e | ||
|
|
041e9e14a2 | ||
|
|
233c84ef3e | ||
|
|
c137b0cf73 | ||
|
|
86e7f5914b | ||
|
|
40f56243d1 | ||
|
|
faa9002cce | ||
|
|
df5e3f6227 | ||
|
|
959d91ec65 | ||
|
|
9b598e5ec3 | ||
|
|
58fe29d705 | ||
|
|
70f21d6ca5 | ||
|
|
ecdedd33e2 | ||
|
|
b761a2994b | ||
|
|
1169fb4b86 | ||
|
|
c4302ddc1f | ||
|
|
332b6b9e0a | ||
|
|
42c5c6b4e4 | ||
|
|
c89c21b47a | ||
|
|
4e1dddab67 | ||
|
|
1d5059c281 | ||
|
|
7e7caa8859 | ||
|
|
18f4e9d828 | ||
|
|
93b30ab865 | ||
|
|
f7228f9465 | ||
|
|
f0fec60841 | ||
|
|
6b2b2b678f | ||
|
|
262d7b2372 | ||
|
|
9c704db12b | ||
|
|
9199c03f0d | ||
|
|
744a02eb5a | ||
|
|
83ad29648e | ||
|
|
c8d23b5947 | ||
|
|
b621754514 | ||
|
|
2a97045317 | ||
|
|
c64dbfcf40 | ||
|
|
05df832ae4 | ||
|
|
f1ea33c658 | ||
|
|
8f500046ac | ||
|
|
3285dd0cf1 | ||
|
|
d873d8489d | ||
|
|
28c5697578 | ||
|
|
a9c98eab8d | ||
|
|
162784e98f | ||
|
|
3ca71e146c | ||
|
|
fd7f4ef080 | ||
|
|
1f2b767696 | ||
|
|
1e810eb2c8 | ||
|
|
95c13ddd92 | ||
|
|
f960cccc51 | ||
|
|
4d5e3201c3 | ||
|
|
e3f2dfaec0 | ||
|
|
9f9ae155e3 | ||
|
|
eba0743d47 | ||
|
|
24c764c91e | ||
|
|
f10eed3ae3 | ||
|
|
9923c909c1 | ||
|
|
c406b875fa | ||
|
|
8be9494672 | ||
|
|
fcc51f3591 | ||
|
|
c2806fe189 | ||
|
|
f43ce74604 | ||
|
|
00eb0e6378 | ||
|
|
b941b29fe5 | ||
|
|
ecc38d1bd8 | ||
|
|
d99f40797e | ||
|
|
a86595bc88 | ||
|
|
851a7dc180 | ||
|
|
216072468f | ||
|
|
6dd2f5369e | ||
|
|
32e45557fa | ||
|
|
f11b1bbbeb | ||
|
|
bb7bb2600a | ||
|
|
2de6a38c8b | ||
|
|
3962194fad | ||
|
|
9c5cda0efa | ||
|
|
91f56742c4 | ||
|
|
fe6d666150 | ||
|
|
99a3902c07 | ||
|
|
ba73b9d3c4 | ||
|
|
3b7ae0d6d9 | ||
|
|
712e2683d6 | ||
|
|
073962a60d | ||
|
|
24b7661837 | ||
|
|
e6270e4291 | ||
|
|
850803f138 | ||
|
|
a40872f74d | ||
|
|
8e13920e7b | ||
|
|
8828668541 | ||
|
|
93b2b6d65d | ||
|
|
ebc2895d15 | ||
|
|
95bd55b754 | ||
|
|
f7666bc641 | ||
|
|
769cf43b52 | ||
|
|
cdbf79fff5 | ||
|
|
bab352fb44 | ||
|
|
708988244d | ||
|
|
4153f128ef | ||
|
|
706c0b8d3a | ||
|
|
15a4565714 | ||
|
|
918b759d03 | ||
|
|
50a8b6a184 | ||
|
|
dc2a67f13f | ||
|
|
0db5fc422a | ||
|
|
6eaafcf79d | ||
|
|
06970a662c | ||
|
|
8594af970f | ||
|
|
6a8e4bf87e | ||
|
|
4a93ad33f9 | ||
|
|
14ad808a00 | ||
|
|
afdfc8807d | ||
|
|
8338be3a6e | ||
|
|
98f3cab24e | ||
|
|
f88e400d74 | ||
|
|
246a9f0e45 | ||
|
|
85993848be | ||
|
|
0336b32fcd | ||
|
|
16347270e0 | ||
|
|
149bb56b8e | ||
|
|
8a83415596 | ||
|
|
c0204cdd83 | ||
|
|
efd435d763 | ||
|
|
fc933858e1 | ||
|
|
c34fb24cef | ||
|
|
56eaf143db | ||
|
|
c2db85b99b | ||
|
|
08e74a90f9 | ||
|
|
05536042f9 | ||
|
|
4e6839b5ef | ||
|
|
0feab0aad4 | ||
|
|
0c5e3e2533 | ||
|
|
eb501f7290 | ||
|
|
5ab9636365 | ||
|
|
624dd7d2ce | ||
|
|
3e976fd74b | ||
|
|
2a10a79460 | ||
|
|
674be39af1 | ||
|
|
e4641fc7ed | ||
|
|
5bf80a60f1 | ||
|
|
bb3789b130 | ||
|
|
ac8aaf2739 | ||
|
|
349b2890bf | ||
|
|
2db9c24817 | ||
|
|
ff5494af4e | ||
|
|
a4cdfe89fd | ||
|
|
23dc776bd1 | ||
|
|
aa5cec39ca | ||
|
|
704978a6ba | ||
|
|
a2554952d1 | ||
|
|
d3ffe48fcb | ||
|
|
e053cd6b9c | ||
|
|
613e61222b | ||
|
|
3426611340 | ||
|
|
1c75bf30ac | ||
|
|
072dd8b744 | ||
|
|
0f0015f45c | ||
|
|
19573c0319 | ||
|
|
4a12a98451 | ||
|
|
6dffe9377f | ||
|
|
b0fe8b8b4c | ||
|
|
37e7c512e6 | ||
|
|
70d654e4a6 | ||
|
|
09b013b7a5 | ||
|
|
83416e2758 | ||
|
|
ac271b021d | ||
|
|
7ee6388686 | ||
|
|
029ffc2dc3 | ||
|
|
1a3ab63e47 | ||
|
|
c5c18659a4 | ||
|
|
4bf784b44f | ||
|
|
ef6fb1b756 | ||
|
|
17e9feb633 | ||
|
|
1bd28bdb6a | ||
|
|
f17929f032 | ||
|
|
661d373fa2 | ||
|
|
d909119bb6 | ||
|
|
f4f649acf4 | ||
|
|
d71ba5039b | ||
|
|
ce4978d8c3 | ||
|
|
69353e8210 | ||
|
|
be85f2aff7 | ||
|
|
9a17beba9e | ||
|
|
2d2b89fcd8 | ||
|
|
f1c890ca54 | ||
|
|
8e0ca306d2 | ||
|
|
2b0bf372a7 | ||
|
|
c16806eb78 | ||
|
|
cda2be93f5 | ||
|
|
00cd89750d | ||
|
|
1120298b05 | ||
|
|
d73a9f4f34 | ||
|
|
056284c00b | ||
|
|
9fd1b95fd5 | ||
|
|
9c250f24fa | ||
|
|
afdb1db529 | ||
|
|
7075eaba26 | ||
|
|
09aa1507c8 | ||
|
|
e36f356908 | ||
|
|
f2dd0f046e | ||
|
|
5524241645 | ||
|
|
7e96b15091 | ||
|
|
9b99850585 | ||
|
|
110c869dd3 | ||
|
|
6603fc5975 | ||
|
|
298e942edf | ||
|
|
086a66efc9 | ||
|
|
43f4db2308 | ||
|
|
4ee8254133 | ||
|
|
0170418fc1 | ||
|
|
c268b5b7ac | ||
|
|
2f4732760a | ||
|
|
ab44ae1354 | ||
|
|
2781e16ef4 | ||
|
|
f53250ee90 | ||
|
|
dcd9e7da27 | ||
|
|
650a31b050 | ||
|
|
3ad16c58d7 | ||
|
|
6c285898a8 | ||
|
|
68fa9f20c3 | ||
|
|
312bab0843 | ||
|
|
8448771ce6 | ||
|
|
f05be81275 | ||
|
|
303d4a31fe | ||
|
|
9a7aa3fc87 | ||
|
|
209e8b3041 | ||
|
|
bb6e8de056 | ||
|
|
ab51501849 | ||
|
|
2d5becaaba | ||
|
|
4c6fa1b2c0 | ||
|
|
4d92798f5b | ||
|
|
6284fad33e | ||
|
|
a63a0b3077 | ||
|
|
147e18e28e | ||
|
|
a1b35aa9d5 | ||
|
|
847bdb203a | ||
|
|
2ddbb19c3a | ||
|
|
23d6dc67cf | ||
|
|
27e81f37f3 | ||
|
|
3ba2b0e365 | ||
|
|
dd6adcd240 | ||
|
|
5b6bbfe903 | ||
|
|
2bdedefa05 | ||
|
|
7a475d5ca5 | ||
|
|
89b15c03cf | ||
|
|
212c346c21 | ||
|
|
11777c5f24 | ||
|
|
beaa3dffab | ||
|
|
d060dda0c5 | ||
|
|
91c9f447cd | ||
|
|
0417e4bd0c | ||
|
|
a5552e4c36 | ||
|
|
698f529602 | ||
|
|
48821e6272 | ||
|
|
8a265a0e8d | ||
|
|
c7e206b993 | ||
|
|
c41ad767c5 | ||
|
|
31571cf655 | ||
|
|
aa23266c6f | ||
|
|
acfb7cee07 | ||
|
|
f6adbc369a | ||
|
|
fe1d61b276 | ||
|
|
c55501024e | ||
|
|
5b9ee1dea9 | ||
|
|
7bbc8a4652 | ||
|
|
67a47f428f | ||
|
|
f15eb8b172 | ||
|
|
fb287e8385 | ||
|
|
fa86359549 | ||
|
|
7ef08bfbdf | ||
|
|
ff510b7542 | ||
|
|
013f441658 | ||
|
|
565099f06f | ||
|
|
f727f1db26 | ||
|
|
d88c2f8a43 | ||
|
|
27d57264d9 | ||
|
|
f6c260c49f | ||
|
|
cdd97ce959 | ||
|
|
417d434973 | ||
|
|
12f288016d | ||
|
|
77a84e7e6b | ||
|
|
7d2f9366b4 | ||
|
|
4ae1238c0c | ||
|
|
13f2c21e1d | ||
|
|
ea135991e3 | ||
|
|
0526ee8042 | ||
|
|
ae3f7522f0 | ||
|
|
0c69e99b7c | ||
|
|
be4ac89b57 | ||
|
|
663e2874a1 | ||
|
|
16cc3e94ac | ||
|
|
5e2a94e22f | ||
|
|
b78ea464e5 | ||
|
|
8ec19812e3 | ||
|
|
8f89501118 | ||
|
|
34e9e9fe34 | ||
|
|
95ee1c1cde | ||
|
|
eb6bc0399a | ||
|
|
c66d3dfad7 | ||
|
|
de1f2be505 | ||
|
|
ddd3b3f587 | ||
|
|
cba97000c8 | ||
|
|
f882c5b229 | ||
|
|
97a7b449e6 | ||
|
|
dc49a81a05 | ||
|
|
3470ed7847 | ||
|
|
93a90fbe82 | ||
|
|
a0b25c3718 | ||
|
|
838a50be39 | ||
|
|
bb7820cfd7 | ||
|
|
672956af5d | ||
|
|
1b50be09af | ||
|
|
59e77ba6c8 | ||
|
|
090261f304 | ||
|
|
06cfbf5742 | ||
|
|
e050e0307e | ||
|
|
326490be86 | ||
|
|
437a418afe | ||
|
|
7b19e26692 | ||
|
|
dc5159ce74 | ||
|
|
1e1d0676ae | ||
|
|
4be54a976c | ||
|
|
ac3ca7981f | ||
|
|
6399499e9b | ||
|
|
edf03c9ba8 | ||
|
|
7a6eb723ad | ||
|
|
300c89f541 | ||
|
|
78b4872793 | ||
|
|
d686fce7f9 | ||
|
|
ae69e0fef1 | ||
|
|
5a7d18e9f3 | ||
|
|
91c640197c | ||
|
|
02e5148720 | ||
|
|
42c9d53ae1 | ||
|
|
05f52b1fad | ||
|
|
1aef03bdd7 | ||
|
|
6851b8d7bf | ||
|
|
3896902d49 | ||
|
|
088d693eea | ||
|
|
00cfb3bdcf | ||
|
|
60ec264470 | ||
|
|
7b9872c473 | ||
|
|
ebcefa80e3 | ||
|
|
c951e0cc83 | ||
|
|
3a0df321d1 | ||
|
|
3896631a1c | ||
|
|
490f85e690 | ||
|
|
ad15750be3 | ||
|
|
654f933d3f | ||
|
|
09a46e5d80 | ||
|
|
08ca60cfa8 | ||
|
|
201d0a4b19 | ||
|
|
cfc65f525a | ||
|
|
ecf53f88ce | ||
|
|
60ea9e6ec8 | ||
|
|
f7b0bb0e9c | ||
|
|
146bfe75f8 | ||
|
|
ec0d0a51db | ||
|
|
5de17e903c | ||
|
|
62cf9a5dd7 | ||
|
|
c7122597be | ||
|
|
5b8c6b687e | ||
|
|
66a682b55d | ||
|
|
726c9c1fe7 | ||
|
|
a2162672a7 | ||
|
|
71f0acd7a3 | ||
|
|
1d16857997 | ||
|
|
d350a9d415 | ||
|
|
d5b1bfe197 | ||
|
|
df13cfa52b | ||
|
|
753e8ff7f5 | ||
|
|
9ccc02d664 | ||
|
|
bc914de388 | ||
|
|
14591e718d | ||
|
|
9e67bef4bd | ||
|
|
2228f13f99 | ||
|
|
6fe245a278 | ||
|
|
066b755b6a | ||
|
|
72c6552b0b | ||
|
|
05d43af3fc | ||
|
|
1c55c6afba | ||
|
|
8bc4195b7a | ||
|
|
d74ede5a79 | ||
|
|
8559c89560 | ||
|
|
719cec13d5 | ||
|
|
8d1fab1e05 | ||
|
|
0bb37d1fd6 | ||
|
|
a3ba8eb0d7 | ||
|
|
80d0f1f2c1 | ||
|
|
cb1e7bd1b1 | ||
|
|
eeec5a1bba | ||
|
|
5e4a7e54df | ||
|
|
cd618dfdbd | ||
|
|
d43f2a2e3b | ||
|
|
881ed6aef7 | ||
|
|
df9aa0e357 | ||
|
|
09c9fcce00 | ||
|
|
66d71d9974 | ||
|
|
971fe6fdfd | ||
|
|
f1dbd35407 | ||
|
|
f3781e8329 | ||
|
|
9d7f19b950 | ||
|
|
2e9a052f62 | ||
|
|
34f755b2b9 | ||
|
|
1076a1a89d | ||
|
|
21255eddee | ||
|
|
6fcfd0a4f0 | ||
|
|
921f11db34 | ||
|
|
709fd034b4 | ||
|
|
e6aa01653a | ||
|
|
c6f67911de | ||
|
|
c57c0e7f55 | ||
|
|
aad449f28a | ||
|
|
a3fd1d4b51 | ||
|
|
90f43d99ca | ||
|
|
628a598ca0 | ||
|
|
0ca935e0b9 | ||
|
|
f9bd0fb2ce | ||
|
|
588a55c9d7 | ||
|
|
84fd969710 | ||
|
|
badaa5fad1 | ||
|
|
4ba4de3379 | ||
|
|
ef288b8a6c | ||
|
|
438aac190f | ||
|
|
7b69697439 | ||
|
|
9df4fce282 | ||
|
|
78de82158d | ||
|
|
5e46c0b4e9 | ||
|
|
c874bd6a15 | ||
|
|
e6b0eaed61 | ||
|
|
100fc47812 | ||
|
|
ac09a6432c | ||
|
|
ae40654907 | ||
|
|
076cc428af | ||
|
|
e0c15b24fc | ||
|
|
978a718159 | ||
|
|
314fba049f | ||
|
|
80cca91584 | ||
|
|
d87b7c264d | ||
|
|
b96aedd720 | ||
|
|
905fc968bf | ||
|
|
2fa3f2578f | ||
|
|
1ed7779701 | ||
|
|
825a3e1d5c | ||
|
|
2d1e30ae3b | ||
|
|
6d9b8ee0dc | ||
|
|
1fb70fc0e0 | ||
|
|
da631478a8 | ||
|
|
d21cb96ddb | ||
|
|
94ebbd9d90 | ||
|
|
7b0fc4ae7e | ||
|
|
f1327b75c3 | ||
|
|
3676daf7dc | ||
|
|
ddf1beac6c | ||
|
|
500ce56371 | ||
|
|
ff5a6773b1 | ||
|
|
366db2f38c | ||
|
|
4458d090b3 | ||
|
|
f59bf6fab1 | ||
|
|
a8dd6a831c | ||
|
|
14e387e3ac | ||
|
|
b2d4d37539 | ||
|
|
6622576cf8 | ||
|
|
ec3e86ad18 | ||
|
|
eacf991bb2 | ||
|
|
163527a021 | ||
|
|
6fda3c877e | ||
|
|
4dfe467e66 | ||
|
|
993505b461 | ||
|
|
f22f427cde | ||
|
|
51cf5d5f1b | ||
|
|
8d0b2947d6 | ||
|
|
66a950942e | ||
|
|
df0a19d5c4 | ||
|
|
d8c8832a37 | ||
|
|
24fb72f926 | ||
|
|
5f2caffb51 | ||
|
|
f259bc6a09 | ||
|
|
d8ce5078d9 | ||
|
|
de1c984b48 | ||
|
|
9e6ec71520 | ||
|
|
7e997e40f3 | ||
|
|
416a0d2722 | ||
|
|
1f9fdf0c5c | ||
|
|
77019c547b | ||
|
|
a71906525a | ||
|
|
630df050c0 | ||
|
|
06ce4f21c5 | ||
|
|
1bc55714ed | ||
|
|
f4bf6ee186 | ||
|
|
b6d5ea629b | ||
|
|
e6ec20b4ca | ||
|
|
96730bae75 | ||
|
|
2c7f579838 | ||
|
|
a75ad492f1 | ||
|
|
bc7fede68b | ||
|
|
86871e5abc | ||
|
|
7371274ea7 | ||
|
|
56968d858c | ||
|
|
954085d33e | ||
|
|
4777ca21c3 | ||
|
|
21b34ba133 | ||
|
|
8d870a03b5 | ||
|
|
a0f1c8c87f | ||
|
|
66add465bd | ||
|
|
ad753c90ff | ||
|
|
3d6577838c | ||
|
|
71afcb0333 | ||
|
|
fb91ea2470 | ||
|
|
4d572d3ae8 | ||
|
|
3971bf0c99 | ||
|
|
9bad73b995 | ||
|
|
d48d073bcf | ||
|
|
16d087cce4 | ||
|
|
7e2e30f94b | ||
|
|
eb61a36fd7 | ||
|
|
65506376a4 | ||
|
|
d4c0dfa851 | ||
|
|
07df6e010f | ||
|
|
6faffd832c | ||
|
|
61e5b05262 | ||
|
|
f8f0d7def7 | ||
|
|
14f9724bc9 | ||
|
|
f3c4288311 | ||
|
|
cc7d25394c | ||
|
|
baca881473 | ||
|
|
da52596d79 | ||
|
|
1beab85d61 | ||
|
|
d19f5e411c | ||
|
|
eeca91e43d | ||
|
|
e9a7c8c7fa | ||
|
|
a9dc2aa58b | ||
|
|
238d022d8d | ||
|
|
bccec5af95 | ||
|
|
0fe55f81f6 | ||
|
|
7b5e7178f4 | ||
|
|
8be9134e32 | ||
|
|
85e17fa5b0 | ||
|
|
114cb2d2aa | ||
|
|
69a854de8b | ||
|
|
c655162dd6 | ||
|
|
dcd12bff7c | ||
|
|
47e3b5b902 | ||
|
|
2bf9f10f16 | ||
|
|
9daaaa3ec2 | ||
|
|
e396454138 | ||
|
|
5d6d75f546 | ||
|
|
26374e2644 | ||
|
|
0c3f4eb7dd | ||
|
|
f3798f9d40 | ||
|
|
24956a49f4 | ||
|
|
0c2af7a8ee | ||
|
|
26ca72e252 | ||
|
|
fe85c4111a | ||
|
|
b3d8273a03 | ||
|
|
01087e01d2 | ||
|
|
946fff39a6 | ||
|
|
70552e3e0d | ||
|
|
f8159dcbfc | ||
|
|
b6880c5f50 | ||
|
|
2eb06760a3 | ||
|
|
06d18484f6 | ||
|
|
5902daf3b5 | ||
|
|
f1ff55f8b6 | ||
|
|
e6ba4a227a | ||
|
|
72bf00bf00 | ||
|
|
1647798396 | ||
|
|
13a8e2c09d | ||
|
|
2ff4a2846b | ||
|
|
6981cfb8bc | ||
|
|
0b14ab8000 | ||
|
|
33aa9bd03f | ||
|
|
1cf26fc652 | ||
|
|
c10142816e | ||
|
|
f9c38ac35e | ||
|
|
93137b02be | ||
|
|
33fcf8eb44 | ||
|
|
c87ebe8590 | ||
|
|
4b576cde50 | ||
|
|
f01e8bc509 | ||
|
|
7c94f2f3be | ||
|
|
a46296d340 | ||
|
|
bf1d3579c1 | ||
|
|
ba0c667cad | ||
|
|
3a94d9d176 | ||
|
|
4aa0669e61 | ||
|
|
bf6948f8d6 | ||
|
|
9216a1eb07 | ||
|
|
4260cc38c9 | ||
|
|
697ab30304 | ||
|
|
85f01e4ded | ||
|
|
ab51c7e2d2 | ||
|
|
cb1e797d72 | ||
|
|
24bf720a9a | ||
|
|
c553a6f458 | ||
|
|
bd708fa205 | ||
|
|
9c75c1365f | ||
|
|
f41f324fbc | ||
|
|
b24fb44fba | ||
|
|
ea38116571 | ||
|
|
0b5991850c | ||
|
|
cdd37bda1c | ||
|
|
34ad13fc22 | ||
|
|
1a31571493 | ||
|
|
ebca777efd | ||
|
|
7628a64a55 | ||
|
|
84a887cc58 | ||
|
|
8f7f2e77c3 | ||
|
|
a2f4d467fd | ||
|
|
f17962182f | ||
|
|
55f155a72c | ||
|
|
a962479ce7 | ||
|
|
d70fe95104 | ||
|
|
4101147a5b | ||
|
|
8a6b3cff57 | ||
|
|
eef48c4be3 | ||
|
|
9f5f70a93f | ||
|
|
7662f03629 | ||
|
|
3a923e8aae | ||
|
|
340afc3503 | ||
|
|
2d63b007f2 | ||
|
|
3cab8bd5d1 | ||
|
|
3d1a5f4bf3 | ||
|
|
79237a9309 | ||
|
|
e51fa5c9ac | ||
|
|
61802b9d42 | ||
|
|
38241d66cb | ||
|
|
b019e41f1c | ||
|
|
ea0dcdcfac | ||
|
|
05a95cace5 | ||
|
|
30d28a942e | ||
|
|
277c037676 | ||
|
|
09c543c526 | ||
|
|
b15c476698 | ||
|
|
3e67ca855a | ||
|
|
bc5cd3c2a1 | ||
|
|
16096e2c71 | ||
|
|
52d0ecd977 | ||
|
|
0b9c770b4e | ||
|
|
7d3e16986c | ||
|
|
1183e082af | ||
|
|
ec00636b52 | ||
|
|
1a2eb56b54 | ||
|
|
e0d0d49ea0 | ||
|
|
ac3d0dc30c | ||
|
|
56cb88819c | ||
|
|
6992fd4dba | ||
|
|
4244ab72d5 | ||
|
|
8a7f249aaa | ||
|
|
0ef6c756b6 | ||
|
|
1c052b7c81 | ||
|
|
a5c6d83cd9 | ||
|
|
d10b84d8cd | ||
|
|
16cb27b5f9 | ||
|
|
1461e475da | ||
|
|
71c87ac32f | ||
|
|
b8394c3573 | ||
|
|
00f5ee6064 | ||
|
|
f4b07a7293 | ||
|
|
a34a6c65e7 | ||
|
|
4e41479464 | ||
|
|
e4728eb719 | ||
|
|
1a0bcfe018 | ||
|
|
2cd06e3ea0 | ||
|
|
a5e5918fc5 | ||
|
|
fa5bc3a5a9 | ||
|
|
7d95e134eb | ||
|
|
f06bf6721e | ||
|
|
d0c748460e | ||
|
|
4beed065b6 | ||
|
|
0760968a3b | ||
|
|
eef3aaee60 | ||
|
|
4e9e3f3dc3 | ||
|
|
c91d970c41 | ||
|
|
e9afba3490 | ||
|
|
f2c518455e | ||
|
|
7db1dd52bd | ||
|
|
76698fe2db | ||
|
|
5076b2b950 | ||
|
|
e483383a44 | ||
|
|
799ff17c7c | ||
|
|
1d3877fab3 | ||
|
|
f50e26e5dd | ||
|
|
37d5baf0b2 | ||
|
|
9bf4aa7546 | ||
|
|
9b98ef5de4 | ||
|
|
4e46d00eb6 | ||
|
|
dcf5fb36f3 | ||
|
|
9169a073b6 | ||
|
|
12d870e274 | ||
|
|
52acfb88ee | ||
|
|
2be2891647 | ||
|
|
99c842e027 | ||
|
|
e7f6554c68 | ||
|
|
0025b3c1dc | ||
|
|
08ce9258d1 | ||
|
|
7b11224130 | ||
|
|
0da2f81252 | ||
|
|
1c864d1eef | ||
|
|
8657b4698f | ||
|
|
dfad346cff | ||
|
|
0f6c365411 | ||
|
|
bd91008462 | ||
|
|
72132c851b | ||
|
|
0de43748f7 | ||
|
|
b60458d236 | ||
|
|
8f1bf7a0ed | ||
|
|
33ca9dabef | ||
|
|
d6723935b9 | ||
|
|
a0dbb6bdd6 | ||
|
|
72c0dbd8e5 | ||
|
|
41bf668189 | ||
|
|
c51a83b404 | ||
|
|
9beacabd66 | ||
|
|
4f2baa8953 | ||
|
|
3abb59a550 | ||
|
|
2cca6ab3db | ||
|
|
c362d75a38 | ||
|
|
bce50a802b | ||
|
|
61fcbd1664 | ||
|
|
e1570eedf3 | ||
|
|
06ea2f573e | ||
|
|
ca882725ce | ||
|
|
60b19c51ef | ||
|
|
65cd13301c | ||
|
|
8283bb994a | ||
|
|
33af2409e8 | ||
|
|
98dbb2f70f | ||
|
|
f4310e8f69 | ||
|
|
3c7880cc10 | ||
|
|
ee3e631b1a | ||
|
|
4521ff3e95 | ||
|
|
fed548623d | ||
|
|
bda5315866 | ||
|
|
0d7afef0c5 | ||
|
|
342e85d378 | ||
|
|
9794c04b94 | ||
|
|
f54a555aa8 | ||
|
|
fa245c1e53 | ||
|
|
194093469a | ||
|
|
c7508a0512 | ||
|
|
1a07cb7bbf | ||
|
|
cf2eefdfbe | ||
|
|
fe8db295e7 | ||
|
|
f6bda96d5a | ||
|
|
115167659d | ||
|
|
713059275d | ||
|
|
5b657a2905 | ||
|
|
b03d2e2f9e | ||
|
|
0945d8725e | ||
|
|
2a1253e45a | ||
|
|
96555aa74b | ||
|
|
44f73103db | ||
|
|
5a4f917c60 | ||
|
|
e1aa4d6265 | ||
|
|
43bcae17b3 | ||
|
|
d5b8defcbb | ||
|
|
7db39115e8 | ||
|
|
a97e7ea52a | ||
|
|
7f731db9dd | ||
|
|
353c5d8b12 | ||
|
|
b9c51e359f | ||
|
|
00efd097c7 | ||
|
|
4dc35285dd | ||
|
|
9161b865f3 | ||
|
|
58f8481a12 | ||
|
|
f83bbc2cbe | ||
|
|
d8dda1ee66 | ||
|
|
e4dcb7441b | ||
|
|
df6e3e83bf | ||
|
|
01ea48a8fe | ||
|
|
48619dc238 | ||
|
|
2aeab13aae | ||
|
|
6c12068fb7 | ||
|
|
c1979f2c7f | ||
|
|
767392332e | ||
|
|
0eeb6c16d9 | ||
|
|
cb09e48e8c | ||
|
|
cf0a3af1f8 | ||
|
|
a3504dd51e | ||
|
|
0ce86e999a | ||
|
|
2d2aefea16 | ||
|
|
6b1e781602 | ||
|
|
f9dac3a291 | ||
|
|
1b6646b55a | ||
|
|
0a9e0acc6f | ||
|
|
ae634ef7df | ||
|
|
cd10a3126b | ||
|
|
f1d9033194 | ||
|
|
1920981713 | ||
|
|
0b845c9b1d | ||
|
|
e2617f2780 | ||
|
|
5d9b869d93 | ||
|
|
794429dd55 | ||
|
|
d787aa5007 | ||
|
|
def0e0a7a1 | ||
|
|
9f0e0ced5e | ||
|
|
a6a63d35e5 | ||
|
|
06cb004c33 | ||
|
|
25e99c6a64 | ||
|
|
929161c070 | ||
|
|
ba6d1091f2 | ||
|
|
cff1fcfe9d | ||
|
|
6df2cf552a | ||
|
|
c0ef65f442 | ||
|
|
66624b1a8f | ||
|
|
1d417685d1 | ||
|
|
917df9a5b8 | ||
|
|
a82d156ccb | ||
|
|
7d499df001 | ||
|
|
97425ea1c9 | ||
|
|
226685dc1c | ||
|
|
1381baf088 | ||
|
|
03b8ab724a | ||
|
|
b8428423a1 | ||
|
|
99c0e14fd8 | ||
|
|
b12482df89 | ||
|
|
e655e8bc19 | ||
|
|
57064c97e0 | ||
|
|
111d35deef | ||
|
|
3b29aacfdc | ||
|
|
18627e77ff | ||
|
|
4e98f2e438 | ||
|
|
d6dffed203 | ||
|
|
5b96b648b4 | ||
|
|
837dd0d241 | ||
|
|
ec50048a29 | ||
|
|
54315ce50f | ||
|
|
49ece5fddf | ||
|
|
852e888232 | ||
|
|
a6268f055d | ||
|
|
104871d6c9 | ||
|
|
c744dc06c6 | ||
|
|
57c43183e6 | ||
|
|
1ad725a04c | ||
|
|
a898b18fd6 | ||
|
|
00d7bd1a77 | ||
|
|
28ec00500b | ||
|
|
69ec3c205a | ||
|
|
35d14788d3 | ||
|
|
c69c9e62c2 | ||
|
|
835e3d803f | ||
|
|
9c31f61db4 | ||
|
|
cb4ae013c6 | ||
|
|
3394645f7a | ||
|
|
0fa362cc23 | ||
|
|
61401e5e82 | ||
|
|
e9019c8bcc | ||
|
|
c742d45b19 | ||
|
|
6afeb2e72b | ||
|
|
b5d50f2b52 | ||
|
|
7e0be91cfd | ||
|
|
4f6ec225cf | ||
|
|
b2a0ad68dd | ||
|
|
6d0eec6395 | ||
|
|
60970008c8 | ||
|
|
4ad160ef80 | ||
|
|
038582ba44 | ||
|
|
4750f32ab6 | ||
|
|
1c9072bf6e | ||
|
|
656c022ed6 | ||
|
|
4d5af19b9d | ||
|
|
fc227561ba | ||
|
|
d26a2bcf6b | ||
|
|
ca0bed47c4 | ||
|
|
4f4f18482a | ||
|
|
d0c158b019 | ||
|
|
140187dc2a | ||
|
|
3e4ebd6c9d | ||
|
|
8af80169c0 | ||
|
|
4ad63d9b63 | ||
|
|
af8c379ac8 | ||
|
|
9f7bd983b3 | ||
|
|
1dafcf24f1 | ||
|
|
b6354dfb74 | ||
|
|
d6cf76a91a | ||
|
|
a34537ae9f | ||
|
|
85c7864f78 | ||
|
|
5ced1ab905 | ||
|
|
a92099258b | ||
|
|
d5027bfbb2 | ||
|
|
c544c2b5c5 | ||
|
|
48c5494cf9 | ||
|
|
74bf9119cc | ||
|
|
f24d947388 | ||
|
|
36aed6f871 | ||
|
|
e1a593c22e | ||
|
|
e5ab64a3ab | ||
|
|
91b2d2c2fe | ||
|
|
4684446315 | ||
|
|
0d6cfc879d | ||
|
|
5afaca6fa5 | ||
|
|
78c0c9b21a | ||
|
|
720e29848b | ||
|
|
255f511abd | ||
|
|
220bd376b4 | ||
|
|
6bf87e28e9 | ||
|
|
49167cc55f | ||
|
|
a2b6aa8e67 | ||
|
|
c7ffaad763 | ||
|
|
14580b6144 | ||
|
|
d68041c394 | ||
|
|
8610bcbec3 | ||
|
|
50702f309f | ||
|
|
5c3ed4d91a | ||
|
|
243b295559 | ||
|
|
0aaf7b25a9 | ||
|
|
526cfea568 | ||
|
|
0661a1bbfc | ||
|
|
22047d20ef | ||
|
|
ca3eabafef | ||
|
|
23b662fe7d | ||
|
|
048759a147 | ||
|
|
a4f6b739be | ||
|
|
5133ff16ba | ||
|
|
63e801e00f | ||
|
|
201eb3bab8 | ||
|
|
c4b790a910 | ||
|
|
dbcf2c3cf5 | ||
|
|
a9cb1d6543 | ||
|
|
3343e82307 | ||
|
|
4d5c4d89ce | ||
|
|
91ca4a841a | ||
|
|
07ae3e49a3 | ||
|
|
e326a444b1 | ||
|
|
fa2c6605c2 | ||
|
|
bc15d5234e | ||
|
|
05c262eb9f | ||
|
|
a593a4f6f4 | ||
|
|
1e1668e949 | ||
|
|
6dce181314 | ||
|
|
c0ed0dda8e | ||
|
|
7e70536bf1 | ||
|
|
c72727b4fd | ||
|
|
41daf5ad28 | ||
|
|
13435aebdc | ||
|
|
352fb9f9ea | ||
|
|
fc46938383 | ||
|
|
0309e2332c | ||
|
|
17c3d158d2 | ||
|
|
d4338047af | ||
|
|
3aa521600a | ||
|
|
fc8bf8c15d | ||
|
|
3d3c1dbbb4 | ||
|
|
1d0d429435 | ||
|
|
604c234484 | ||
|
|
72c89ab89b | ||
|
|
53d5efa944 | ||
|
|
44c1e3a347 | ||
|
|
709fd12b7f | ||
|
|
21a603ed8f | ||
|
|
d2c5d81e59 | ||
|
|
85f91a1114 | ||
|
|
411a69233b | ||
|
|
cbf63aab1e | ||
|
|
dbb504e3d8 | ||
|
|
d42cd6a5e2 | ||
|
|
97b645cd55 | ||
|
|
faf05cafcf | ||
|
|
e7a1be4dde | ||
|
|
e9470d873e | ||
|
|
cf8e90fa3e | ||
|
|
62146b7bb3 | ||
|
|
c013ef3b30 | ||
|
|
575e962d16 | ||
|
|
70da3e124a | ||
|
|
f357edb8a4 | ||
|
|
028a113737 | ||
|
|
4bd6d7be1a | ||
|
|
8d1ef188b1 | ||
|
|
63af9f2a9e | ||
|
|
1a9c775407 | ||
|
|
955b86deea | ||
|
|
6eb342b8ee | ||
|
|
5bc1cb2aa3 | ||
|
|
cb05001e5a | ||
|
|
ad9e4db3d6 | ||
|
|
5e6ab42cb8 | ||
|
|
63ee6b6791 | ||
|
|
54076e9a6b | ||
|
|
d9f3b6c121 | ||
|
|
d7548ab2f7 | ||
|
|
0e81c1cf7e | ||
|
|
f837cb5513 | ||
|
|
b7ff44485a | ||
|
|
9cee31193c | ||
|
|
9935e3acfc | ||
|
|
90ecfbe0e4 | ||
|
|
0ca349033d | ||
|
|
30a922513b | ||
|
|
8c169bb236 | ||
|
|
cd976ad79f | ||
|
|
baa3d09577 | ||
|
|
c73c5987c7 | ||
|
|
dfe87c9f6f | ||
|
|
5cc089be12 | ||
|
|
a8d18c8ac9 | ||
|
|
cc0635335c | ||
|
|
65d8769002 | ||
|
|
d8f5330c29 | ||
|
|
ea2f53745e | ||
|
|
c9bd682c33 | ||
|
|
04c8bca073 | ||
|
|
7f1335a8dd | ||
|
|
ff77198a08 | ||
|
|
8da64d710e | ||
|
|
e453a38865 | ||
|
|
ce7be477ce | ||
|
|
01b6ddbbd3 | ||
|
|
37929ff75f | ||
|
|
b6ba0ed47c | ||
|
|
87f7315f5a | ||
|
|
1b6ad6d2a1 | ||
|
|
a62572bb76 | ||
|
|
f752770a47 | ||
|
|
d117cb61b6 | ||
|
|
c071b2dc47 | ||
|
|
d70ebd2ba5 | ||
|
|
cda7f08b76 | ||
|
|
19a65319ee | ||
|
|
7402b14df3 | ||
|
|
0622eb7634 | ||
|
|
95f344540e | ||
|
|
ee9060aa14 | ||
|
|
c6fd45727d | ||
|
|
cd27f5a4bc | ||
|
|
8ad8fa902d | ||
|
|
7861b5e6cb | ||
|
|
70aaa8fc92 | ||
|
|
e441b48ea7 | ||
|
|
6ed57ede6a | ||
|
|
aefc463586 | ||
|
|
f2340579a5 | ||
|
|
de5577ca70 | ||
|
|
316b11445e | ||
|
|
b42e481600 | ||
|
|
e5a48800ab | ||
|
|
db50d01db9 | ||
|
|
cec2085790 | ||
|
|
75ee71e35a | ||
|
|
7f9a3ac584 | ||
|
|
1e9f293994 | ||
|
|
b5f9a385ae | ||
|
|
f1114d3852 | ||
|
|
d27187b1de | ||
|
|
4aaa758543 | ||
|
|
03f07042ff | ||
|
|
bcbfc931ba | ||
|
|
e2b2026fa5 | ||
|
|
4b8b79dbe0 | ||
|
|
9e5cc46638 | ||
|
|
ba542cf67a | ||
|
|
3f58ffe572 | ||
|
|
25f544f3db | ||
|
|
df747c8216 | ||
|
|
53da298c4f | ||
|
|
73c7f40ce3 | ||
|
|
66bfadb71a | ||
|
|
5af64c53b6 | ||
|
|
b60860456d | ||
|
|
00941e4461 | ||
|
|
095a309aef | ||
|
|
834383b7e5 | ||
|
|
503b16f6b1 | ||
|
|
433f9ff211 | ||
|
|
be26c56362 | ||
|
|
1a2c3adb2f | ||
|
|
b8077fadaa | ||
|
|
17a4a99f9c | ||
|
|
71289b58ff | ||
|
|
1cdf29244c | ||
|
|
cb665caf9e | ||
|
|
eb4b85aea5 | ||
|
|
9c6c5aef5d | ||
|
|
050d552b9d | ||
|
|
cf86bc6e23 | ||
|
|
af61dfd8cd | ||
|
|
2a2a18a163 | ||
|
|
29f3d6aa28 | ||
|
|
4379ff60f6 | ||
|
|
a55a37c17b | ||
|
|
5532093d58 | ||
|
|
752be27815 | ||
|
|
80903e86fa | ||
|
|
dc076c6441 | ||
|
|
c6e63db7b4 | ||
|
|
dff6e87ce9 | ||
|
|
c765289da9 | ||
|
|
3106f512b2 | ||
|
|
2841edfb96 | ||
|
|
c9c437cb3b | ||
|
|
c0e317f829 | ||
|
|
54f2e6ea3a | ||
|
|
dbd51ff8e7 | ||
|
|
63a5124f57 | ||
|
|
eda726ec34 | ||
|
|
bab23aa7b1 | ||
|
|
7b258eddc4 | ||
|
|
3bf0bd6ce9 | ||
|
|
0ed981be19 | ||
|
|
77113844a2 | ||
|
|
9da4296e21 | ||
|
|
9fc5f0cd9d | ||
|
|
2f2e7be12a | ||
|
|
9892bf4379 | ||
|
|
351a683812 | ||
|
|
445b7f3388 | ||
|
|
9e95372e5d | ||
|
|
b5660b58b3 | ||
|
|
b472ea371a | ||
|
|
6703bd2c71 | ||
|
|
36388de4e1 | ||
|
|
10159ba1bb | ||
|
|
792a2cad82 | ||
|
|
7f1b12d29e | ||
|
|
23b46a05f7 | ||
|
|
cd14a5eb40 | ||
|
|
2256e62648 | ||
|
|
6b42a78c3f | ||
|
|
6b2b22782b | ||
|
|
de52d3cadd | ||
|
|
7e43c40529 | ||
|
|
df9dfa9d0c | ||
|
|
08a2e0f82c | ||
|
|
01dc9c767e | ||
|
|
67b441838b | ||
|
|
94f2948020 | ||
|
|
f5540195de | ||
|
|
558e4c7669 | ||
|
|
c3a5da8586 | ||
|
|
02c8ab7839 | ||
|
|
8414569428 | ||
|
|
bd97eb98c5 | ||
|
|
e67fbda305 | ||
|
|
26e6bc32ee | ||
|
|
11a6bff416 | ||
|
|
e67ee227a3 | ||
|
|
f8ec1d9d22 | ||
|
|
7fb3360d6f | ||
|
|
cb35625a53 | ||
|
|
26870501a7 | ||
|
|
f5d061f34f | ||
|
|
362fbbf913 | ||
|
|
f8d5f610b6 | ||
|
|
e43174f2af | ||
|
|
16523c2ef9 | ||
|
|
d3a9006272 | ||
|
|
a404d535ee | ||
|
|
dcd01df2d3 | ||
|
|
ba5a5d1248 | ||
|
|
052b090bbf | ||
|
|
4654d9c192 | ||
|
|
1e877eae86 | ||
|
|
3c934ea21f | ||
|
|
52718e514c | ||
|
|
4310cc23ce | ||
|
|
bfb1cc6379 | ||
|
|
cbfc675f4f | ||
|
|
e7b5d678a9 | ||
|
|
effacec817 | ||
|
|
3a89388039 | ||
|
|
3366c02a36 | ||
|
|
a26cc96a49 | ||
|
|
6d9ac84359 | ||
|
|
882507e915 | ||
|
|
08e1b52281 | ||
|
|
cf153f89df | ||
|
|
d2401bc339 | ||
|
|
fd0bfc8777 | ||
|
|
7b295d7bb0 | ||
|
|
b6563ffc29 | ||
|
|
2594ff6233 | ||
|
|
807d803a81 | ||
|
|
2076cb106d | ||
|
|
5b304fa20a | ||
|
|
fbc0330fa8 | ||
|
|
f5bfaff003 | ||
|
|
eac17a8328 | ||
|
|
f9c9316990 | ||
|
|
d9f5f6e2c6 | ||
|
|
955b755055 | ||
|
|
0c6e4c6c40 | ||
|
|
57812fc61d | ||
|
|
62f05d4c14 | ||
|
|
e4de5c55ac | ||
|
|
efaf2c720b | ||
|
|
d1f419c9d1 | ||
|
|
c5ece2a892 | ||
|
|
7712d97270 | ||
|
|
8e90d5fb17 | ||
|
|
b2fcca87ff | ||
|
|
a46a9bb42e | ||
|
|
62d661f898 | ||
|
|
4436148fa1 | ||
|
|
3886e050b0 | ||
|
|
ff4b0c8f5d | ||
|
|
f82ee0a2a7 | ||
|
|
b5d603dd46 | ||
|
|
0b8ae006d3 | ||
|
|
55fd0a4e60 | ||
|
|
3c4acbc6cb | ||
|
|
8224aab851 | ||
|
|
28604f5a4c | ||
|
|
533081454a | ||
|
|
e94e64652b | ||
|
|
489ecef47b | ||
|
|
8b42ca76f8 | ||
|
|
be2d138aac | ||
|
|
1e782b3de2 | ||
|
|
8bd62165f5 | ||
|
|
ae8a9a93bf | ||
|
|
f7ab2103ce | ||
|
|
c3d5a9848b | ||
|
|
6d6d82a304 | ||
|
|
bc83c0221b | ||
|
|
c6bf252a21 | ||
|
|
f9ea6969c3 | ||
|
|
9e956ff9f4 | ||
|
|
0a45e97699 | ||
|
|
3962d4204b | ||
|
|
fd76216652 | ||
|
|
2a7dcb6e17 | ||
|
|
b02eaee0d4 | ||
|
|
6ccf24ce6c | ||
|
|
c6186a03f1 | ||
|
|
ee4f503e10 | ||
|
|
0c227e89b2 | ||
|
|
128933a655 | ||
|
|
055b215bc7 | ||
|
|
611dd77442 | ||
|
|
fb173c058e | ||
|
|
2edb32979f | ||
|
|
e3bed8d12a | ||
|
|
84de47377c | ||
|
|
06c99d0e18 | ||
|
|
0aebec6697 | ||
|
|
d4c50b8ea0 | ||
|
|
cc424d64fe | ||
|
|
48ea36d37f | ||
|
|
ffdd436b52 | ||
|
|
ec9286eb4b | ||
|
|
68f8d60323 | ||
|
|
5cabb068c0 | ||
|
|
01079b0815 | ||
|
|
7e15aa243e | ||
|
|
e9ca6c0871 | ||
|
|
604bf64ef6 | ||
|
|
925e057acb | ||
|
|
c47e96ca45 | ||
|
|
6354a8ce03 | ||
|
|
85ec972abb | ||
|
|
a8d38ad82f | ||
|
|
be6badc6a5 | ||
|
|
cf9275a2a2 | ||
|
|
66f707c243 | ||
|
|
1163e46622 | ||
|
|
08b99ff413 | ||
|
|
23472aa4f2 | ||
|
|
05edf50931 | ||
|
|
a8e39962d0 | ||
|
|
5a42bb166e | ||
|
|
3953227702 | ||
|
|
d8d8ce7526 | ||
|
|
95e754601e | ||
|
|
5e11da7316 | ||
|
|
24e36852af | ||
|
|
458acbf33d | ||
|
|
8a9707788c | ||
|
|
e9351135bc | ||
|
|
1bb9cfee53 | ||
|
|
7da41f0fdc | ||
|
|
f22e933ab1 | ||
|
|
affa4c1b07 | ||
|
|
0011c321e2 | ||
|
|
1924eb1471 | ||
|
|
ce8c2fd656 | ||
|
|
c2fdca470d | ||
|
|
4f1bead0de | ||
|
|
2dc2e0ba56 | ||
|
|
14128819de | ||
|
|
a1e2a4452c | ||
|
|
3b34eba612 | ||
|
|
11ac026b62 | ||
|
|
05970ffadd | ||
|
|
488a156e6f | ||
|
|
a0f2bb0755 | ||
|
|
9fded49470 | ||
|
|
69752b419d | ||
|
|
3d3e85c52d | ||
|
|
94a21e3aeb | ||
|
|
2ca0c152ba | ||
|
|
a2bcc1fec2 | ||
|
|
6cdac5df4f | ||
|
|
92357b3a8b | ||
|
|
872ee5b7e7 | ||
|
|
4d30ab77be | ||
|
|
85f0f0bf81 | ||
|
|
d6174292b3 | ||
|
|
0bf41bcd10 | ||
|
|
027a0e805b | ||
|
|
fefd21ad9c | ||
|
|
d9c803ea9e | ||
|
|
0414e99fe7 | ||
|
|
0fb51a95cc | ||
|
|
9298d183bf | ||
|
|
0402abe36b | ||
|
|
44112da971 | ||
|
|
2ed89f496f | ||
|
|
2fecf3a8f1 | ||
|
|
2ef8c1ae0c | ||
|
|
2fea9433d8 | ||
|
|
e06c19b51c | ||
|
|
34abc3d023 | ||
|
|
d78a5e0a9f | ||
|
|
719171faa3 | ||
|
|
a896b30039 | ||
|
|
50bac00329 | ||
|
|
b6fda30719 | ||
|
|
fd4af3f7a9 | ||
|
|
f6d41ee77d | ||
|
|
0ce1bf706b | ||
|
|
56653eeca6 | ||
|
|
83605dfd95 | ||
|
|
353f93fddf | ||
|
|
65ecb33630 | ||
|
|
0a4eeb7cb1 | ||
|
|
469136ee86 | ||
|
|
ccf359d057 | ||
|
|
d6b0648610 | ||
|
|
da4528932a | ||
|
|
5f460b380d | ||
|
|
55f76f19d3 | ||
|
|
0bafc00b52 | ||
|
|
d309b56ad6 | ||
|
|
6c9289df0a | ||
|
|
a82a0769fa | ||
|
|
61849ef7ea | ||
|
|
960b6012c2 | ||
|
|
da2a11ed76 | ||
|
|
f0f8d83980 | ||
|
|
45e576addf | ||
|
|
7534ac1e1a | ||
|
|
bda85e4097 | ||
|
|
5e16aab51b | ||
|
|
f572d9735d | ||
|
|
fac47bfc54 | ||
|
|
cc56389dbb | ||
|
|
60969c69a7 | ||
|
|
99309ec0dc | ||
|
|
d08898ef37 | ||
|
|
b7e35ae3ed | ||
|
|
7ba34535b7 | ||
|
|
8cbcbbd7a4 | ||
|
|
36a5c7d8f5 | ||
|
|
5b74a71dd3 | ||
|
|
716b2a4bcb | ||
|
|
13c8fe3fb3 | ||
|
|
da38822dfd | ||
|
|
f36d81070b | ||
|
|
959b725ab6 | ||
|
|
3891f015d7 | ||
|
|
8cfb4aafcd | ||
|
|
6ce9a7f225 | ||
|
|
1ed62628a7 | ||
|
|
b5328d01cf | ||
|
|
72a62d49b3 | ||
|
|
886898b137 | ||
|
|
a9ca6d4065 | ||
|
|
00683549ae | ||
|
|
f32bacb32d | ||
|
|
5a12d60c6f | ||
|
|
f627ff2383 | ||
|
|
79028397ad | ||
|
|
1b5e1c4753 | ||
|
|
855fb26a81 | ||
|
|
ab922ed1ba | ||
|
|
f198e81af1 | ||
|
|
83cbf141a8 | ||
|
|
9fd183e74f | ||
|
|
31173ff4b4 | ||
|
|
bf9aa4465b | ||
|
|
5312340bf9 | ||
|
|
61e2568b8d | ||
|
|
1392dcc273 | ||
|
|
117bea4e4e | ||
|
|
86a22a3896 | ||
|
|
95a89ad192 | ||
|
|
5153f67a2e | ||
|
|
4de01190a1 | ||
|
|
d213bd1fee | ||
|
|
80c19df4bb | ||
|
|
897cf72933 | ||
|
|
11bfab67f9 | ||
|
|
62db7a6dda | ||
|
|
a11e8093e5 | ||
|
|
3965e1053e | ||
|
|
e41dbdad90 | ||
|
|
64496690bb | ||
|
|
f572cdea47 | ||
|
|
5e775929be | ||
|
|
65724133ea | ||
|
|
a63c34a902 | ||
|
|
dbc729d049 | ||
|
|
7b134b8317 | ||
|
|
22e9815928 | ||
|
|
a109ae1490 | ||
|
|
f019b80af0 | ||
|
|
0ffec0a9ad | ||
|
|
841f9c3627 | ||
|
|
20321bb476 | ||
|
|
4bbc693d9c | ||
|
|
3ae1bd5b78 | ||
|
|
ce837da935 | ||
|
|
1b4ddefe27 | ||
|
|
abf6acc211 | ||
|
|
7873213b11 | ||
|
|
97ae184d0a | ||
|
|
8f0c02043b | ||
|
|
1e7f10e387 | ||
|
|
718acb0a86 | ||
|
|
85097a871f | ||
|
|
14ecd1929b | ||
|
|
8418464d91 | ||
|
|
f6b96480d1 | ||
|
|
29af953cec | ||
|
|
8e407acd76 | ||
|
|
2635028933 | ||
|
|
605562467d | ||
|
|
2d22f9c3d3 | ||
|
|
19db1d88bc | ||
|
|
6a740a6d67 | ||
|
|
9fd56318fe | ||
|
|
fdbbf65fea | ||
|
|
f6c6fba812 | ||
|
|
a24fca671e | ||
|
|
d313d97947 | ||
|
|
5ecd5093a9 | ||
|
|
99de8aa557 | ||
|
|
5e04e5b85d | ||
|
|
0458062bce | ||
|
|
9ae6bcc5df | ||
|
|
727ae2b877 | ||
|
|
30b857bc8a | ||
|
|
dafad17a9b | ||
|
|
8098d23768 | ||
|
|
5d9d51ba2d | ||
|
|
eea9144ebe | ||
|
|
97cf3f70a5 | ||
|
|
2e9655a4e0 | ||
|
|
3d666b3992 | ||
|
|
a246be587e | ||
|
|
f2ea12571f | ||
|
|
165ac1d556 | ||
|
|
1018999096 | ||
|
|
04691a13da | ||
|
|
e34e5b27ba | ||
|
|
66ee9d209d | ||
|
|
830f2cbdd3 | ||
|
|
b6b06f67dc | ||
|
|
3edc93f117 | ||
|
|
86f078dbea | ||
|
|
f739b7e377 |
+138
-41
@@ -1,12 +1,15 @@
|
||||
version: 2.1
|
||||
orbs:
|
||||
go: gotest/tools@0.0.9
|
||||
go: gotest/tools@0.0.13
|
||||
|
||||
executors:
|
||||
golang:
|
||||
docker:
|
||||
- image: circleci/golang:1.13
|
||||
- image: circleci/golang:1.14.2
|
||||
resource_class: 2xlarge
|
||||
ubuntu:
|
||||
docker:
|
||||
- image: ubuntu:19.10
|
||||
|
||||
commands:
|
||||
install-deps:
|
||||
@@ -24,6 +27,8 @@ commands:
|
||||
description: is a darwin build environment?
|
||||
type: boolean
|
||||
steps:
|
||||
- checkout
|
||||
- git_fetch_all_tags
|
||||
- checkout
|
||||
- when:
|
||||
condition: << parameters.linux >>
|
||||
@@ -37,17 +42,36 @@ commands:
|
||||
- restore_cache:
|
||||
name: Restore parameters cache
|
||||
keys:
|
||||
- 'v20-1k-lotus-params-{{ checksum "build/proof-params/parameters.json" }}-{{ checksum "build/paramfetch.go" }}'
|
||||
- 'v20-1k-lotus-params-{{ checksum "build/proof-params/parameters.json" }}-'
|
||||
- 'v25-2k-lotus-params'
|
||||
paths:
|
||||
- /var/tmp/filecoin-proof-parameters/
|
||||
- run: ./lotus fetch-params --proving-params 1024
|
||||
- run: ./lotus fetch-params --proving-params 2048
|
||||
- save_cache:
|
||||
name: Save parameters cache
|
||||
key: 'v20-1k-lotus-params-{{ checksum "build/proof-params/parameters.json" }}-{{ checksum "build/paramfetch.go" }}'
|
||||
key: 'v25-2k-lotus-params'
|
||||
paths:
|
||||
- /var/tmp/filecoin-proof-parameters/
|
||||
|
||||
install_ipfs:
|
||||
steps:
|
||||
- run: |
|
||||
apt update
|
||||
apt install -y wget
|
||||
wget https://github.com/ipfs/go-ipfs/releases/download/v0.4.22/go-ipfs_v0.4.22_linux-amd64.tar.gz
|
||||
wget https://github.com/ipfs/go-ipfs/releases/download/v0.4.22/go-ipfs_v0.4.22_linux-amd64.tar.gz.sha512
|
||||
if [ "$(sha512sum go-ipfs_v0.4.22_linux-amd64.tar.gz)" != "$(cat go-ipfs_v0.4.22_linux-amd64.tar.gz.sha512)" ]
|
||||
then
|
||||
echo "ipfs failed checksum check"
|
||||
exit 1
|
||||
fi
|
||||
tar -xf go-ipfs_v0.4.22_linux-amd64.tar.gz
|
||||
mv go-ipfs/ipfs /usr/local/bin/ipfs
|
||||
chmod +x /usr/local/bin/ipfs
|
||||
git_fetch_all_tags:
|
||||
steps:
|
||||
- run:
|
||||
name: fetch all tags
|
||||
command: |
|
||||
git fetch --all
|
||||
|
||||
jobs:
|
||||
mod-tidy-check:
|
||||
@@ -55,7 +79,6 @@ jobs:
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- go/mod-download
|
||||
- go/mod-tidy-check
|
||||
|
||||
build-all:
|
||||
@@ -63,18 +86,29 @@ jobs:
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- go/mod-download
|
||||
- run: sudo apt-get update
|
||||
- run: sudo apt-get install npm
|
||||
- restore_cache:
|
||||
name: restore go mod cache
|
||||
key: v1-go-deps-{{ arch }}-{{ checksum "/home/circleci/project/go.mod" }}
|
||||
- run:
|
||||
command: make buildall
|
||||
- store_artifacts:
|
||||
path: lotus
|
||||
- store_artifacts:
|
||||
path: lotus-storage-miner
|
||||
- store_artifacts:
|
||||
path: lotus-seal-worker
|
||||
- run: mkdir linux && mv lotus lotus-storage-miner lotus-seal-worker linux/
|
||||
- persist_to_workspace:
|
||||
root: "."
|
||||
paths:
|
||||
- linux
|
||||
|
||||
build-debug:
|
||||
executor: golang
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- run:
|
||||
command: make debug
|
||||
|
||||
test: &test
|
||||
description: |
|
||||
@@ -91,6 +125,9 @@ jobs:
|
||||
type: string
|
||||
default: "./..."
|
||||
description: Import paths of packages to be tested.
|
||||
winpost-test:
|
||||
type: string
|
||||
default: "0"
|
||||
test-suite-name:
|
||||
type: string
|
||||
default: unit
|
||||
@@ -101,7 +138,7 @@ jobs:
|
||||
description: gotestsum format. https://github.com/gotestyourself/gotestsum#format
|
||||
coverage:
|
||||
type: string
|
||||
default: -coverprofile=coverage.txt
|
||||
default: -coverprofile=coverage.txt -coverpkg=github.com/filecoin-project/lotus/...
|
||||
description: Coverage flag. Set to the empty string to disable.
|
||||
codecov-upload:
|
||||
type: boolean
|
||||
@@ -113,10 +150,6 @@ jobs:
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- go/mod-download
|
||||
- restore_cache:
|
||||
name: restore go mod cache
|
||||
key: v1-go-deps-{{ arch }}-{{ checksum "/home/circleci/project/go.mod" }}
|
||||
- run:
|
||||
command: make deps lotus
|
||||
no_output_timeout: 30m
|
||||
@@ -128,6 +161,7 @@ jobs:
|
||||
environment:
|
||||
GOTESTSUM_JUNITFILE: /tmp/test-reports/<< parameters.test-suite-name >>/junit.xml
|
||||
GOTESTSUM_FORMAT: << parameters.gotestsum-format >>
|
||||
LOTUS_TEST_WINDOW_POST: << parameters.winpost-test >>
|
||||
command: |
|
||||
mkdir -p /tmp/test-reports/<< parameters.test-suite-name >>
|
||||
gotestsum -- \
|
||||
@@ -146,15 +180,10 @@ jobs:
|
||||
shell: /bin/bash -eo pipefail
|
||||
command: |
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
- save_cache:
|
||||
name: save go mod cache
|
||||
key: v1-go-deps-{{ arch }}-{{ checksum "/home/circleci/project/go.mod" }}
|
||||
paths:
|
||||
- "~/go/pkg"
|
||||
- "~/go/src/github.com"
|
||||
- "~/go/src/golang.org"
|
||||
|
||||
test-short:
|
||||
test-short:
|
||||
<<: *test
|
||||
test-window-post:
|
||||
<<: *test
|
||||
|
||||
build-macos:
|
||||
@@ -169,8 +198,8 @@ jobs:
|
||||
- run:
|
||||
name: Install go
|
||||
command: |
|
||||
curl -O https://dl.google.com/go/go1.13.4.darwin-amd64.pkg && \
|
||||
sudo installer -pkg go1.13.4.darwin-amd64.pkg -target /
|
||||
curl -O https://dl.google.com/go/go1.14.2.darwin-amd64.pkg && \
|
||||
sudo installer -pkg go1.14.2.darwin-amd64.pkg -target /
|
||||
- run:
|
||||
name: Install pkg-config
|
||||
command: HOMEBREW_NO_AUTO_UPDATE=1 brew install pkg-config
|
||||
@@ -182,21 +211,26 @@ jobs:
|
||||
- run:
|
||||
name: Install jq
|
||||
command: |
|
||||
mkdir $HOME/.bin
|
||||
curl --location https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64 --output $HOME/.bin/jq
|
||||
chmod +x $HOME/.bin/jq
|
||||
curl --location https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64 --output /usr/local/bin/jq
|
||||
chmod +x /usr/local/bin/jq
|
||||
- restore_cache:
|
||||
name: restore go mod and cargo cache
|
||||
name: restore cargo cache
|
||||
key: v3-go-deps-{{ arch }}-{{ checksum "~/go/src/github.com/filecoin-project/lotus/go.sum" }}
|
||||
- install-deps
|
||||
- go/mod-download
|
||||
- run:
|
||||
command: make buildall
|
||||
command: make build
|
||||
no_output_timeout: 30m
|
||||
- store_artifacts:
|
||||
path: lotus
|
||||
- store_artifacts:
|
||||
path: lotus-storage-miner
|
||||
- store_artifacts:
|
||||
path: lotus-seal-worker
|
||||
- run: mkdir darwin && mv lotus lotus-storage-miner lotus-seal-worker darwin/
|
||||
- persist_to_workspace:
|
||||
root: "."
|
||||
paths:
|
||||
- darwin
|
||||
- save_cache:
|
||||
name: save cargo cache
|
||||
key: v3-go-deps-{{ arch }}-{{ checksum "~/go/src/github.com/filecoin-project/lotus/go.sum" }}
|
||||
@@ -204,6 +238,14 @@ jobs:
|
||||
- "~/.rustup"
|
||||
- "~/.cargo"
|
||||
|
||||
gofmt:
|
||||
executor: golang
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- run:
|
||||
command: "! go fmt ./... 2>&1 | read"
|
||||
|
||||
lint: &lint
|
||||
description: |
|
||||
Run golangci-lint.
|
||||
@@ -213,7 +255,7 @@ jobs:
|
||||
default: golang
|
||||
golangci-lint-version:
|
||||
type: string
|
||||
default: 1.17.1
|
||||
default: 1.27.0
|
||||
concurrency:
|
||||
type: string
|
||||
default: '2'
|
||||
@@ -229,7 +271,6 @@ jobs:
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- go/mod-download
|
||||
- run:
|
||||
command: make deps
|
||||
no_output_timeout: 30m
|
||||
@@ -239,7 +280,7 @@ jobs:
|
||||
- run:
|
||||
name: Lint
|
||||
command: |
|
||||
$HOME/.local/bin/golangci-lint run -v \
|
||||
$HOME/.local/bin/golangci-lint run -v --timeout 2m \
|
||||
--concurrency << parameters.concurrency >> << parameters.args >>
|
||||
lint-changes:
|
||||
<<: *lint
|
||||
@@ -247,16 +288,72 @@ jobs:
|
||||
lint-all:
|
||||
<<: *lint
|
||||
|
||||
publish:
|
||||
description: publish binary artifacts
|
||||
executor: ubuntu
|
||||
steps:
|
||||
- run:
|
||||
name: Install git jq curl
|
||||
command: apt update && apt install -y git jq curl
|
||||
- checkout
|
||||
- git_fetch_all_tags
|
||||
- checkout
|
||||
- install_ipfs
|
||||
- attach_workspace:
|
||||
at: "."
|
||||
- run:
|
||||
name: Create bundles
|
||||
command: ./scripts/build-bundle.sh
|
||||
- run:
|
||||
name: Publish release
|
||||
command: ./scripts/publish-release.sh
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2.1
|
||||
ci:
|
||||
jobs:
|
||||
- lint-changes:
|
||||
args: "--new-from-rev origin/master"
|
||||
- test
|
||||
args: "--new-from-rev origin/next"
|
||||
- mod-tidy-check
|
||||
- gofmt
|
||||
- test:
|
||||
codecov-upload: true
|
||||
- test-window-post:
|
||||
go-test-flags: "-run=TestWindowedPost"
|
||||
winpost-test: "1"
|
||||
- test-short:
|
||||
go-test-flags: "--timeout 10m --short"
|
||||
- mod-tidy-check
|
||||
- build-all
|
||||
- build-macos
|
||||
filters:
|
||||
tags:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
- build-debug
|
||||
- build-all:
|
||||
requires:
|
||||
- test-short
|
||||
filters:
|
||||
tags:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
- build-macos:
|
||||
requires:
|
||||
- test-short
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- /.*/
|
||||
tags:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
- publish:
|
||||
requires:
|
||||
- build-all
|
||||
- build-macos
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- /.*/
|
||||
tags:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
comment: off
|
||||
ignore:
|
||||
- "cbor_gen.go"
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
.gitignore
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Sealing Issues
|
||||
about: Create a report for help with sealing (commit) failures.
|
||||
title: ''
|
||||
labels: 'sealing'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please provide all the information requested here to help us troubleshoot "commit failed" issues.
|
||||
If the information requested is missing, we will probably have to just ask you to provide it anyway,
|
||||
before we can help debug.
|
||||
|
||||
**Describe the problem**
|
||||
|
||||
A brief description of the problem you encountered while proving (sealing) a sector.
|
||||
|
||||
Including what commands you ran, and a description of your setup, is very helpful.
|
||||
|
||||
**Sectors list**
|
||||
|
||||
The output of `./lotus-storage-miner sectors list`.
|
||||
|
||||
**Sectors status**
|
||||
|
||||
The output of `./lotus-storage-miner sectors status --log <sectorId>` for the failed sector(s).
|
||||
|
||||
**Lotus storage miner logs**
|
||||
|
||||
Please go through the logs of your storage miner, and include screenshots of any error-like messages you find.
|
||||
|
||||
**Version**
|
||||
|
||||
The output of `./lotus --version`.
|
||||
+5
-1
@@ -2,6 +2,8 @@
|
||||
/lotus-storage-miner
|
||||
/lotus-seal-worker
|
||||
/lotus-seed
|
||||
/lotus-health
|
||||
/lotus-shed
|
||||
/pond
|
||||
/townhall
|
||||
/fountain
|
||||
@@ -13,7 +15,6 @@
|
||||
/cmd/lotus-townhall/townhall/node_modules
|
||||
/cmd/lotus-townhall/townhall/build
|
||||
extern/filecoin-ffi/rust/target
|
||||
**/*.h
|
||||
**/*.a
|
||||
**/*.pc
|
||||
/**/*/.DS_STORE
|
||||
@@ -25,6 +26,9 @@ build/paramfetch.sh
|
||||
/blocks.svg
|
||||
/chainwatch
|
||||
/chainwatch.db
|
||||
/bundle
|
||||
/darwin
|
||||
/linux
|
||||
|
||||
*-fuzz.zip
|
||||
/chain/types/work_msg/
|
||||
|
||||
@@ -2,3 +2,6 @@
|
||||
path = extern/filecoin-ffi
|
||||
url = https://github.com/filecoin-project/filecoin-ffi.git
|
||||
branch = master
|
||||
[submodule "extern/serialization-vectors"]
|
||||
path = extern/serialization-vectors
|
||||
url = https://github.com/filecoin-project/serialization-vectors
|
||||
|
||||
+17
-2
@@ -1,7 +1,8 @@
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- vet
|
||||
- gofmt
|
||||
- govet
|
||||
- goimports
|
||||
- misspell
|
||||
- goconst
|
||||
@@ -21,13 +22,27 @@ issues:
|
||||
- "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this"
|
||||
- "Potential file inclusion via variable"
|
||||
- "should have( a package)? comment"
|
||||
- "Error return value of `logging.SetLogLevel` is not checked"
|
||||
|
||||
exclude-use-default: false
|
||||
exclude-rules:
|
||||
- path: lotuspond
|
||||
linters:
|
||||
- errcheck
|
||||
|
||||
- path: node/modules/lp2p
|
||||
linters:
|
||||
- golint
|
||||
- path: ".*_test.go"
|
||||
|
||||
- path: build/params_.*\.go
|
||||
linters:
|
||||
- golint
|
||||
|
||||
- path: api/apistruct/struct.go
|
||||
linters:
|
||||
- golint
|
||||
|
||||
- path: .*_test.go
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ SHELL=/usr/bin/env bash
|
||||
all: build
|
||||
.PHONY: all
|
||||
|
||||
unexport GOFLAGS
|
||||
|
||||
GOVERSION:=$(shell go version | cut -d' ' -f 3 | cut -d. -f 2)
|
||||
ifeq ($(shell expr $(GOVERSION) \< 13), 1)
|
||||
$(warning Your Golang version is go 1.$(GOVERSION))
|
||||
@@ -14,12 +16,19 @@ MODULES:=
|
||||
|
||||
CLEAN:=
|
||||
BINS:=
|
||||
GOFLAGS+=-ldflags="-X "github.com/filecoin-project/lotus/build".CurrentCommit=+git$(subst -,.,$(shell git describe --always --match=NeVeRmAtCh --dirty 2>/dev/null || git rev-parse --short HEAD 2>/dev/null))"
|
||||
|
||||
ldflags=-X=github.com/filecoin-project/lotus/build.CurrentCommit=+git.$(subst -,.,$(shell git describe --always --match=NeVeRmAtCh --dirty 2>/dev/null || git rev-parse --short HEAD 2>/dev/null))
|
||||
ifneq ($(strip $(LDFLAGS)),)
|
||||
ldflags+=-extldflags=$(LDFLAGS)
|
||||
endif
|
||||
|
||||
GOFLAGS+=-ldflags="$(ldflags)"
|
||||
|
||||
|
||||
## FFI
|
||||
|
||||
FFI_PATH:=extern/filecoin-ffi/
|
||||
FFI_DEPS:=libfilecoin.a filecoin.pc filecoin.h
|
||||
FFI_DEPS:=.install-filcrypto
|
||||
FFI_DEPS:=$(addprefix $(FFI_PATH),$(FFI_DEPS))
|
||||
|
||||
$(FFI_DEPS): build/.filecoin-install ;
|
||||
@@ -51,6 +60,9 @@ deps: $(BUILD_DEPS)
|
||||
debug: GOFLAGS+=-tags=debug
|
||||
debug: lotus lotus-storage-miner lotus-seal-worker lotus-seed
|
||||
|
||||
2k: GOFLAGS+=-tags=2k
|
||||
2k: lotus lotus-storage-miner lotus-seal-worker lotus-seed
|
||||
|
||||
lotus: $(BUILD_DEPS)
|
||||
rm -f lotus
|
||||
go build $(GOFLAGS) -o lotus ./cmd/lotus
|
||||
@@ -76,8 +88,9 @@ BINS+=lotus-seal-worker
|
||||
lotus-shed: $(BUILD_DEPS)
|
||||
rm -f lotus-shed
|
||||
go build $(GOFLAGS) -o lotus-shed ./cmd/lotus-shed
|
||||
.PHONY: lotus-seal-worker
|
||||
BINS+=lotus-seal-worker
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec lotus-shed -i ./build
|
||||
.PHONY: lotus-shed
|
||||
BINS+=lotus-shed
|
||||
|
||||
build: lotus lotus-storage-miner lotus-seal-worker
|
||||
@[[ $$(type -P "lotus") ]] && echo "Caution: you have \
|
||||
@@ -90,6 +103,19 @@ install:
|
||||
install -C ./lotus-storage-miner /usr/local/bin/lotus-storage-miner
|
||||
install -C ./lotus-seal-worker /usr/local/bin/lotus-seal-worker
|
||||
|
||||
install-services: install
|
||||
mkdir -p /usr/local/lib/systemd/system
|
||||
install -C -m 0644 ./scripts/lotus-daemon.service /usr/local/lib/systemd/system/lotus-daemon.service
|
||||
install -C -m 0644 ./scripts/lotus-miner.service /usr/local/lib/systemd/system/lotus-miner.service
|
||||
systemctl daemon-reload
|
||||
@echo
|
||||
@echo "lotus and lotus-miner services installed. Don't forget to 'systemctl enable lotus|lotus-miner' for it to be enabled on startup."
|
||||
|
||||
clean-services:
|
||||
rm -f /usr/local/lib/systemd/system/lotus-daemon.service
|
||||
rm -f /usr/local/lib/systemd/system/lotus-miner.service
|
||||
systemctl daemon-reload
|
||||
|
||||
# TOOLS
|
||||
|
||||
lotus-seed: $(BUILD_DEPS)
|
||||
@@ -106,7 +132,7 @@ benchmarks:
|
||||
@curl -X POST 'http://benchmark.kittyhawk.wtf/benchmark' -d '@bench.json' -u "${benchmark_http_cred}"
|
||||
.PHONY: benchmarks
|
||||
|
||||
pond: build
|
||||
pond: 2k
|
||||
go build -o pond ./lotuspond
|
||||
(cd lotuspond/front && npm i && CI=false npm run build)
|
||||
.PHONY: pond
|
||||
@@ -123,14 +149,14 @@ BINS+=townhall
|
||||
fountain:
|
||||
rm -f fountain
|
||||
go build -o fountain ./cmd/lotus-fountain
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec fountain -i ./cmd/lotus-fountain
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec fountain -i ./cmd/lotus-fountain -i ./build
|
||||
.PHONY: fountain
|
||||
BINS+=fountain
|
||||
|
||||
chainwatch:
|
||||
rm -f chainwatch
|
||||
go build -o chainwatch ./cmd/lotus-chainwatch
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec chainwatch -i ./cmd/lotus-chainwatch
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec chainwatch -i ./cmd/lotus-chainwatch -i ./build
|
||||
.PHONY: chainwatch
|
||||
BINS+=chainwatch
|
||||
|
||||
@@ -144,13 +170,34 @@ BINS+=bench
|
||||
stats:
|
||||
rm -f stats
|
||||
go build -o stats ./tools/stats
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec stats -i ./build
|
||||
.PHONY: stats
|
||||
BINS+=stats
|
||||
|
||||
health:
|
||||
rm -f lotus-health
|
||||
go build -o lotus-health ./cmd/lotus-health
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec lotus-health -i ./build
|
||||
|
||||
.PHONY: health
|
||||
BINS+=health
|
||||
|
||||
# MISC
|
||||
|
||||
buildall: $(BINS)
|
||||
|
||||
completions:
|
||||
./scripts/make-completions.sh lotus
|
||||
./scripts/make-completions.sh lotus-storage-miner
|
||||
.PHONY: completions
|
||||
|
||||
install-completions:
|
||||
mkdir -p /usr/share/bash-completion/completions /usr/local/share/zsh/site-functions/
|
||||
install -C ./scripts/bash-completion/lotus /usr/share/bash-completion/completions/lotus
|
||||
install -C ./scripts/bash-completion/lotus-storage-miner /usr/share/bash-completion/completions/lotus-storage-miner
|
||||
install -C ./scripts/zsh-completion/lotus /usr/local/share/zsh/site-functions/_lotus
|
||||
install -C ./scripts/zsh-completion/lotus-storage-miner /usr/local/share/zsh/site-functions/_lotus-storage-miner
|
||||
|
||||
clean:
|
||||
rm -rf $(CLEAN) $(BINS)
|
||||
-$(MAKE) -C $(FFI_PATH) clean
|
||||
@@ -164,5 +211,10 @@ dist-clean:
|
||||
type-gen:
|
||||
go run ./gen/main.go
|
||||
|
||||
method-gen:
|
||||
(cd ./lotuspond/front/src/chain && go run ./methodgen.go)
|
||||
|
||||
gen: type-gen method-gen
|
||||
|
||||
print-%:
|
||||
@echo $*=$($*)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Project Lotus - 莲
|
||||
|
||||
Lotus is an experimental implementation of the Filecoin Distributed Storage Network. For more details about Filecoin, check out the [Filecoin Spec](https://github.com/filecoin-project/specs).
|
||||
Lotus is an implementation of the Filecoin Distributed Storage Network. For more details about Filecoin, check out the [Filecoin Spec](https://github.com/filecoin-project/specs).
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
+14
-5
@@ -4,17 +4,18 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
|
||||
type Permission = string
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
type Common interface {
|
||||
// Auth
|
||||
AuthVerify(ctx context.Context, token string) ([]Permission, error)
|
||||
AuthNew(ctx context.Context, perms []Permission) ([]byte, error)
|
||||
AuthVerify(ctx context.Context, token string) ([]auth.Permission, error)
|
||||
AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error)
|
||||
|
||||
// network
|
||||
|
||||
@@ -23,12 +24,20 @@ type Common interface {
|
||||
NetConnect(context.Context, peer.AddrInfo) error
|
||||
NetAddrsListen(context.Context) (peer.AddrInfo, error)
|
||||
NetDisconnect(context.Context, peer.ID) error
|
||||
NetFindPeer(context.Context, peer.ID) (peer.AddrInfo, error)
|
||||
NetPubsubScores(context.Context) ([]PubsubScore, error)
|
||||
|
||||
// ID returns peerID of libp2p node backing this API
|
||||
ID(context.Context) (peer.ID, error)
|
||||
|
||||
// Version provides information about API provider
|
||||
Version(context.Context) (Version, error)
|
||||
|
||||
LogList(context.Context) ([]string, error)
|
||||
LogSetLevel(context.Context, string, string) error
|
||||
|
||||
// trigger graceful shutdown
|
||||
Shutdown(context.Context) error
|
||||
}
|
||||
|
||||
// Version provides various build-time information
|
||||
|
||||
+246
-88
@@ -8,63 +8,95 @@ import (
|
||||
"github.com/ipfs/go-filestore"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
// FullNode API is a low-level interface to the Filecoin network full node
|
||||
type FullNode interface {
|
||||
Common
|
||||
|
||||
// chain
|
||||
// TODO: TipSetKeys
|
||||
|
||||
// MethodGroup: Chain
|
||||
// The Chain method group contains methods for interacting with the
|
||||
// blockchain, but that do not require any form of state computation
|
||||
|
||||
// ChainNotify returns channel with chain head updates
|
||||
// First message is guaranteed to be of len == 1, and type == 'current'
|
||||
ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
|
||||
ChainNotify(context.Context) (<-chan []*HeadChange, error)
|
||||
// ChainHead returns the current head of the chain
|
||||
ChainHead(context.Context) (*types.TipSet, error)
|
||||
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
|
||||
// ChainGetRandomness is used to sample the chain for randomness
|
||||
ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error)
|
||||
// ChainGetBlock returns the block specified by the given CID
|
||||
ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error)
|
||||
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
|
||||
ChainGetParentReceipts(context.Context, cid.Cid) ([]*types.MessageReceipt, error)
|
||||
ChainGetParentMessages(context.Context, cid.Cid) ([]Message, error)
|
||||
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
ChainSetHead(context.Context, *types.TipSet) error
|
||||
ChainHasObj(context.Context, cid.Cid) (bool, error)
|
||||
ChainStatObj(context.Context, cid.Cid, cid.Cid) (ObjStat, error)
|
||||
ChainSetHead(context.Context, types.TipSetKey) error
|
||||
ChainGetGenesis(context.Context) (*types.TipSet, error)
|
||||
ChainTipSetWeight(context.Context, *types.TipSet) (types.BigInt, error)
|
||||
ChainGetNode(ctx context.Context, p string) (interface{}, error)
|
||||
ChainTipSetWeight(context.Context, types.TipSetKey) (types.BigInt, error)
|
||||
ChainGetNode(ctx context.Context, p string) (*IpldObject, error)
|
||||
ChainGetMessage(context.Context, cid.Cid) (*types.Message, error)
|
||||
ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*HeadChange, error)
|
||||
ChainExport(context.Context, types.TipSetKey) (<-chan []byte, error)
|
||||
|
||||
// syncer
|
||||
// MethodGroup: Sync
|
||||
// The Sync method group contains methods for interacting with and
|
||||
// observing the lotus sync service
|
||||
|
||||
// SyncState returns the current status of the lotus sync system
|
||||
SyncState(context.Context) (*SyncState, error)
|
||||
// SyncSubmitBlock can be used to submit a newly created block to the
|
||||
// network through this node
|
||||
SyncSubmitBlock(ctx context.Context, blk *types.BlockMsg) error
|
||||
SyncIncomingBlocks(ctx context.Context) (<-chan *types.BlockHeader, error)
|
||||
SyncMarkBad(ctx context.Context, bcid cid.Cid) error
|
||||
SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error)
|
||||
|
||||
// messages
|
||||
MpoolPending(context.Context, *types.TipSet) ([]*types.SignedMessage, error)
|
||||
MpoolPush(context.Context, *types.SignedMessage) error // TODO: remove
|
||||
// MethodGroup: Mpool
|
||||
// The Mpool methods are for interacting with the message pool. The message pool
|
||||
// manages all incoming and outgoing 'messages' going over the network.
|
||||
|
||||
MpoolPending(context.Context, types.TipSetKey) ([]*types.SignedMessage, error)
|
||||
MpoolPush(context.Context, *types.SignedMessage) (cid.Cid, error)
|
||||
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error) // get nonce, sign, push
|
||||
MpoolGetNonce(context.Context, address.Address) (uint64, error)
|
||||
MpoolSub(context.Context) (<-chan MpoolUpdate, error)
|
||||
MpoolEstimateGasPrice(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error)
|
||||
|
||||
// FullNodeStruct
|
||||
// MethodGroup: Miner
|
||||
|
||||
// miner
|
||||
|
||||
MinerCreateBlock(context.Context, address.Address, *types.TipSet, *types.Ticket, *types.EPostProof, []*types.SignedMessage, uint64, uint64) (*types.BlockMsg, error)
|
||||
MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*MiningBaseInfo, error)
|
||||
MinerCreateBlock(context.Context, *BlockTemplate) (*types.BlockMsg, error)
|
||||
|
||||
// // UX ?
|
||||
|
||||
// wallet
|
||||
// MethodGroup: Wallet
|
||||
|
||||
WalletNew(context.Context, string) (address.Address, error)
|
||||
WalletNew(context.Context, crypto.SigType) (address.Address, error)
|
||||
WalletHas(context.Context, address.Address) (bool, error)
|
||||
WalletList(context.Context) ([]address.Address, error)
|
||||
WalletBalance(context.Context, address.Address) (types.BigInt, error)
|
||||
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
WalletSign(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
WalletSignMessage(context.Context, address.Address, *types.Message) (*types.SignedMessage, error)
|
||||
WalletVerify(context.Context, address.Address, []byte, *crypto.Signature) bool
|
||||
WalletDefaultAddress(context.Context) (address.Address, error)
|
||||
WalletSetDefault(context.Context, address.Address) error
|
||||
WalletExport(context.Context, address.Address) (*types.KeyInfo, error)
|
||||
@@ -72,15 +104,23 @@ type FullNode interface {
|
||||
|
||||
// Other
|
||||
|
||||
// MethodGroup: Client
|
||||
// The Client methods all have to do with interacting with the storage and
|
||||
// retrieval markets as a client
|
||||
|
||||
// ClientImport imports file under the specified path into filestore
|
||||
ClientImport(ctx context.Context, path string) (cid.Cid, error)
|
||||
ClientStartDeal(ctx context.Context, data cid.Cid, addr address.Address, miner address.Address, epochPrice types.BigInt, blocksDuration uint64) (*cid.Cid, error)
|
||||
ClientImport(ctx context.Context, ref FileRef) (cid.Cid, error)
|
||||
// ClientStartDeal proposes a deal with a miner
|
||||
ClientStartDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error)
|
||||
// ClientGetDeal info returns the latest information about a given deal
|
||||
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error)
|
||||
ClientListDeals(ctx context.Context) ([]DealInfo, error)
|
||||
ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error)
|
||||
ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error)
|
||||
ClientRetrieve(ctx context.Context, order RetrievalOrder, path string) error
|
||||
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error)
|
||||
ClientRetrieve(ctx context.Context, order RetrievalOrder, ref *FileRef) error
|
||||
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error)
|
||||
ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error)
|
||||
ClientGenCar(ctx context.Context, ref FileRef, outpath string) error
|
||||
|
||||
// ClientUnimport removes references to the specified file from filestore
|
||||
//ClientUnimport(path string)
|
||||
@@ -90,52 +130,85 @@ type FullNode interface {
|
||||
|
||||
//ClientListAsks() []Ask
|
||||
|
||||
// MethodGroup: State
|
||||
// The State methods are used to query, inspect, and interact with chain state
|
||||
|
||||
// if tipset is nil, we'll use heaviest
|
||||
StateCall(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error)
|
||||
StateReplay(context.Context, *types.TipSet, cid.Cid) (*ReplayResults, error)
|
||||
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
|
||||
StateReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*ActorState, error)
|
||||
StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error)
|
||||
StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error)
|
||||
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error)
|
||||
StateReadState(ctx context.Context, act *types.Actor, tsk types.TipSetKey) (*ActorState, error)
|
||||
StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error)
|
||||
|
||||
StateMinerSectors(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error)
|
||||
StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*ChainSectorInfo, error)
|
||||
StateMinerPower(context.Context, address.Address, *types.TipSet) (MinerPower, error)
|
||||
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
|
||||
StateMinerPeerID(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error)
|
||||
StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error)
|
||||
StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error)
|
||||
StatePledgeCollateral(context.Context, *types.TipSet) (types.BigInt, error)
|
||||
StateWaitMsg(context.Context, cid.Cid) (*MsgWait, error)
|
||||
StateListMiners(context.Context, *types.TipSet) ([]address.Address, error)
|
||||
StateListActors(context.Context, *types.TipSet) ([]address.Address, error)
|
||||
StateMarketBalance(context.Context, address.Address, *types.TipSet) (actors.StorageParticipantBalance, error)
|
||||
StateMarketParticipants(context.Context, *types.TipSet) (map[string]actors.StorageParticipantBalance, error)
|
||||
StateMarketDeals(context.Context, *types.TipSet) (map[string]actors.OnChainDeal, error)
|
||||
StateMarketStorageDeal(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error)
|
||||
StateLookupID(context.Context, address.Address, *types.TipSet) (address.Address, error)
|
||||
StateNetworkName(context.Context) (dtypes.NetworkName, error)
|
||||
StateMinerSectors(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*ChainSectorInfo, error)
|
||||
StateMinerProvingSet(context.Context, address.Address, types.TipSetKey) ([]*ChainSectorInfo, error)
|
||||
StateMinerProvingDeadline(context.Context, address.Address, types.TipSetKey) (*miner.DeadlineInfo, error)
|
||||
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*MinerPower, error)
|
||||
StateMinerInfo(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error)
|
||||
StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error)
|
||||
StateMinerFaults(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error)
|
||||
// Returns all non-expired Faults that occur within lookback epochs of the given tipset
|
||||
StateAllMinerFaults(ctx context.Context, lookback abi.ChainEpoch, ts types.TipSetKey) ([]*Fault, error)
|
||||
StateMinerRecoveries(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error)
|
||||
StateMinerInitialPledgeCollateral(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (types.BigInt, error)
|
||||
StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
|
||||
StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error)
|
||||
StateSectorGetInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorOnChainInfo, error)
|
||||
StatePledgeCollateral(context.Context, types.TipSetKey) (types.BigInt, error)
|
||||
StateWaitMsg(ctx context.Context, cid cid.Cid, confidence uint64) (*MsgLookup, error)
|
||||
StateSearchMsg(context.Context, cid.Cid) (*MsgLookup, error)
|
||||
StateListMiners(context.Context, types.TipSetKey) ([]address.Address, error)
|
||||
StateListActors(context.Context, types.TipSetKey) ([]address.Address, error)
|
||||
StateMarketBalance(context.Context, address.Address, types.TipSetKey) (MarketBalance, error)
|
||||
StateMarketParticipants(context.Context, types.TipSetKey) (map[string]MarketBalance, error)
|
||||
StateMarketDeals(context.Context, types.TipSetKey) (map[string]MarketDeal, error)
|
||||
StateMarketStorageDeal(context.Context, abi.DealID, types.TipSetKey) (*MarketDeal, error)
|
||||
StateLookupID(context.Context, address.Address, types.TipSetKey) (address.Address, error)
|
||||
StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error)
|
||||
StateChangedActors(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error)
|
||||
StateMinerSectorCount(context.Context, address.Address, *types.TipSet) (MinerSectors, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error)
|
||||
StateMinerSectorCount(context.Context, address.Address, types.TipSetKey) (MinerSectors, error)
|
||||
StateCompute(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*ComputeStateOutput, error)
|
||||
|
||||
MarketEnsureAvailable(context.Context, address.Address, types.BigInt) error
|
||||
// MethodGroup: Msig
|
||||
// The Msig methods are used to interact with multisig wallets on the
|
||||
// filecoin network
|
||||
|
||||
MsigGetAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
|
||||
MsigCreate(context.Context, int64, []address.Address, types.BigInt, address.Address, types.BigInt) (cid.Cid, error)
|
||||
MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
|
||||
MsigApprove(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
|
||||
MsigCancel(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
|
||||
|
||||
MarketEnsureAvailable(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error)
|
||||
// MarketFreeBalance
|
||||
|
||||
// MethodGroup: Paych
|
||||
// The Paych methods are for interacting with and managing payment channels
|
||||
|
||||
PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error)
|
||||
PaychList(context.Context) ([]address.Address, error)
|
||||
PaychStatus(context.Context, address.Address) (*PaychStatus, error)
|
||||
PaychClose(context.Context, address.Address) (cid.Cid, error)
|
||||
PaychAllocateLane(ctx context.Context, ch address.Address) (uint64, error)
|
||||
PaychNewPayment(ctx context.Context, from, to address.Address, vouchers []VoucherSpec) (*PaymentInfo, error)
|
||||
PaychVoucherCheckValid(context.Context, address.Address, *types.SignedVoucher) error
|
||||
PaychVoucherCheckSpendable(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, error)
|
||||
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*types.SignedVoucher, error)
|
||||
PaychVoucherAdd(context.Context, address.Address, *types.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
|
||||
PaychVoucherList(context.Context, address.Address) ([]*types.SignedVoucher, error)
|
||||
PaychVoucherSubmit(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error)
|
||||
PaychVoucherCheckValid(context.Context, address.Address, *paych.SignedVoucher) error
|
||||
PaychVoucherCheckSpendable(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (bool, error)
|
||||
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*paych.SignedVoucher, error)
|
||||
PaychVoucherAdd(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
|
||||
PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error)
|
||||
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher) (cid.Cid, error)
|
||||
}
|
||||
|
||||
type FileRef struct {
|
||||
Path string
|
||||
IsCAR bool
|
||||
}
|
||||
|
||||
type MinerSectors struct {
|
||||
Pset uint64
|
||||
Sset uint64
|
||||
Pset uint64
|
||||
}
|
||||
|
||||
type Import struct {
|
||||
@@ -147,17 +220,20 @@ type Import struct {
|
||||
|
||||
type DealInfo struct {
|
||||
ProposalCid cid.Cid
|
||||
State DealState
|
||||
State storagemarket.StorageDealStatus
|
||||
Message string // more information about deal state, particularly errors
|
||||
Provider address.Address
|
||||
|
||||
PieceRef []byte // cid bytes
|
||||
PieceCID cid.Cid
|
||||
Size uint64
|
||||
|
||||
PricePerEpoch types.BigInt
|
||||
Duration uint64
|
||||
|
||||
DealID abi.DealID
|
||||
}
|
||||
|
||||
type MsgWait struct {
|
||||
type MsgLookup struct {
|
||||
Receipt types.MessageReceipt
|
||||
TipSet *types.TipSet
|
||||
}
|
||||
@@ -175,9 +251,8 @@ type Message struct {
|
||||
}
|
||||
|
||||
type ChainSectorInfo struct {
|
||||
SectorID uint64
|
||||
CommD []byte
|
||||
CommR []byte
|
||||
Info miner.SectorOnChainInfo
|
||||
ID abi.SectorNumber
|
||||
}
|
||||
|
||||
type ActorState struct {
|
||||
@@ -206,20 +281,21 @@ type ChannelInfo struct {
|
||||
type PaymentInfo struct {
|
||||
Channel address.Address
|
||||
ChannelMessage *cid.Cid
|
||||
Vouchers []*types.SignedVoucher
|
||||
Vouchers []*paych.SignedVoucher
|
||||
}
|
||||
|
||||
type VoucherSpec struct {
|
||||
Amount types.BigInt
|
||||
TimeLock uint64
|
||||
MinClose uint64
|
||||
Amount types.BigInt
|
||||
TimeLockMin abi.ChainEpoch
|
||||
TimeLockMax abi.ChainEpoch
|
||||
MinSettle abi.ChainEpoch
|
||||
|
||||
Extra *types.ModVerifyParams
|
||||
Extra *paych.ModVerifyParams
|
||||
}
|
||||
|
||||
type MinerPower struct {
|
||||
MinerPower types.BigInt
|
||||
TotalPower types.BigInt
|
||||
MinerPower power.Claim
|
||||
TotalPower power.Claim
|
||||
}
|
||||
|
||||
type QueryOffer struct {
|
||||
@@ -227,42 +303,76 @@ type QueryOffer struct {
|
||||
|
||||
Root cid.Cid
|
||||
|
||||
Size uint64
|
||||
MinPrice types.BigInt
|
||||
|
||||
Miner address.Address
|
||||
MinerPeerID peer.ID
|
||||
Size uint64
|
||||
MinPrice types.BigInt
|
||||
PaymentInterval uint64
|
||||
PaymentIntervalIncrease uint64
|
||||
Miner address.Address
|
||||
MinerPeerID peer.ID
|
||||
}
|
||||
|
||||
func (o *QueryOffer) Order(client address.Address) RetrievalOrder {
|
||||
return RetrievalOrder{
|
||||
Root: o.Root,
|
||||
Size: o.Size,
|
||||
Total: o.MinPrice,
|
||||
|
||||
Client: client,
|
||||
Root: o.Root,
|
||||
Size: o.Size,
|
||||
Total: o.MinPrice,
|
||||
PaymentInterval: o.PaymentInterval,
|
||||
PaymentIntervalIncrease: o.PaymentIntervalIncrease,
|
||||
Client: client,
|
||||
|
||||
Miner: o.Miner,
|
||||
MinerPeerID: o.MinerPeerID,
|
||||
}
|
||||
}
|
||||
|
||||
type MarketBalance struct {
|
||||
Escrow big.Int
|
||||
Locked big.Int
|
||||
}
|
||||
|
||||
type MarketDeal struct {
|
||||
Proposal market.DealProposal
|
||||
State market.DealState
|
||||
}
|
||||
|
||||
type RetrievalOrder struct {
|
||||
// TODO: make this less unixfs specific
|
||||
Root cid.Cid
|
||||
Size uint64
|
||||
// TODO: support offset
|
||||
Total types.BigInt
|
||||
|
||||
Client address.Address
|
||||
Miner address.Address
|
||||
MinerPeerID peer.ID
|
||||
Total types.BigInt
|
||||
PaymentInterval uint64
|
||||
PaymentIntervalIncrease uint64
|
||||
Client address.Address
|
||||
Miner address.Address
|
||||
MinerPeerID peer.ID
|
||||
}
|
||||
|
||||
type ReplayResults struct {
|
||||
Msg *types.Message
|
||||
Receipt *types.MessageReceipt
|
||||
Error string
|
||||
type InvocResult struct {
|
||||
Msg *types.Message
|
||||
MsgRct *types.MessageReceipt
|
||||
InternalExecutions []*types.ExecutionResult
|
||||
Error string
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
type MethodCall struct {
|
||||
types.MessageReceipt
|
||||
Error string
|
||||
}
|
||||
|
||||
type StartDealParams struct {
|
||||
Data *storagemarket.DataRef
|
||||
Wallet address.Address
|
||||
Miner address.Address
|
||||
EpochPrice types.BigInt
|
||||
MinBlocksDuration uint64
|
||||
DealStartEpoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
type IpldObject struct {
|
||||
Cid cid.Cid
|
||||
Obj interface{}
|
||||
}
|
||||
|
||||
type ActiveSync struct {
|
||||
@@ -270,7 +380,7 @@ type ActiveSync struct {
|
||||
Target *types.TipSet
|
||||
|
||||
Stage SyncStateStage
|
||||
Height uint64
|
||||
Height abi.ChainEpoch
|
||||
|
||||
Start time.Time
|
||||
End time.Time
|
||||
@@ -303,3 +413,51 @@ type MpoolUpdate struct {
|
||||
Type MpoolChange
|
||||
Message *types.SignedMessage
|
||||
}
|
||||
|
||||
type ComputeStateOutput struct {
|
||||
Root cid.Cid
|
||||
Trace []*InvocResult
|
||||
}
|
||||
|
||||
type MiningBaseInfo struct {
|
||||
MinerPower types.BigInt
|
||||
NetworkPower types.BigInt
|
||||
Sectors []abi.SectorInfo
|
||||
WorkerKey address.Address
|
||||
SectorSize abi.SectorSize
|
||||
PrevBeaconEntry types.BeaconEntry
|
||||
BeaconEntries []types.BeaconEntry
|
||||
}
|
||||
|
||||
type BlockTemplate struct {
|
||||
Miner address.Address
|
||||
Parents types.TipSetKey
|
||||
Ticket *types.Ticket
|
||||
Eproof *types.ElectionProof
|
||||
BeaconValues []types.BeaconEntry
|
||||
Messages []*types.SignedMessage
|
||||
Epoch abi.ChainEpoch
|
||||
Timestamp uint64
|
||||
WinningPoStProof []abi.PoStProof
|
||||
}
|
||||
|
||||
type CommPRet struct {
|
||||
Root cid.Cid
|
||||
Size abi.UnpaddedPieceSize
|
||||
}
|
||||
type HeadChange struct {
|
||||
Type string
|
||||
Val *types.TipSet
|
||||
}
|
||||
|
||||
type MsigProposeResponse int
|
||||
|
||||
const (
|
||||
MsigApprove MsigProposeResponse = iota
|
||||
MsigCancel
|
||||
)
|
||||
|
||||
type Fault struct {
|
||||
Miner address.Address
|
||||
Epoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
+78
-69
@@ -1,112 +1,121 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/sector-storage/stores"
|
||||
"github.com/filecoin-project/sector-storage/storiface"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
)
|
||||
|
||||
// alias because cbor-gen doesn't like non-alias types
|
||||
type SectorState = uint64
|
||||
|
||||
const (
|
||||
UndefinedSectorState SectorState = iota
|
||||
|
||||
Empty // TODO: Is this useful
|
||||
Packing // sector not in sealStore, and not on chain
|
||||
|
||||
Unsealed // sealing / queued
|
||||
PreCommitting // on chain pre-commit
|
||||
PreCommitted // waiting for seed
|
||||
Committing
|
||||
CommitWait // waiting for message to land on chain
|
||||
Proving
|
||||
|
||||
SealFailed
|
||||
PreCommitFailed
|
||||
SealCommitFailed
|
||||
CommitFailed
|
||||
|
||||
FailedUnrecoverable
|
||||
|
||||
Faulty // sector is corrupted or gone for some reason
|
||||
FaultReported // sector has been declared as a fault on chain
|
||||
FaultedFinal // fault declared on chain
|
||||
)
|
||||
|
||||
var SectorStates = []string{
|
||||
UndefinedSectorState: "UndefinedSectorState",
|
||||
Empty: "Empty",
|
||||
Packing: "Packing",
|
||||
Unsealed: "Unsealed",
|
||||
PreCommitting: "PreCommitting",
|
||||
PreCommitted: "PreCommitted",
|
||||
Committing: "Committing",
|
||||
CommitWait: "CommitWait",
|
||||
Proving: "Proving",
|
||||
|
||||
SealFailed: "SealFailed",
|
||||
PreCommitFailed: "PreCommitFailed",
|
||||
SealCommitFailed: "SealCommitFailed",
|
||||
CommitFailed: "CommitFailed",
|
||||
|
||||
FailedUnrecoverable: "FailedUnrecoverable",
|
||||
|
||||
Faulty: "Faulty",
|
||||
FaultReported: "FaultReported",
|
||||
FaultedFinal: "FaultedFinal",
|
||||
}
|
||||
|
||||
// StorageMiner is a low-level interface to the Filecoin network storage miner node
|
||||
type StorageMiner interface {
|
||||
Common
|
||||
|
||||
ActorAddress(context.Context) (address.Address, error)
|
||||
|
||||
ActorSectorSize(context.Context, address.Address) (uint64, error)
|
||||
ActorSectorSize(context.Context, address.Address) (abi.SectorSize, error)
|
||||
|
||||
MiningBase(context.Context) (*types.TipSet, error)
|
||||
|
||||
// Temp api for testing
|
||||
PledgeSector(context.Context) error
|
||||
|
||||
// Get the status of a given sector by ID
|
||||
SectorsStatus(context.Context, uint64) (SectorInfo, error)
|
||||
SectorsStatus(context.Context, abi.SectorNumber) (SectorInfo, error)
|
||||
|
||||
// List all staged sectors
|
||||
SectorsList(context.Context) ([]uint64, error)
|
||||
SectorsList(context.Context) ([]abi.SectorNumber, error)
|
||||
|
||||
SectorsRefs(context.Context) (map[string][]SealedRef, error)
|
||||
|
||||
SectorsUpdate(context.Context, uint64, SectorState) error
|
||||
SectorsUpdate(context.Context, abi.SectorNumber, SectorState) error
|
||||
|
||||
WorkerStats(context.Context) (sectorbuilder.WorkerStats, error)
|
||||
StorageList(ctx context.Context) (map[stores.ID][]stores.Decl, error)
|
||||
StorageLocal(ctx context.Context) (map[stores.ID]string, error)
|
||||
StorageStat(ctx context.Context, id stores.ID) (stores.FsStat, error)
|
||||
|
||||
// WorkerQueue registers a remote worker
|
||||
WorkerQueue(context.Context, sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error)
|
||||
// WorkerConnect tells the node to connect to workers RPC
|
||||
WorkerConnect(context.Context, string) error
|
||||
WorkerStats(context.Context) (map[uint64]storiface.WorkerStats, error)
|
||||
|
||||
WorkerDone(ctx context.Context, task uint64, res sectorbuilder.SealRes) error
|
||||
stores.SectorIndex
|
||||
|
||||
MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error
|
||||
MarketListDeals(ctx context.Context) ([]storagemarket.StorageDeal, error)
|
||||
MarketListIncompleteDeals(ctx context.Context) ([]storagemarket.MinerDeal, error)
|
||||
MarketSetPrice(context.Context, types.BigInt) error
|
||||
|
||||
DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error
|
||||
DealsList(ctx context.Context) ([]storagemarket.StorageDeal, error)
|
||||
|
||||
StorageAddLocal(ctx context.Context, path string) error
|
||||
}
|
||||
|
||||
type SealRes struct {
|
||||
Err string
|
||||
GoErr error `json:"-"`
|
||||
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
type SectorLog struct {
|
||||
Kind string
|
||||
Timestamp uint64
|
||||
|
||||
Trace string
|
||||
|
||||
Message string
|
||||
}
|
||||
|
||||
type SectorInfo struct {
|
||||
SectorID uint64
|
||||
SectorID abi.SectorNumber
|
||||
State SectorState
|
||||
CommD []byte
|
||||
CommR []byte
|
||||
CommD *cid.Cid
|
||||
CommR *cid.Cid
|
||||
Proof []byte
|
||||
Deals []uint64
|
||||
Ticket sectorbuilder.SealTicket
|
||||
Seed sectorbuilder.SealSeed
|
||||
Deals []abi.DealID
|
||||
Ticket SealTicket
|
||||
Seed SealSeed
|
||||
Retries uint64
|
||||
|
||||
LastErr string
|
||||
|
||||
Log []SectorLog
|
||||
}
|
||||
|
||||
type SealedRef struct {
|
||||
SectorID uint64
|
||||
SectorID abi.SectorNumber
|
||||
Offset uint64
|
||||
Size uint64
|
||||
Size abi.UnpaddedPieceSize
|
||||
}
|
||||
|
||||
type SealedRefs struct {
|
||||
Refs []SealedRef
|
||||
}
|
||||
|
||||
type SealTicket struct {
|
||||
Value abi.SealRandomness
|
||||
Epoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
type SealSeed struct {
|
||||
Value abi.InteractiveSealRandomness
|
||||
Epoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
func (st *SealTicket) Equals(ost *SealTicket) bool {
|
||||
return bytes.Equal(st.Value, ost.Value) && st.Epoch == ost.Epoch
|
||||
}
|
||||
|
||||
func (st *SealSeed) Equals(ost *SealSeed) bool {
|
||||
return bytes.Equal(st.Value, ost.Value) && st.Epoch == ost.Epoch
|
||||
}
|
||||
|
||||
type SectorState string
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func goCmd() string {
|
||||
var exeSuffix string
|
||||
if runtime.GOOS == "windows" {
|
||||
exeSuffix = ".exe"
|
||||
}
|
||||
path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return path
|
||||
}
|
||||
return "go"
|
||||
}
|
||||
|
||||
func TestDoesntDependOnFFI(t *testing.T) {
|
||||
deps, err := exec.Command(goCmd(), "list", "-deps", "github.com/filecoin-project/lotus/api").Output()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, pkg := range strings.Fields(string(deps)) {
|
||||
if pkg == "github.com/filecoin-project/filecoin-ffi" {
|
||||
t.Fatal("api depends on filecoin-ffi")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/sector-storage/sealtasks"
|
||||
"github.com/filecoin-project/sector-storage/stores"
|
||||
"github.com/filecoin-project/sector-storage/storiface"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
)
|
||||
|
||||
type WorkerAPI interface {
|
||||
Version(context.Context) (build.Version, error)
|
||||
// TODO: Info() (name, ...) ?
|
||||
|
||||
TaskTypes(context.Context) (map[sealtasks.TaskType]struct{}, error) // TaskType -> Weight
|
||||
Paths(context.Context) ([]stores.StoragePath, error)
|
||||
Info(context.Context) (storiface.WorkerInfo, error)
|
||||
|
||||
storage.Sealer
|
||||
|
||||
MoveStorage(ctx context.Context, sector abi.SectorID) error
|
||||
|
||||
UnsealPiece(context.Context, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize, abi.SealRandomness, cid.Cid) error
|
||||
ReadPiece(context.Context, io.Writer, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) error
|
||||
|
||||
Fetch(context.Context, abi.SectorID, stores.SectorFileType, stores.PathType, stores.AcquireMode) error
|
||||
|
||||
Closing(context.Context) (<-chan struct{}, error)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package apibstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-cid"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ChainIO interface {
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
ChainHasObj(context.Context, cid.Cid) (bool, error)
|
||||
}
|
||||
|
||||
type apiBStore struct {
|
||||
api ChainIO
|
||||
}
|
||||
|
||||
func NewAPIBlockstore(cio ChainIO) blockstore.Blockstore {
|
||||
return &apiBStore{
|
||||
api: cio,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *apiBStore) DeleteBlock(cid.Cid) error {
|
||||
return xerrors.New("not supported")
|
||||
}
|
||||
|
||||
func (a *apiBStore) Has(c cid.Cid) (bool, error) {
|
||||
return a.api.ChainHasObj(context.TODO(), c)
|
||||
}
|
||||
|
||||
func (a *apiBStore) Get(c cid.Cid) (blocks.Block, error) {
|
||||
bb, err := a.api.ChainReadObj(context.TODO(), c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blocks.NewBlockWithCid(bb, c)
|
||||
}
|
||||
|
||||
func (a *apiBStore) GetSize(c cid.Cid) (int, error) {
|
||||
bb, err := a.api.ChainReadObj(context.TODO(), c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(bb), nil
|
||||
}
|
||||
|
||||
func (a *apiBStore) Put(blocks.Block) error {
|
||||
return xerrors.New("not supported")
|
||||
}
|
||||
|
||||
func (a *apiBStore) PutMany([]blocks.Block) error {
|
||||
return xerrors.New("not supported")
|
||||
}
|
||||
|
||||
func (a *apiBStore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) {
|
||||
return nil, xerrors.New("not supported")
|
||||
}
|
||||
|
||||
func (a *apiBStore) HashOnRead(enabled bool) {
|
||||
return
|
||||
}
|
||||
|
||||
var _ blockstore.Blockstore = &apiBStore{}
|
||||
@@ -1,105 +1,38 @@
|
||||
package apistruct
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
)
|
||||
|
||||
type permKey int
|
||||
|
||||
var permCtxKey permKey
|
||||
|
||||
const (
|
||||
// When changing these, update docs/API.md too
|
||||
|
||||
PermRead api.Permission = "read" // default
|
||||
PermWrite api.Permission = "write"
|
||||
PermSign api.Permission = "sign" // Use wallet keys for signing
|
||||
PermAdmin api.Permission = "admin" // Manage permissions
|
||||
PermRead auth.Permission = "read" // default
|
||||
PermWrite auth.Permission = "write"
|
||||
PermSign auth.Permission = "sign" // Use wallet keys for signing
|
||||
PermAdmin auth.Permission = "admin" // Manage permissions
|
||||
)
|
||||
|
||||
var AllPermissions = []api.Permission{PermRead, PermWrite, PermSign, PermAdmin}
|
||||
var defaultPerms = []api.Permission{PermRead}
|
||||
|
||||
func WithPerm(ctx context.Context, perms []api.Permission) context.Context {
|
||||
return context.WithValue(ctx, permCtxKey, perms)
|
||||
}
|
||||
var AllPermissions = []auth.Permission{PermRead, PermWrite, PermSign, PermAdmin}
|
||||
var DefaultPerms = []auth.Permission{PermRead}
|
||||
|
||||
func PermissionedStorMinerAPI(a api.StorageMiner) api.StorageMiner {
|
||||
var out StorageMinerStruct
|
||||
permissionedAny(a, &out.Internal)
|
||||
permissionedAny(a, &out.CommonStruct.Internal)
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.CommonStruct.Internal)
|
||||
return &out
|
||||
}
|
||||
|
||||
func PermissionedFullAPI(a api.FullNode) api.FullNode {
|
||||
var out FullNodeStruct
|
||||
permissionedAny(a, &out.Internal)
|
||||
permissionedAny(a, &out.CommonStruct.Internal)
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.CommonStruct.Internal)
|
||||
return &out
|
||||
}
|
||||
|
||||
func HasPerm(ctx context.Context, perm api.Permission) bool {
|
||||
callerPerms, ok := ctx.Value(permCtxKey).([]api.Permission)
|
||||
if !ok {
|
||||
callerPerms = defaultPerms
|
||||
}
|
||||
|
||||
for _, callerPerm := range callerPerms {
|
||||
if callerPerm == perm {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func permissionedAny(in interface{}, out interface{}) {
|
||||
rint := reflect.ValueOf(out).Elem()
|
||||
ra := reflect.ValueOf(in)
|
||||
|
||||
for f := 0; f < rint.NumField(); f++ {
|
||||
field := rint.Type().Field(f)
|
||||
requiredPerm := api.Permission(field.Tag.Get("perm"))
|
||||
if requiredPerm == "" {
|
||||
panic("missing 'perm' tag on " + field.Name) // ok
|
||||
}
|
||||
|
||||
// Validate perm tag
|
||||
ok := false
|
||||
for _, perm := range AllPermissions {
|
||||
if requiredPerm == perm {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
panic("unknown 'perm' tag on " + field.Name) // ok
|
||||
}
|
||||
|
||||
fn := ra.MethodByName(field.Name)
|
||||
|
||||
rint.Field(f).Set(reflect.MakeFunc(field.Type, func(args []reflect.Value) (results []reflect.Value) {
|
||||
ctx := args[0].Interface().(context.Context)
|
||||
if HasPerm(ctx, requiredPerm) {
|
||||
return fn.Call(args)
|
||||
}
|
||||
|
||||
err := xerrors.Errorf("missing permission to invoke '%s' (need '%s')", field.Name, requiredPerm)
|
||||
rerr := reflect.ValueOf(&err).Elem()
|
||||
|
||||
if field.Type.NumOut() == 2 {
|
||||
return []reflect.Value{
|
||||
reflect.Zero(field.Type.Out(0)),
|
||||
rerr,
|
||||
}
|
||||
} else {
|
||||
return []reflect.Value{rerr}
|
||||
}
|
||||
}))
|
||||
|
||||
}
|
||||
func PermissionedWorkerAPI(a api.WorkerAPI) api.WorkerAPI {
|
||||
var out WorkerStruct
|
||||
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
|
||||
return &out
|
||||
}
|
||||
|
||||
+531
-167
@@ -2,18 +2,30 @@ package apistruct
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
"io"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
|
||||
"github.com/filecoin-project/sector-storage/sealtasks"
|
||||
"github.com/filecoin-project/sector-storage/stores"
|
||||
"github.com/filecoin-project/sector-storage/storiface"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
// All permissions are listed in permissioned.go
|
||||
@@ -21,17 +33,24 @@ var _ = AllPermissions
|
||||
|
||||
type CommonStruct struct {
|
||||
Internal struct {
|
||||
AuthVerify func(ctx context.Context, token string) ([]api.Permission, error) `perm:"read"`
|
||||
AuthNew func(ctx context.Context, perms []api.Permission) ([]byte, error) `perm:"admin"`
|
||||
AuthVerify func(ctx context.Context, token string) ([]auth.Permission, error) `perm:"read"`
|
||||
AuthNew func(ctx context.Context, perms []auth.Permission) ([]byte, error) `perm:"admin"`
|
||||
|
||||
NetConnectedness func(context.Context, peer.ID) (network.Connectedness, error) `perm:"read"`
|
||||
NetPeers func(context.Context) ([]peer.AddrInfo, error) `perm:"read"`
|
||||
NetConnect func(context.Context, peer.AddrInfo) error `perm:"write"`
|
||||
NetAddrsListen func(context.Context) (peer.AddrInfo, error) `perm:"read"`
|
||||
NetDisconnect func(context.Context, peer.ID) error `perm:"write"`
|
||||
NetFindPeer func(context.Context, peer.ID) (peer.AddrInfo, error) `perm:"read"`
|
||||
NetPubsubScores func(context.Context) ([]api.PubsubScore, error) `perm:"read"`
|
||||
|
||||
ID func(context.Context) (peer.ID, error) `perm:"read"`
|
||||
Version func(context.Context) (api.Version, error) `perm:"read"`
|
||||
|
||||
LogList func(context.Context) ([]string, error) `perm:"write"`
|
||||
LogSetLevel func(context.Context, string, string) error `perm:"write"`
|
||||
|
||||
Shutdown func(context.Context) error `perm:"admin"`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,79 +59,108 @@ type FullNodeStruct struct {
|
||||
CommonStruct
|
||||
|
||||
Internal struct {
|
||||
ChainNotify func(context.Context) (<-chan []*store.HeadChange, error) `perm:"read"`
|
||||
ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainGetRandomness func(context.Context, types.TipSetKey, int64) ([]byte, error) `perm:"read"`
|
||||
ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"`
|
||||
ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
ChainGetBlockMessages func(context.Context, cid.Cid) (*api.BlockMessages, error) `perm:"read"`
|
||||
ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
|
||||
ChainGetParentMessages func(context.Context, cid.Cid) ([]api.Message, error) `perm:"read"`
|
||||
ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"`
|
||||
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
|
||||
ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"`
|
||||
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainTipSetWeight func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"`
|
||||
ChainGetNode func(ctx context.Context, p string) (interface{}, error) `perm:"read"`
|
||||
ChainNotify func(context.Context) (<-chan []*api.HeadChange, error) `perm:"read"`
|
||||
ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainGetRandomness func(context.Context, types.TipSetKey, crypto.DomainSeparationTag, abi.ChainEpoch, []byte) (abi.Randomness, error) `perm:"read"`
|
||||
ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"`
|
||||
ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
ChainGetBlockMessages func(context.Context, cid.Cid) (*api.BlockMessages, error) `perm:"read"`
|
||||
ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
|
||||
ChainGetParentMessages func(context.Context, cid.Cid) ([]api.Message, error) `perm:"read"`
|
||||
ChainGetTipSetByHeight func(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
|
||||
ChainHasObj func(context.Context, cid.Cid) (bool, error) `perm:"read"`
|
||||
ChainStatObj func(context.Context, cid.Cid, cid.Cid) (api.ObjStat, error) `perm:"read"`
|
||||
ChainSetHead func(context.Context, types.TipSetKey) error `perm:"admin"`
|
||||
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
ChainTipSetWeight func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
ChainGetNode func(ctx context.Context, p string) (*api.IpldObject, error) `perm:"read"`
|
||||
ChainGetMessage func(context.Context, cid.Cid) (*types.Message, error) `perm:"read"`
|
||||
ChainGetPath func(context.Context, types.TipSetKey, types.TipSetKey) ([]*api.HeadChange, error) `perm:"read"`
|
||||
ChainExport func(context.Context, types.TipSetKey) (<-chan []byte, error) `perm:"read"`
|
||||
|
||||
SyncState func(context.Context) (*api.SyncState, error) `perm:"read"`
|
||||
SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"`
|
||||
SyncIncomingBlocks func(ctx context.Context) (<-chan *types.BlockHeader, error) `perm:"read"`
|
||||
SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
|
||||
SyncCheckBad func(ctx context.Context, bcid cid.Cid) (string, error) `perm:"read"`
|
||||
|
||||
MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"`
|
||||
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
MpoolSub func(context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"`
|
||||
MpoolPending func(context.Context, types.TipSetKey) ([]*types.SignedMessage, error) `perm:"read"`
|
||||
MpoolPush func(context.Context, *types.SignedMessage) (cid.Cid, error) `perm:"write"`
|
||||
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
MpoolSub func(context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"`
|
||||
MpoolEstimateGasPrice func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
|
||||
MinerCreateBlock func(context.Context, address.Address, *types.TipSet, *types.Ticket, *types.EPostProof, []*types.SignedMessage, uint64, uint64) (*types.BlockMsg, error) `perm:"write"`
|
||||
MinerGetBaseInfo func(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"`
|
||||
MinerCreateBlock func(context.Context, *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"`
|
||||
|
||||
WalletNew func(context.Context, string) (address.Address, error) `perm:"write"`
|
||||
WalletNew func(context.Context, crypto.SigType) (address.Address, error) `perm:"write"`
|
||||
WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"`
|
||||
WalletList func(context.Context) ([]address.Address, error) `perm:"write"`
|
||||
WalletBalance func(context.Context, address.Address) (types.BigInt, error) `perm:"read"`
|
||||
WalletSign func(context.Context, address.Address, []byte) (*types.Signature, error) `perm:"sign"`
|
||||
WalletSign func(context.Context, address.Address, []byte) (*crypto.Signature, error) `perm:"sign"`
|
||||
WalletSignMessage func(context.Context, address.Address, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
|
||||
WalletVerify func(context.Context, address.Address, []byte, *crypto.Signature) bool `perm:"read"`
|
||||
WalletDefaultAddress func(context.Context) (address.Address, error) `perm:"write"`
|
||||
WalletSetDefault func(context.Context, address.Address) error `perm:"admin"`
|
||||
WalletExport func(context.Context, address.Address) (*types.KeyInfo, error) `perm:"admin"`
|
||||
WalletImport func(context.Context, *types.KeyInfo) (address.Address, error) `perm:"admin"`
|
||||
|
||||
ClientImport func(ctx context.Context, path string) (cid.Cid, error) `perm:"admin"`
|
||||
ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"`
|
||||
ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"`
|
||||
ClientFindData func(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) `perm:"read"`
|
||||
ClientStartDeal func(ctx context.Context, data cid.Cid, addr address.Address, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) `perm:"admin"`
|
||||
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
|
||||
ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
|
||||
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, path string) error `perm:"admin"`
|
||||
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error) `perm:"read"`
|
||||
ClientImport func(ctx context.Context, ref api.FileRef) (cid.Cid, error) `perm:"admin"`
|
||||
ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"`
|
||||
ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"`
|
||||
ClientFindData func(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) `perm:"read"`
|
||||
ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
|
||||
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
|
||||
ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
|
||||
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error `perm:"admin"`
|
||||
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
|
||||
ClientCalcCommP func(ctx context.Context, inpath string, miner address.Address) (*api.CommPRet, error) `perm:"read"`
|
||||
ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"`
|
||||
|
||||
StateMinerSectors func(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerProvingSet func(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerPower func(context.Context, address.Address, *types.TipSet) (api.MinerPower, error) `perm:"read"`
|
||||
StateMinerWorker func(context.Context, address.Address, *types.TipSet) (address.Address, error) `perm:"read"`
|
||||
StateMinerPeerID func(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) `perm:"read"`
|
||||
StateMinerElectionPeriodStart func(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) `perm:"read"`
|
||||
StateMinerSectorSize func(context.Context, address.Address, *types.TipSet) (uint64, error) `perm:"read"`
|
||||
StateCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateReplay func(context.Context, *types.TipSet, cid.Cid) (*api.ReplayResults, error) `perm:"read"`
|
||||
StateGetActor func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) `perm:"read"`
|
||||
StateReadState func(context.Context, *types.Actor, *types.TipSet) (*api.ActorState, error) `perm:"read"`
|
||||
StatePledgeCollateral func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"`
|
||||
StateWaitMsg func(context.Context, cid.Cid) (*api.MsgWait, error) `perm:"read"`
|
||||
StateListMiners func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"`
|
||||
StateListActors func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"`
|
||||
StateMarketBalance func(context.Context, address.Address, *types.TipSet) (actors.StorageParticipantBalance, error) `perm:"read"`
|
||||
StateMarketParticipants func(context.Context, *types.TipSet) (map[string]actors.StorageParticipantBalance, error) `perm:"read"`
|
||||
StateMarketDeals func(context.Context, *types.TipSet) (map[string]actors.OnChainDeal, error) `perm:"read"`
|
||||
StateMarketStorageDeal func(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error) `perm:"read"`
|
||||
StateLookupID func(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) `perm:"read"`
|
||||
StateChangedActors func(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error) `perm:"read"`
|
||||
StateGetReceipt func(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateMinerSectorCount func(context.Context, address.Address, *types.TipSet) (api.MinerSectors, error) `perm:"read"`
|
||||
StateNetworkName func(context.Context) (dtypes.NetworkName, error) `perm:"read"`
|
||||
StateMinerSectors func(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerProvingSet func(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
|
||||
StateMinerProvingDeadline func(context.Context, address.Address, types.TipSetKey) (*miner.DeadlineInfo, error) `perm:"read"`
|
||||
StateMinerPower func(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error) `perm:"read"`
|
||||
StateMinerInfo func(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error) `perm:"read"`
|
||||
StateMinerDeadlines func(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error) `perm:"read"`
|
||||
StateMinerFaults func(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error) `perm:"read"`
|
||||
StateAllMinerFaults func(context.Context, abi.ChainEpoch, types.TipSetKey) ([]*api.Fault, error) `perm:"read"`
|
||||
StateMinerRecoveries func(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error) `perm:"read"`
|
||||
StateMinerInitialPledgeCollateral func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateMinerAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateSectorPreCommitInfo func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) `perm:"read"`
|
||||
StateSectorGetInfo func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorOnChainInfo, error) `perm:"read"`
|
||||
StateCall func(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) `perm:"read"`
|
||||
StateReplay func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"`
|
||||
StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"`
|
||||
StateReadState func(context.Context, *types.Actor, types.TipSetKey) (*api.ActorState, error) `perm:"read"`
|
||||
StatePledgeCollateral func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
StateWaitMsg func(ctx context.Context, cid cid.Cid, confidence uint64) (*api.MsgLookup, error) `perm:"read"`
|
||||
StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"`
|
||||
StateListMiners func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
|
||||
StateListActors func(context.Context, types.TipSetKey) ([]address.Address, error) `perm:"read"`
|
||||
StateMarketBalance func(context.Context, address.Address, types.TipSetKey) (api.MarketBalance, error) `perm:"read"`
|
||||
StateMarketParticipants func(context.Context, types.TipSetKey) (map[string]api.MarketBalance, error) `perm:"read"`
|
||||
StateMarketDeals func(context.Context, types.TipSetKey) (map[string]api.MarketDeal, error) `perm:"read"`
|
||||
StateMarketStorageDeal func(context.Context, abi.DealID, types.TipSetKey) (*api.MarketDeal, error) `perm:"read"`
|
||||
StateLookupID func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) `perm:"read"`
|
||||
StateAccountKey func(context.Context, address.Address, types.TipSetKey) (address.Address, error) `perm:"read"`
|
||||
StateChangedActors func(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error) `perm:"read"`
|
||||
StateGetReceipt func(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) `perm:"read"`
|
||||
StateMinerSectorCount func(context.Context, address.Address, types.TipSetKey) (api.MinerSectors, error) `perm:"read"`
|
||||
StateListMessages func(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) `perm:"read"`
|
||||
StateCompute func(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*api.ComputeStateOutput, error) `perm:"read"`
|
||||
|
||||
MarketEnsureAvailable func(context.Context, address.Address, types.BigInt) error `perm:"sign"`
|
||||
MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
|
||||
MsigCreate func(context.Context, int64, []address.Address, types.BigInt, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"`
|
||||
MsigPropose func(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
|
||||
MsigApprove func(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
|
||||
MsigCancel func(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) `perm:"sign"`
|
||||
|
||||
MarketEnsureAvailable func(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error) `perm:"sign"`
|
||||
|
||||
PaychGet func(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*api.ChannelInfo, error) `perm:"sign"`
|
||||
PaychList func(context.Context) ([]address.Address, error) `perm:"read"`
|
||||
@@ -120,49 +168,102 @@ type FullNodeStruct struct {
|
||||
PaychClose func(context.Context, address.Address) (cid.Cid, error) `perm:"sign"`
|
||||
PaychAllocateLane func(context.Context, address.Address) (uint64, error) `perm:"sign"`
|
||||
PaychNewPayment func(ctx context.Context, from, to address.Address, vouchers []api.VoucherSpec) (*api.PaymentInfo, error) `perm:"sign"`
|
||||
PaychVoucherCheck func(context.Context, *types.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckValid func(context.Context, address.Address, *types.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckSpendable func(context.Context, address.Address, *types.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"`
|
||||
PaychVoucherAdd func(context.Context, address.Address, *types.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
|
||||
PaychVoucherCreate func(context.Context, address.Address, types.BigInt, uint64) (*types.SignedVoucher, error) `perm:"sign"`
|
||||
PaychVoucherList func(context.Context, address.Address) ([]*types.SignedVoucher, error) `perm:"write"`
|
||||
PaychVoucherSubmit func(context.Context, address.Address, *types.SignedVoucher) (cid.Cid, error) `perm:"sign"`
|
||||
PaychVoucherCheck func(context.Context, *paych.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckValid func(context.Context, address.Address, *paych.SignedVoucher) error `perm:"read"`
|
||||
PaychVoucherCheckSpendable func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (bool, error) `perm:"read"`
|
||||
PaychVoucherAdd func(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
|
||||
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*paych.SignedVoucher, error) `perm:"sign"`
|
||||
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
|
||||
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher) (cid.Cid, error) `perm:"sign"`
|
||||
}
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerSectorCount(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MinerSectors, error) {
|
||||
return c.Internal.StateMinerSectorCount(ctx, addr, ts)
|
||||
func (c *FullNodeStruct) StateMinerSectorCount(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MinerSectors, error) {
|
||||
return c.Internal.StateMinerSectorCount(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
type StorageMinerStruct struct {
|
||||
CommonStruct
|
||||
|
||||
Internal struct {
|
||||
ActorAddress func(context.Context) (address.Address, error) `perm:"read"`
|
||||
ActorSectorSize func(context.Context, address.Address) (uint64, error) `perm:"read"`
|
||||
ActorAddress func(context.Context) (address.Address, error) `perm:"read"`
|
||||
ActorSectorSize func(context.Context, address.Address) (abi.SectorSize, error) `perm:"read"`
|
||||
|
||||
MiningBase func(context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
|
||||
MarketImportDealData func(context.Context, cid.Cid, string) error `perm:"write"`
|
||||
MarketListDeals func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"`
|
||||
MarketListIncompleteDeals func(ctx context.Context) ([]storagemarket.MinerDeal, error) `perm:"read"`
|
||||
MarketSetPrice func(context.Context, types.BigInt) error `perm:"admin"`
|
||||
|
||||
PledgeSector func(context.Context) error `perm:"write"`
|
||||
|
||||
SectorsStatus func(context.Context, uint64) (api.SectorInfo, error) `perm:"read"`
|
||||
SectorsList func(context.Context) ([]uint64, error) `perm:"read"`
|
||||
SectorsRefs func(context.Context) (map[string][]api.SealedRef, error) `perm:"read"`
|
||||
SectorsUpdate func(context.Context, uint64, api.SectorState) error `perm:"write"`
|
||||
SectorsStatus func(context.Context, abi.SectorNumber) (api.SectorInfo, error) `perm:"read"`
|
||||
SectorsList func(context.Context) ([]abi.SectorNumber, error) `perm:"read"`
|
||||
SectorsRefs func(context.Context) (map[string][]api.SealedRef, error) `perm:"read"`
|
||||
SectorsUpdate func(context.Context, abi.SectorNumber, api.SectorState) error `perm:"write"`
|
||||
|
||||
WorkerStats func(context.Context) (sectorbuilder.WorkerStats, error) `perm:"read"`
|
||||
WorkerConnect func(context.Context, string) error `perm:"admin"` // TODO: worker perm
|
||||
WorkerStats func(context.Context) (map[uint64]storiface.WorkerStats, error) `perm:"admin"`
|
||||
|
||||
WorkerQueue func(ctx context.Context, cfg sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error) `perm:"admin"` // TODO: worker perm
|
||||
WorkerDone func(ctx context.Context, task uint64, res sectorbuilder.SealRes) error `perm:"admin"`
|
||||
StorageList func(context.Context) (map[stores.ID][]stores.Decl, error) `perm:"admin"`
|
||||
StorageLocal func(context.Context) (map[stores.ID]string, error) `perm:"admin"`
|
||||
StorageStat func(context.Context, stores.ID) (stores.FsStat, error) `perm:"admin"`
|
||||
StorageAttach func(context.Context, stores.StorageInfo, stores.FsStat) error `perm:"admin"`
|
||||
StorageDeclareSector func(context.Context, stores.ID, abi.SectorID, stores.SectorFileType, bool) error `perm:"admin"`
|
||||
StorageDropSector func(context.Context, stores.ID, abi.SectorID, stores.SectorFileType) error `perm:"admin"`
|
||||
StorageFindSector func(context.Context, abi.SectorID, stores.SectorFileType, bool) ([]stores.SectorStorageInfo, error) `perm:"admin"`
|
||||
StorageInfo func(context.Context, stores.ID) (stores.StorageInfo, error) `perm:"admin"`
|
||||
StorageBestAlloc func(ctx context.Context, allocate stores.SectorFileType, spt abi.RegisteredProof, sealing stores.PathType) ([]stores.StorageInfo, error) `perm:"admin"`
|
||||
StorageReportHealth func(ctx context.Context, id stores.ID, report stores.HealthReport) error `perm:"admin"`
|
||||
StorageLock func(ctx context.Context, sector abi.SectorID, read stores.SectorFileType, write stores.SectorFileType) error `perm:"admin"`
|
||||
|
||||
DealsImportData func(ctx context.Context, dealPropCid cid.Cid, file string) error `perm:"write"`
|
||||
DealsList func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"`
|
||||
|
||||
StorageAddLocal func(ctx context.Context, path string) error `perm:"admin"`
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]api.Permission, error) {
|
||||
type WorkerStruct struct {
|
||||
Internal struct {
|
||||
// TODO: lower perms
|
||||
|
||||
Version func(context.Context) (build.Version, error) `perm:"admin"`
|
||||
|
||||
TaskTypes func(context.Context) (map[sealtasks.TaskType]struct{}, error) `perm:"admin"`
|
||||
Paths func(context.Context) ([]stores.StoragePath, error) `perm:"admin"`
|
||||
Info func(context.Context) (storiface.WorkerInfo, error) `perm:"admin"`
|
||||
|
||||
SealPreCommit1 func(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, pieces []abi.PieceInfo) (storage.PreCommit1Out, error) `perm:"admin"`
|
||||
SealPreCommit2 func(context.Context, abi.SectorID, storage.PreCommit1Out) (cids storage.SectorCids, err error) `perm:"admin"`
|
||||
SealCommit1 func(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (storage.Commit1Out, error) `perm:"admin"`
|
||||
SealCommit2 func(context.Context, abi.SectorID, storage.Commit1Out) (storage.Proof, error) `perm:"admin"`
|
||||
FinalizeSector func(context.Context, abi.SectorID) error `perm:"admin"`
|
||||
MoveStorage func(ctx context.Context, sector abi.SectorID) error `perm:"admin"`
|
||||
|
||||
UnsealPiece func(context.Context, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize, abi.SealRandomness, cid.Cid) error `perm:"admin"`
|
||||
ReadPiece func(context.Context, io.Writer, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) error `perm:"admin"`
|
||||
|
||||
Fetch func(context.Context, abi.SectorID, stores.SectorFileType, stores.PathType, stores.AcquireMode) error `perm:"admin"`
|
||||
|
||||
Closing func(context.Context) (<-chan struct{}, error) `perm:"admin"`
|
||||
}
|
||||
}
|
||||
|
||||
// CommonStruct
|
||||
|
||||
func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) {
|
||||
return c.Internal.AuthVerify(ctx, token)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) AuthNew(ctx context.Context, perms []api.Permission) ([]byte, error) {
|
||||
func (c *CommonStruct) AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error) {
|
||||
return c.Internal.AuthNew(ctx, perms)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) NetPubsubScores(ctx context.Context) ([]api.PubsubScore, error) {
|
||||
return c.Internal.NetPubsubScores(ctx)
|
||||
}
|
||||
func (c *CommonStruct) NetConnectedness(ctx context.Context, pid peer.ID) (network.Connectedness, error) {
|
||||
return c.Internal.NetConnectedness(ctx, pid)
|
||||
}
|
||||
@@ -183,6 +284,10 @@ func (c *CommonStruct) NetDisconnect(ctx context.Context, p peer.ID) error {
|
||||
return c.Internal.NetDisconnect(ctx, p)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) NetFindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, error) {
|
||||
return c.Internal.NetFindPeer(ctx, p)
|
||||
}
|
||||
|
||||
// ID implements API.ID
|
||||
func (c *CommonStruct) ID(ctx context.Context) (peer.ID, error) {
|
||||
return c.Internal.ID(ctx)
|
||||
@@ -193,12 +298,26 @@ func (c *CommonStruct) Version(ctx context.Context) (api.Version, error) {
|
||||
return c.Internal.Version(ctx)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) LogList(ctx context.Context) ([]string, error) {
|
||||
return c.Internal.LogList(ctx)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) LogSetLevel(ctx context.Context, group, level string) error {
|
||||
return c.Internal.LogSetLevel(ctx, group, level)
|
||||
}
|
||||
|
||||
func (c *CommonStruct) Shutdown(ctx context.Context) error {
|
||||
return c.Internal.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// FullNodeStruct
|
||||
|
||||
func (c *FullNodeStruct) ClientListImports(ctx context.Context) ([]api.Import, error) {
|
||||
return c.Internal.ClientListImports(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientImport(ctx context.Context, path string) (cid.Cid, error) {
|
||||
return c.Internal.ClientImport(ctx, path)
|
||||
func (c *FullNodeStruct) ClientImport(ctx context.Context, ref api.FileRef) (cid.Cid, error) {
|
||||
return c.Internal.ClientImport(ctx, ref)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error) {
|
||||
@@ -209,8 +328,8 @@ func (c *FullNodeStruct) ClientFindData(ctx context.Context, root cid.Cid) ([]ap
|
||||
return c.Internal.ClientFindData(ctx, root)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, data cid.Cid, addr address.Address, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) {
|
||||
return c.Internal.ClientStartDeal(ctx, data, addr, miner, price, blocksDuration)
|
||||
func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) {
|
||||
return c.Internal.ClientStartDeal(ctx, params)
|
||||
}
|
||||
func (c *FullNodeStruct) ClientGetDealInfo(ctx context.Context, deal cid.Cid) (*api.DealInfo, error) {
|
||||
return c.Internal.ClientGetDealInfo(ctx, deal)
|
||||
@@ -220,19 +339,26 @@ func (c *FullNodeStruct) ClientListDeals(ctx context.Context) ([]api.DealInfo, e
|
||||
return c.Internal.ClientListDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, path string) error {
|
||||
return c.Internal.ClientRetrieve(ctx, order, path)
|
||||
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error {
|
||||
return c.Internal.ClientRetrieve(ctx, order, ref)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*types.SignedStorageAsk, error) {
|
||||
func (c *FullNodeStruct) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) {
|
||||
return c.Internal.ClientQueryAsk(ctx, p, miner)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolPending(ctx context.Context, ts *types.TipSet) ([]*types.SignedMessage, error) {
|
||||
return c.Internal.MpoolPending(ctx, ts)
|
||||
func (c *FullNodeStruct) ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*api.CommPRet, error) {
|
||||
return c.Internal.ClientCalcCommP(ctx, inpath, miner)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolPush(ctx context.Context, smsg *types.SignedMessage) error {
|
||||
func (c *FullNodeStruct) ClientGenCar(ctx context.Context, ref api.FileRef, outpath string) error {
|
||||
return c.Internal.ClientGenCar(ctx, ref, outpath)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolPending(ctx context.Context, tsk types.TipSetKey) ([]*types.SignedMessage, error) {
|
||||
return c.Internal.MpoolPending(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) {
|
||||
return c.Internal.MpoolPush(ctx, smsg)
|
||||
}
|
||||
|
||||
@@ -244,23 +370,31 @@ func (c *FullNodeStruct) MpoolSub(ctx context.Context) (<-chan api.MpoolUpdate,
|
||||
return c.Internal.MpoolSub(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, ticket *types.Ticket, eproof *types.EPostProof, msgs []*types.SignedMessage, height, ts uint64) (*types.BlockMsg, error) {
|
||||
return c.Internal.MinerCreateBlock(ctx, addr, base, ticket, eproof, msgs, height, ts)
|
||||
func (c *FullNodeStruct) MpoolEstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, limit int64, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.MpoolEstimateGasPrice(ctx, nblocksincl, sender, limit, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) {
|
||||
return c.Internal.MinerGetBaseInfo(ctx, maddr, epoch, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, bt *api.BlockTemplate) (*types.BlockMsg, error) {
|
||||
return c.Internal.MinerCreateBlock(ctx, bt)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) {
|
||||
return c.Internal.ChainHead(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, round int64) ([]byte, error) {
|
||||
return c.Internal.ChainGetRandomness(ctx, pts, round)
|
||||
func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) {
|
||||
return c.Internal.ChainGetRandomness(ctx, tsk, personalization, randEpoch, entropy)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetTipSetByHeight(ctx context.Context, h uint64, ts *types.TipSet) (*types.TipSet, error) {
|
||||
return c.Internal.ChainGetTipSetByHeight(ctx, h, ts)
|
||||
func (c *FullNodeStruct) ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
return c.Internal.ChainGetTipSetByHeight(ctx, h, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) WalletNew(ctx context.Context, typ string) (address.Address, error) {
|
||||
func (c *FullNodeStruct) WalletNew(ctx context.Context, typ crypto.SigType) (address.Address, error) {
|
||||
return c.Internal.WalletNew(ctx, typ)
|
||||
}
|
||||
|
||||
@@ -276,7 +410,7 @@ func (c *FullNodeStruct) WalletBalance(ctx context.Context, a address.Address) (
|
||||
return c.Internal.WalletBalance(ctx, a)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) {
|
||||
func (c *FullNodeStruct) WalletSign(ctx context.Context, k address.Address, msg []byte) (*crypto.Signature, error) {
|
||||
return c.Internal.WalletSign(ctx, k, msg)
|
||||
}
|
||||
|
||||
@@ -284,6 +418,10 @@ func (c *FullNodeStruct) WalletSignMessage(ctx context.Context, k address.Addres
|
||||
return c.Internal.WalletSignMessage(ctx, k, msg)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) WalletVerify(ctx context.Context, k address.Address, msg []byte, sig *crypto.Signature) bool {
|
||||
return c.Internal.WalletVerify(ctx, k, msg, sig)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) WalletDefaultAddress(ctx context.Context) (address.Address, error) {
|
||||
return c.Internal.WalletDefaultAddress(ctx)
|
||||
}
|
||||
@@ -324,7 +462,7 @@ func (c *FullNodeStruct) ChainGetParentMessages(ctx context.Context, b cid.Cid)
|
||||
return c.Internal.ChainGetParentMessages(ctx, b)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainNotify(ctx context.Context) (<-chan []*store.HeadChange, error) {
|
||||
func (c *FullNodeStruct) ChainNotify(ctx context.Context) (<-chan []*api.HeadChange, error) {
|
||||
return c.Internal.ChainNotify(ctx)
|
||||
}
|
||||
|
||||
@@ -332,22 +470,42 @@ func (c *FullNodeStruct) ChainReadObj(ctx context.Context, obj cid.Cid) ([]byte,
|
||||
return c.Internal.ChainReadObj(ctx, obj)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainSetHead(ctx context.Context, ts *types.TipSet) error {
|
||||
return c.Internal.ChainSetHead(ctx, ts)
|
||||
func (c *FullNodeStruct) ChainHasObj(ctx context.Context, o cid.Cid) (bool, error) {
|
||||
return c.Internal.ChainHasObj(ctx, o)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainStatObj(ctx context.Context, obj, base cid.Cid) (api.ObjStat, error) {
|
||||
return c.Internal.ChainStatObj(ctx, obj, base)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainSetHead(ctx context.Context, tsk types.TipSetKey) error {
|
||||
return c.Internal.ChainSetHead(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetGenesis(ctx context.Context) (*types.TipSet, error) {
|
||||
return c.Internal.ChainGetGenesis(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainTipSetWeight(ctx context.Context, ts *types.TipSet) (types.BigInt, error) {
|
||||
return c.Internal.ChainTipSetWeight(ctx, ts)
|
||||
func (c *FullNodeStruct) ChainTipSetWeight(ctx context.Context, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.ChainTipSetWeight(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetNode(ctx context.Context, p string) (interface{}, error) {
|
||||
func (c *FullNodeStruct) ChainGetNode(ctx context.Context, p string) (*api.IpldObject, error) {
|
||||
return c.Internal.ChainGetNode(ctx, p)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error) {
|
||||
return c.Internal.ChainGetMessage(ctx, mc)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*api.HeadChange, error) {
|
||||
return c.Internal.ChainGetPath(ctx, from, to)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ChainExport(ctx context.Context, tsk types.TipSetKey) (<-chan []byte, error) {
|
||||
return c.Internal.ChainExport(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) SyncState(ctx context.Context) (*api.SyncState, error) {
|
||||
return c.Internal.SyncState(ctx)
|
||||
}
|
||||
@@ -360,95 +518,168 @@ func (c *FullNodeStruct) SyncIncomingBlocks(ctx context.Context) (<-chan *types.
|
||||
return c.Internal.SyncIncomingBlocks(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerSectors(ctx context.Context, addr address.Address, ts *types.TipSet) ([]*api.ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerSectors(ctx, addr, ts)
|
||||
func (c *FullNodeStruct) SyncMarkBad(ctx context.Context, bcid cid.Cid) error {
|
||||
return c.Internal.SyncMarkBad(ctx, bcid)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerProvingSet(ctx context.Context, addr address.Address, ts *types.TipSet) ([]*api.ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerProvingSet(ctx, addr, ts)
|
||||
func (c *FullNodeStruct) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error) {
|
||||
return c.Internal.SyncCheckBad(ctx, bcid)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerPower(ctx context.Context, a address.Address, ts *types.TipSet) (api.MinerPower, error) {
|
||||
return c.Internal.StateMinerPower(ctx, a, ts)
|
||||
func (c *FullNodeStruct) StateNetworkName(ctx context.Context) (dtypes.NetworkName, error) {
|
||||
return c.Internal.StateNetworkName(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerWorker(ctx context.Context, m address.Address, ts *types.TipSet) (address.Address, error) {
|
||||
return c.Internal.StateMinerWorker(ctx, m, ts)
|
||||
func (c *FullNodeStruct) StateMinerSectors(ctx context.Context, addr address.Address, filter *abi.BitField, filterOut bool, tsk types.TipSetKey) ([]*api.ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerSectors(ctx, addr, filter, filterOut, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerPeerID(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) {
|
||||
return c.Internal.StateMinerPeerID(ctx, m, ts)
|
||||
func (c *FullNodeStruct) StateMinerProvingSet(ctx context.Context, addr address.Address, tsk types.TipSetKey) ([]*api.ChainSectorInfo, error) {
|
||||
return c.Internal.StateMinerProvingSet(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) {
|
||||
return c.Internal.StateMinerElectionPeriodStart(ctx, actor, ts)
|
||||
func (c *FullNodeStruct) StateMinerProvingDeadline(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*miner.DeadlineInfo, error) {
|
||||
return c.Internal.StateMinerProvingDeadline(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMinerSectorSize(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) {
|
||||
return c.Internal.StateMinerSectorSize(ctx, actor, ts)
|
||||
func (c *FullNodeStruct) StateMinerPower(ctx context.Context, a address.Address, tsk types.TipSetKey) (*api.MinerPower, error) {
|
||||
return c.Internal.StateMinerPower(ctx, a, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
|
||||
return c.Internal.StateCall(ctx, msg, ts)
|
||||
func (c *FullNodeStruct) StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (miner.MinerInfo, error) {
|
||||
return c.Internal.StateMinerInfo(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateReplay(ctx context.Context, ts *types.TipSet, mc cid.Cid) (*api.ReplayResults, error) {
|
||||
return c.Internal.StateReplay(ctx, ts, mc)
|
||||
func (c *FullNodeStruct) StateMinerDeadlines(ctx context.Context, m address.Address, tsk types.TipSetKey) (*miner.Deadlines, error) {
|
||||
return c.Internal.StateMinerDeadlines(ctx, m, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error) {
|
||||
return c.Internal.StateGetActor(ctx, actor, ts)
|
||||
func (c *FullNodeStruct) StateMinerFaults(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*abi.BitField, error) {
|
||||
return c.Internal.StateMinerFaults(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*api.ActorState, error) {
|
||||
return c.Internal.StateReadState(ctx, act, ts)
|
||||
func (c *FullNodeStruct) StateAllMinerFaults(ctx context.Context, cutoff abi.ChainEpoch, endTsk types.TipSetKey) ([]*api.Fault, error) {
|
||||
return c.Internal.StateAllMinerFaults(ctx, cutoff, endTsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StatePledgeCollateral(ctx context.Context, ts *types.TipSet) (types.BigInt, error) {
|
||||
return c.Internal.StatePledgeCollateral(ctx, ts)
|
||||
func (c *FullNodeStruct) StateMinerRecoveries(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*abi.BitField, error) {
|
||||
return c.Internal.StateMinerRecoveries(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid) (*api.MsgWait, error) {
|
||||
return c.Internal.StateWaitMsg(ctx, msgc)
|
||||
}
|
||||
func (c *FullNodeStruct) StateListMiners(ctx context.Context, ts *types.TipSet) ([]address.Address, error) {
|
||||
return c.Internal.StateListMiners(ctx, ts)
|
||||
func (c *FullNodeStruct) StateMinerInitialPledgeCollateral(ctx context.Context, maddr address.Address, snum abi.SectorNumber, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.StateMinerInitialPledgeCollateral(ctx, maddr, snum, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateListActors(ctx context.Context, ts *types.TipSet) ([]address.Address, error) {
|
||||
return c.Internal.StateListActors(ctx, ts)
|
||||
func (c *FullNodeStruct) StateMinerAvailableBalance(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.StateMinerAvailableBalance(ctx, maddr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (actors.StorageParticipantBalance, error) {
|
||||
return c.Internal.StateMarketBalance(ctx, addr, ts)
|
||||
func (c *FullNodeStruct) StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) {
|
||||
return c.Internal.StateSectorPreCommitInfo(ctx, maddr, n, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketParticipants(ctx context.Context, ts *types.TipSet) (map[string]actors.StorageParticipantBalance, error) {
|
||||
return c.Internal.StateMarketParticipants(ctx, ts)
|
||||
func (c *FullNodeStruct) StateSectorGetInfo(ctx context.Context, maddr address.Address, n abi.SectorNumber, tsk types.TipSetKey) (*miner.SectorOnChainInfo, error) {
|
||||
return c.Internal.StateSectorGetInfo(ctx, maddr, n, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketDeals(ctx context.Context, ts *types.TipSet) (map[string]actors.OnChainDeal, error) {
|
||||
return c.Internal.StateMarketDeals(ctx, ts)
|
||||
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (*api.InvocResult, error) {
|
||||
return c.Internal.StateCall(ctx, msg, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketStorageDeal(ctx context.Context, dealid uint64, ts *types.TipSet) (*actors.OnChainDeal, error) {
|
||||
return c.Internal.StateMarketStorageDeal(ctx, dealid, ts)
|
||||
func (c *FullNodeStruct) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid.Cid) (*api.InvocResult, error) {
|
||||
return c.Internal.StateReplay(ctx, tsk, mc)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateLookupID(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
|
||||
return c.Internal.StateLookupID(ctx, addr, ts)
|
||||
func (c *FullNodeStruct) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) {
|
||||
return c.Internal.StateGetActor(ctx, actor, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateReadState(ctx context.Context, act *types.Actor, tsk types.TipSetKey) (*api.ActorState, error) {
|
||||
return c.Internal.StateReadState(ctx, act, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StatePledgeCollateral(ctx context.Context, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.StatePledgeCollateral(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateWaitMsg(ctx context.Context, msgc cid.Cid, confidence uint64) (*api.MsgLookup, error) {
|
||||
return c.Internal.StateWaitMsg(ctx, msgc, confidence)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateSearchMsg(ctx context.Context, msgc cid.Cid) (*api.MsgLookup, error) {
|
||||
return c.Internal.StateSearchMsg(ctx, msgc)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateListMiners(ctx context.Context, tsk types.TipSetKey) ([]address.Address, error) {
|
||||
return c.Internal.StateListMiners(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateListActors(ctx context.Context, tsk types.TipSetKey) ([]address.Address, error) {
|
||||
return c.Internal.StateListActors(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MarketBalance, error) {
|
||||
return c.Internal.StateMarketBalance(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketParticipants(ctx context.Context, tsk types.TipSetKey) (map[string]api.MarketBalance, error) {
|
||||
return c.Internal.StateMarketParticipants(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketDeals(ctx context.Context, tsk types.TipSetKey) (map[string]api.MarketDeal, error) {
|
||||
return c.Internal.StateMarketDeals(ctx, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateMarketStorageDeal(ctx context.Context, dealid abi.DealID, tsk types.TipSetKey) (*api.MarketDeal, error) {
|
||||
return c.Internal.StateMarketStorageDeal(ctx, dealid, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateLookupID(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
|
||||
return c.Internal.StateLookupID(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {
|
||||
return c.Internal.StateAccountKey(ctx, addr, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateChangedActors(ctx context.Context, olnstate cid.Cid, newstate cid.Cid) (map[string]types.Actor, error) {
|
||||
return c.Internal.StateChangedActors(ctx, olnstate, newstate)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateGetReceipt(ctx context.Context, msg cid.Cid, ts *types.TipSet) (*types.MessageReceipt, error) {
|
||||
return c.Internal.StateGetReceipt(ctx, msg, ts)
|
||||
func (c *FullNodeStruct) StateGetReceipt(ctx context.Context, msg cid.Cid, tsk types.TipSetKey) (*types.MessageReceipt, error) {
|
||||
return c.Internal.StateGetReceipt(ctx, msg, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr address.Address, amt types.BigInt) error {
|
||||
return c.Internal.MarketEnsureAvailable(ctx, addr, amt)
|
||||
func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) {
|
||||
return c.Internal.StateListMessages(ctx, match, tsk, toht)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) StateCompute(ctx context.Context, height abi.ChainEpoch, msgs []*types.Message, tsk types.TipSetKey) (*api.ComputeStateOutput, error) {
|
||||
return c.Internal.StateCompute(ctx, height, msgs, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MsigGetAvailableBalance(ctx context.Context, a address.Address, tsk types.TipSetKey) (types.BigInt, error) {
|
||||
return c.Internal.MsigGetAvailableBalance(ctx, a, tsk)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MsigCreate(ctx context.Context, req int64, addrs []address.Address, val types.BigInt, src address.Address, gp types.BigInt) (cid.Cid, error) {
|
||||
return c.Internal.MsigCreate(ctx, req, addrs, val, src, gp)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MsigPropose(ctx context.Context, msig address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) {
|
||||
return c.Internal.MsigPropose(ctx, msig, to, amt, src, method, params)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MsigApprove(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) {
|
||||
return c.Internal.MsigApprove(ctx, msig, txID, proposer, to, amt, src, method, params)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MsigCancel(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) {
|
||||
return c.Internal.MsigCancel(ctx, msig, txID, proposer, to, amt, src, method, params)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr, wallet address.Address, amt types.BigInt) (cid.Cid, error) {
|
||||
return c.Internal.MarketEnsureAvailable(ctx, addr, wallet, amt)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*api.ChannelInfo, error) {
|
||||
@@ -463,23 +694,23 @@ func (c *FullNodeStruct) PaychStatus(ctx context.Context, pch address.Address) (
|
||||
return c.Internal.PaychStatus(ctx, pch)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherCheckValid(ctx context.Context, addr address.Address, sv *types.SignedVoucher) error {
|
||||
func (c *FullNodeStruct) PaychVoucherCheckValid(ctx context.Context, addr address.Address, sv *paych.SignedVoucher) error {
|
||||
return c.Internal.PaychVoucherCheckValid(ctx, addr, sv)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherCheckSpendable(ctx context.Context, addr address.Address, sv *types.SignedVoucher, secret []byte, proof []byte) (bool, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherCheckSpendable(ctx context.Context, addr address.Address, sv *paych.SignedVoucher, secret []byte, proof []byte) (bool, error) {
|
||||
return c.Internal.PaychVoucherCheckSpendable(ctx, addr, sv, secret, proof)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Address, sv *types.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherAdd(ctx context.Context, addr address.Address, sv *paych.SignedVoucher, proof []byte, minDelta types.BigInt) (types.BigInt, error) {
|
||||
return c.Internal.PaychVoucherAdd(ctx, addr, sv, proof, minDelta)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*types.SignedVoucher, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherCreate(ctx context.Context, pch address.Address, amt types.BigInt, lane uint64) (*paych.SignedVoucher, error) {
|
||||
return c.Internal.PaychVoucherCreate(ctx, pch, amt, lane)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherList(ctx context.Context, pch address.Address) ([]*types.SignedVoucher, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherList(ctx context.Context, pch address.Address) ([]*paych.SignedVoucher, error) {
|
||||
return c.Internal.PaychVoucherList(ctx, pch)
|
||||
}
|
||||
|
||||
@@ -495,15 +726,21 @@ func (c *FullNodeStruct) PaychNewPayment(ctx context.Context, from, to address.A
|
||||
return c.Internal.PaychNewPayment(ctx, from, to, vouchers)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *types.SignedVoucher) (cid.Cid, error) {
|
||||
func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (cid.Cid, error) {
|
||||
return c.Internal.PaychVoucherSubmit(ctx, ch, sv)
|
||||
}
|
||||
|
||||
// StorageMinerStruct
|
||||
|
||||
func (c *StorageMinerStruct) ActorAddress(ctx context.Context) (address.Address, error) {
|
||||
return c.Internal.ActorAddress(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) ActorSectorSize(ctx context.Context, addr address.Address) (uint64, error) {
|
||||
func (c *StorageMinerStruct) MiningBase(ctx context.Context) (*types.TipSet, error) {
|
||||
return c.Internal.MiningBase(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) ActorSectorSize(ctx context.Context, addr address.Address) (abi.SectorSize, error) {
|
||||
return c.Internal.ActorSectorSize(ctx, addr)
|
||||
}
|
||||
|
||||
@@ -512,12 +749,12 @@ func (c *StorageMinerStruct) PledgeSector(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Get the status of a given sector by ID
|
||||
func (c *StorageMinerStruct) SectorsStatus(ctx context.Context, sid uint64) (api.SectorInfo, error) {
|
||||
func (c *StorageMinerStruct) SectorsStatus(ctx context.Context, sid abi.SectorNumber) (api.SectorInfo, error) {
|
||||
return c.Internal.SectorsStatus(ctx, sid)
|
||||
}
|
||||
|
||||
// List all staged sectors
|
||||
func (c *StorageMinerStruct) SectorsList(ctx context.Context) ([]uint64, error) {
|
||||
func (c *StorageMinerStruct) SectorsList(ctx context.Context) ([]abi.SectorNumber, error) {
|
||||
return c.Internal.SectorsList(ctx)
|
||||
}
|
||||
|
||||
@@ -525,22 +762,149 @@ func (c *StorageMinerStruct) SectorsRefs(ctx context.Context) (map[string][]api.
|
||||
return c.Internal.SectorsRefs(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) SectorsUpdate(ctx context.Context, id uint64, state api.SectorState) error {
|
||||
func (c *StorageMinerStruct) SectorsUpdate(ctx context.Context, id abi.SectorNumber, state api.SectorState) error {
|
||||
return c.Internal.SectorsUpdate(ctx, id, state)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerStats(ctx context.Context) (sectorbuilder.WorkerStats, error) {
|
||||
func (c *StorageMinerStruct) WorkerConnect(ctx context.Context, url string) error {
|
||||
return c.Internal.WorkerConnect(ctx, url)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerStats(ctx context.Context) (map[uint64]storiface.WorkerStats, error) {
|
||||
return c.Internal.WorkerStats(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerQueue(ctx context.Context, cfg sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error) {
|
||||
return c.Internal.WorkerQueue(ctx, cfg)
|
||||
func (c *StorageMinerStruct) StorageAttach(ctx context.Context, si stores.StorageInfo, st stores.FsStat) error {
|
||||
return c.Internal.StorageAttach(ctx, si, st)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) WorkerDone(ctx context.Context, task uint64, res sectorbuilder.SealRes) error {
|
||||
return c.Internal.WorkerDone(ctx, task, res)
|
||||
func (c *StorageMinerStruct) StorageDeclareSector(ctx context.Context, storageId stores.ID, s abi.SectorID, ft stores.SectorFileType, primary bool) error {
|
||||
return c.Internal.StorageDeclareSector(ctx, storageId, s, ft, primary)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageDropSector(ctx context.Context, storageId stores.ID, s abi.SectorID, ft stores.SectorFileType) error {
|
||||
return c.Internal.StorageDropSector(ctx, storageId, s, ft)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageFindSector(ctx context.Context, si abi.SectorID, types stores.SectorFileType, allowFetch bool) ([]stores.SectorStorageInfo, error) {
|
||||
return c.Internal.StorageFindSector(ctx, si, types, allowFetch)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageList(ctx context.Context) (map[stores.ID][]stores.Decl, error) {
|
||||
return c.Internal.StorageList(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageLocal(ctx context.Context) (map[stores.ID]string, error) {
|
||||
return c.Internal.StorageLocal(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageStat(ctx context.Context, id stores.ID) (stores.FsStat, error) {
|
||||
return c.Internal.StorageStat(ctx, id)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageInfo(ctx context.Context, id stores.ID) (stores.StorageInfo, error) {
|
||||
return c.Internal.StorageInfo(ctx, id)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageBestAlloc(ctx context.Context, allocate stores.SectorFileType, spt abi.RegisteredProof, pt stores.PathType) ([]stores.StorageInfo, error) {
|
||||
return c.Internal.StorageBestAlloc(ctx, allocate, spt, pt)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageReportHealth(ctx context.Context, id stores.ID, report stores.HealthReport) error {
|
||||
return c.Internal.StorageReportHealth(ctx, id, report)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageLock(ctx context.Context, sector abi.SectorID, read stores.SectorFileType, write stores.SectorFileType) error {
|
||||
return c.Internal.StorageLock(ctx, sector, read, write)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error {
|
||||
return c.Internal.MarketImportDealData(ctx, propcid, path)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketListDeals(ctx context.Context) ([]storagemarket.StorageDeal, error) {
|
||||
return c.Internal.MarketListDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketListIncompleteDeals(ctx context.Context) ([]storagemarket.MinerDeal, error) {
|
||||
return c.Internal.MarketListIncompleteDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) MarketSetPrice(ctx context.Context, p types.BigInt) error {
|
||||
return c.Internal.MarketSetPrice(ctx, p)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error {
|
||||
return c.Internal.DealsImportData(ctx, dealPropCid, file)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) DealsList(ctx context.Context) ([]storagemarket.StorageDeal, error) {
|
||||
return c.Internal.DealsList(ctx)
|
||||
}
|
||||
|
||||
func (c *StorageMinerStruct) StorageAddLocal(ctx context.Context, path string) error {
|
||||
return c.Internal.StorageAddLocal(ctx, path)
|
||||
}
|
||||
|
||||
// WorkerStruct
|
||||
|
||||
func (w *WorkerStruct) Version(ctx context.Context) (build.Version, error) {
|
||||
return w.Internal.Version(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) TaskTypes(ctx context.Context) (map[sealtasks.TaskType]struct{}, error) {
|
||||
return w.Internal.TaskTypes(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) Paths(ctx context.Context) ([]stores.StoragePath, error) {
|
||||
return w.Internal.Paths(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) Info(ctx context.Context) (storiface.WorkerInfo, error) {
|
||||
return w.Internal.Info(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) SealPreCommit1(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, pieces []abi.PieceInfo) (storage.PreCommit1Out, error) {
|
||||
return w.Internal.SealPreCommit1(ctx, sector, ticket, pieces)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) SealPreCommit2(ctx context.Context, sector abi.SectorID, p1o storage.PreCommit1Out) (storage.SectorCids, error) {
|
||||
return w.Internal.SealPreCommit2(ctx, sector, p1o)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) SealCommit1(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (storage.Commit1Out, error) {
|
||||
return w.Internal.SealCommit1(ctx, sector, ticket, seed, pieces, cids)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) SealCommit2(ctx context.Context, sector abi.SectorID, c1o storage.Commit1Out) (storage.Proof, error) {
|
||||
return w.Internal.SealCommit2(ctx, sector, c1o)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) FinalizeSector(ctx context.Context, sector abi.SectorID) error {
|
||||
return w.Internal.FinalizeSector(ctx, sector)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) MoveStorage(ctx context.Context, sector abi.SectorID) error {
|
||||
return w.Internal.MoveStorage(ctx, sector)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) UnsealPiece(ctx context.Context, id abi.SectorID, index storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize, randomness abi.SealRandomness, c cid.Cid) error {
|
||||
return w.Internal.UnsealPiece(ctx, id, index, size, randomness, c)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) ReadPiece(ctx context.Context, writer io.Writer, id abi.SectorID, index storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize) error {
|
||||
return w.Internal.ReadPiece(ctx, writer, id, index, size)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) Fetch(ctx context.Context, id abi.SectorID, fileType stores.SectorFileType, ptype stores.PathType, am stores.AcquireMode) error {
|
||||
return w.Internal.Fetch(ctx, id, fileType, ptype, am)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) Closing(ctx context.Context) (<-chan struct{}, error) {
|
||||
return w.Internal.Closing(ctx)
|
||||
}
|
||||
|
||||
var _ api.Common = &CommonStruct{}
|
||||
var _ api.FullNode = &FullNodeStruct{}
|
||||
var _ api.StorageMiner = &StorageMinerStruct{}
|
||||
var _ api.WorkerAPI = &WorkerStruct{}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package apistruct
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPermTags(t *testing.T) {
|
||||
_ = PermissionedFullAPI(&FullNodeStruct{})
|
||||
_ = PermissionedStorMinerAPI(&StorageMinerStruct{})
|
||||
_ = PermissionedWorkerAPI(&WorkerStruct{})
|
||||
}
|
||||
+530
-214
@@ -1,16 +1,17 @@
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
|
||||
func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
@@ -22,11 +23,17 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Channel (address.Address) (struct)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Channel")))); err != nil {
|
||||
if len("Channel") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Channel\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Channel"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Channel")); err != nil {
|
||||
if _, err := io.WriteString(w, "Channel"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -35,10 +42,14 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
|
||||
// t.ChannelMessage (cid.Cid) (struct)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("ChannelMessage")))); err != nil {
|
||||
if len("ChannelMessage") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"ChannelMessage\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("ChannelMessage"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("ChannelMessage")); err != nil {
|
||||
if _, err := io.WriteString(w, "ChannelMessage"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -47,20 +58,28 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteCid(w, *t.ChannelMessage); err != nil {
|
||||
if err := cbg.WriteCidBuf(scratch, w, *t.ChannelMessage); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.ChannelMessage: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// t.Vouchers ([]*types.SignedVoucher) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Vouchers")))); err != nil {
|
||||
// t.Vouchers ([]*paych.SignedVoucher) (slice)
|
||||
if len("Vouchers") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Vouchers\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Vouchers"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Vouchers")); err != nil {
|
||||
if _, err := io.WriteString(w, "Vouchers"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Vouchers)))); err != nil {
|
||||
if len(t.Vouchers) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Vouchers was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Vouchers))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Vouchers {
|
||||
@@ -73,8 +92,9 @@ func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -82,114 +102,97 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra != 3 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("PaymentInfo: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
// t.Channel (address.Address) (struct)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "Channel" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Channel", name)
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
if err := t.Channel.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.ChannelMessage (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "ChannelMessage" {
|
||||
return fmt.Errorf("expected struct map entry %s to be ChannelMessage", name)
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
{
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.Channel (address.Address) (struct)
|
||||
case "Channel":
|
||||
|
||||
{
|
||||
|
||||
if err := t.Channel.UnmarshalCBOR(br); err != nil {
|
||||
return xerrors.Errorf("unmarshaling t.Channel: %w", err)
|
||||
}
|
||||
|
||||
}
|
||||
// t.ChannelMessage (cid.Cid) (struct)
|
||||
case "ChannelMessage":
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.ChannelMessage: %w", err)
|
||||
}
|
||||
|
||||
t.ChannelMessage = &c
|
||||
}
|
||||
|
||||
}
|
||||
// t.Vouchers ([]*paych.SignedVoucher) (slice)
|
||||
case "Vouchers":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.ChannelMessage: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
t.ChannelMessage = &c
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Vouchers: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Vouchers = make([]*paych.SignedVoucher, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v paych.SignedVoucher
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Vouchers[i] = &v
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
|
||||
}
|
||||
// t.Vouchers ([]*types.SignedVoucher) (slice)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "Vouchers" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Vouchers", name)
|
||||
}
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Vouchers: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
if extra > 0 {
|
||||
t.Vouchers = make([]*types.SignedVoucher, extra)
|
||||
}
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.SignedVoucher
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Vouchers[i] = &v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRef) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
@@ -199,48 +202,64 @@ func (t *SealedRef) MarshalCBOR(w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.SectorID (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("SectorID")))); err != nil {
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.SectorID (abi.SectorNumber) (uint64)
|
||||
if len("SectorID") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"SectorID\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("SectorID"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("SectorID")); err != nil {
|
||||
if _, err := io.WriteString(w, "SectorID"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.SectorID))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.SectorID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Offset (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Offset")))); err != nil {
|
||||
if len("Offset") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Offset\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Offset"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Offset")); err != nil {
|
||||
if _, err := io.WriteString(w, "Offset"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Offset))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Offset)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Size (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Size")))); err != nil {
|
||||
// t.Size (abi.UnpaddedPieceSize) (uint64)
|
||||
if len("Size") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Size\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Size"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Size")); err != nil {
|
||||
if _, err := io.WriteString(w, "Size"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Size))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Size)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRef) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -248,84 +267,78 @@ func (t *SealedRef) UnmarshalCBOR(r io.Reader) error {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra != 3 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("SealedRef: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
// t.SectorID (uint64) (uint64)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
switch name {
|
||||
// t.SectorID (abi.SectorNumber) (uint64)
|
||||
case "SectorID":
|
||||
|
||||
if name != "SectorID" {
|
||||
return fmt.Errorf("expected struct map entry %s to be SectorID", name)
|
||||
}
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.SectorID = uint64(extra)
|
||||
// t.Offset (uint64) (uint64)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.SectorID = abi.SectorNumber(extra)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// t.Offset (uint64) (uint64)
|
||||
case "Offset":
|
||||
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Offset = uint64(extra)
|
||||
|
||||
}
|
||||
// t.Size (abi.UnpaddedPieceSize) (uint64)
|
||||
case "Size":
|
||||
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Size = abi.UnpaddedPieceSize(extra)
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "Offset" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Offset", name)
|
||||
}
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Offset = uint64(extra)
|
||||
// t.Size (uint64) (uint64)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
if name != "Size" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Size", name)
|
||||
}
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Size = uint64(extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
@@ -335,15 +348,25 @@ func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Refs ([]api.SealedRef) (slice)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Refs")))); err != nil {
|
||||
if len("Refs") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Refs\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Refs"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte("Refs")); err != nil {
|
||||
if _, err := io.WriteString(w, "Refs"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Refs)))); err != nil {
|
||||
if len(t.Refs) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Refs was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Refs))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Refs {
|
||||
@@ -356,8 +379,9 @@ func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -365,50 +389,342 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra != 1 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("SealedRefs: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
// t.Refs ([]api.SealedRef) (slice)
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
switch name {
|
||||
// t.Refs ([]api.SealedRef) (slice)
|
||||
case "Refs":
|
||||
|
||||
if name != "Refs" {
|
||||
return fmt.Errorf("expected struct map entry %s to be Refs", name)
|
||||
}
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Refs: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Refs: array too large (%d)", extra)
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
if extra > 0 {
|
||||
t.Refs = make([]SealedRef, extra)
|
||||
}
|
||||
for i := 0; i < int(extra); i++ {
|
||||
if extra > 0 {
|
||||
t.Refs = make([]SealedRef, extra)
|
||||
}
|
||||
|
||||
var v SealedRef
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v SealedRef
|
||||
if err := v.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Refs[i] = v
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (t *SealTicket) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{162}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Value (abi.SealRandomness) (slice)
|
||||
if len("Value") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Value\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Value"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, "Value"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(t.Value) > cbg.ByteArrayMaxLen {
|
||||
return xerrors.Errorf("Byte array in field t.Value was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajByteString, uint64(len(t.Value))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Epoch (abi.ChainEpoch) (int64)
|
||||
if len("Epoch") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Epoch\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Epoch"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, "Epoch"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.Epoch >= 0 {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Epoch)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajNegativeInt, uint64(-t.Epoch-1)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealTicket) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajMap {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("SealTicket: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.Value (abi.SealRandomness) (slice)
|
||||
case "Value":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.ByteArrayMaxLen {
|
||||
return fmt.Errorf("t.Value: byte array too large (%d)", extra)
|
||||
}
|
||||
if maj != cbg.MajByteString {
|
||||
return fmt.Errorf("expected byte array")
|
||||
}
|
||||
t.Value = make([]byte, extra)
|
||||
if _, err := io.ReadFull(br, t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
// t.Epoch (abi.ChainEpoch) (int64)
|
||||
case "Epoch":
|
||||
{
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
var extraI int64
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch maj {
|
||||
case cbg.MajUnsignedInt:
|
||||
extraI = int64(extra)
|
||||
if extraI < 0 {
|
||||
return fmt.Errorf("int64 positive overflow")
|
||||
}
|
||||
case cbg.MajNegativeInt:
|
||||
extraI = int64(extra)
|
||||
if extraI < 0 {
|
||||
return fmt.Errorf("int64 negative oveflow")
|
||||
}
|
||||
extraI = -1 - extraI
|
||||
default:
|
||||
return fmt.Errorf("wrong type for int64 field: %d", maj)
|
||||
}
|
||||
|
||||
t.Epoch = abi.ChainEpoch(extraI)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (t *SealSeed) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{162}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Value (abi.InteractiveSealRandomness) (slice)
|
||||
if len("Value") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Value\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Value"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, "Value"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(t.Value) > cbg.ByteArrayMaxLen {
|
||||
return xerrors.Errorf("Byte array in field t.Value was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajByteString, uint64(len(t.Value))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Epoch (abi.ChainEpoch) (int64)
|
||||
if len("Epoch") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Epoch\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Epoch"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, "Epoch"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.Epoch >= 0 {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Epoch)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajNegativeInt, uint64(-t.Epoch-1)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SealSeed) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajMap {
|
||||
return fmt.Errorf("cbor input should be of type map")
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("SealSeed: map struct too large (%d)", extra)
|
||||
}
|
||||
|
||||
var name string
|
||||
n := extra
|
||||
|
||||
for i := uint64(0); i < n; i++ {
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = string(sval)
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.Value (abi.InteractiveSealRandomness) (slice)
|
||||
case "Value":
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.ByteArrayMaxLen {
|
||||
return fmt.Errorf("t.Value: byte array too large (%d)", extra)
|
||||
}
|
||||
if maj != cbg.MajByteString {
|
||||
return fmt.Errorf("expected byte array")
|
||||
}
|
||||
t.Value = make([]byte, extra)
|
||||
if _, err := io.ReadFull(br, t.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
// t.Epoch (abi.ChainEpoch) (int64)
|
||||
case "Epoch":
|
||||
{
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
var extraI int64
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch maj {
|
||||
case cbg.MajUnsignedInt:
|
||||
extraI = int64(extra)
|
||||
if extraI < 0 {
|
||||
return fmt.Errorf("int64 positive overflow")
|
||||
}
|
||||
case cbg.MajNegativeInt:
|
||||
extraI = int64(extra)
|
||||
if extraI < 0 {
|
||||
return fmt.Errorf("int64 negative oveflow")
|
||||
}
|
||||
extraI = -1 - extraI
|
||||
default:
|
||||
return fmt.Errorf("wrong type for int64 field: %d", maj)
|
||||
}
|
||||
|
||||
t.Epoch = abi.ChainEpoch(extraI)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
|
||||
}
|
||||
|
||||
t.Refs[i] = v
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+21
-4
@@ -1,11 +1,12 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
"net/http"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
"github.com/filecoin-project/lotus/api/apistruct"
|
||||
)
|
||||
|
||||
// NewCommonRPC creates a new http jsonrpc client.
|
||||
@@ -14,7 +15,9 @@ func NewCommonRPC(addr string, requestHeader http.Header) (api.Common, jsonrpc.C
|
||||
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
|
||||
[]interface{}{
|
||||
&res.Internal,
|
||||
}, requestHeader)
|
||||
},
|
||||
requestHeader,
|
||||
)
|
||||
|
||||
return &res, closer, err
|
||||
}
|
||||
@@ -38,7 +41,21 @@ func NewStorageMinerRPC(addr string, requestHeader http.Header) (api.StorageMine
|
||||
[]interface{}{
|
||||
&res.CommonStruct.Internal,
|
||||
&res.Internal,
|
||||
}, requestHeader)
|
||||
},
|
||||
requestHeader,
|
||||
)
|
||||
|
||||
return &res, closer, err
|
||||
}
|
||||
|
||||
func NewWorkerRPC(addr string, requestHeader http.Header) (api.WorkerAPI, jsonrpc.ClientCloser, error) {
|
||||
var res apistruct.WorkerStruct
|
||||
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
|
||||
[]interface{}{
|
||||
&res.Internal,
|
||||
},
|
||||
requestHeader,
|
||||
)
|
||||
|
||||
return &res, closer, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-filestore"
|
||||
"github.com/libp2p/go-libp2p-core/network"
|
||||
peer "github.com/libp2p/go-libp2p-peer"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
var ExampleValues = map[reflect.Type]interface{}{
|
||||
reflect.TypeOf(auth.Permission("")): auth.Permission("write"),
|
||||
reflect.TypeOf(""): "string value",
|
||||
reflect.TypeOf(uint64(42)): uint64(42),
|
||||
reflect.TypeOf(byte(7)): byte(7),
|
||||
reflect.TypeOf([]byte{}): []byte("byte array"),
|
||||
}
|
||||
|
||||
func addExample(v interface{}) {
|
||||
ExampleValues[reflect.TypeOf(v)] = v
|
||||
}
|
||||
|
||||
func init() {
|
||||
c, err := cid.Decode("bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ExampleValues[reflect.TypeOf(c)] = c
|
||||
|
||||
c2, err := cid.Decode("bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tsk := types.NewTipSetKey(c, c2)
|
||||
|
||||
ExampleValues[reflect.TypeOf(tsk)] = tsk
|
||||
|
||||
addr, err := address.NewIDAddress(1234)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ExampleValues[reflect.TypeOf(addr)] = addr
|
||||
|
||||
pid, err := peer.IDB58Decode("12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addExample(pid)
|
||||
|
||||
addExample(bitfield.NewFromSet([]uint64{5}))
|
||||
addExample(abi.RegisteredProof_StackedDRG32GiBPoSt)
|
||||
addExample(abi.ChainEpoch(10101))
|
||||
addExample(crypto.SigTypeBLS)
|
||||
addExample(int64(9))
|
||||
addExample(abi.MethodNum(1))
|
||||
addExample(exitcode.ExitCode(0))
|
||||
addExample(crypto.DomainSeparationTag_ElectionProofProduction)
|
||||
addExample(true)
|
||||
addExample(abi.UnpaddedPieceSize(1024))
|
||||
addExample(abi.UnpaddedPieceSize(1024).Padded())
|
||||
addExample(abi.DealID(5432))
|
||||
addExample(filestore.StatusFileChanged)
|
||||
addExample(abi.SectorNumber(9))
|
||||
addExample(abi.SectorSize(32 * 1024 * 1024 * 1024))
|
||||
addExample(api.MpoolChange(0))
|
||||
addExample(network.Connected)
|
||||
addExample(dtypes.NetworkName("lotus"))
|
||||
addExample(api.SyncStateStage(1))
|
||||
addExample(build.APIVersion)
|
||||
addExample(api.PCHInbound)
|
||||
addExample(time.Minute)
|
||||
addExample(&types.ExecutionResult{
|
||||
Msg: exampleValue(reflect.TypeOf(&types.Message{})).(*types.Message),
|
||||
MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{})).(*types.MessageReceipt),
|
||||
})
|
||||
addExample(map[string]types.Actor{
|
||||
"t01236": exampleValue(reflect.TypeOf(types.Actor{})).(types.Actor),
|
||||
})
|
||||
addExample(map[string]api.MarketDeal{
|
||||
"t026363": exampleValue(reflect.TypeOf(api.MarketDeal{})).(api.MarketDeal),
|
||||
})
|
||||
addExample(map[string]api.MarketBalance{
|
||||
"t026363": exampleValue(reflect.TypeOf(api.MarketBalance{})).(api.MarketBalance),
|
||||
})
|
||||
|
||||
maddr, err := multiaddr.NewMultiaddr("/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// because reflect.TypeOf(maddr) returns the concrete type...
|
||||
ExampleValues[reflect.TypeOf(struct{ A multiaddr.Multiaddr }{}).Field(0).Type] = maddr
|
||||
|
||||
}
|
||||
|
||||
func exampleValue(t reflect.Type) interface{} {
|
||||
v, ok := ExampleValues[t]
|
||||
if ok {
|
||||
return v
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Slice:
|
||||
out := reflect.New(t).Elem()
|
||||
reflect.Append(out, reflect.ValueOf(exampleValue(t.Elem())))
|
||||
return out.Interface()
|
||||
case reflect.Chan:
|
||||
return exampleValue(t.Elem())
|
||||
case reflect.Struct:
|
||||
es := exampleStruct(t)
|
||||
v := reflect.ValueOf(es).Elem().Interface()
|
||||
ExampleValues[t] = v
|
||||
return v
|
||||
case reflect.Array:
|
||||
out := reflect.New(t).Elem()
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
out.Index(i).Set(reflect.ValueOf(exampleValue(t.Elem())))
|
||||
}
|
||||
return out.Interface()
|
||||
|
||||
case reflect.Ptr:
|
||||
if t.Elem().Kind() == reflect.Struct {
|
||||
es := exampleStruct(t.Elem())
|
||||
//ExampleValues[t] = es
|
||||
return es
|
||||
}
|
||||
case reflect.Interface:
|
||||
return struct{}{}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("No example value for type: %s", t))
|
||||
}
|
||||
|
||||
func exampleStruct(t reflect.Type) interface{} {
|
||||
ns := reflect.New(t)
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
if strings.Title(f.Name) == f.Name {
|
||||
ns.Elem().Field(i).Set(reflect.ValueOf(exampleValue(f.Type)))
|
||||
}
|
||||
}
|
||||
|
||||
return ns.Interface()
|
||||
}
|
||||
|
||||
type Visitor struct {
|
||||
Methods map[string]ast.Node
|
||||
}
|
||||
|
||||
func (v *Visitor) Visit(node ast.Node) ast.Visitor {
|
||||
st, ok := node.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return v
|
||||
}
|
||||
|
||||
if st.Name.Name != "FullNode" {
|
||||
return nil
|
||||
}
|
||||
|
||||
iface := st.Type.(*ast.InterfaceType)
|
||||
for _, m := range iface.Methods.List {
|
||||
if len(m.Names) > 0 {
|
||||
v.Methods[m.Names[0].Name] = m
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
const noComment = "There are not yet any comments for this method."
|
||||
|
||||
func parseApiASTInfo() (map[string]string, map[string]string) { //nolint:golint
|
||||
fset := token.NewFileSet()
|
||||
pkgs, err := parser.ParseDir(fset, "./api", nil, parser.AllErrors|parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Println("parse error: ", err)
|
||||
}
|
||||
|
||||
ap := pkgs["api"]
|
||||
|
||||
f := ap.Files["api/api_full.go"]
|
||||
|
||||
cmap := ast.NewCommentMap(fset, f, f.Comments)
|
||||
|
||||
v := &Visitor{make(map[string]ast.Node)}
|
||||
ast.Walk(v, pkgs["api"])
|
||||
|
||||
groupDocs := make(map[string]string)
|
||||
out := make(map[string]string)
|
||||
for mn, node := range v.Methods {
|
||||
cs := cmap.Filter(node).Comments()
|
||||
if len(cs) == 0 {
|
||||
out[mn] = noComment
|
||||
} else {
|
||||
for _, c := range cs {
|
||||
if strings.HasPrefix(c.Text(), "MethodGroup:") {
|
||||
parts := strings.Split(c.Text(), "\n")
|
||||
groupName := strings.TrimSpace(parts[0][12:])
|
||||
comment := strings.Join(parts[1:], "\n")
|
||||
groupDocs[groupName] = comment
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
last := cs[len(cs)-1].Text()
|
||||
if !strings.HasPrefix(last, "MethodGroup:") {
|
||||
out[mn] = last
|
||||
} else {
|
||||
out[mn] = noComment
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, groupDocs
|
||||
}
|
||||
|
||||
type MethodGroup struct {
|
||||
GroupName string
|
||||
Header string
|
||||
Methods []*Method
|
||||
}
|
||||
|
||||
type Method struct {
|
||||
Comment string
|
||||
Name string
|
||||
InputExample string
|
||||
ResponseExample string
|
||||
}
|
||||
|
||||
func methodGroupFromName(mn string) string {
|
||||
i := strings.IndexFunc(mn[1:], func(r rune) bool {
|
||||
return unicode.IsUpper(r)
|
||||
})
|
||||
if i < 0 {
|
||||
return ""
|
||||
}
|
||||
return mn[:i+1]
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
comments, groupComments := parseApiASTInfo()
|
||||
|
||||
groups := make(map[string]*MethodGroup)
|
||||
|
||||
var api struct{ api.FullNode }
|
||||
t := reflect.TypeOf(api)
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
m := t.Method(i)
|
||||
|
||||
groupName := methodGroupFromName(m.Name)
|
||||
|
||||
g, ok := groups[groupName]
|
||||
if !ok {
|
||||
g = new(MethodGroup)
|
||||
g.Header = groupComments[groupName]
|
||||
g.GroupName = groupName
|
||||
groups[groupName] = g
|
||||
}
|
||||
|
||||
var args []interface{}
|
||||
ft := m.Func.Type()
|
||||
for j := 2; j < ft.NumIn(); j++ {
|
||||
inp := ft.In(j)
|
||||
args = append(args, exampleValue(inp))
|
||||
}
|
||||
|
||||
v, err := json.Marshal(args)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outv := exampleValue(ft.Out(0))
|
||||
|
||||
ov, err := json.Marshal(outv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g.Methods = append(g.Methods, &Method{
|
||||
Name: m.Name,
|
||||
Comment: comments[m.Name],
|
||||
InputExample: string(v),
|
||||
ResponseExample: string(ov),
|
||||
})
|
||||
}
|
||||
|
||||
var groupslice []*MethodGroup
|
||||
for _, g := range groups {
|
||||
groupslice = append(groupslice, g)
|
||||
}
|
||||
|
||||
sort.Slice(groupslice, func(i, j int) bool {
|
||||
return groupslice[i].GroupName < groupslice[j].GroupName
|
||||
})
|
||||
|
||||
for _, g := range groupslice {
|
||||
g := g
|
||||
fmt.Printf("## %s\n", g.GroupName)
|
||||
fmt.Printf("%s\n\n", g.Header)
|
||||
|
||||
sort.Slice(g.Methods, func(i, j int) bool {
|
||||
return g.Methods[i].Name < g.Methods[j].Name
|
||||
})
|
||||
|
||||
for _, m := range g.Methods {
|
||||
fmt.Printf("### %s\n", m.Name)
|
||||
fmt.Printf("%s\n\n", m.Comment)
|
||||
|
||||
fmt.Printf("Inputs: `%s`\n\n", m.InputExample)
|
||||
fmt.Printf("Response: `%s`\n\n", m.ResponseExample)
|
||||
}
|
||||
}
|
||||
}
|
||||
+142
-33
@@ -11,12 +11,22 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/ipld/go-car"
|
||||
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
dstest "github.com/ipfs/go-merkledag/test"
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
ipld "github.com/ipfs/go-ipld-format"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -24,11 +34,11 @@ func init() {
|
||||
build.InsecurePoStValidation = true
|
||||
}
|
||||
|
||||
func TestDealFlow(t *testing.T, b APIBuilder) {
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport bool) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, []int{0})
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
@@ -42,21 +52,42 @@ func TestDealFlow(t *testing.T, b APIBuilder) {
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
|
||||
data := make([]byte, 1000)
|
||||
rand.New(rand.NewSource(5)).Read(data)
|
||||
mine := true
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
for mine {
|
||||
time.Sleep(blocktime)
|
||||
if err := sn[0].MineOne(ctx, func(bool) {}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
fcid, err := client.ClientImportLocal(ctx, r)
|
||||
makeDeal(t, ctx, 6, client, miner, carExport)
|
||||
|
||||
mine = false
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
addrinfo, err := client.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
maddr, err := miner.ActorAddress(ctx)
|
||||
if err != nil {
|
||||
if err := miner.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("FILE CID: ", fcid)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
mine := true
|
||||
done := make(chan struct{})
|
||||
@@ -64,24 +95,68 @@ func TestDealFlow(t *testing.T, b APIBuilder) {
|
||||
go func() {
|
||||
defer close(done)
|
||||
for mine {
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("mining a block now")
|
||||
if err := sn[0].MineOne(ctx); err != nil {
|
||||
time.Sleep(blocktime)
|
||||
if err := sn[0].MineOne(ctx, func(bool) {}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
addr, err := client.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deal, err := client.ClientStartDeal(ctx, fcid, addr, maddr, types.NewInt(40000000), 100)
|
||||
|
||||
makeDeal(t, ctx, 6, client, miner, false)
|
||||
makeDeal(t, ctx, 7, client, miner, false)
|
||||
|
||||
mine = false
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
}
|
||||
|
||||
func makeDeal(t *testing.T, ctx context.Context, rseed int, client *impl.FullNodeAPI, miner TestStorageNode, carExport bool) {
|
||||
data := make([]byte, 1600)
|
||||
rand.New(rand.NewSource(int64(rseed))).Read(data)
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
fcid, err := client.ClientImportLocal(ctx, r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("FILE CID: ", fcid)
|
||||
|
||||
deal := startDeal(t, ctx, miner, client, fcid)
|
||||
|
||||
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
||||
time.Sleep(time.Second)
|
||||
waitDealSealed(t, ctx, client, deal)
|
||||
|
||||
// Retrieval
|
||||
|
||||
testRetrieval(t, ctx, err, client, fcid, carExport, data)
|
||||
}
|
||||
|
||||
func startDeal(t *testing.T, ctx context.Context, miner TestStorageNode, client *impl.FullNodeAPI, fcid cid.Cid) *cid.Cid {
|
||||
maddr, err := miner.ActorAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addr, err := client.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deal, err := client.ClientStartDeal(ctx, &api.StartDealParams{
|
||||
Data: &storagemarket.DataRef{Root: fcid},
|
||||
Wallet: addr,
|
||||
Miner: maddr,
|
||||
EpochPrice: types.NewInt(1000000),
|
||||
MinBlocksDuration: 100,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
return deal
|
||||
}
|
||||
|
||||
func waitDealSealed(t *testing.T, ctx context.Context, client *impl.FullNodeAPI, deal *cid.Cid) {
|
||||
loop:
|
||||
for {
|
||||
di, err := client.ClientGetDealInfo(ctx, *deal)
|
||||
@@ -89,22 +164,22 @@ loop:
|
||||
t.Fatal(err)
|
||||
}
|
||||
switch di.State {
|
||||
case api.DealRejected:
|
||||
case storagemarket.StorageDealProposalRejected:
|
||||
t.Fatal("deal rejected")
|
||||
case api.DealFailed:
|
||||
case storagemarket.StorageDealFailing:
|
||||
t.Fatal("deal failed")
|
||||
case api.DealError:
|
||||
t.Fatal("deal errored")
|
||||
case api.DealComplete:
|
||||
case storagemarket.StorageDealError:
|
||||
t.Fatal("deal errored", di.Message)
|
||||
case storagemarket.StorageDealActive:
|
||||
fmt.Println("COMPLETE", di)
|
||||
break loop
|
||||
}
|
||||
fmt.Println("Deal state: ", api.DealStates[di.State])
|
||||
fmt.Println("Deal state: ", storagemarket.DealStates[di.State])
|
||||
time.Sleep(time.Second / 2)
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieval
|
||||
|
||||
func testRetrieval(t *testing.T, ctx context.Context, err error, client *impl.FullNodeAPI, fcid cid.Cid, carExport bool, data []byte) {
|
||||
offers, err := client.ClientFindData(ctx, fcid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -118,14 +193,18 @@ loop:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(rpath)
|
||||
defer os.RemoveAll(rpath) //nolint:errcheck
|
||||
|
||||
caddr, err := client.WalletDefaultAddress(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = client.ClientRetrieve(ctx, offers[0].Order(caddr), filepath.Join(rpath, "ret"))
|
||||
ref := &api.FileRef{
|
||||
Path: filepath.Join(rpath, "ret"),
|
||||
IsCAR: carExport,
|
||||
}
|
||||
err = client.ClientRetrieve(ctx, offers[0].Order(caddr), ref)
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
@@ -135,11 +214,41 @@ loop:
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if carExport {
|
||||
rdata = extractCarData(t, ctx, rdata, rpath)
|
||||
}
|
||||
|
||||
if !bytes.Equal(rdata, data) {
|
||||
t.Fatal("wrong data retrieved")
|
||||
}
|
||||
}
|
||||
|
||||
mine = false
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
func extractCarData(t *testing.T, ctx context.Context, rdata []byte, rpath string) []byte {
|
||||
bserv := dstest.Bserv()
|
||||
ch, err := car.LoadCar(bserv.Blockstore(), bytes.NewReader(rdata))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, err := bserv.GetBlock(ctx, ch.Roots[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
nd, err := ipld.Decode(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dserv := dag.NewDAGService(bserv)
|
||||
fil, err := unixfile.NewUnixfsFile(ctx, dserv, nd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outPath := filepath.Join(rpath, "retLoadedCAR")
|
||||
if err := files.WriteTo(fil, outPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rdata, err = ioutil.ReadFile(outPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return rdata
|
||||
}
|
||||
|
||||
+172
-4
@@ -1,31 +1,199 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
)
|
||||
|
||||
var log = logging.Logger("apitest")
|
||||
|
||||
func (ts *testSuite) testMining(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, sn := ts.makeNodes(t, 1, []int{0})
|
||||
apis, sn := ts.makeNodes(t, 1, oneMiner)
|
||||
api := apis[0]
|
||||
|
||||
h1, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), h1.Height())
|
||||
require.Equal(t, abi.ChainEpoch(0), h1.Height())
|
||||
|
||||
newHeads, err := api.ChainNotify(ctx)
|
||||
require.NoError(t, err)
|
||||
<-newHeads
|
||||
|
||||
err = sn[0].MineOne(ctx)
|
||||
err = sn[0].MineOne(ctx, func(bool) {})
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
|
||||
h2, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), h2.Height())
|
||||
require.Equal(t, abi.ChainEpoch(1), h2.Height())
|
||||
}
|
||||
|
||||
func (ts *testSuite) testMiningReal(t *testing.T) {
|
||||
build.InsecurePoStValidation = false
|
||||
defer func() {
|
||||
build.InsecurePoStValidation = true
|
||||
}()
|
||||
|
||||
ctx := context.Background()
|
||||
apis, sn := ts.makeNodes(t, 1, oneMiner)
|
||||
api := apis[0]
|
||||
|
||||
h1, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(0), h1.Height())
|
||||
|
||||
newHeads, err := api.ChainNotify(ctx)
|
||||
require.NoError(t, err)
|
||||
<-newHeads
|
||||
|
||||
err = sn[0].MineOne(ctx, func(bool) {})
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
|
||||
h2, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(1), h2.Height())
|
||||
|
||||
err = sn[0].MineOne(ctx, func(bool) {})
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
|
||||
h2, err = api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(2), h2.Height())
|
||||
}
|
||||
|
||||
func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExport bool) {
|
||||
_ = os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
// test making a deal with a fresh miner, and see if it starts to mine
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, []StorageMiner{
|
||||
{Full: 0, Preseal: PresealGenesis},
|
||||
{Full: 0, Preseal: 0}, // TODO: Add support for storage miners on non-first full node
|
||||
})
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
provider := sn[1]
|
||||
genesisMiner := sn[0]
|
||||
|
||||
addrinfo, err := client.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := provider.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := genesisMiner.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
data := make([]byte, 600)
|
||||
rand.New(rand.NewSource(5)).Read(data)
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
fcid, err := client.ClientImportLocal(ctx, r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("FILE CID: ", fcid)
|
||||
|
||||
var mine int32 = 1
|
||||
done := make(chan struct{})
|
||||
minedTwo := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
defer close(done)
|
||||
|
||||
prevExpect := 0
|
||||
for atomic.LoadInt32(&mine) != 0 {
|
||||
wait := make(chan int, 2)
|
||||
mdone := func(mined bool) {
|
||||
go func() {
|
||||
n := 0
|
||||
if mined {
|
||||
n = 1
|
||||
}
|
||||
wait <- n
|
||||
}()
|
||||
}
|
||||
|
||||
if err := sn[0].MineOne(ctx, mdone); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := sn[1].MineOne(ctx, mdone); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
expect := <-wait
|
||||
expect += <-wait
|
||||
|
||||
time.Sleep(blocktime)
|
||||
|
||||
for {
|
||||
n := 0
|
||||
for i, node := range sn {
|
||||
mb, err := node.MiningBase(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(mb.Cids()) != expect {
|
||||
log.Warnf("node %d mining base not complete (%d, want %d)", i, len(mb.Cids()), expect)
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n == len(sn) {
|
||||
break
|
||||
}
|
||||
time.Sleep(blocktime)
|
||||
}
|
||||
|
||||
if prevExpect == 2 && expect == 2 && minedTwo != nil {
|
||||
close(minedTwo)
|
||||
minedTwo = nil
|
||||
}
|
||||
|
||||
prevExpect = expect
|
||||
}
|
||||
}()
|
||||
|
||||
deal := startDeal(t, ctx, provider, client, fcid)
|
||||
|
||||
// TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this
|
||||
time.Sleep(time.Second)
|
||||
|
||||
waitDealSealed(t, ctx, client, deal)
|
||||
|
||||
<-minedTwo
|
||||
|
||||
atomic.StoreInt32(&mine, 0)
|
||||
fmt.Println("shutting down mining")
|
||||
<-done
|
||||
}
|
||||
|
||||
+21
-9
@@ -4,9 +4,11 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestNode struct {
|
||||
@@ -16,7 +18,16 @@ type TestNode struct {
|
||||
type TestStorageNode struct {
|
||||
api.StorageMiner
|
||||
|
||||
MineOne func(context.Context) error
|
||||
MineOne func(context.Context, func(bool)) error
|
||||
}
|
||||
|
||||
var PresealGenesis = -1
|
||||
|
||||
const GenesisPreseals = 2
|
||||
|
||||
type StorageMiner struct {
|
||||
Full int
|
||||
Preseal int
|
||||
}
|
||||
|
||||
// APIBuilder is a function which is invoked in test suite to provide
|
||||
@@ -24,7 +35,7 @@ type TestStorageNode struct {
|
||||
//
|
||||
// storage array defines storage nodes, numbers in the array specify full node
|
||||
// index the storage node 'belongs' to
|
||||
type APIBuilder func(t *testing.T, nFull int, storage []int) ([]TestNode, []TestStorageNode)
|
||||
type APIBuilder func(t *testing.T, nFull int, storage []StorageMiner) ([]TestNode, []TestStorageNode)
|
||||
type testSuite struct {
|
||||
makeNodes APIBuilder
|
||||
}
|
||||
@@ -39,25 +50,26 @@ func TestApis(t *testing.T, b APIBuilder) {
|
||||
t.Run("id", ts.testID)
|
||||
t.Run("testConnectTwo", ts.testConnectTwo)
|
||||
t.Run("testMining", ts.testMining)
|
||||
t.Run("testMiningReal", ts.testMiningReal)
|
||||
}
|
||||
|
||||
var oneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}}
|
||||
|
||||
func (ts *testSuite) testVersion(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 1, []int{0})
|
||||
apis, _ := ts.makeNodes(t, 1, oneMiner)
|
||||
api := apis[0]
|
||||
|
||||
v, err := api.Version(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Version != build.BuildVersion {
|
||||
t.Error("Version didn't work properly")
|
||||
}
|
||||
require.Equal(t, v.Version, build.BuildVersion)
|
||||
}
|
||||
|
||||
func (ts *testSuite) testID(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 1, []int{0})
|
||||
apis, _ := ts.makeNodes(t, 1, oneMiner)
|
||||
api := apis[0]
|
||||
|
||||
id, err := api.ID(ctx)
|
||||
@@ -69,7 +81,7 @@ func (ts *testSuite) testID(t *testing.T) {
|
||||
|
||||
func (ts *testSuite) testConnectTwo(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
apis, _ := ts.makeNodes(t, 2, []int{0})
|
||||
apis, _ := ts.makeNodes(t, 2, oneMiner)
|
||||
|
||||
p, err := apis[0].NetPeers(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
miner2 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
sealing "github.com/filecoin-project/storage-fsm"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
)
|
||||
|
||||
func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) {
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
addrinfo, err := client.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := miner.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
|
||||
mine := true
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
for mine {
|
||||
time.Sleep(blocktime)
|
||||
if err := sn[0].MineOne(ctx, func(bool) {}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
pledgeSectors(t, ctx, miner, nSectors)
|
||||
|
||||
mine = false
|
||||
<-done
|
||||
}
|
||||
|
||||
func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
err := miner.PledgeSector(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
for {
|
||||
s, err := miner.SectorsList(ctx) // Note - the test builder doesn't import genesis sectors into FSM
|
||||
require.NoError(t, err)
|
||||
fmt.Printf("Sectors: %d\n", len(s))
|
||||
if len(s) >= n {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
fmt.Printf("All sectors is fsm\n")
|
||||
|
||||
s, err := miner.SectorsList(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
toCheck := map[abi.SectorNumber]struct{}{}
|
||||
for _, number := range s {
|
||||
toCheck[number] = struct{}{}
|
||||
}
|
||||
|
||||
for len(toCheck) > 0 {
|
||||
for n := range toCheck {
|
||||
st, err := miner.SectorsStatus(ctx, n)
|
||||
require.NoError(t, err)
|
||||
if st.State == api.SectorState(sealing.Proving) {
|
||||
delete(toCheck, n)
|
||||
}
|
||||
if strings.Contains(string(st.State), "Fail") {
|
||||
t.Fatal("sector in a failed state", st.State)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
fmt.Printf("WaitSeal: %d\n", len(s))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) {
|
||||
os.Setenv("BELLMAN_NO_GPU", "1")
|
||||
|
||||
ctx := context.Background()
|
||||
n, sn := b(t, 1, oneMiner)
|
||||
client := n[0].FullNode.(*impl.FullNodeAPI)
|
||||
miner := sn[0]
|
||||
|
||||
addrinfo, err := client.NetAddrsListen(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := miner.NetConnect(ctx, addrinfo); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
|
||||
mine := true
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
for mine {
|
||||
time.Sleep(blocktime)
|
||||
if err := sn[0].MineOne(ctx, func(bool) {}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
pledgeSectors(t, ctx, miner, nSectors)
|
||||
|
||||
maddr, err := miner.ActorAddress(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
di, err := client.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Printf("Running one proving periods\n")
|
||||
|
||||
for {
|
||||
head, err := client.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
if head.Height() > di.PeriodStart+(miner2.WPoStProvingPeriod)+2 {
|
||||
break
|
||||
}
|
||||
|
||||
if head.Height()%100 == 0 {
|
||||
fmt.Printf("@%d\n", head.Height())
|
||||
}
|
||||
time.Sleep(blocktime)
|
||||
}
|
||||
|
||||
p, err := client.StateMinerPower(ctx, maddr, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
|
||||
ssz, err := miner.ActorSectorSize(ctx, maddr)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, p.MinerPower, p.TotalPower)
|
||||
require.Equal(t, p.MinerPower.RawBytePower, types.NewInt(uint64(ssz)*uint64(nSectors+GenesisPreseals)))
|
||||
|
||||
// TODO: Inject faults here
|
||||
|
||||
mine = false
|
||||
<-done
|
||||
}
|
||||
+12
-30
@@ -3,40 +3,12 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
type DealState = uint64
|
||||
|
||||
const (
|
||||
DealUnknown = DealState(iota)
|
||||
DealRejected // Provider didn't like the proposal
|
||||
DealAccepted // Proposal accepted, data moved
|
||||
DealStaged // Data put into the sector
|
||||
DealSealing // Data in process of being sealed
|
||||
|
||||
DealFailed
|
||||
DealComplete
|
||||
|
||||
// Internal
|
||||
|
||||
DealError // deal failed with an unexpected error
|
||||
|
||||
DealNoUpdate = DealUnknown
|
||||
)
|
||||
|
||||
var DealStates = []string{
|
||||
"DealUnknown",
|
||||
"DealRejected",
|
||||
"DealAccepted",
|
||||
"DealStaged",
|
||||
"DealSealing",
|
||||
"DealFailed",
|
||||
"DealComplete",
|
||||
"DealError",
|
||||
}
|
||||
|
||||
// TODO: check if this exists anywhere else
|
||||
|
||||
type MultiaddrSlice []ma.Multiaddr
|
||||
|
||||
func (m *MultiaddrSlice) UnmarshalJSON(raw []byte) (err error) {
|
||||
@@ -57,3 +29,13 @@ func (m *MultiaddrSlice) UnmarshalJSON(raw []byte) (err error) {
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = new(MultiaddrSlice)
|
||||
|
||||
type ObjStat struct {
|
||||
Size uint64
|
||||
Links uint64
|
||||
}
|
||||
|
||||
type PubsubScore struct {
|
||||
ID peer.ID
|
||||
Score float64
|
||||
}
|
||||
|
||||
+5
-5
@@ -3,13 +3,13 @@ package api
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
)
|
||||
|
||||
type SignFunc = func(context.Context, []byte) (*types.Signature, error)
|
||||
type SignFunc = func(context.Context, []byte) (*crypto.Signature, error)
|
||||
|
||||
type Signer func(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
type Signer func(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
|
||||
type Signable interface {
|
||||
Sign(context.Context, SignFunc) error
|
||||
@@ -17,7 +17,7 @@ type Signable interface {
|
||||
|
||||
func SignWith(ctx context.Context, signer Signer, addr address.Address, signable ...Signable) error {
|
||||
for _, s := range signable {
|
||||
err := s.Sign(ctx, func(ctx context.Context, b []byte) (*types.Signature, error) {
|
||||
err := s.Sign(ctx, func(ctx context.Context, b []byte) (*crypto.Signature, error) {
|
||||
return signer(ctx, addr, b)
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
GOCC=${GOCC=go}
|
||||
|
||||
die() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
have_binary() {
|
||||
type "$1" > /dev/null 2> /dev/null
|
||||
}
|
||||
|
||||
check_writable() {
|
||||
printf "" > "$1" && rm "$1"
|
||||
}
|
||||
|
||||
try_download() {
|
||||
url="$1"
|
||||
output="$2"
|
||||
command="$3"
|
||||
util_name="$(set -- $command; echo "$1")"
|
||||
|
||||
if ! have_binary "$util_name"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '==> Using %s to download "%s" to "%s"\n' "$util_name" "$url" "$output"
|
||||
if eval "$command"; then
|
||||
echo "==> Download complete!"
|
||||
return
|
||||
else
|
||||
echo "error: couldn't download with $util_name ($?)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
download() {
|
||||
dl_url="$1"
|
||||
dl_output="$2"
|
||||
|
||||
test "$#" -eq "2" || die "download requires exactly two arguments, was given $@"
|
||||
|
||||
if ! check_writable "$dl_output"; then
|
||||
die "download error: cannot write to $dl_output"
|
||||
fi
|
||||
|
||||
try_download "$dl_url" "$dl_output" "wget '$dl_url' -O '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "curl --silent --fail --output '$dl_output' '$dl_url'" && return
|
||||
try_download "$dl_url" "$dl_output" "fetch '$dl_url' -o '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "http '$dl_url' > '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "ftp -o '$dl_output' '$dl_url'" && return
|
||||
|
||||
die "Unable to download $dl_url. exiting."
|
||||
}
|
||||
|
||||
unarchive() {
|
||||
ua_archivetype="$1"
|
||||
ua_infile="$2"
|
||||
ua_outfile="$3"
|
||||
ua_distname="$4"
|
||||
ua_binpostfix=""
|
||||
ua_os=$(uname -o)
|
||||
|
||||
if [ "$ua_os" = "Msys" ] || [ "$ua_os" = "Cygwin" ] ; then
|
||||
ua_binpostfix=".exe"
|
||||
fi
|
||||
ua_outfile="$ua_outfile$ua_binpostfix"
|
||||
|
||||
if ! check_writable "$ua_outfile"; then
|
||||
die "unarchive error: cannot write to $ua_outfile"
|
||||
fi
|
||||
|
||||
case "$ua_archivetype" in
|
||||
tar.gz)
|
||||
if have_binary tar; then
|
||||
echo "==> using 'tar' to extract binary from archive"
|
||||
< "$ua_infile" tar -Ozxf - "$ua_distname/$ua_distname$ua_binpostfix" > "$ua_outfile" \
|
||||
|| die "tar has failed"
|
||||
else
|
||||
die "no binary on system for extracting tar files"
|
||||
fi
|
||||
;;
|
||||
zip)
|
||||
if have_binary unzip; then
|
||||
echo "==> using 'unzip' to extract binary from archive"
|
||||
unzip -p "$ua_infile" "$ua_distname/$ua_distname$ua_binpostfix" > "$ua_outfile" \
|
||||
|| die "unzip has failed"
|
||||
else
|
||||
die "no installed method for extracting .zip archives"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
die "unrecognized archive type '$ua_archivetype'"
|
||||
esac
|
||||
|
||||
chmod +x "$ua_outfile" || die "chmod has failed"
|
||||
}
|
||||
|
||||
get_go_vars() {
|
||||
if [ ! -z "$GOOS" ] && [ ! -z "$GOARCH" ]; then
|
||||
printf "%s-%s" "$GOOS" "$GOARCH"
|
||||
elif have_binary go; then
|
||||
printf "%s-%s" "$($GOCC env GOOS)" "$($GOCC env GOARCH)"
|
||||
else
|
||||
die "no way of determining system GOOS and GOARCH\nPlease manually set GOOS and GOARCH then retry."
|
||||
fi
|
||||
}
|
||||
|
||||
mkurl() {
|
||||
m_root="$1"
|
||||
m_name="$2"
|
||||
m_vers="$3"
|
||||
m_archive="$4"
|
||||
m_govars=$(get_go_vars) || die "could not get go env vars"
|
||||
|
||||
echo "https://ipfs.io$m_root/$m_name/$m_vers/${m_name}_${m_vers}_$m_govars.$m_archive"
|
||||
}
|
||||
|
||||
distroot="$1"
|
||||
distname="$2"
|
||||
outpath="$3"
|
||||
version="$4"
|
||||
|
||||
if [ -z "$distroot" ] || [ -z "$distname" ] || [ -z "$outpath" ] || [ -z "$version" ]; then
|
||||
die "usage: dist_get <distroot> <distname> <outpath> <version>"
|
||||
fi
|
||||
|
||||
case $version in
|
||||
v*)
|
||||
# correct input
|
||||
;;
|
||||
*)
|
||||
echo "invalid version '$version'" >&2
|
||||
die "versions must begin with 'v', for example: v0.4.0"
|
||||
;;
|
||||
esac
|
||||
|
||||
# TODO: don't depend on the go tool being installed to detect this
|
||||
goenv=$(get_go_vars) || die "could not get go env vars"
|
||||
|
||||
case $goenv in
|
||||
linux-*)
|
||||
archive="tar.gz"
|
||||
;;
|
||||
darwin-*)
|
||||
archive="tar.gz"
|
||||
;;
|
||||
windows-*)
|
||||
archive="zip"
|
||||
;;
|
||||
freebsd-*)
|
||||
archive="tar.gz"
|
||||
;;
|
||||
openbsd-*)
|
||||
archive="tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo "unrecognized system environment: $goenv" >&2
|
||||
die "currently only linux, darwin, windows and freebsd are supported by this script"
|
||||
esac
|
||||
|
||||
|
||||
mkdir -p bin/tmp
|
||||
|
||||
url=$(mkurl "$distroot" "$distname" "$version" "$archive")
|
||||
tmpfi="bin/tmp/$distname.$archive"
|
||||
|
||||
download "$url" "$tmpfi"
|
||||
if [ $? -ne 0 ]; then
|
||||
die "failed to download $url to $tmpfi"
|
||||
fi
|
||||
|
||||
unarchive "$archive" "$tmpfi" "$outpath" "$distname"
|
||||
if [ $? -ne 0 ]; then
|
||||
die "failed to extract archive $tmpfi"
|
||||
fi
|
||||
@@ -1,6 +1,6 @@
|
||||
/dns4/lotus-bootstrap-0.dfw.fil-test.net/tcp/1347/p2p/12D3KooWHwGBSiLR5ts7KW9MgH4BMzC2iXe18kwAQ8Ee3LUd1jeR
|
||||
/dns4/lotus-bootstrap-1.dfw.fil-test.net/tcp/1347/p2p/12D3KooWCLFaawdhLGcSpiqg43DtZ9QzPQ6HcB8Vvyu2Cnta8UWc
|
||||
/dns4/lotus-bootstrap-0.fra.fil-test.net/tcp/1347/p2p/12D3KooWMmaL7eaUCF6tVAghVmgozxz4uztbuFUQv6dyFpHRarHR
|
||||
/dns4/lotus-bootstrap-1.fra.fil-test.net/tcp/1347/p2p/12D3KooWLLpNYoKdf9NgcWudBhXLdTcXncqAsTzozw1scMMu6nS5
|
||||
/dns4/lotus-bootstrap-0.sin.fil-test.net/tcp/1347/p2p/12D3KooWCNL9vXaXwNs3Bu8uRAJK4pxpCyPeM7jZLSDpJma1wrV8
|
||||
/dns4/lotus-bootstrap-1.sin.fil-test.net/tcp/1347/p2p/12D3KooWNGGxFda1eC5U2YKAgs4ypoFHn3Z3xHCsjmFdrCcytoxm
|
||||
/dns4/t01000.miner.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWALjK9ai1gzssE4H9kwmS7CVqkENedeJJzmgVS9YMtLii
|
||||
/ip4/34.217.110.132/tcp/1347/p2p/12D3KooWALjK9ai1gzssE4H9kwmS7CVqkENedeJJzmgVS9YMtLii
|
||||
/dns4/peer0.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWSJYjQMi2MGaeyRAWvDnEAAC7GdFSRXk5Lns6G1ajSMA7
|
||||
/ip4/54.187.182.170/tcp/1347/p2p/12D3KooWSJYjQMi2MGaeyRAWvDnEAAC7GdFSRXk5Lns6G1ajSMA7
|
||||
/dns4/peer1.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWPdeoNGcaGTxWdENfzEYJ9WqGVkJAxi2aHpX4TjkbeGkj
|
||||
/ip4/52.24.84.39/tcp/1347/p2p/12D3KooWPdeoNGcaGTxWdENfzEYJ9WqGVkJAxi2aHpX4TjkbeGkj
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
package build
|
||||
|
||||
const ForkCCM = 1750
|
||||
const ForkNoPowerEPSUpdates = 16450
|
||||
|
||||
+9
-3
@@ -1,16 +1,22 @@
|
||||
package build
|
||||
|
||||
import rice "github.com/GeertJohan/go.rice"
|
||||
import (
|
||||
rice "github.com/GeertJohan/go.rice"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
// moved from now-defunct build/paramfetch.go
|
||||
var log = logging.Logger("build")
|
||||
|
||||
func MaybeGenesis() []byte {
|
||||
builtinGen, err := rice.FindBox("genesis")
|
||||
if err != nil {
|
||||
log.Warn("loading built-in genesis: %s", err)
|
||||
log.Warnf("loading built-in genesis: %s", err)
|
||||
return nil
|
||||
}
|
||||
genBytes, err := builtinGen.Bytes("devnet.car")
|
||||
if err != nil {
|
||||
log.Warn("loading built-in genesis: %s", err)
|
||||
log.Warnf("loading built-in genesis: %s", err)
|
||||
}
|
||||
|
||||
return genBytes
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
package build
|
||||
|
||||
import rice "github.com/GeertJohan/go.rice"
|
||||
|
||||
func ParametersJSON() []byte {
|
||||
return rice.MustFindBox("proof-params").MustBytes("parameters.json")
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
rice "github.com/GeertJohan/go.rice"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/minio/blake2b-simd"
|
||||
"go.uber.org/multierr"
|
||||
"golang.org/x/xerrors"
|
||||
pb "gopkg.in/cheggaaa/pb.v1"
|
||||
)
|
||||
|
||||
var log = logging.Logger("build")
|
||||
|
||||
//const gateway = "http://198.211.99.118/ipfs/"
|
||||
const gateway = "https://ipfs.io/ipfs/"
|
||||
const paramdir = "/var/tmp/filecoin-proof-parameters"
|
||||
const dirEnv = "FIL_PROOFS_PARAMETER_CACHE"
|
||||
|
||||
type paramFile struct {
|
||||
Cid string `json:"cid"`
|
||||
Digest string `json:"digest"`
|
||||
SectorSize uint64 `json:"sector_size"`
|
||||
}
|
||||
|
||||
type fetch struct {
|
||||
wg sync.WaitGroup
|
||||
fetchLk sync.Mutex
|
||||
|
||||
errs []error
|
||||
}
|
||||
|
||||
func getParamDir() string {
|
||||
if os.Getenv(dirEnv) == "" {
|
||||
return paramdir
|
||||
}
|
||||
return os.Getenv(dirEnv)
|
||||
}
|
||||
|
||||
func GetParams(storageSize uint64) error {
|
||||
if err := os.Mkdir(getParamDir(), 0755); err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
var params map[string]paramFile
|
||||
|
||||
paramBytes := rice.MustFindBox("proof-params").MustBytes("parameters.json")
|
||||
if err := json.Unmarshal(paramBytes, ¶ms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ft := &fetch{}
|
||||
|
||||
for name, info := range params {
|
||||
if storageSize != info.SectorSize && strings.HasSuffix(name, ".params") {
|
||||
continue
|
||||
}
|
||||
|
||||
ft.maybeFetchAsync(name, info)
|
||||
}
|
||||
|
||||
return ft.wait()
|
||||
}
|
||||
|
||||
func (ft *fetch) maybeFetchAsync(name string, info paramFile) {
|
||||
ft.wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer ft.wg.Done()
|
||||
|
||||
path := filepath.Join(getParamDir(), name)
|
||||
|
||||
err := ft.checkFile(path, info)
|
||||
if !os.IsNotExist(err) && err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ft.fetchLk.Lock()
|
||||
defer ft.fetchLk.Unlock()
|
||||
|
||||
if err := doFetch(path, info); err != nil {
|
||||
ft.errs = append(ft.errs, xerrors.Errorf("fetching file %s failed: %w", path, err))
|
||||
return
|
||||
}
|
||||
err = ft.checkFile(path, info)
|
||||
if err != nil {
|
||||
ft.errs = append(ft.errs, xerrors.Errorf("checking file %s failed: %w", path, err))
|
||||
err := os.Remove(path)
|
||||
if err != nil {
|
||||
ft.errs = append(ft.errs, xerrors.Errorf("remove file %s failed: %w", path, err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (ft *fetch) checkFile(path string, info paramFile) error {
|
||||
if os.Getenv("TRUST_PARAMS") == "1" {
|
||||
log.Warn("Assuming parameter files are ok. DO NOT USE IN PRODUCTION")
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := blake2b.New512()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sum := h.Sum(nil)
|
||||
strSum := hex.EncodeToString(sum[:16])
|
||||
if strSum == info.Digest {
|
||||
log.Infof("Parameter file %s is ok", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
return xerrors.Errorf("checksum mismatch in param file %s, %s != %s", path, strSum, info.Digest)
|
||||
}
|
||||
|
||||
func (ft *fetch) wait() error {
|
||||
ft.wg.Wait()
|
||||
return multierr.Combine(ft.errs...)
|
||||
}
|
||||
|
||||
func doFetch(out string, info paramFile) error {
|
||||
gw := os.Getenv("IPFS_GATEWAY")
|
||||
if gw == "" {
|
||||
gw = gateway
|
||||
}
|
||||
log.Infof("Fetching %s from %s", out, gw)
|
||||
|
||||
outf, err := os.OpenFile(out, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outf.Close()
|
||||
|
||||
fStat, err := outf.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header := http.Header{}
|
||||
header.Set("Range", "bytes="+strconv.FormatInt(fStat.Size(), 10)+"-")
|
||||
url, err := url.Parse(gw + info.Cid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("GET %s", url)
|
||||
|
||||
req := http.Request{
|
||||
Method: "GET",
|
||||
URL: url,
|
||||
Header: header,
|
||||
Close: true,
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(&req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bar := pb.New64(resp.ContentLength)
|
||||
bar.Units = pb.U_BYTES
|
||||
bar.ShowSpeed = true
|
||||
bar.Start()
|
||||
|
||||
_, err = io.Copy(outf, bar.NewProxyReader(resp.Body))
|
||||
|
||||
bar.Finish()
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// +build debug 2k
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
)
|
||||
|
||||
func init() {
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
|
||||
BuildType |= Build2k
|
||||
}
|
||||
|
||||
// Seconds
|
||||
const BlockDelay = 2
|
||||
|
||||
const PropagationDelay = 3
|
||||
|
||||
// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after
|
||||
// which the miner is slashed
|
||||
//
|
||||
// Epochs
|
||||
const SlashablePowerDelay = 20
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepConfidence = 6
|
||||
+4
-31
@@ -2,36 +2,9 @@
|
||||
|
||||
package build
|
||||
|
||||
import "os"
|
||||
|
||||
var SectorSizes = []uint64{1024}
|
||||
|
||||
// Seconds
|
||||
const BlockDelay = 6
|
||||
|
||||
const PropagationDelay = 3
|
||||
|
||||
// FallbackPoStDelay is the number of epochs the miner needs to wait after
|
||||
// ElectionPeriodStart before starting fallback post computation
|
||||
//
|
||||
// Epochs
|
||||
const FallbackPoStDelay = 10
|
||||
|
||||
// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after
|
||||
// which the miner is slashed
|
||||
//
|
||||
// Epochs
|
||||
const SlashablePowerDelay = 20
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepDelay = 2
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepConfidence = 6
|
||||
|
||||
// Bytes
|
||||
var MinimumMinerPower uint64 = 2 << 10 // 2KiB
|
||||
|
||||
func init() {
|
||||
os.Setenv("TRUST_PARAMS", "1")
|
||||
InsecurePoStValidation = true
|
||||
BuildType |= BuildDebug
|
||||
}
|
||||
|
||||
// NOTE: Also includes settings from params_2k
|
||||
|
||||
+58
-38
@@ -2,31 +2,49 @@ package build
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
func DefaultSectorSize() abi.SectorSize {
|
||||
szs := make([]abi.SectorSize, 0, len(miner.SupportedProofTypes))
|
||||
for spt := range miner.SupportedProofTypes {
|
||||
ss, err := spt.SectorSize()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
szs = append(szs, ss)
|
||||
}
|
||||
|
||||
sort.Slice(szs, func(i, j int) bool {
|
||||
return szs[i] < szs[j]
|
||||
})
|
||||
|
||||
return szs[0]
|
||||
}
|
||||
|
||||
// Core network constants
|
||||
|
||||
func BlocksTopic(netName dtypes.NetworkName) string { return "/fil/blocks/" + string(netName) }
|
||||
func MessagesTopic(netName dtypes.NetworkName) string { return "/fil/msgs/" + string(netName) }
|
||||
func DhtProtocolName(netName dtypes.NetworkName) protocol.ID {
|
||||
return protocol.ID("/fil/kad/" + string(netName))
|
||||
}
|
||||
|
||||
// /////
|
||||
// Storage
|
||||
|
||||
const UnixfsChunkSize uint64 = 1 << 20
|
||||
const UnixfsLinksPerLevel = 1024
|
||||
|
||||
func SupportedSectorSize(ssize uint64) bool {
|
||||
for _, ss := range SectorSizes {
|
||||
if ssize == ss {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// /////
|
||||
// Payments
|
||||
|
||||
// Epochs
|
||||
const PaymentChannelClosingDelay = 6 * 60 * 60 / BlockDelay // six hours
|
||||
|
||||
// /////
|
||||
// Consensus / Network
|
||||
|
||||
@@ -37,10 +55,11 @@ const AllowableClockDrift = 1
|
||||
const ForkLengthThreshold = Finality
|
||||
|
||||
// Blocks (e)
|
||||
const BlocksPerEpoch = 5
|
||||
var BlocksPerEpoch = uint64(builtin.ExpectedLeadersPerEpoch)
|
||||
|
||||
// Epochs
|
||||
const Finality = 500
|
||||
const Finality = miner.ChainFinalityish
|
||||
const MessageConfidence = 5
|
||||
|
||||
// constants for Weight calculation
|
||||
// The ratio of weight contributed by short-term vs long-term factors in a given round
|
||||
@@ -54,22 +73,18 @@ const WRatioDen = 2
|
||||
const SealRandomnessLookback = Finality
|
||||
|
||||
// Epochs
|
||||
const SealRandomnessLookbackLimit = SealRandomnessLookback + 2000
|
||||
const SealRandomnessLookbackLimit = SealRandomnessLookback + 2000 // TODO: Get from spec specs-actors
|
||||
|
||||
// 1 / n
|
||||
const SectorChallengeRatioDiv = 25
|
||||
|
||||
const MaxFallbackPostChallengeCount = 10
|
||||
// Maximum lookback that randomness can be sourced from for a seal proof submission
|
||||
const MaxSealLookback = SealRandomnessLookbackLimit + 2000 // TODO: Get from specs-actors
|
||||
|
||||
// /////
|
||||
// Mining
|
||||
|
||||
// Epochs
|
||||
const EcRandomnessLookback = 300
|
||||
const TicketRandomnessLookback = 1
|
||||
|
||||
const PowerCollateralProportion = 5
|
||||
const PerCapitaCollateralProportion = 1
|
||||
const CollateralPrecision = 1000
|
||||
const WinningPoStSectorSetLookback = 10
|
||||
|
||||
// /////
|
||||
// Devnet settings
|
||||
@@ -77,23 +92,15 @@ const CollateralPrecision = 1000
|
||||
const TotalFilecoin = 2_000_000_000
|
||||
const MiningRewardTotal = 1_400_000_000
|
||||
|
||||
const InitialRewardStr = "153856861913558700202"
|
||||
|
||||
var InitialReward *big.Int
|
||||
|
||||
const FilecoinPrecision = 1_000_000_000_000_000_000
|
||||
|
||||
var InitialRewardBalance *big.Int
|
||||
|
||||
// TODO: Move other important consts here
|
||||
|
||||
func init() {
|
||||
InitialReward = new(big.Int)
|
||||
|
||||
var ok bool
|
||||
InitialReward, ok = InitialReward.
|
||||
SetString(InitialRewardStr, 10)
|
||||
if !ok {
|
||||
panic("could not parse InitialRewardStr")
|
||||
}
|
||||
InitialRewardBalance = big.NewInt(MiningRewardTotal)
|
||||
InitialRewardBalance = InitialRewardBalance.Mul(InitialRewardBalance, big.NewInt(FilecoinPrecision))
|
||||
}
|
||||
|
||||
// Sync
|
||||
@@ -102,3 +109,16 @@ const BadBlockCacheSize = 1 << 15
|
||||
// assuming 4000 messages per round, this lets us not lose any messages across a
|
||||
// 10 block reorg.
|
||||
const BlsSignatureCacheSize = 40000
|
||||
|
||||
// Size of signature verification cache
|
||||
// 32k keeps the cache around 10MB in size, max
|
||||
const VerifSigCacheSize = 32000
|
||||
|
||||
// ///////
|
||||
// Limits
|
||||
|
||||
// TODO: If this is gonna stay, it should move to specs-actors
|
||||
const BlockMessageLimit = 512
|
||||
const BlockGasLimit = 100_000_000_000
|
||||
|
||||
var DrandChain = `{"public_key":"922a2e93828ff83345bae533f5172669a26c02dc76d6bf59c80892e12ab1455c229211886f35bb56af6d5bea981024df","period":25,"genesis_time":1590445175,"hash":"138a324aa6540f93d0dad002aa89454b1bec2b6e948682cde6bd4db40f4b7c9b"}`
|
||||
|
||||
+17
-25
@@ -1,34 +1,26 @@
|
||||
// +build !debug
|
||||
// +build !2k
|
||||
|
||||
package build
|
||||
|
||||
var SectorSizes = []uint64{
|
||||
1 << 30,
|
||||
32 << 30,
|
||||
import (
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
)
|
||||
|
||||
func init() {
|
||||
power.ConsensusMinerMinPower = big.NewInt(1024 << 20)
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG512MiBSeal: {},
|
||||
abi.RegisteredProof_StackedDRG32GiBSeal: {},
|
||||
abi.RegisteredProof_StackedDRG64GiBSeal: {},
|
||||
}
|
||||
}
|
||||
|
||||
// Seconds
|
||||
const BlockDelay = 45
|
||||
const BlockDelay = builtin.EpochDurationSeconds
|
||||
|
||||
const PropagationDelay = 6
|
||||
|
||||
// FallbackPoStDelay is the number of epochs the miner needs to wait after
|
||||
// ElectionPeriodStart before starting fallback post computation
|
||||
//
|
||||
// Epochs
|
||||
const FallbackPoStDelay = 30
|
||||
|
||||
// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after
|
||||
// which the miner is slashed
|
||||
//
|
||||
// Epochs
|
||||
const SlashablePowerDelay = 200
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepDelay = 8
|
||||
|
||||
// Epochs
|
||||
const InteractivePoRepConfidence = 6
|
||||
|
||||
// Bytes
|
||||
var MinimumMinerPower uint64 = 512 << 30 // 512GB
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
function b64() {
|
||||
f="$1"
|
||||
case `uname` in
|
||||
Darwin)
|
||||
base64 -i "$f"
|
||||
;;
|
||||
Linux)
|
||||
base64 "$f" -w 0
|
||||
;;
|
||||
esac
|
||||
printf "unsupported system" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
sed "s/{{PARAMSJSON}}/$(b64 build/proof-params/parameters.json)/g" build/proof-params/paramfetch.sh.template > ./build/paramfetch.sh
|
||||
chmod +x ./build/paramfetch.sh
|
||||
@@ -1,103 +1,152 @@
|
||||
{
|
||||
"v20-proof-of-spacetime-election-5f585aca354eb68e411c8582ed0efd800792430e4e76d73468c4fc03f1a8d6d2.params": {
|
||||
"cid": "QmX7tYeNPWae2fjZ3Am6GB9dmHvLqvoz8dKo3PR98VYxH9",
|
||||
"digest": "39a9edec3355516674f0d12b926be493",
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.params": {
|
||||
"cid": "QmYkygifkXnrnsN4MJsjBFHTQJHx294CyikDgDK8nYxdGh",
|
||||
"digest": "df3f30442a6d6b4192f5071fb17e820c",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.vk": {
|
||||
"cid": "QmdXyqbmy2bkJA9Kyhh6z25GrTCq48LwX6c1mxPsm54wi7",
|
||||
"digest": "0bea3951abf9557a3569f68e52a30c6c",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.params": {
|
||||
"cid": "Qmf5XZZtP5VcYTf65MbKjLVabcS6cYMbr2rFShmfJzh5e5",
|
||||
"digest": "655e6277638edc8c658094f6f0b33d54",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.vk": {
|
||||
"cid": "QmPuhdWnAXBks43emnkqi9FQzyU1gASKyz23zrD27BPGs8",
|
||||
"digest": "57690e3a6a94c3f704802a674b34f36b",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.params": {
|
||||
"cid": "QmPNVgTN7N5vDtD5u7ERMTLcvUtrKRBfYVUDr6uW3pKhX7",
|
||||
"digest": "3d390654f58e603b896ac70c653f5676",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.vk": {
|
||||
"cid": "Qmbj61Zez7v5xA7nSCnmWbyLYznWJDWeusz7Yg8EcgVdoN",
|
||||
"digest": "8c170a164743c39576a7f47a1b51e6f3",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.params": {
|
||||
"cid": "QmRApb8RZoBK3cqicT7V3ydXg8yVvqPFMPrQNXP33aBihp",
|
||||
"digest": "b1b58ff9a297b82885e8a7dfb035f83c",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.vk": {
|
||||
"cid": "QmcytF1dTdqMFoyXi931j1RgmGtLfR9LLLaBznRt1tPQyD",
|
||||
"digest": "1a09e00c641f192f55af3433a028f050",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.params": {
|
||||
"cid": "QmPvr54tWaVeP4WnekivzUAJitTqsQfvikBvAHNEaDNQSw",
|
||||
"digest": "9380e41368ed4083dbc922b290d3b786",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.vk": {
|
||||
"cid": "QmXyVLVDRCcxA9SjT7PeK8HFtyxZ2ZH3SHa8KoGLw8VGJt",
|
||||
"digest": "f0731a7e20f90704bd38fc5d27882f6d",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.params": {
|
||||
"cid": "Qmf5f6ko3dqj7qauzXpZqxM9B2x2sL977K6gE2ppNwuJPv",
|
||||
"digest": "273ebb8c896326b7c292bee8b775fd38",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.vk": {
|
||||
"cid": "QmfP3MQe8koW63n5MkDENENVHxib78MJYYyZvbneCsuze8",
|
||||
"digest": "3dd94da9da64e51b3445bc528d84e76d",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.params": {
|
||||
"cid": "QmYEeeCE8uT2bsVkxcqqUYeMmMEbe6rfmo8wQCv7jFHqqm",
|
||||
"digest": "c947f2021304ed43b7216f7a8436e294",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v20-proof-of-spacetime-election-5f585aca354eb68e411c8582ed0efd800792430e4e76d73468c4fc03f1a8d6d2.vk": {
|
||||
"cid": "QmbNGx7pNbGiEr8ykoHxVXHW2LNSmGdsxKtj1onZCyguCX",
|
||||
"digest": "0227ae7df4f2affe529ebafbbc7540ee",
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.vk": {
|
||||
"cid": "QmXB63ExriFjB4ywWnXTnFwCcLFfCeEP3h15qtL5i7F4aX",
|
||||
"digest": "ab20d7b253e7e9a0d2ccdf7599ec8ec3",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v20-proof-of-spacetime-election-a4e18190d4b4657ba1b4d08a341871b2a6f398e327cb9951b28ab141fbdbf49d.params": {
|
||||
"cid": "QmRGZsNp4mp1cZshcXqt3VMuWscAEsiMa2iepF4CsWWoiv",
|
||||
"digest": "991041a354b12c280542741f58c7f2ca",
|
||||
"sector_size": 1024
|
||||
},
|
||||
"v20-proof-of-spacetime-election-a4e18190d4b4657ba1b4d08a341871b2a6f398e327cb9951b28ab141fbdbf49d.vk": {
|
||||
"cid": "QmWpmrhCGVcfqLyqp5oGAnhPmCE5hGTPaauHi25mpQwRSU",
|
||||
"digest": "91fac550e1f9bccab213830bb0c85bd6",
|
||||
"sector_size": 1024
|
||||
},
|
||||
"v20-proof-of-spacetime-election-a9eb6d90b896a282ec2d3a875c6143e3fcff778f0da1460709e051833651559b.params": {
|
||||
"cid": "QmenSZXh1EsSyHiSRvA6wb8yaPhYBTjrKehJw96Px5HnN4",
|
||||
"digest": "6322eacd2773163ddd51f9ca7d645fc4",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v20-proof-of-spacetime-election-a9eb6d90b896a282ec2d3a875c6143e3fcff778f0da1460709e051833651559b.vk": {
|
||||
"cid": "QmPvZoMKofw6eDhDg5ESJA2QAZP8HvM6qMQk7fw4pq9bQf",
|
||||
"digest": "0df62745fceac922e3e70847cfc70b52",
|
||||
"sector_size": 1073741824
|
||||
},
|
||||
"v20-proof-of-spacetime-election-bf872523641b1de33553db2a177df13e412d7b3b0103e6696ae0a1cf5d525259.params": {
|
||||
"cid": "QmVibFqzkZoL8cwQmzj8njPokCQGCCx4pBcUH77bzgJgV9",
|
||||
"digest": "de9d71e672f286706a1673bd57abdaac",
|
||||
"sector_size": 16777216
|
||||
},
|
||||
"v20-proof-of-spacetime-election-bf872523641b1de33553db2a177df13e412d7b3b0103e6696ae0a1cf5d525259.vk": {
|
||||
"cid": "QmZa5FX27XyiEXQQLQpHqtMJKLzrcY8wMuj3pxzmSimSyu",
|
||||
"digest": "7f796d3a0f13499181e44b5eee0cc744",
|
||||
"sector_size": 16777216
|
||||
},
|
||||
"v20-proof-of-spacetime-election-ffc3fb192364238b60977839d14e3154d4a98313e30d46694a12af54b6874975.params": {
|
||||
"cid": "Qmbt2SWWAmMcYoY3DAiRDXA8fAuqdqRLWucJMSxYmzBCmN",
|
||||
"digest": "151ae0ae183fc141e8c2bebc28e5cc10",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v20-proof-of-spacetime-election-ffc3fb192364238b60977839d14e3154d4a98313e30d46694a12af54b6874975.vk": {
|
||||
"cid": "QmUxvPu4xdVmjMFihUKoYyEdXBqxsXkvmxRweU7KouWHji",
|
||||
"digest": "95eb89588e9d1832aca044c3a13178af",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v20-stacked-proof-of-replication-117839dacd1ef31e5968a6fd13bcd6fa86638d85c40c9241a1d07c2a954eb89b.params": {
|
||||
"cid": "QmQZe8eLo2xXbhSDxtyYZNqEjqjdcWGdADywECRvNEZQdX",
|
||||
"digest": "fcd50e2e08a8560a6bb3418e883567ed",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v20-stacked-proof-of-replication-117839dacd1ef31e5968a6fd13bcd6fa86638d85c40c9241a1d07c2a954eb89b.vk": {
|
||||
"cid": "Qme1hn6QT1covfoUFGDZkqoE1pMTax9FNW3nWWmTNqFe7y",
|
||||
"digest": "872e244d86499fd659082e3bcf3f13e7",
|
||||
"sector_size": 268435456
|
||||
},
|
||||
"v20-stacked-proof-of-replication-b46f3a1051afbb67f70aae7082da95def62eee943662f3e1bf69837fb08aaae4.params": {
|
||||
"cid": "QmSfrPDC9jwY4MKrjzhCqDBBAG44wSDM8oE5NuDwWSh2xN",
|
||||
"digest": "0a338b941c5f17946340de5fc95cab30",
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.params": {
|
||||
"cid": "QmW5Yxg3L1NSzuQVcRMHMbG3uvVoi4dTLzVaDpnEUPQpnA",
|
||||
"digest": "079ba19645828ae42b22b0e3f4866e8d",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v20-stacked-proof-of-replication-b46f3a1051afbb67f70aae7082da95def62eee943662f3e1bf69837fb08aaae4.vk": {
|
||||
"cid": "QmTDGynCmnbaZNBP3Bv3F3duC3ecKRubCKeMUiQQZYbGpF",
|
||||
"digest": "c752e070a6b7aa8b79aa661a6b600b55",
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.vk": {
|
||||
"cid": "QmQzZ5dJ11tcSBees38WX41tZLXS9BqpEti253m5QcnTNs",
|
||||
"digest": "c76125a50a7de315165de359b5174ae4",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v20-stacked-proof-of-replication-e71093863cadc71de61f38311ee45816633973bbf34849316b147f8d2e66f199.params": {
|
||||
"cid": "QmXjSSnMUnc7EjQBYtTHhvLU3kXJTbUyhVhJRSTRehh186",
|
||||
"digest": "efa407fd09202dffd15799a8518e73d3",
|
||||
"sector_size": 1024
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.params": {
|
||||
"cid": "QmNk3wga1tS53FUu1QnkK8ehWA2cqpCnSEAPv3KLxdJxNa",
|
||||
"digest": "421e4790c0b80e0107a7ff67acf14084",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v20-stacked-proof-of-replication-e71093863cadc71de61f38311ee45816633973bbf34849316b147f8d2e66f199.vk": {
|
||||
"cid": "QmYHW3zhQouDP4okFbXSsRMcZ8bokKGvzxqbv7ZrunPMiG",
|
||||
"digest": "b2f09a0ccb62da28c890d5b881c8dcd2",
|
||||
"sector_size": 1024
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.vk": {
|
||||
"cid": "QmVQCHGsrUtbn9RjHs1e6GXfeXDW5m9w4ge48PSX3Z2as2",
|
||||
"digest": "8b60e9cc1470a6729c687d6cf0a1f79c",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v20-stacked-proof-of-replication-e99a585174b6a45b254ba4780d72c89ad808c305c6d11711009ade4f39dba8e9.params": {
|
||||
"cid": "QmUhyfNeLb32LfSkjsUwTFYLXQGMj6JQ8daff4DdVMt79q",
|
||||
"digest": "b53c1916a63839ec345aa2224e9198b7",
|
||||
"sector_size": 1073741824
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.params": {
|
||||
"cid": "QmTL3VvydaMFWKvE5VzxjgKsJYgL9JMM4JVYNtQxdj9JK1",
|
||||
"digest": "2685f31124b22ea6b2857e5a5e87ffa3",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v20-stacked-proof-of-replication-e99a585174b6a45b254ba4780d72c89ad808c305c6d11711009ade4f39dba8e9.vk": {
|
||||
"cid": "QmWReGfbuoozNErbskmFvqV4q36BY6F2WWb4cVFc3zoYkA",
|
||||
"digest": "20d58a3fae7343481f8298a2dd493dd7",
|
||||
"sector_size": 1073741824
|
||||
"v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.vk": {
|
||||
"cid": "QmSVWbLqQYbUbbJyfsRMzEib2rfSqMtnPks1Nw22omcBQm",
|
||||
"digest": "efe703cd2839597c7ca5c2a906b74296",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v20-stacked-proof-of-replication-f571ee2386f4c65a68e802747f2d78691006fc81a67971c4d9641403fffece16.params": {
|
||||
"cid": "QmSAHu14Pe8iav6BYCt9XkpHJ73XM7tcpY4d9JK9BST9HU",
|
||||
"digest": "7698426202c7e07b26ef056d31485b3a",
|
||||
"sector_size": 16777216
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.params": {
|
||||
"cid": "QmU9dH31nZZUJnsogR4Ld4ySUcH6wm2RgmGiujwnqtbU6k",
|
||||
"digest": "fcef8e87ae2afd7a28aae44347b804cf",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v20-stacked-proof-of-replication-f571ee2386f4c65a68e802747f2d78691006fc81a67971c4d9641403fffece16.vk": {
|
||||
"cid": "QmaKtFLShnhMGVn7P9UsHjkgqtqRFSwCStqqykBN7u8dax",
|
||||
"digest": "834408e5c3fce6ec5d1bf64e64cee94e",
|
||||
"sector_size": 16777216
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.vk": {
|
||||
"cid": "QmdJ15DMGPooye5NaPcRfXUdHUDibcN7hKjbmTGuu1K4AQ",
|
||||
"digest": "2ee2b3518229680db15161d4f582af37",
|
||||
"sector_size": 2048
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.params": {
|
||||
"cid": "QmZgtxcY3tMXXQxZTA7ZTUDXLVUnfxNcerXgeW4gG2NnfP",
|
||||
"digest": "3273c7135cb75684248b475781b738ee",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.vk": {
|
||||
"cid": "QmSS6ZkAV2aGZcgKgdPpEEgihXF1ryZX8PSAZDWSoeL1d4",
|
||||
"digest": "1519b5f61d9044a59f2bdc57537c094b",
|
||||
"sector_size": 536870912
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.params": {
|
||||
"cid": "QmQBGXeiNn6hVwbR6qFarQqiNGDdKk4h9ucfyvcXyfYz2N",
|
||||
"digest": "7d5f896f435c38e93bcda6dd168d860b",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.vk": {
|
||||
"cid": "QmPrZgBVGMckEAeu5eSJnLmiAwcPQjKjZe5ir6VaQ5AxKs",
|
||||
"digest": "fe6d2de44580a0db5a4934688899b92f",
|
||||
"sector_size": 8388608
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.params": {
|
||||
"cid": "QmZL2cq45XJn5BFzagAZwgFmLrcM1W6CXoiEF9C5j5tjEF",
|
||||
"digest": "acdfed9f0512bc85a01a9fb871d475d5",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.vk": {
|
||||
"cid": "QmQ4zB7nNa1tDYNifBkExRnZtwtxZw775iaqvVsZyRi6Q2",
|
||||
"digest": "524a2f3e9d6826593caebc41bb545c40",
|
||||
"sector_size": 34359738368
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.params": {
|
||||
"cid": "QmY7DitNKXFeLQt9QoVQkfjM1EvRnprqUVxjmkTXkHDNka",
|
||||
"digest": "f27271c0537ba65ade2ec045f8fbd069",
|
||||
"sector_size": 68719476736
|
||||
},
|
||||
"v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.vk": {
|
||||
"cid": "QmUJsvoCuQ4LszPmeRVAkMYb5qY95ctz3UXKhu8xLzyFKo",
|
||||
"digest": "576b292938c6c9d0a0e721bd867a543b",
|
||||
"sector_size": 68719476736
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PARAMS='{{PARAMSJSON}}'
|
||||
|
||||
die() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
have_binary() {
|
||||
type "$1" > /dev/null 2> /dev/null
|
||||
}
|
||||
|
||||
check_writable() {
|
||||
printf "" > "$1" && rm "$1"
|
||||
}
|
||||
|
||||
try_download() {
|
||||
url="$1"
|
||||
output="$2"
|
||||
command="$3"
|
||||
util_name="$(set -- $command; echo "$1")"
|
||||
|
||||
if ! have_binary "$util_name"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '==> Using %s to download "%s" to "%s"\n' "$util_name" "$url" "$output"
|
||||
if eval "$command"; then
|
||||
echo "==> Download complete!"
|
||||
return
|
||||
else
|
||||
echo "error: couldn't download with $util_name ($?)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
download() {
|
||||
dl_url="$1"
|
||||
dl_output="$2"
|
||||
|
||||
test "$#" -eq "2" || die "download requires exactly two arguments, was given $@"
|
||||
|
||||
if ! check_writable "$dl_output"; then
|
||||
die "download error: cannot write to $dl_output"
|
||||
fi
|
||||
|
||||
try_download "$dl_url" "$dl_output" "wget '$dl_url' -O '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "curl --silent --fail --output '$dl_output' '$dl_url'" && return
|
||||
try_download "$dl_url" "$dl_output" "fetch '$dl_url' -o '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "http '$dl_url' > '$dl_output'" && return
|
||||
try_download "$dl_url" "$dl_output" "ftp -o '$dl_output' '$dl_url'" && return
|
||||
|
||||
die "Unable to download $dl_url. exiting."
|
||||
}
|
||||
|
||||
fetch_ipget() {
|
||||
local dest="$1"
|
||||
local cid="$2"
|
||||
|
||||
IPGET_PARAMS="--node=spawn -p=/ip4/138.201.67.219/tcp/4002/ws/ipfs/QmUd6zHcbkbcs7SMxwLs48qZVX3vpcM8errYS7xEczwRMA -p=/ip4/138.201.67.218/tcp/4002/ws/ipfs/QmbVWZQhCGrS7DhgLqWbgvdmKN7JueKCREVanfnVpgyq8x -p=/ip4/94.130.135.167/tcp/4002/ws/ipfs/QmUEMvxS2e7iDrereVYc5SWPauXPyNwxcy9BXZrC1QTcHE -p=/ip4/138.201.68.74/tcp/4001/ipfs/QmdnXwLrC8p1ueiq2Qya8joNvk3TVVDAut7PrikmZwubtR -p=/ip4/138.201.67.220/tcp/4001/ipfs/QmNSYxZAiJHeLdkBg38roksAR9So7Y5eojks1yjEcUtZ7i"
|
||||
|
||||
./bin/ipget $IPGET_PARAMS -o "$dest" "$cid"
|
||||
}
|
||||
|
||||
fetch_gateway() {
|
||||
local dest="$1"
|
||||
local cid="$2"
|
||||
|
||||
local url="http://198.211.99.118/ipfs/$cid"
|
||||
|
||||
download "$url" "$dest"
|
||||
}
|
||||
|
||||
OUT_DIR="/var/tmp/filecoin-proof-parameters"
|
||||
|
||||
mkdir -p $OUT_DIR
|
||||
printf $PARAMS | base64 -d | jq '. | to_entries | map("'$OUT_DIR'/\(.key) \(.value.cid) \(.value.digest)") | .[]' --raw-output | \
|
||||
while read -r dest cid digest; do
|
||||
if [[ -f "$dest" ]]; then
|
||||
b2=$(b2sum "$dest" | head -c 32)
|
||||
if [[ "$digest" == "$b2" ]]; then
|
||||
echo "$dest exists and has correct hash"
|
||||
continue
|
||||
else
|
||||
echo "$dest has incorrect hash"
|
||||
rm -f "$dest"
|
||||
fi
|
||||
fi
|
||||
echo "downloading $dest"
|
||||
|
||||
fetch_gateway "$dest" "$cid"
|
||||
done
|
||||
+26
-3
@@ -3,11 +3,33 @@ package build
|
||||
import "fmt"
|
||||
|
||||
var CurrentCommit string
|
||||
var BuildType int
|
||||
|
||||
const (
|
||||
BuildDefault = 0
|
||||
Build2k = 0x1
|
||||
BuildDebug = 0x3
|
||||
)
|
||||
|
||||
func buildType() string {
|
||||
switch BuildType {
|
||||
case BuildDefault:
|
||||
return ""
|
||||
case BuildDebug:
|
||||
return "+debug"
|
||||
case Build2k:
|
||||
return "+2k"
|
||||
default:
|
||||
return "+huh?"
|
||||
}
|
||||
}
|
||||
|
||||
// BuildVersion is the local build version, set by build system
|
||||
const BuildVersion = "0.1.5"
|
||||
const BuildVersion = "0.4.0"
|
||||
|
||||
var UserVersion = BuildVersion + CurrentCommit
|
||||
func UserVersion() string {
|
||||
return BuildVersion + buildType() + CurrentCommit
|
||||
}
|
||||
|
||||
type Version uint32
|
||||
|
||||
@@ -31,8 +53,9 @@ func (ve Version) EqMajorMinor(v2 Version) bool {
|
||||
}
|
||||
|
||||
// APIVersion is a semver version of the rpc api exposed
|
||||
var APIVersion Version = newVer(0, 1, 5)
|
||||
var APIVersion Version = newVer(0, 3, 0)
|
||||
|
||||
//nolint:varcheck,deadcode
|
||||
const (
|
||||
majorMask = 0xff0000
|
||||
minorMask = 0xffff00
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type CronActor struct{}
|
||||
|
||||
type callTuple struct {
|
||||
addr address.Address
|
||||
method uint64
|
||||
}
|
||||
|
||||
var CronActors = []callTuple{
|
||||
{StoragePowerAddress, SPAMethods.CheckProofSubmissions},
|
||||
}
|
||||
|
||||
type CronActorState struct{}
|
||||
|
||||
type cAMethods struct {
|
||||
EpochTick uint64
|
||||
}
|
||||
|
||||
var CAMethods = cAMethods{2}
|
||||
|
||||
func (ca CronActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: nil,
|
||||
2: ca.EpochTick,
|
||||
}
|
||||
}
|
||||
|
||||
func (ca CronActor) EpochTick(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
if vmctx.Message().From != CronAddress {
|
||||
return nil, aerrors.New(1, "EpochTick is only callable as a part of tipset state computation")
|
||||
}
|
||||
|
||||
for _, call := range CronActors {
|
||||
_, err := vmctx.Send(call.addr, call.method, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return nil, err // todo: this very bad?
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
var log = logging.Logger("actors")
|
||||
|
||||
var EmptyCBOR cid.Cid
|
||||
|
||||
const (
|
||||
GasCreateActor = 100
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
n, err := cbor.WrapObject(map[string]string{}, mh.SHA2_256, -1)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
|
||||
EmptyCBOR = n.Cid()
|
||||
}
|
||||
|
||||
type InitActor struct{}
|
||||
|
||||
type InitActorState struct {
|
||||
AddressMap cid.Cid
|
||||
|
||||
NextID uint64
|
||||
}
|
||||
|
||||
type iAMethods struct {
|
||||
Exec uint64
|
||||
}
|
||||
|
||||
var IAMethods = iAMethods{2}
|
||||
|
||||
func (ia InitActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: nil,
|
||||
2: ia.Exec,
|
||||
}
|
||||
}
|
||||
|
||||
type ExecParams struct {
|
||||
Code cid.Cid
|
||||
Params []byte
|
||||
}
|
||||
|
||||
func CreateExecParams(act cid.Cid, obj cbg.CBORMarshaler) ([]byte, aerrors.ActorError) {
|
||||
encparams, err := SerializeParams(obj)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "creating ExecParams")
|
||||
}
|
||||
|
||||
return SerializeParams(&ExecParams{
|
||||
Code: act,
|
||||
Params: encparams,
|
||||
})
|
||||
}
|
||||
|
||||
func (ia InitActor) Exec(act *types.Actor, vmctx types.VMContext, p *ExecParams) ([]byte, aerrors.ActorError) {
|
||||
beginState := vmctx.Storage().GetHead()
|
||||
|
||||
var self InitActorState
|
||||
if err := vmctx.Storage().Get(beginState, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.ChargeGas(GasCreateActor); err != nil {
|
||||
return nil, aerrors.Wrap(err, "run out of gas")
|
||||
}
|
||||
|
||||
// Make sure that only the actors defined in the spec can be launched.
|
||||
if !IsBuiltinActor(p.Code) {
|
||||
return nil, aerrors.New(1,
|
||||
"cannot launch actor instance that is not a builtin actor")
|
||||
}
|
||||
|
||||
// Ensure that singletons can be only launched once.
|
||||
// TODO: do we want to enforce this? If so how should actors be marked as such?
|
||||
if IsSingletonActor(p.Code) {
|
||||
return nil, aerrors.New(1, "cannot launch another actor of this type")
|
||||
}
|
||||
|
||||
// This generates a unique address for this actor that is stable across message
|
||||
// reordering
|
||||
creator := vmctx.Message().From
|
||||
nonce := vmctx.Message().Nonce
|
||||
addr, err := ComputeActorAddress(creator, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set up the actor itself
|
||||
actor := types.Actor{
|
||||
Code: p.Code,
|
||||
Balance: types.NewInt(0),
|
||||
Head: EmptyCBOR,
|
||||
Nonce: 0,
|
||||
}
|
||||
|
||||
// The call to the actors constructor will set up the initial state
|
||||
// from the given parameters, setting `actor.Head` to a new value when successful.
|
||||
// TODO: can constructors fail?
|
||||
//actor.Constructor(p.Params)
|
||||
|
||||
// Store the mapping of address to actor ID.
|
||||
idAddr, nerr := self.AddActor(vmctx.Ipld(), addr)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Escalate(err, "adding new actor mapping")
|
||||
}
|
||||
|
||||
// NOTE: This is a privileged call that only the init actor is allowed to make
|
||||
// FIXME: Had to comment this because state is not in interface
|
||||
state, err := vmctx.StateTree()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := state.SetActor(idAddr, &actor); err != nil {
|
||||
if xerrors.Is(err, types.ErrActorNotFound) {
|
||||
return nil, aerrors.Absorb(err, 1, "SetActor, actor not found")
|
||||
}
|
||||
return nil, aerrors.Escalate(err, "inserting new actor into state tree")
|
||||
}
|
||||
|
||||
// '1' is reserved for constructor methods
|
||||
_, err = vmctx.Send(idAddr, 1, vmctx.Message().Value, p.Params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(beginState, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return idAddr.Bytes(), nil
|
||||
}
|
||||
|
||||
func IsBuiltinActor(code cid.Cid) bool {
|
||||
switch code {
|
||||
case StorageMarketCodeCid, StoragePowerCodeCid, StorageMinerCodeCid, AccountCodeCid, InitCodeCid, MultisigCodeCid, PaymentChannelCodeCid:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func IsSingletonActor(code cid.Cid) bool {
|
||||
return code == StoragePowerCodeCid || code == StorageMarketCodeCid || code == InitCodeCid || code == CronCodeCid
|
||||
}
|
||||
|
||||
func (ias *InitActorState) AddActor(cst *hamt.CborIpldStore, addr address.Address) (address.Address, error) {
|
||||
nid := ias.NextID
|
||||
|
||||
amap, err := hamt.LoadNode(context.TODO(), cst, ias.AddressMap)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
if err := amap.Set(context.TODO(), string(addr.Bytes()), nid); err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
if err := amap.Flush(context.TODO()); err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
|
||||
ncid, err := cst.Put(context.TODO(), amap)
|
||||
if err != nil {
|
||||
return address.Undef, err
|
||||
}
|
||||
ias.AddressMap = ncid
|
||||
ias.NextID++
|
||||
|
||||
return NewIDAddress(nid)
|
||||
}
|
||||
|
||||
func (ias *InitActorState) Lookup(cst *hamt.CborIpldStore, addr address.Address) (address.Address, error) {
|
||||
amap, err := hamt.LoadNode(context.TODO(), cst, ias.AddressMap)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("ias lookup failed loading hamt node: %w", err)
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
err = amap.Find(context.TODO(), string(addr.Bytes()), &val)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("ias lookup failed to do find: %w", err)
|
||||
}
|
||||
|
||||
ival, ok := val.(uint64)
|
||||
if !ok {
|
||||
return address.Undef, fmt.Errorf("invalid value in init actor state, expected uint64, got %T", val)
|
||||
}
|
||||
|
||||
return address.NewIDAddress(ival)
|
||||
}
|
||||
|
||||
type AccountActorState struct {
|
||||
Address address.Address
|
||||
}
|
||||
|
||||
func ComputeActorAddress(creator address.Address, nonce uint64) (address.Address, ActorError) {
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := buf.Write(creator.Bytes())
|
||||
if err != nil {
|
||||
return address.Undef, aerrors.Escalate(err, "could not write address")
|
||||
}
|
||||
|
||||
err = binary.Write(buf, binary.BigEndian, nonce)
|
||||
if err != nil {
|
||||
return address.Undef, aerrors.Escalate(err, "could not write nonce")
|
||||
}
|
||||
|
||||
addr, err := address.NewActorAddress(buf.Bytes())
|
||||
if err != nil {
|
||||
return address.Undef, aerrors.Escalate(err, "could not create address")
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,157 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
func TestMinerCommitSectors(t *testing.T) {
|
||||
var worker, client address.Address
|
||||
var minerAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&worker, 1000000),
|
||||
HarnessAddr(&client, 1000000),
|
||||
HarnessActor(&minerAddr, &worker, actors.StorageMinerCodeCid,
|
||||
func() cbg.CBORMarshaler {
|
||||
return &actors.StorageMinerConstructorParams{
|
||||
Owner: worker,
|
||||
Worker: worker,
|
||||
SectorSize: 1024,
|
||||
PeerID: "fakepeerid",
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
h.vm.Syscalls.ValidatePoRep = func(ctx context.Context, maddr address.Address, ssize uint64, commD, commR, ticket, proof, seed []byte, sectorID uint64) (bool, aerrors.ActorError) {
|
||||
// all proofs are valid
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ret, _ := h.SendFunds(t, worker, minerAddr, types.NewInt(100000))
|
||||
ApplyOK(t, ret)
|
||||
|
||||
ret, _ = h.InvokeWithValue(t, client, actors.StorageMarketAddress, actors.SMAMethods.AddBalance, types.NewInt(2000), nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
addSectorToMiner(h, t, minerAddr, worker, client, 1)
|
||||
|
||||
assertSectorIDs(h, t, minerAddr, []uint64{1})
|
||||
}
|
||||
|
||||
func addSectorToMiner(h *Harness, t *testing.T, minerAddr, worker, client address.Address, sid uint64) {
|
||||
t.Helper()
|
||||
s := sectorbuilder.UserBytesForSectorSize(1024)
|
||||
deal := h.makeFakeDeal(t, minerAddr, worker, client, s)
|
||||
ret, _ := h.Invoke(t, worker, actors.StorageMarketAddress, actors.SMAMethods.PublishStorageDeals,
|
||||
&actors.PublishStorageDealsParams{
|
||||
Deals: []actors.StorageDealProposal{*deal},
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
var dealIds actors.PublishStorageDealResponse
|
||||
if err := dealIds.UnmarshalCBOR(bytes.NewReader(ret.Return)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dealid := dealIds.DealIDs[0]
|
||||
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.PreCommitSector,
|
||||
&actors.SectorPreCommitInfo{
|
||||
SectorNumber: sid,
|
||||
CommR: []byte("cats"),
|
||||
SealEpoch: 10,
|
||||
DealIDs: []uint64{dealid},
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
h.BlockHeight += 100
|
||||
ret, _ = h.Invoke(t, worker, minerAddr, actors.MAMethods.ProveCommitSector,
|
||||
&actors.SectorProveCommitInfo{
|
||||
Proof: []byte("prooofy"),
|
||||
SectorID: sid,
|
||||
DealIDs: []uint64{dealid}, // TODO: weird that i have to pass this again
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
}
|
||||
|
||||
func assertSectorIDs(h *Harness, t *testing.T, maddr address.Address, ids []uint64) {
|
||||
t.Helper()
|
||||
sectors, err := getMinerSectorSet(context.TODO(), h.vm.StateTree(), h.bs, maddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(sectors) != len(ids) {
|
||||
t.Fatal("miner has wrong number of sectors in their sector set")
|
||||
}
|
||||
|
||||
all := make(map[uint64]bool)
|
||||
for _, s := range sectors {
|
||||
all[s.SectorID] = true
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
if !all[id] {
|
||||
t.Fatal("expected to find sector ID: ", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getMinerSectorSet(ctx context.Context, st types.StateTree, bs blockstore.Blockstore, maddr address.Address) ([]*api.ChainSectorInfo, error) {
|
||||
mact, err := st.GetActor(maddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
|
||||
var mstate actors.StorageMinerActorState
|
||||
if err := cst.Get(ctx, mact.Head, &mstate); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stmgr.LoadSectorsFromSet(ctx, bs, mstate.Sectors)
|
||||
}
|
||||
|
||||
func (h *Harness) makeFakeDeal(t *testing.T, miner, worker, client address.Address, size uint64) *actors.StorageDealProposal {
|
||||
data := make([]byte, size)
|
||||
rand.Read(data)
|
||||
commP, err := sectorbuilder.GeneratePieceCommitment(bytes.NewReader(data), size)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
prop := actors.StorageDealProposal{
|
||||
PieceRef: commP[:],
|
||||
PieceSize: size,
|
||||
//PieceSerialization SerializationMode // Needs to be here as it tells how data in the sector maps to PieceRef cid
|
||||
|
||||
Client: client,
|
||||
Provider: miner,
|
||||
|
||||
ProposalExpiration: 10000,
|
||||
Duration: 150,
|
||||
|
||||
StoragePricePerEpoch: types.NewInt(1),
|
||||
StorageCollateral: types.NewInt(0),
|
||||
}
|
||||
|
||||
if err := api.SignWith(context.TODO(), h.w.Sign, client, &prop); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return &prop
|
||||
}
|
||||
@@ -1,431 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
type MultiSigActor struct{}
|
||||
type MultiSigActorState struct {
|
||||
Signers []address.Address
|
||||
Required uint64
|
||||
NextTxID uint64
|
||||
|
||||
InitialBalance types.BigInt
|
||||
StartingBlock uint64
|
||||
UnlockDuration uint64
|
||||
|
||||
//TODO: make this map/sharray/whatever
|
||||
Transactions []MTransaction
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) canSpend(act *types.Actor, amnt types.BigInt, height uint64) bool {
|
||||
if msas.UnlockDuration == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
offset := height - msas.StartingBlock
|
||||
if offset > msas.UnlockDuration {
|
||||
return true
|
||||
}
|
||||
|
||||
minBalance := types.BigDiv(msas.InitialBalance, types.NewInt(msas.UnlockDuration))
|
||||
minBalance = types.BigMul(minBalance, types.NewInt(offset))
|
||||
return !minBalance.LessThan(types.BigSub(act.Balance, amnt))
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) isSigner(addr address.Address) bool {
|
||||
for _, s := range msas.Signers {
|
||||
if s == addr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (msas MultiSigActorState) getTransaction(txid uint64) (*MTransaction, ActorError) {
|
||||
if txid >= uint64(len(msas.Transactions)) {
|
||||
return nil, aerrors.Newf(1, "could not get transaction (numbers of tx %d,want to get txid %d)", len(msas.Transactions), txid)
|
||||
}
|
||||
return &msas.Transactions[txid], nil
|
||||
}
|
||||
|
||||
type MTransaction struct {
|
||||
Created uint64 // NOT USED ??
|
||||
TxID uint64
|
||||
|
||||
To address.Address
|
||||
Value types.BigInt
|
||||
Method uint64
|
||||
Params []byte
|
||||
|
||||
Approved []address.Address
|
||||
Complete bool
|
||||
Canceled bool
|
||||
RetCode uint64
|
||||
}
|
||||
|
||||
func (tx MTransaction) Active() ActorError {
|
||||
if tx.Complete {
|
||||
return aerrors.New(2, "transaction already completed")
|
||||
}
|
||||
if tx.Canceled {
|
||||
return aerrors.New(3, "transaction canceled")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type musigMethods struct {
|
||||
MultiSigConstructor uint64
|
||||
Propose uint64
|
||||
Approve uint64
|
||||
Cancel uint64
|
||||
ClearCompleted uint64
|
||||
AddSigner uint64
|
||||
RemoveSigner uint64
|
||||
SwapSigner uint64
|
||||
ChangeRequirement uint64
|
||||
}
|
||||
|
||||
var MultiSigMethods = musigMethods{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
func (msa MultiSigActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: msa.MultiSigConstructor,
|
||||
2: msa.Propose,
|
||||
3: msa.Approve,
|
||||
4: msa.Cancel,
|
||||
//5: msa.ClearCompleted,
|
||||
6: msa.AddSigner,
|
||||
7: msa.RemoveSigner,
|
||||
8: msa.SwapSigner,
|
||||
9: msa.ChangeRequirement,
|
||||
}
|
||||
}
|
||||
|
||||
type MultiSigConstructorParams struct {
|
||||
Signers []address.Address
|
||||
Required uint64
|
||||
UnlockDuration uint64
|
||||
}
|
||||
|
||||
func (MultiSigActor) MultiSigConstructor(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigConstructorParams) ([]byte, ActorError) {
|
||||
self := &MultiSigActorState{
|
||||
Signers: params.Signers,
|
||||
Required: params.Required,
|
||||
}
|
||||
|
||||
if params.UnlockDuration != 0 {
|
||||
self.InitialBalance = vmctx.Message().Value
|
||||
self.UnlockDuration = params.UnlockDuration
|
||||
self.StartingBlock = vmctx.BlockHeight()
|
||||
}
|
||||
|
||||
head, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not put new head")
|
||||
}
|
||||
err = vmctx.Storage().Commit(EmptyCBOR, head)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not commit new head")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type MultiSigProposeParams struct {
|
||||
To address.Address
|
||||
Value types.BigInt
|
||||
Method uint64
|
||||
Params []byte
|
||||
}
|
||||
|
||||
func (MultiSigActor) load(vmctx types.VMContext) (cid.Cid, *MultiSigActorState, ActorError) {
|
||||
var self MultiSigActorState
|
||||
head := vmctx.Storage().GetHead()
|
||||
|
||||
err := vmctx.Storage().Get(head, &self)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, aerrors.Wrap(err, "could not get self")
|
||||
}
|
||||
return head, &self, nil
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) loadAndVerify(vmctx types.VMContext) (cid.Cid, *MultiSigActorState, ActorError) {
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
|
||||
if !self.isSigner(vmctx.Message().From) {
|
||||
return cid.Undef, nil, aerrors.New(1, "not authorized")
|
||||
}
|
||||
return head, self, nil
|
||||
}
|
||||
|
||||
func (MultiSigActor) save(vmctx types.VMContext, oldHead cid.Cid, self *MultiSigActorState) ActorError {
|
||||
newHead, err := vmctx.Storage().Put(self)
|
||||
if err != nil {
|
||||
return aerrors.Wrap(err, "could not put new head")
|
||||
}
|
||||
err = vmctx.Storage().Commit(oldHead, newHead)
|
||||
if err != nil {
|
||||
return aerrors.Wrap(err, "could not commit new head")
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) Propose(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigProposeParams) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.loadAndVerify(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txid := self.NextTxID
|
||||
self.NextTxID++
|
||||
|
||||
{
|
||||
tx := MTransaction{
|
||||
TxID: txid,
|
||||
To: params.To,
|
||||
Value: params.Value,
|
||||
Method: params.Method,
|
||||
Params: params.Params,
|
||||
Approved: []address.Address{vmctx.Message().From},
|
||||
}
|
||||
self.Transactions = append(self.Transactions, tx)
|
||||
}
|
||||
|
||||
tx, err := self.getTransaction(txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if self.Required == 1 {
|
||||
if !self.canSpend(act, tx.Value, vmctx.BlockHeight()) {
|
||||
return nil, aerrors.New(100, "transaction amount exceeds available")
|
||||
}
|
||||
_, err := vmctx.Send(tx.To, tx.Method, tx.Value, tx.Params)
|
||||
if aerrors.IsFatal(err) {
|
||||
return nil, err
|
||||
}
|
||||
tx.RetCode = uint64(aerrors.RetCode(err))
|
||||
tx.Complete = true
|
||||
}
|
||||
|
||||
err = msa.save(vmctx, head, self)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "saving state")
|
||||
}
|
||||
|
||||
// REVIEW: On one hand, I like being very explicit about how we're doing the serialization
|
||||
// on the other, maybe we shouldnt do direct calls to underlying serialization libs?
|
||||
return cbg.CborEncodeMajorType(cbg.MajUnsignedInt, tx.TxID), nil
|
||||
}
|
||||
|
||||
type MultiSigTxID struct {
|
||||
TxID uint64
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) Approve(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigTxID) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.loadAndVerify(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx, err := self.getTransaction(params.TxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Active(); err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not approve")
|
||||
}
|
||||
|
||||
for _, signer := range tx.Approved {
|
||||
if signer == vmctx.Message().From {
|
||||
return nil, aerrors.New(4, "already signed this message")
|
||||
}
|
||||
}
|
||||
tx.Approved = append(tx.Approved, vmctx.Message().From)
|
||||
if uint64(len(tx.Approved)) >= self.Required {
|
||||
if !self.canSpend(act, tx.Value, vmctx.BlockHeight()) {
|
||||
return nil, aerrors.New(100, "transaction amount exceeds available")
|
||||
}
|
||||
_, err := vmctx.Send(tx.To, tx.Method, tx.Value, tx.Params)
|
||||
if aerrors.IsFatal(err) {
|
||||
return nil, err
|
||||
}
|
||||
tx.RetCode = uint64(aerrors.RetCode(err))
|
||||
tx.Complete = true
|
||||
}
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) Cancel(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigTxID) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.loadAndVerify(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx, err := self.getTransaction(params.TxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Active(); err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not cancel")
|
||||
}
|
||||
|
||||
proposer := tx.Approved[0]
|
||||
if proposer != vmctx.Message().From && self.isSigner(proposer) {
|
||||
return nil, aerrors.New(4, "cannot cancel another signers transaction")
|
||||
}
|
||||
tx.Canceled = true
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
type MultiSigAddSignerParam struct {
|
||||
Signer address.Address
|
||||
Increase bool
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) AddSigner(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigAddSignerParam) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := vmctx.Message()
|
||||
if msg.From != msg.To {
|
||||
return nil, aerrors.New(4, "add signer must be called by wallet itself")
|
||||
}
|
||||
if self.isSigner(params.Signer) {
|
||||
return nil, aerrors.New(5, "new address is already a signer")
|
||||
}
|
||||
|
||||
self.Signers = append(self.Signers, params.Signer)
|
||||
if params.Increase {
|
||||
self.Required = self.Required + 1
|
||||
}
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
type MultiSigRemoveSignerParam struct {
|
||||
Signer address.Address
|
||||
Decrease bool
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) RemoveSigner(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigRemoveSignerParam) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := vmctx.Message()
|
||||
if msg.From != msg.To {
|
||||
return nil, aerrors.New(4, "remove signer must be called by wallet itself")
|
||||
}
|
||||
if !self.isSigner(params.Signer) {
|
||||
return nil, aerrors.New(5, "given address was not a signer")
|
||||
}
|
||||
|
||||
newSigners := make([]address.Address, 0, len(self.Signers)-1)
|
||||
for _, s := range self.Signers {
|
||||
if s != params.Signer {
|
||||
newSigners = append(newSigners, s)
|
||||
}
|
||||
}
|
||||
if params.Decrease || uint64(len(self.Signers)-1) < self.Required {
|
||||
self.Required = self.Required - 1
|
||||
}
|
||||
|
||||
self.Signers = newSigners
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
type MultiSigSwapSignerParams struct {
|
||||
From address.Address
|
||||
To address.Address
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) SwapSigner(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigSwapSignerParams) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := vmctx.Message()
|
||||
if msg.From != msg.To {
|
||||
return nil, aerrors.New(4, "swap signer must be called by wallet itself")
|
||||
}
|
||||
|
||||
if !self.isSigner(params.From) {
|
||||
return nil, aerrors.New(5, "given old address was not a signer")
|
||||
}
|
||||
if self.isSigner(params.To) {
|
||||
return nil, aerrors.New(6, "given new address was already a signer")
|
||||
}
|
||||
|
||||
newSigners := make([]address.Address, 0, len(self.Signers))
|
||||
for _, s := range self.Signers {
|
||||
if s != params.From {
|
||||
newSigners = append(newSigners, s)
|
||||
}
|
||||
}
|
||||
newSigners = append(newSigners, params.To)
|
||||
self.Signers = newSigners
|
||||
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
|
||||
type MultiSigChangeReqParams struct {
|
||||
Req uint64
|
||||
}
|
||||
|
||||
func (msa MultiSigActor) ChangeRequirement(act *types.Actor, vmctx types.VMContext,
|
||||
params *MultiSigChangeReqParams) ([]byte, ActorError) {
|
||||
|
||||
head, self, err := msa.load(vmctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := vmctx.Message()
|
||||
if msg.From != msg.To {
|
||||
return nil, aerrors.New(4, "change requirement must be called by wallet itself")
|
||||
}
|
||||
|
||||
if params.Req < 1 {
|
||||
return nil, aerrors.New(5, "requirement must be at least 1")
|
||||
}
|
||||
|
||||
if params.Req > uint64(len(self.Signers)) {
|
||||
return nil, aerrors.New(6, "requirement must be at most the numbers of signers")
|
||||
}
|
||||
|
||||
self.Required = params.Req
|
||||
return nil, msa.save(vmctx, head, self)
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/stretchr/testify/assert"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
)
|
||||
|
||||
func TestMultiSigCreate(t *testing.T) {
|
||||
var creatorAddr, sig1Addr, sig2Addr, outsideAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&creatorAddr, 100000),
|
||||
HarnessAddr(&sig1Addr, 100000),
|
||||
HarnessAddr(&sig2Addr, 100000),
|
||||
HarnessAddr(&outsideAddr, 100000),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.MultisigCodeCid,
|
||||
&actors.MultiSigConstructorParams{
|
||||
Signers: []address.Address{creatorAddr, sig1Addr, sig2Addr},
|
||||
Required: 2,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
}
|
||||
|
||||
func ApplyOK(t testing.TB, ret *vm.ApplyRet) {
|
||||
t.Helper()
|
||||
if ret.ExitCode != 0 {
|
||||
t.Fatalf("exit code should be 0, got %d, actorErr: %+v", ret.ExitCode, ret.ActorErr)
|
||||
}
|
||||
if ret.ActorErr != nil {
|
||||
t.Fatalf("somehow got an error with exit == 0: %s", ret.ActorErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiSigOps(t *testing.T) {
|
||||
var creatorAddr, sig1Addr, sig2Addr, outsideAddr address.Address
|
||||
var multSigAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&creatorAddr, 100000),
|
||||
HarnessAddr(&sig1Addr, 100000),
|
||||
HarnessAddr(&sig2Addr, 100000),
|
||||
HarnessAddr(&outsideAddr, 100000),
|
||||
HarnessActor(&multSigAddr, &creatorAddr, actors.MultisigCodeCid,
|
||||
func() cbg.CBORMarshaler {
|
||||
return &actors.MultiSigConstructorParams{
|
||||
Signers: []address.Address{creatorAddr, sig1Addr, sig2Addr},
|
||||
Required: 2,
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
{
|
||||
const chargeVal = 2000
|
||||
// Send funds into the multisig
|
||||
ret, _ := h.SendFunds(t, creatorAddr, multSigAddr, types.NewInt(chargeVal))
|
||||
ApplyOK(t, ret)
|
||||
h.AssertBalanceChange(t, creatorAddr, -chargeVal)
|
||||
h.AssertBalanceChange(t, multSigAddr, chargeVal)
|
||||
}
|
||||
|
||||
{
|
||||
// Transfer funds outside of multsig
|
||||
const sendVal = 1000
|
||||
ret, _ := h.Invoke(t, creatorAddr, multSigAddr, actors.MultiSigMethods.Propose,
|
||||
&actors.MultiSigProposeParams{
|
||||
To: outsideAddr,
|
||||
Value: types.NewInt(sendVal),
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
var txIDParam actors.MultiSigTxID
|
||||
err := cbor.DecodeInto(ret.Return, &txIDParam.TxID)
|
||||
assert.NoError(t, err, "decoding txid")
|
||||
|
||||
ret, _ = h.Invoke(t, outsideAddr, multSigAddr, actors.MultiSigMethods.Approve,
|
||||
&txIDParam)
|
||||
assert.Equal(t, uint8(1), ret.ExitCode, "outsideAddr should not approve")
|
||||
h.AssertBalanceChange(t, multSigAddr, 0)
|
||||
|
||||
ret2, _ := h.Invoke(t, sig1Addr, multSigAddr, actors.MultiSigMethods.Approve,
|
||||
&txIDParam)
|
||||
ApplyOK(t, ret2)
|
||||
|
||||
h.AssertBalanceChange(t, outsideAddr, sendVal)
|
||||
h.AssertBalanceChange(t, multSigAddr, -sendVal)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/minio/blake2b-simd"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type PaymentChannelActor struct{}
|
||||
|
||||
type PaymentInfo struct {
|
||||
PayChActor address.Address
|
||||
Payer address.Address
|
||||
ChannelMessage *cid.Cid
|
||||
|
||||
Vouchers []*types.SignedVoucher
|
||||
}
|
||||
|
||||
type LaneState struct {
|
||||
Closed bool
|
||||
Redeemed types.BigInt
|
||||
Nonce uint64
|
||||
}
|
||||
|
||||
type PaymentChannelActorState struct {
|
||||
From address.Address
|
||||
To address.Address
|
||||
|
||||
ToSend types.BigInt
|
||||
|
||||
ClosingAt uint64
|
||||
MinCloseHeight uint64
|
||||
|
||||
// TODO: needs to be map[uint64]*laneState
|
||||
// waiting on refmt#35 to be fixed
|
||||
LaneStates map[string]*LaneState
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
1: pca.Constructor,
|
||||
2: pca.UpdateChannelState,
|
||||
3: pca.Close,
|
||||
4: pca.Collect,
|
||||
5: pca.GetOwner,
|
||||
6: pca.GetToSend,
|
||||
}
|
||||
}
|
||||
|
||||
type pcaMethods struct {
|
||||
Constructor uint64
|
||||
UpdateChannelState uint64
|
||||
Close uint64
|
||||
Collect uint64
|
||||
GetOwner uint64
|
||||
GetToSend uint64
|
||||
}
|
||||
|
||||
var PCAMethods = pcaMethods{1, 2, 3, 4, 5, 6}
|
||||
|
||||
type PCAConstructorParams struct {
|
||||
To address.Address
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) Constructor(act *types.Actor, vmctx types.VMContext, params *PCAConstructorParams) ([]byte, ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
self.From = vmctx.Origin()
|
||||
self.To = params.To
|
||||
self.LaneStates = make(map[string]*LaneState)
|
||||
|
||||
storage := vmctx.Storage()
|
||||
c, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := storage.Commit(EmptyCBOR, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type PCAUpdateChannelStateParams struct {
|
||||
Sv types.SignedVoucher
|
||||
Secret []byte
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
func hash(b []byte) []byte {
|
||||
s := blake2b.Sum256(b)
|
||||
return s[:]
|
||||
}
|
||||
|
||||
type PaymentVerifyParams struct {
|
||||
Extra []byte
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) UpdateChannelState(act *types.Actor, vmctx types.VMContext, params *PCAUpdateChannelStateParams) ([]byte, ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
oldstate := vmctx.Storage().GetHead()
|
||||
storage := vmctx.Storage()
|
||||
if err := storage.Get(oldstate, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sv := params.Sv
|
||||
|
||||
vb, nerr := sv.SigningBytes()
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Escalate(nerr, "failed to serialize signedvoucher")
|
||||
}
|
||||
|
||||
if err := vmctx.VerifySignature(sv.Signature, self.From, vb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() < sv.TimeLock {
|
||||
return nil, aerrors.New(2, "cannot use this voucher yet!")
|
||||
}
|
||||
|
||||
if len(sv.SecretPreimage) > 0 {
|
||||
if !bytes.Equal(hash(params.Secret), sv.SecretPreimage) {
|
||||
return nil, aerrors.New(3, "incorrect secret!")
|
||||
}
|
||||
}
|
||||
|
||||
if sv.Extra != nil {
|
||||
encoded, err := SerializeParams(&PaymentVerifyParams{sv.Extra.Data, params.Proof})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = vmctx.Send(sv.Extra.Actor, sv.Extra.Method, types.NewInt(0), encoded)
|
||||
if err != nil {
|
||||
return nil, aerrors.Newf(4, "spend voucher verification failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
ls, ok := self.LaneStates[fmt.Sprint(sv.Lane)]
|
||||
if !ok {
|
||||
ls = new(LaneState)
|
||||
ls.Redeemed = types.NewInt(0) // TODO: kinda annoying that this doesnt default to a usable value
|
||||
self.LaneStates[fmt.Sprint(sv.Lane)] = ls
|
||||
}
|
||||
if ls.Closed {
|
||||
return nil, aerrors.New(5, "cannot redeem a voucher on a closed lane")
|
||||
}
|
||||
|
||||
if ls.Nonce > sv.Nonce {
|
||||
return nil, aerrors.New(6, "voucher has an outdated nonce, cannot redeem")
|
||||
}
|
||||
|
||||
mergeValue := types.NewInt(0)
|
||||
for _, merge := range sv.Merges {
|
||||
if merge.Lane == sv.Lane {
|
||||
return nil, aerrors.New(7, "voucher cannot merge its own lane")
|
||||
}
|
||||
|
||||
ols := self.LaneStates[fmt.Sprint(merge.Lane)]
|
||||
|
||||
if ols.Nonce >= merge.Nonce {
|
||||
return nil, aerrors.New(8, "merge in voucher has outdated nonce, cannot redeem")
|
||||
}
|
||||
|
||||
mergeValue = types.BigAdd(mergeValue, ols.Redeemed)
|
||||
ols.Nonce = merge.Nonce
|
||||
}
|
||||
|
||||
ls.Nonce = sv.Nonce
|
||||
balanceDelta := types.BigSub(sv.Amount, types.BigAdd(mergeValue, ls.Redeemed))
|
||||
ls.Redeemed = sv.Amount
|
||||
|
||||
newSendBalance := types.BigAdd(self.ToSend, balanceDelta)
|
||||
if newSendBalance.LessThan(types.NewInt(0)) {
|
||||
// TODO: is this impossible?
|
||||
return nil, aerrors.New(9, "voucher would leave channel balance negative")
|
||||
}
|
||||
|
||||
if newSendBalance.GreaterThan(act.Balance) {
|
||||
return nil, aerrors.New(10, "not enough funds in channel to cover voucher")
|
||||
}
|
||||
|
||||
log.Info("vals: ", newSendBalance, sv.Amount, balanceDelta, mergeValue, ls.Redeemed)
|
||||
self.ToSend = newSendBalance
|
||||
|
||||
if sv.MinCloseHeight != 0 {
|
||||
if self.ClosingAt != 0 && self.ClosingAt < sv.MinCloseHeight {
|
||||
self.ClosingAt = sv.MinCloseHeight
|
||||
}
|
||||
if self.MinCloseHeight < sv.MinCloseHeight {
|
||||
self.MinCloseHeight = sv.MinCloseHeight
|
||||
}
|
||||
}
|
||||
|
||||
ncid, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := storage.Commit(oldstate, ncid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) Close(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
storage := vmctx.Storage()
|
||||
oldstate := storage.GetHead()
|
||||
if err := storage.Get(oldstate, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.Message().From != self.From && vmctx.Message().From != self.To {
|
||||
return nil, aerrors.New(1, "not authorized to close channel")
|
||||
}
|
||||
|
||||
if self.ClosingAt != 0 {
|
||||
return nil, aerrors.New(2, "channel already closing")
|
||||
}
|
||||
|
||||
self.ClosingAt = vmctx.BlockHeight() + build.PaymentChannelClosingDelay
|
||||
if self.ClosingAt < self.MinCloseHeight {
|
||||
self.ClosingAt = self.MinCloseHeight
|
||||
}
|
||||
|
||||
ncid, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := storage.Commit(oldstate, ncid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) Collect(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
storage := vmctx.Storage()
|
||||
oldstate := storage.GetHead()
|
||||
if err := storage.Get(oldstate, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if self.ClosingAt == 0 {
|
||||
return nil, aerrors.New(1, "payment channel not closing or closed")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() < self.ClosingAt {
|
||||
return nil, aerrors.New(2, "payment channel not closed yet")
|
||||
}
|
||||
_, err := vmctx.Send(self.From, 0, types.BigSub(act.Balance, self.ToSend), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = vmctx.Send(self.To, 0, self.ToSend, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.ToSend = types.NewInt(0)
|
||||
|
||||
ncid, err := storage.Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := storage.Commit(oldstate, ncid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) GetOwner(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
storage := vmctx.Storage()
|
||||
if err := storage.Get(storage.GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.From.Bytes(), nil
|
||||
}
|
||||
|
||||
func (pca PaymentChannelActor) GetToSend(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
|
||||
var self PaymentChannelActorState
|
||||
storage := vmctx.Storage()
|
||||
if err := storage.Get(storage.GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.ToSend.Bytes(), nil
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
)
|
||||
|
||||
func TestPaychCreate(t *testing.T) {
|
||||
var creatorAddr, targetAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&creatorAddr, 100000),
|
||||
HarnessAddr(&targetAddr, 100000),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.PaymentChannelCodeCid,
|
||||
&actors.PCAConstructorParams{
|
||||
To: targetAddr,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
}
|
||||
|
||||
func signVoucher(t *testing.T, w *wallet.Wallet, addr address.Address, sv *types.SignedVoucher) {
|
||||
vb, err := sv.SigningBytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig, err := w.Sign(context.TODO(), addr, vb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sv.Signature = sig
|
||||
}
|
||||
|
||||
func TestPaychUpdate(t *testing.T) {
|
||||
var creatorAddr, targetAddr address.Address
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&creatorAddr, 100000),
|
||||
HarnessAddr(&targetAddr, 100000),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
ret, _ := h.CreateActor(t, creatorAddr, actors.PaymentChannelCodeCid,
|
||||
&actors.PCAConstructorParams{
|
||||
To: targetAddr,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
pch, err := address.NewFromBytes(ret.Return)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ret, _ = h.SendFunds(t, creatorAddr, pch, types.NewInt(5000))
|
||||
ApplyOK(t, ret)
|
||||
|
||||
sv := &types.SignedVoucher{
|
||||
Amount: types.NewInt(100),
|
||||
Nonce: 1,
|
||||
}
|
||||
signVoucher(t, h.w, creatorAddr, sv)
|
||||
|
||||
ret, _ = h.Invoke(t, targetAddr, pch, actors.PCAMethods.UpdateChannelState, &actors.PCAUpdateChannelStateParams{
|
||||
Sv: *sv,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
ret, _ = h.Invoke(t, targetAddr, pch, actors.PCAMethods.GetToSend, nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
bi := types.BigFromBytes(ret.Return)
|
||||
if bi.String() != "100" {
|
||||
t.Fatal("toSend amount was wrong: ", bi.String())
|
||||
}
|
||||
|
||||
ret, _ = h.Invoke(t, targetAddr, pch, actors.PCAMethods.Close, nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
// now we have to 'wait' for the chain to advance.
|
||||
h.BlockHeight = 1000
|
||||
|
||||
ret, _ = h.Invoke(t, targetAddr, pch, actors.PCAMethods.Collect, nil)
|
||||
ApplyOK(t, ret)
|
||||
|
||||
h.AssertBalanceChange(t, targetAddr, 100)
|
||||
h.AssertBalanceChange(t, creatorAddr, -100)
|
||||
}
|
||||
@@ -1,670 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-hamt-ipld"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
)
|
||||
|
||||
type StorageMarketActor struct{}
|
||||
|
||||
type smaMethods struct {
|
||||
Constructor uint64
|
||||
WithdrawBalance uint64
|
||||
AddBalance uint64
|
||||
CheckLockedBalance uint64
|
||||
PublishStorageDeals uint64
|
||||
HandleCronAction uint64
|
||||
SettleExpiredDeals uint64
|
||||
ProcessStorageDealsPayment uint64
|
||||
SlashStorageDealCollateral uint64
|
||||
GetLastExpirationFromDealIDs uint64
|
||||
ActivateStorageDeals uint64
|
||||
ComputeDataCommitment uint64
|
||||
}
|
||||
|
||||
var SMAMethods = smaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
|
||||
|
||||
func (sma StorageMarketActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
2: sma.WithdrawBalance,
|
||||
3: sma.AddBalance,
|
||||
// 4: sma.CheckLockedBalance,
|
||||
5: sma.PublishStorageDeals,
|
||||
// 6: sma.HandleCronAction,
|
||||
// 7: sma.SettleExpiredDeals,
|
||||
// 8: sma.ProcessStorageDealsPayment,
|
||||
// 9: sma.SlashStorageDealCollateral,
|
||||
// 10: sma.GetLastExpirationFromDealIDs,
|
||||
11: sma.ActivateStorageDeals, // TODO: move under PublishStorageDeals after specs team approves
|
||||
12: sma.ComputeDataCommitment,
|
||||
}
|
||||
}
|
||||
|
||||
type StorageParticipantBalance struct {
|
||||
Locked types.BigInt
|
||||
Available types.BigInt
|
||||
}
|
||||
|
||||
type StorageMarketState struct {
|
||||
Balances cid.Cid // hamt<addr, StorageParticipantBalance>
|
||||
Deals cid.Cid // amt<StorageDeal>
|
||||
|
||||
NextDealID uint64 // TODO: spec
|
||||
}
|
||||
|
||||
// TODO: Drop in favour of car storage
|
||||
type SerializationMode = uint64
|
||||
|
||||
const (
|
||||
SerializationUnixFSv0 = iota
|
||||
// IPLD / car
|
||||
)
|
||||
|
||||
type StorageDealProposal struct {
|
||||
PieceRef []byte // cid bytes // TODO: spec says to use cid.Cid, probably not a good idea
|
||||
PieceSize uint64
|
||||
|
||||
Client address.Address
|
||||
Provider address.Address
|
||||
|
||||
ProposalExpiration uint64
|
||||
Duration uint64 // TODO: spec
|
||||
|
||||
StoragePricePerEpoch types.BigInt
|
||||
StorageCollateral types.BigInt
|
||||
|
||||
ProposerSignature *types.Signature
|
||||
}
|
||||
|
||||
func (sdp *StorageDealProposal) TotalStoragePrice() types.BigInt {
|
||||
return types.BigMul(sdp.StoragePricePerEpoch, types.NewInt(sdp.Duration))
|
||||
}
|
||||
|
||||
type SignFunc = func(context.Context, []byte) (*types.Signature, error)
|
||||
|
||||
func (sdp *StorageDealProposal) Sign(ctx context.Context, sign SignFunc) error {
|
||||
if sdp.ProposerSignature != nil {
|
||||
return xerrors.New("signature already present in StorageDealProposal")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := sdp.MarshalCBOR(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
sig, err := sign(ctx, buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sdp.ProposerSignature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sdp *StorageDealProposal) Cid() (cid.Cid, error) {
|
||||
nd, err := cborutil.AsIpld(sdp)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
return nd.Cid(), nil
|
||||
}
|
||||
|
||||
func (sdp *StorageDealProposal) Verify() error {
|
||||
unsigned := *sdp
|
||||
unsigned.ProposerSignature = nil
|
||||
var buf bytes.Buffer
|
||||
if err := unsigned.MarshalCBOR(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sdp.ProposerSignature.Verify(sdp.Client, buf.Bytes())
|
||||
}
|
||||
|
||||
type OnChainDeal struct {
|
||||
PieceRef []byte // cid bytes // TODO: spec says to use cid.Cid, probably not a good idea
|
||||
PieceSize uint64
|
||||
|
||||
Client address.Address
|
||||
Provider address.Address
|
||||
|
||||
ProposalExpiration uint64
|
||||
Duration uint64 // TODO: spec
|
||||
|
||||
StoragePricePerEpoch types.BigInt
|
||||
StorageCollateral types.BigInt
|
||||
ActivationEpoch uint64 // 0 = inactive
|
||||
}
|
||||
|
||||
type WithdrawBalanceParams struct {
|
||||
Balance types.BigInt
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) WithdrawBalance(act *types.Actor, vmctx types.VMContext, params *WithdrawBalanceParams) ([]byte, ActorError) {
|
||||
// TODO: (spec) this should be 2-stage
|
||||
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, bnd, err := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, vmctx.Message().From)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not get balance")
|
||||
}
|
||||
|
||||
balance := b[0]
|
||||
|
||||
if balance.Available.LessThan(params.Balance) {
|
||||
return nil, aerrors.Newf(1, "can not withdraw more funds than available: %s > %s", params.Balance, b[0].Available)
|
||||
}
|
||||
|
||||
balance.Available = types.BigSub(balance.Available, params.Balance)
|
||||
|
||||
_, err = vmctx.Send(vmctx.Message().From, 0, params.Balance, nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "sending funds failed")
|
||||
}
|
||||
|
||||
bcid, err := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
vmctx.Message().From: balance,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, vmctx.Storage().Commit(old, nroot)
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) AddBalance(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, bnd, err := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, vmctx.Message().From)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "could not get balance")
|
||||
}
|
||||
|
||||
balance := b[0]
|
||||
|
||||
balance.Available = types.BigAdd(balance.Available, vmctx.Message().Value)
|
||||
|
||||
bcid, err := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
vmctx.Message().From: balance,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, vmctx.Storage().Commit(old, nroot)
|
||||
}
|
||||
|
||||
func setMarketBalances(vmctx types.VMContext, nd *hamt.Node, set map[address.Address]StorageParticipantBalance) (cid.Cid, ActorError) {
|
||||
keys := make([]address.Address, 0, len(set))
|
||||
for k := range set {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return bytes.Compare(keys[i].Bytes(), keys[j].Bytes()) < 0
|
||||
})
|
||||
for _, addr := range keys {
|
||||
balance := set[addr]
|
||||
if err := nd.Set(vmctx.Context(), string(addr.Bytes()), &balance); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "setting new balance")
|
||||
}
|
||||
}
|
||||
if err := nd.Flush(vmctx.Context()); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "flushing balance hamt")
|
||||
}
|
||||
|
||||
c, err := vmctx.Ipld().Put(vmctx.Context(), nd)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to balances storage")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func GetMarketBalances(ctx context.Context, store *hamt.CborIpldStore, rcid cid.Cid, addrs ...address.Address) ([]StorageParticipantBalance, *hamt.Node, ActorError) {
|
||||
ctx, span := trace.StartSpan(ctx, "GetMarketBalances")
|
||||
defer span.End()
|
||||
|
||||
nd, err := hamt.LoadNode(ctx, store, rcid)
|
||||
if err != nil {
|
||||
return nil, nil, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
out := make([]StorageParticipantBalance, len(addrs))
|
||||
|
||||
for i, a := range addrs {
|
||||
var balance StorageParticipantBalance
|
||||
err = nd.Find(ctx, string(a.Bytes()), &balance)
|
||||
switch err {
|
||||
case hamt.ErrNotFound:
|
||||
out[i] = StorageParticipantBalance{
|
||||
Locked: types.NewInt(0),
|
||||
Available: types.NewInt(0),
|
||||
}
|
||||
case nil:
|
||||
out[i] = balance
|
||||
default:
|
||||
return nil, nil, aerrors.HandleExternalError(err, "failed to do set lookup")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return out, nd, nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (sma StorageMarketActor) CheckLockedBalance(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
type PublishStorageDealsParams struct {
|
||||
Deals []StorageDealProposal
|
||||
}
|
||||
|
||||
type PublishStorageDealResponse struct {
|
||||
DealIDs []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) PublishStorageDeals(act *types.Actor, vmctx types.VMContext, params *PublishStorageDealsParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
// todo: handle duplicate deals
|
||||
|
||||
if len(params.Deals) == 0 {
|
||||
return nil, aerrors.New(1, "no storage deals in params.Deals")
|
||||
}
|
||||
|
||||
out := PublishStorageDealResponse{
|
||||
DealIDs: make([]uint64, len(params.Deals)),
|
||||
}
|
||||
|
||||
workerBytes, aerr := vmctx.Send(params.Deals[0].Provider, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
providerWorker, err := address.NewFromBytes(workerBytes)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "parsing provider worker address bytes")
|
||||
}
|
||||
|
||||
// TODO: REVIEW: Do we want to check if provider exists in the power actor?
|
||||
|
||||
for i, deal := range params.Deals {
|
||||
if err := self.validateDeal(vmctx, deal, providerWorker); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err := deals.Set(self.NextDealID, &OnChainDeal{
|
||||
PieceRef: deal.PieceRef,
|
||||
PieceSize: deal.PieceSize,
|
||||
|
||||
Client: deal.Client,
|
||||
Provider: deal.Provider,
|
||||
|
||||
ProposalExpiration: deal.ProposalExpiration,
|
||||
Duration: deal.Duration,
|
||||
|
||||
StoragePricePerEpoch: deal.StoragePricePerEpoch,
|
||||
StorageCollateral: deal.StorageCollateral,
|
||||
ActivationEpoch: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "setting deal in deal AMT")
|
||||
}
|
||||
out.DealIDs[i] = self.NextDealID
|
||||
|
||||
self.NextDealID++
|
||||
}
|
||||
|
||||
dealsCid, err := deals.Flush()
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "saving deals AMT")
|
||||
}
|
||||
|
||||
self.Deals = dealsCid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr = vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
var outBuf bytes.Buffer
|
||||
if err := out.MarshalCBOR(&outBuf); err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "serialising output")
|
||||
}
|
||||
|
||||
return outBuf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (st *StorageMarketState) validateDeal(vmctx types.VMContext, deal StorageDealProposal, providerWorker address.Address) aerrors.ActorError {
|
||||
ctx, span := trace.StartSpan(vmctx.Context(), "validateDeal")
|
||||
defer span.End()
|
||||
|
||||
if vmctx.BlockHeight() > deal.ProposalExpiration {
|
||||
return aerrors.New(1, "deal proposal already expired")
|
||||
}
|
||||
|
||||
if vmctx.Message().From != providerWorker {
|
||||
return aerrors.New(2, "Deals must be submitted by the miner worker")
|
||||
}
|
||||
|
||||
if err := deal.Verify(); err != nil {
|
||||
return aerrors.Absorb(err, 3, "verifying proposer signature")
|
||||
}
|
||||
|
||||
// TODO: do some caching (changes gas so needs to be in spec too)
|
||||
b, bnd, aerr := GetMarketBalances(ctx, vmctx.Ipld(), st.Balances, deal.Client, providerWorker)
|
||||
if aerr != nil {
|
||||
return aerrors.Wrap(aerr, "getting client, and provider balances")
|
||||
}
|
||||
clientBalance := b[0]
|
||||
providerBalance := b[1]
|
||||
|
||||
totalPrice := deal.TotalStoragePrice()
|
||||
|
||||
if clientBalance.Available.LessThan(totalPrice) {
|
||||
return aerrors.Newf(5, "client doesn't have enough available funds to cover storage price; %d < %d", clientBalance.Available, totalPrice)
|
||||
}
|
||||
|
||||
clientBalance = lockFunds(clientBalance, totalPrice)
|
||||
|
||||
// TODO: REVIEW: Not clear who pays for this
|
||||
if providerBalance.Available.LessThan(deal.StorageCollateral) {
|
||||
return aerrors.Newf(6, "provider doesn't have enough available funds to cover StorageCollateral; %d < %d", providerBalance.Available, deal.StorageCollateral)
|
||||
}
|
||||
|
||||
providerBalance = lockFunds(providerBalance, deal.StorageCollateral)
|
||||
|
||||
// TODO: piece checks (e.g. size > sectorSize)?
|
||||
|
||||
bcid, aerr := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
deal.Client: clientBalance,
|
||||
providerWorker: providerBalance,
|
||||
})
|
||||
if aerr != nil {
|
||||
return aerr
|
||||
}
|
||||
|
||||
st.Balances = bcid
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ActivateStorageDealsParams struct {
|
||||
Deals []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ActivateStorageDeals(act *types.Actor, vmctx types.VMContext, params *ActivateStorageDealsParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
for _, deal := range params.Deals {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(3, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if vmctx.Message().From != dealInfo.Provider {
|
||||
return nil, aerrors.New(1, "ActivateStorageDeals can only be called by the deal provider")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > dealInfo.ProposalExpiration {
|
||||
return nil, aerrors.New(2, "deal cannot be activated: proposal expired")
|
||||
}
|
||||
|
||||
if dealInfo.ActivationEpoch > 0 {
|
||||
// this probably can't happen in practice
|
||||
return nil, aerrors.New(3, "deal already active")
|
||||
}
|
||||
|
||||
dealInfo.ActivationEpoch = vmctx.BlockHeight()
|
||||
|
||||
if err := deals.Set(deal, &dealInfo); err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "setting deal info in AMT failed")
|
||||
}
|
||||
}
|
||||
|
||||
dealsCid, err := deals.Flush()
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "saving deals AMT")
|
||||
}
|
||||
|
||||
self.Deals = dealsCid
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr := vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type ProcessStorageDealsPaymentParams struct {
|
||||
DealIDs []uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ProcessStorageDealsPayment(act *types.Actor, vmctx types.VMContext, params *ProcessStorageDealsPaymentParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
// TODO: Would be nice if send could assert actor type
|
||||
workerBytes, aerr := vmctx.Send(vmctx.Message().From, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
providerWorker, err := address.NewFromBytes(workerBytes)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "parsing provider worker address bytes")
|
||||
}
|
||||
|
||||
for _, deal := range params.DealIDs {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(2, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if dealInfo.Provider != vmctx.Message().From {
|
||||
return nil, aerrors.New(3, "ProcessStorageDealsPayment can only be called by deal provider")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() < dealInfo.ActivationEpoch {
|
||||
// TODO: This is probably fatal
|
||||
return nil, aerrors.New(4, "ActivationEpoch lower than block height")
|
||||
}
|
||||
|
||||
if vmctx.BlockHeight() > dealInfo.ActivationEpoch+dealInfo.Duration {
|
||||
// Deal expired, miner should drop it
|
||||
// TODO: process payment for the remainder of last proving period
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
toPay := types.BigMul(dealInfo.StoragePricePerEpoch, types.NewInt(build.SlashablePowerDelay))
|
||||
|
||||
b, bnd, aerr := GetMarketBalances(vmctx.Context(), vmctx.Ipld(), self.Balances, dealInfo.Client, providerWorker)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
clientBal := b[0]
|
||||
providerBal := b[1]
|
||||
|
||||
clientBal.Locked, providerBal.Available = transferFunds(clientBal.Locked, providerBal.Available, toPay)
|
||||
|
||||
// TODO: call set once
|
||||
bcid, aerr := setMarketBalances(vmctx, bnd, map[address.Address]StorageParticipantBalance{
|
||||
dealInfo.Client: clientBal,
|
||||
providerWorker: providerBal,
|
||||
})
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
self.Balances = bcid
|
||||
}
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "storing state failed")
|
||||
}
|
||||
|
||||
aerr = vmctx.Storage().Commit(old, nroot)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func lockFunds(p StorageParticipantBalance, amt types.BigInt) StorageParticipantBalance {
|
||||
p.Available, p.Locked = transferFunds(p.Available, p.Locked, amt)
|
||||
return p
|
||||
}
|
||||
|
||||
func transferFunds(from, to, amt types.BigInt) (types.BigInt, types.BigInt) {
|
||||
// TODO: some asserts
|
||||
return types.BigSub(from, amt), types.BigAdd(to, amt)
|
||||
}
|
||||
|
||||
type ComputeDataCommitmentParams struct {
|
||||
DealIDs []uint64
|
||||
SectorSize uint64
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) ComputeDataCommitment(act *types.Actor, vmctx types.VMContext, params *ComputeDataCommitmentParams) ([]byte, ActorError) {
|
||||
var self StorageMarketState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deals, err := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Deals)
|
||||
if err != nil {
|
||||
return nil, aerrors.HandleExternalError(err, "loading deals amt")
|
||||
}
|
||||
|
||||
if len(params.DealIDs) == 0 {
|
||||
return nil, aerrors.New(3, "no deal IDs")
|
||||
}
|
||||
|
||||
var pieces []sectorbuilder.PublicPieceInfo
|
||||
for _, deal := range params.DealIDs {
|
||||
var dealInfo OnChainDeal
|
||||
if err := deals.Get(deal, &dealInfo); err != nil {
|
||||
if _, is := err.(*amt.ErrNotFound); is {
|
||||
return nil, aerrors.New(4, "deal not found")
|
||||
}
|
||||
return nil, aerrors.HandleExternalError(err, "getting deal info failed")
|
||||
}
|
||||
|
||||
if dealInfo.Provider != vmctx.Message().From {
|
||||
return nil, aerrors.New(5, "referenced deal was not from caller")
|
||||
}
|
||||
|
||||
var commP [32]byte
|
||||
copy(commP[:], dealInfo.PieceRef)
|
||||
|
||||
pieces = append(pieces, sectorbuilder.PublicPieceInfo{
|
||||
Size: dealInfo.PieceSize,
|
||||
CommP: commP,
|
||||
})
|
||||
}
|
||||
|
||||
commd, err := sectorbuilder.GenerateDataCommitment(params.SectorSize, pieces)
|
||||
if err != nil {
|
||||
return nil, aerrors.Absorb(err, 6, "failed to generate data commitment from pieces")
|
||||
}
|
||||
|
||||
return commd[:], nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (sma StorageMarketActor) HandleCronAction(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) SettleExpiredDeals(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) SlashStorageDealCollateral(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
|
||||
func (sma StorageMarketActor) GetLastExpirationFromDealIDs(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -1,797 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"go.opencensus.io/trace"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type StoragePowerActor struct{}
|
||||
|
||||
type spaMethods struct {
|
||||
Constructor uint64
|
||||
CreateStorageMiner uint64
|
||||
ArbitrateConsensusFault uint64
|
||||
UpdateStorage uint64
|
||||
GetTotalStorage uint64
|
||||
PowerLookup uint64
|
||||
IsValidMiner uint64
|
||||
PledgeCollateralForSize uint64
|
||||
CheckProofSubmissions uint64
|
||||
}
|
||||
|
||||
var SPAMethods = spaMethods{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
func (spa StoragePowerActor) Exports() []interface{} {
|
||||
return []interface{}{
|
||||
//1: spa.StoragePowerConstructor,
|
||||
2: spa.CreateStorageMiner,
|
||||
3: spa.ArbitrateConsensusFault,
|
||||
4: spa.UpdateStorage,
|
||||
5: spa.GetTotalStorage,
|
||||
6: spa.PowerLookup,
|
||||
7: spa.IsValidMiner,
|
||||
8: spa.PledgeCollateralForSize,
|
||||
9: spa.CheckProofSubmissions,
|
||||
}
|
||||
}
|
||||
|
||||
type StoragePowerState struct {
|
||||
Miners cid.Cid
|
||||
ProvingBuckets cid.Cid // amt[ProvingPeriodBucket]hamt[minerAddress]struct{}
|
||||
MinerCount uint64
|
||||
LastMinerCheck uint64
|
||||
|
||||
TotalStorage types.BigInt
|
||||
}
|
||||
|
||||
type CreateStorageMinerParams struct {
|
||||
Owner address.Address
|
||||
Worker address.Address
|
||||
SectorSize uint64
|
||||
PeerID peer.ID
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) CreateStorageMiner(act *types.Actor, vmctx types.VMContext, params *CreateStorageMinerParams) ([]byte, ActorError) {
|
||||
if !build.SupportedSectorSize(params.SectorSize) {
|
||||
return nil, aerrors.New(1, "Unsupported sector size")
|
||||
}
|
||||
|
||||
var self StoragePowerState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqColl, err := pledgeCollateralForSize(vmctx, types.NewInt(0), self.TotalStorage, self.MinerCount+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if vmctx.Message().Value.LessThan(reqColl) {
|
||||
return nil, aerrors.Newf(1, "not enough funds passed to cover required miner collateral (needed %s, got %s)", reqColl, vmctx.Message().Value)
|
||||
}
|
||||
|
||||
encoded, err := CreateExecParams(StorageMinerCodeCid, &StorageMinerConstructorParams{
|
||||
Owner: params.Owner,
|
||||
Worker: params.Worker,
|
||||
SectorSize: params.SectorSize,
|
||||
PeerID: params.PeerID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := vmctx.Send(InitAddress, IAMethods.Exec, vmctx.Message().Value, encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
naddr, nerr := address.NewFromBytes(ret)
|
||||
if nerr != nil {
|
||||
return nil, aerrors.Absorb(nerr, 2, "could not read address of new actor")
|
||||
}
|
||||
|
||||
ncid, err := MinerSetAdd(context.TODO(), vmctx, self.Miners, naddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
self.Miners = ncid
|
||||
self.MinerCount++
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return naddr.Bytes(), nil
|
||||
}
|
||||
|
||||
type ArbitrateConsensusFaultParams struct {
|
||||
Block1 *types.BlockHeader
|
||||
Block2 *types.BlockHeader
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) ArbitrateConsensusFault(act *types.Actor, vmctx types.VMContext, params *ArbitrateConsensusFaultParams) ([]byte, ActorError) {
|
||||
if params.Block1.Miner != params.Block2.Miner {
|
||||
return nil, aerrors.New(2, "blocks must be from the same miner")
|
||||
}
|
||||
|
||||
rval, err := vmctx.Send(params.Block1.Miner, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrap(err, "failed to get miner worker")
|
||||
}
|
||||
|
||||
worker, oerr := address.NewFromBytes(rval)
|
||||
if oerr != nil {
|
||||
// REVIEW: should this be fatal? i can't think of a real situation that would get us here
|
||||
return nil, aerrors.Absorb(oerr, 3, "response from 'GetWorkerAddr' was not a valid address")
|
||||
}
|
||||
|
||||
if err := params.Block1.CheckBlockSignature(vmctx.Context(), worker); err != nil {
|
||||
return nil, aerrors.Absorb(err, 4, "block1 did not have valid signature")
|
||||
}
|
||||
|
||||
if err := params.Block2.CheckBlockSignature(vmctx.Context(), worker); err != nil {
|
||||
return nil, aerrors.Absorb(err, 5, "block2 did not have valid signature")
|
||||
}
|
||||
|
||||
// see the "Consensus Faults" section of the faults spec (faults.md)
|
||||
// for details on these slashing conditions.
|
||||
if !shouldSlash(params.Block1, params.Block2) {
|
||||
return nil, aerrors.New(6, "blocks do not prove a slashable offense")
|
||||
}
|
||||
|
||||
var self StoragePowerState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if types.BigCmp(self.TotalStorage, types.NewInt(0)) == 0 {
|
||||
return nil, aerrors.Fatal("invalid state, storage power actor has zero total storage")
|
||||
}
|
||||
|
||||
miner := params.Block1.Miner
|
||||
if has, err := MinerSetHas(vmctx, self.Miners, miner); err != nil {
|
||||
return nil, aerrors.Wrapf(err, "failed to check miner in set")
|
||||
} else if !has {
|
||||
return nil, aerrors.New(7, "either already slashed or not a miner")
|
||||
}
|
||||
|
||||
minerPower, err := powerLookup(context.TODO(), vmctx, &self, miner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slashedCollateral, err := pledgeCollateralForSize(vmctx, minerPower, self.TotalStorage, self.MinerCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enc, err := SerializeParams(&MinerSlashConsensusFault{
|
||||
Slasher: vmctx.Message().From,
|
||||
AtHeight: params.Block1.Height,
|
||||
SlashedCollateral: slashedCollateral,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = vmctx.Send(miner, MAMethods.SlashConsensusFault, types.NewInt(0), enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove the miner from the list of network miners
|
||||
ncid, err := MinerSetRemove(context.TODO(), vmctx, self.Miners, miner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
self.Miners = ncid
|
||||
self.MinerCount--
|
||||
|
||||
self.TotalStorage = types.BigSub(self.TotalStorage, minerPower)
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func cidArrContains(a []cid.Cid, b cid.Cid) bool {
|
||||
for _, c := range a {
|
||||
if b == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldSlash(block1, block2 *types.BlockHeader) bool {
|
||||
// First slashing condition, blocks have the same ticket round
|
||||
if block1.Height == block2.Height {
|
||||
return true
|
||||
}
|
||||
|
||||
/* Second slashing condition requires having access to the parent tipset blocks
|
||||
// This might not always be available, needs some thought on the best way to deal with this
|
||||
|
||||
|
||||
// Second slashing condition, miner ignored own block when mining
|
||||
// Case A: block2 could have been in block1's parent set but is not
|
||||
b1ParentHeight := block1.Height - len(block1.Tickets)
|
||||
|
||||
block1ParentTipSet := block1.Parents
|
||||
if !cidArrContains(block1.Parents, block2.Cid()) &&
|
||||
b1ParentHeight == block2.Height &&
|
||||
block1ParentTipSet.ParentCids == block2.ParentCids {
|
||||
return true
|
||||
}
|
||||
|
||||
// Case B: block1 could have been in block2's parent set but is not
|
||||
block2ParentTipSet := parentOf(block2)
|
||||
if !block2Parent.contains(block1) &&
|
||||
block2ParentTipSet.Height == block1.Height &&
|
||||
block2ParentTipSet.ParentCids == block1.ParentCids {
|
||||
return true
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type UpdateStorageParams struct {
|
||||
Delta types.BigInt
|
||||
NextSlashDeadline uint64
|
||||
PreviousSlashDeadline uint64
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) UpdateStorage(act *types.Actor, vmctx types.VMContext, params *UpdateStorageParams) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
has, err := MinerSetHas(vmctx, self.Miners, vmctx.Message().From)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, aerrors.New(1, "update storage must only be called by a miner actor")
|
||||
}
|
||||
|
||||
self.TotalStorage = types.BigAdd(self.TotalStorage, params.Delta)
|
||||
|
||||
previousBucket := params.PreviousSlashDeadline % build.SlashablePowerDelay
|
||||
nextBucket := params.NextSlashDeadline % build.SlashablePowerDelay
|
||||
|
||||
if previousBucket == nextBucket && params.PreviousSlashDeadline != 0 {
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil // Nothing to do
|
||||
}
|
||||
|
||||
buckets, eerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingBuckets)
|
||||
if eerr != nil {
|
||||
return nil, aerrors.HandleExternalError(eerr, "loading proving buckets amt")
|
||||
}
|
||||
|
||||
if params.PreviousSlashDeadline != 0 { // delete from previous bucket
|
||||
err := deleteMinerFromBucket(vmctx, buckets, previousBucket)
|
||||
if err != nil {
|
||||
return nil, aerrors.Wrapf(err, "delete from bucket %d, next %d", previousBucket, nextBucket)
|
||||
}
|
||||
}
|
||||
|
||||
err = addMinerToBucket(vmctx, buckets, nextBucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
self.ProvingBuckets, eerr = buckets.Flush()
|
||||
if eerr != nil {
|
||||
return nil, aerrors.HandleExternalError(eerr, "flushing proving buckets")
|
||||
}
|
||||
|
||||
nroot, err := vmctx.Storage().Put(&self)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func deleteMinerFromBucket(vmctx types.VMContext, buckets *amt.Root, previousBucket uint64) aerrors.ActorError {
|
||||
var bucket cid.Cid
|
||||
err := buckets.Get(previousBucket, &bucket)
|
||||
switch err.(type) {
|
||||
case *amt.ErrNotFound:
|
||||
return aerrors.HandleExternalError(err, "proving bucket missing")
|
||||
case nil: // noop
|
||||
default:
|
||||
return aerrors.HandleExternalError(err, "getting proving bucket")
|
||||
}
|
||||
|
||||
bhamt, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "failed to load proving bucket")
|
||||
}
|
||||
err = bhamt.Delete(vmctx.Context(), string(vmctx.Message().From.Bytes()))
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "deleting miner from proving bucket")
|
||||
}
|
||||
|
||||
err = bhamt.Flush(vmctx.Context())
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "flushing previous proving bucket")
|
||||
}
|
||||
|
||||
bucket, err = vmctx.Ipld().Put(vmctx.Context(), bhamt)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "putting previous proving bucket hamt")
|
||||
}
|
||||
|
||||
err = buckets.Set(previousBucket, bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "setting previous proving bucket cid in amt")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func addMinerToBucket(vmctx types.VMContext, buckets *amt.Root, nextBucket uint64) aerrors.ActorError {
|
||||
var bhamt *hamt.Node
|
||||
var bucket cid.Cid
|
||||
err := buckets.Get(nextBucket, &bucket)
|
||||
switch err.(type) {
|
||||
case *amt.ErrNotFound:
|
||||
bhamt = hamt.NewNode(vmctx.Ipld())
|
||||
case nil:
|
||||
bhamt, err = hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "failed to load proving bucket")
|
||||
}
|
||||
default:
|
||||
return aerrors.HandleExternalError(err, "getting proving bucket")
|
||||
}
|
||||
|
||||
err = bhamt.Set(vmctx.Context(), string(vmctx.Message().From.Bytes()), CborNull)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "setting miner in proving bucket")
|
||||
}
|
||||
|
||||
err = bhamt.Flush(vmctx.Context())
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "flushing previous proving bucket")
|
||||
}
|
||||
|
||||
bucket, err = vmctx.Ipld().Put(vmctx.Context(), bhamt)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "putting previous proving bucket hamt")
|
||||
}
|
||||
|
||||
err = buckets.Set(nextBucket, bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "setting previous proving bucket cid in amt")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) GetTotalStorage(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.TotalStorage.Bytes(), nil
|
||||
}
|
||||
|
||||
type PowerLookupParams struct {
|
||||
Miner address.Address
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) PowerLookup(act *types.Actor, vmctx types.VMContext, params *PowerLookupParams) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||
return nil, aerrors.Wrap(err, "getting head")
|
||||
}
|
||||
|
||||
pow, err := powerLookup(context.TODO(), vmctx, &self, params.Miner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pow.Bytes(), nil
|
||||
}
|
||||
|
||||
func powerLookup(ctx context.Context, vmctx types.VMContext, self *StoragePowerState, miner address.Address) (types.BigInt, ActorError) {
|
||||
has, err := MinerSetHas(vmctx, self.Miners, miner)
|
||||
if err != nil {
|
||||
return types.EmptyInt, err
|
||||
}
|
||||
|
||||
if !has {
|
||||
return types.EmptyInt, aerrors.New(1, "miner not registered with storage power actor")
|
||||
}
|
||||
|
||||
// TODO: Use local amt
|
||||
ret, err := vmctx.Send(miner, MAMethods.GetPower, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return types.EmptyInt, aerrors.Wrap(err, "invoke Miner.GetPower")
|
||||
}
|
||||
|
||||
return types.BigFromBytes(ret), nil
|
||||
}
|
||||
|
||||
type IsValidMinerParam struct {
|
||||
Addr address.Address
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) IsValidMiner(act *types.Actor, vmctx types.VMContext, param *IsValidMinerParam) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
has, err := MinerSetHas(vmctx, self.Miners, param.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !has {
|
||||
log.Warnf("Miner INVALID: not in set: %s", param.Addr)
|
||||
|
||||
return cbg.CborBoolFalse, nil
|
||||
}
|
||||
|
||||
ret, err := vmctx.Send(param.Addr, MAMethods.IsSlashed, types.NewInt(0), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slashed := bytes.Equal(ret, cbg.CborBoolTrue)
|
||||
|
||||
if slashed {
|
||||
log.Warnf("Miner INVALID: /SLASHED/ : %s", param.Addr)
|
||||
}
|
||||
|
||||
return cbg.EncodeBool(!slashed), nil
|
||||
}
|
||||
|
||||
type PledgeCollateralParams struct {
|
||||
Size types.BigInt
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) PledgeCollateralForSize(act *types.Actor, vmctx types.VMContext, param *PledgeCollateralParams) ([]byte, ActorError) {
|
||||
var self StoragePowerState
|
||||
if err := vmctx.Storage().Get(vmctx.Storage().GetHead(), &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalCollateral, err := pledgeCollateralForSize(vmctx, param.Size, self.TotalStorage, self.MinerCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return totalCollateral.Bytes(), nil
|
||||
}
|
||||
|
||||
func pledgeCollateralForSize(vmctx types.VMContext, size, totalStorage types.BigInt, minerCount uint64) (types.BigInt, aerrors.ActorError) {
|
||||
netBalance, err := vmctx.GetBalance(NetworkAddress)
|
||||
if err != nil {
|
||||
return types.EmptyInt, err
|
||||
}
|
||||
|
||||
// TODO: the spec says to also grab 'total vested filecoin' and include it as available
|
||||
// If we don't factor that in, we effectively assume all of the locked up filecoin is 'available'
|
||||
// the blocker on that right now is that its hard to tell how much filecoin is unlocked
|
||||
|
||||
availableFilecoin := types.BigSub(
|
||||
types.BigMul(types.NewInt(build.TotalFilecoin), types.NewInt(build.FilecoinPrecision)),
|
||||
netBalance,
|
||||
)
|
||||
|
||||
totalPowerCollateral := types.BigDiv(
|
||||
types.BigMul(
|
||||
availableFilecoin,
|
||||
types.NewInt(build.PowerCollateralProportion),
|
||||
),
|
||||
types.NewInt(build.CollateralPrecision),
|
||||
)
|
||||
|
||||
totalPerCapitaCollateral := types.BigDiv(
|
||||
types.BigMul(
|
||||
availableFilecoin,
|
||||
types.NewInt(build.PerCapitaCollateralProportion),
|
||||
),
|
||||
types.NewInt(build.CollateralPrecision),
|
||||
)
|
||||
|
||||
// REVIEW: for bootstrapping purposes, we skip the power portion of the
|
||||
// collateral if there is no collateral in the network yet
|
||||
powerCollateral := types.NewInt(0)
|
||||
if types.BigCmp(totalStorage, types.NewInt(0)) != 0 {
|
||||
powerCollateral = types.BigDiv(
|
||||
types.BigMul(
|
||||
totalPowerCollateral,
|
||||
size,
|
||||
),
|
||||
totalStorage,
|
||||
)
|
||||
}
|
||||
|
||||
perCapCollateral := types.BigDiv(
|
||||
totalPerCapitaCollateral,
|
||||
types.NewInt(minerCount),
|
||||
)
|
||||
|
||||
return types.BigAdd(powerCollateral, perCapCollateral), nil
|
||||
}
|
||||
|
||||
func (spa StoragePowerActor) CheckProofSubmissions(act *types.Actor, vmctx types.VMContext, param *struct{}) ([]byte, ActorError) {
|
||||
if vmctx.Message().From != CronAddress {
|
||||
return nil, aerrors.New(1, "CheckProofSubmissions is only callable from the cron actor")
|
||||
}
|
||||
|
||||
var self StoragePowerState
|
||||
old := vmctx.Storage().GetHead()
|
||||
if err := vmctx.Storage().Get(old, &self); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := self.LastMinerCheck; i < vmctx.BlockHeight(); i++ {
|
||||
height := i + 1
|
||||
|
||||
err := checkProofSubmissionsAtH(vmctx, &self, height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
self.LastMinerCheck = vmctx.BlockHeight()
|
||||
|
||||
nroot, aerr := vmctx.Storage().Put(&self)
|
||||
if aerr != nil {
|
||||
return nil, aerr
|
||||
}
|
||||
|
||||
if err := vmctx.Storage().Commit(old, nroot); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func checkProofSubmissionsAtH(vmctx types.VMContext, self *StoragePowerState, height uint64) aerrors.ActorError {
|
||||
bucketID := height % build.SlashablePowerDelay
|
||||
|
||||
buckets, eerr := amt.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingBuckets)
|
||||
if eerr != nil {
|
||||
return aerrors.HandleExternalError(eerr, "loading proving buckets amt")
|
||||
}
|
||||
|
||||
var bucket cid.Cid
|
||||
err := buckets.Get(bucketID, &bucket)
|
||||
switch err.(type) {
|
||||
case *amt.ErrNotFound:
|
||||
return nil // nothing to do
|
||||
case nil:
|
||||
default:
|
||||
return aerrors.HandleExternalError(err, "getting proving bucket")
|
||||
}
|
||||
|
||||
bhamt, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), bucket)
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "failed to load proving bucket")
|
||||
}
|
||||
|
||||
err = bhamt.ForEach(vmctx.Context(), func(k string, val interface{}) error {
|
||||
_, span := trace.StartSpan(vmctx.Context(), "StoragePowerActor.CheckProofSubmissions.loop")
|
||||
defer span.End()
|
||||
|
||||
maddr, err := address.NewFromBytes([]byte(k))
|
||||
if err != nil {
|
||||
return aerrors.Escalate(err, "parsing miner address")
|
||||
}
|
||||
|
||||
span.AddAttributes(trace.StringAttribute("miner", maddr.String()))
|
||||
|
||||
params, err := SerializeParams(&CheckMinerParams{NetworkPower: self.TotalStorage})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, err := vmctx.Send(maddr, MAMethods.CheckMiner, types.NewInt(0), params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(ret) == 0 {
|
||||
return nil // miner is fine
|
||||
}
|
||||
|
||||
var power types.BigInt
|
||||
if err := power.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
||||
return xerrors.Errorf("unmarshaling CheckMiner response (%x): %w", ret, err)
|
||||
}
|
||||
|
||||
if power.GreaterThan(types.NewInt(0)) {
|
||||
log.Warnf("slashing miner %s for missed PoSt (%s B, H: %d, Bucket: %d)", maddr, power, height, bucketID)
|
||||
|
||||
self.TotalStorage = types.BigSub(self.TotalStorage, power)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return aerrors.HandleExternalError(err, "iterating miners in proving bucket")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MinerSetHas(vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (bool, aerrors.ActorError) {
|
||||
nd, err := hamt.LoadNode(vmctx.Context(), vmctx.Ipld(), rcid)
|
||||
if err != nil {
|
||||
return false, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
err = nd.Find(vmctx.Context(), string(maddr.Bytes()), nil)
|
||||
switch err {
|
||||
case hamt.ErrNotFound:
|
||||
return false, nil
|
||||
case nil:
|
||||
return true, nil
|
||||
default:
|
||||
return false, aerrors.HandleExternalError(err, "failed to do set lookup")
|
||||
}
|
||||
}
|
||||
|
||||
func MinerSetList(ctx context.Context, cst *hamt.CborIpldStore, rcid cid.Cid) ([]address.Address, error) {
|
||||
nd, err := hamt.LoadNode(ctx, cst, rcid)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load miner set: %w", err)
|
||||
}
|
||||
|
||||
var out []address.Address
|
||||
err = nd.ForEach(ctx, func(k string, val interface{}) error {
|
||||
addr, err := address.NewFromBytes([]byte(k))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out = append(out, addr)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func MinerSetAdd(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (cid.Cid, aerrors.ActorError) {
|
||||
nd, err := hamt.LoadNode(ctx, vmctx.Ipld(), rcid)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
mkey := string(maddr.Bytes())
|
||||
err = nd.Find(ctx, mkey, nil)
|
||||
if err == nil {
|
||||
return cid.Undef, aerrors.New(20, "miner already in set")
|
||||
}
|
||||
|
||||
if !xerrors.Is(err, hamt.ErrNotFound) {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to do miner set check")
|
||||
}
|
||||
|
||||
if err := nd.Set(ctx, mkey, uint64(1)); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "adding miner address to set failed")
|
||||
}
|
||||
|
||||
if err := nd.Flush(ctx); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to flush miner set")
|
||||
}
|
||||
|
||||
c, err := vmctx.Ipld().Put(ctx, nd)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to persist miner set to storage")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func MinerSetRemove(ctx context.Context, vmctx types.VMContext, rcid cid.Cid, maddr address.Address) (cid.Cid, aerrors.ActorError) {
|
||||
nd, err := hamt.LoadNode(ctx, vmctx.Ipld(), rcid)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to load miner set")
|
||||
}
|
||||
|
||||
mkey := string(maddr.Bytes())
|
||||
switch nd.Delete(ctx, mkey) {
|
||||
default:
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to delete miner from set")
|
||||
case hamt.ErrNotFound:
|
||||
return cid.Undef, aerrors.New(1, "miner not found in set on delete")
|
||||
case nil:
|
||||
}
|
||||
|
||||
if err := nd.Flush(ctx); err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to flush miner set")
|
||||
}
|
||||
|
||||
c, err := vmctx.Ipld().Put(ctx, nd)
|
||||
if err != nil {
|
||||
return cid.Undef, aerrors.HandleExternalError(err, "failed to persist miner set to storage")
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type cbgNull struct{}
|
||||
|
||||
var CborNull = &cbgNull{}
|
||||
|
||||
func (cbgNull) MarshalCBOR(w io.Writer) error {
|
||||
n, err := w.Write(cbg.CborNull)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return xerrors.New("expected to write 1 byte")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cbgNull) UnmarshalCBOR(r io.Reader) error {
|
||||
b := [1]byte{}
|
||||
n, err := r.Read(b[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return xerrors.New("expected 1 byte")
|
||||
}
|
||||
if !bytes.Equal(b[:], cbg.CborNull) {
|
||||
return xerrors.New("expected cbor null")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
|
||||
. "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
|
||||
var ownerAddr, workerAddr address.Address
|
||||
|
||||
opts := []HarnessOpt{
|
||||
HarnessAddr(&ownerAddr, 1000000),
|
||||
HarnessAddr(&workerAddr, 100000),
|
||||
}
|
||||
|
||||
h := NewHarness(t, opts...)
|
||||
|
||||
var minerAddr address.Address
|
||||
{
|
||||
// cheating the bootstrapping problem
|
||||
cheatStorageMarketTotal(t, h.vm, h.cs.Blockstore())
|
||||
|
||||
ret, _ := h.InvokeWithValue(t, ownerAddr, StoragePowerAddress, SPAMethods.CreateStorageMiner,
|
||||
types.NewInt(500000),
|
||||
&CreateStorageMinerParams{
|
||||
Owner: ownerAddr,
|
||||
Worker: workerAddr,
|
||||
SectorSize: build.SectorSizes[0],
|
||||
PeerID: "fakepeerid",
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
var err error
|
||||
minerAddr, err = address.NewFromBytes(ret.Return)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner,
|
||||
&IsValidMinerParam{Addr: minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
|
||||
var output bool
|
||||
err := cbor.DecodeInto(ret.Return, &output)
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding: %+v", err)
|
||||
}
|
||||
|
||||
if !output {
|
||||
t.Fatalf("%s is miner but IsValidMiner call returned false", minerAddr)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.PowerLookup,
|
||||
&PowerLookupParams{Miner: minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
power := types.BigFromBytes(ret.Return)
|
||||
|
||||
if types.BigCmp(power, types.NewInt(0)) != 0 {
|
||||
t.Fatalf("power should be zero, is: %s", power)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, minerAddr, MAMethods.GetOwner, nil)
|
||||
ApplyOK(t, ret)
|
||||
oA, err := address.NewFromBytes(ret.Return)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ownerAddr, oA, "return from GetOwner should be equal to the owner")
|
||||
}
|
||||
|
||||
{
|
||||
b1 := fakeBlock(t, minerAddr, 100)
|
||||
b2 := fakeBlock(t, minerAddr, 101)
|
||||
|
||||
signBlock(t, h.w, workerAddr, b1)
|
||||
signBlock(t, h.w, workerAddr, b2)
|
||||
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.ArbitrateConsensusFault,
|
||||
&ArbitrateConsensusFaultParams{
|
||||
Block1: b1,
|
||||
Block2: b2,
|
||||
})
|
||||
ApplyOK(t, ret)
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.PowerLookup,
|
||||
&PowerLookupParams{Miner: minerAddr})
|
||||
assert.Equal(t, ret.ExitCode, byte(1))
|
||||
}
|
||||
|
||||
{
|
||||
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.IsValidMiner, &IsValidMinerParam{minerAddr})
|
||||
ApplyOK(t, ret)
|
||||
assert.Equal(t, ret.Return, cbg.CborBoolFalse)
|
||||
}
|
||||
}
|
||||
|
||||
func cheatStorageMarketTotal(t *testing.T, vm *vm.VM, bs bstore.Blockstore) {
|
||||
t.Helper()
|
||||
|
||||
sma, err := vm.StateTree().GetActor(StoragePowerAddress)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cst := hamt.CSTFromBstore(bs)
|
||||
|
||||
var smastate StoragePowerState
|
||||
if err := cst.Get(context.TODO(), sma.Head, &smastate); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
smastate.TotalStorage = types.NewInt(10000)
|
||||
|
||||
c, err := cst.Put(context.TODO(), &smastate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sma.Head = c
|
||||
|
||||
if err := vm.StateTree().SetActor(StoragePowerAddress, sma); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeBlock(t *testing.T, minerAddr address.Address, ts uint64) *types.BlockHeader {
|
||||
c := fakeCid(t, 1)
|
||||
return &types.BlockHeader{Height: 5, Miner: minerAddr, Timestamp: ts, ParentStateRoot: c, Messages: c, ParentMessageReceipts: c, BLSAggregate: types.Signature{Type: types.KTBLS}}
|
||||
}
|
||||
|
||||
func fakeCid(t *testing.T, s int) cid.Cid {
|
||||
t.Helper()
|
||||
c, err := cid.NewPrefixV1(cid.Raw, mh.IDENTITY).Sum([]byte(fmt.Sprintf("%d", s)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func signBlock(t *testing.T, w *wallet.Wallet, worker address.Address, blk *types.BlockHeader) {
|
||||
t.Helper()
|
||||
sb, err := blk.SigningBytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sig, err := w.Sign(context.TODO(), worker, sb)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
blk.BlockSig = sig
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
var AccountCodeCid cid.Cid
|
||||
var CronCodeCid cid.Cid
|
||||
var StoragePowerCodeCid cid.Cid
|
||||
var StorageMarketCodeCid cid.Cid
|
||||
var StorageMinerCodeCid cid.Cid
|
||||
var MultisigCodeCid cid.Cid
|
||||
var InitCodeCid cid.Cid
|
||||
var PaymentChannelCodeCid cid.Cid
|
||||
|
||||
var InitAddress = mustIDAddress(0)
|
||||
var NetworkAddress = mustIDAddress(1)
|
||||
var StoragePowerAddress = mustIDAddress(2)
|
||||
var StorageMarketAddress = mustIDAddress(3) // TODO: missing from spec
|
||||
var CronAddress = mustIDAddress(4)
|
||||
var BurntFundsAddress = mustIDAddress(99)
|
||||
|
||||
func mustIDAddress(i uint64) address.Address {
|
||||
a, err := address.NewIDAddress(i)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func init() {
|
||||
pref := cid.NewPrefixV1(cid.Raw, mh.IDENTITY)
|
||||
mustSum := func(s string) cid.Cid {
|
||||
c, err := pref.Sum([]byte(s))
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
AccountCodeCid = mustSum("fil/1/account") // TODO: spec
|
||||
CronCodeCid = mustSum("fil/1/cron")
|
||||
StoragePowerCodeCid = mustSum("fil/1/power")
|
||||
StorageMarketCodeCid = mustSum("fil/1/market")
|
||||
StorageMinerCodeCid = mustSum("fil/1/miner")
|
||||
MultisigCodeCid = mustSum("fil/1/multisig")
|
||||
InitCodeCid = mustSum("fil/1/init")
|
||||
PaymentChannelCodeCid = mustSum("fil/1/paych")
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
|
||||
. "github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
)
|
||||
|
||||
func blsaddr(n uint64) address.Address {
|
||||
buf := make([]byte, 48)
|
||||
binary.PutUvarint(buf, n)
|
||||
|
||||
addr, err := address.NewBLSAddress(buf)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address, bstore.Blockstore) {
|
||||
bs := bstore.NewBlockstore(dstore.NewMapDatastore())
|
||||
|
||||
from := blsaddr(0)
|
||||
maddr := blsaddr(1)
|
||||
|
||||
actors := map[address.Address]types.BigInt{
|
||||
from: types.NewInt(1000000),
|
||||
maddr: types.NewInt(0),
|
||||
}
|
||||
st, err := gen.MakeInitialStateTree(bs, actors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateroot, err := st.Flush()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cs := store.NewChainStore(bs, nil)
|
||||
|
||||
// TODO: should probabaly mock out the randomness bit, nil works for now
|
||||
vm, err := vm.NewVM(stateroot, 1, nil, maddr, cs.Blockstore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return vm, []address.Address{from, maddr}, bs
|
||||
}
|
||||
|
||||
func TestVMInvokeMethod(t *testing.T) {
|
||||
vm, addrs, _ := setupVMTestEnv(t)
|
||||
from := addrs[0]
|
||||
|
||||
var err error
|
||||
cenc, err := SerializeParams(&StorageMinerConstructorParams{Owner: from, Worker: from})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
execparams := &ExecParams{
|
||||
Code: StorageMinerCodeCid,
|
||||
Params: cenc,
|
||||
}
|
||||
enc, err := SerializeParams(execparams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: InitAddress,
|
||||
From: from,
|
||||
Method: IAMethods.Exec,
|
||||
Params: enc,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(10000),
|
||||
Value: types.NewInt(0),
|
||||
}
|
||||
|
||||
ret, err := vm.ApplyMessage(context.TODO(), msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
t.Fatal("invocation failed")
|
||||
}
|
||||
|
||||
outaddr, err := address.NewFromBytes(ret.Return)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if outaddr.String() != "t0102" {
|
||||
t.Fatal("hold up")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageMarketActorCreateMiner(t *testing.T) {
|
||||
vm, addrs, bs := setupVMTestEnv(t)
|
||||
from := addrs[0]
|
||||
maddr := addrs[1]
|
||||
|
||||
cheatStorageMarketTotal(t, vm, bs)
|
||||
|
||||
params := &StorageMinerConstructorParams{
|
||||
Owner: maddr,
|
||||
Worker: maddr,
|
||||
SectorSize: build.SectorSizes[0],
|
||||
PeerID: "fakepeerid",
|
||||
}
|
||||
var err error
|
||||
enc, err := SerializeParams(params)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: StoragePowerAddress,
|
||||
From: from,
|
||||
Method: SPAMethods.CreateStorageMiner,
|
||||
Params: enc,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(10000),
|
||||
Value: types.NewInt(50000),
|
||||
}
|
||||
|
||||
ret, err := vm.ApplyMessage(context.TODO(), msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
fmt.Println(ret.ActorErr)
|
||||
t.Fatal("invocation failed: ", ret.ExitCode)
|
||||
}
|
||||
|
||||
outaddr, err := address.NewFromBytes(ret.Return)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if outaddr.String() != "t0102" {
|
||||
t.Fatal("hold up")
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
)
|
||||
|
||||
func NewIDAddress(id uint64) (address.Address, ActorError) {
|
||||
a, err := address.NewIDAddress(id)
|
||||
if err != nil {
|
||||
return address.Undef, aerrors.Escalate(err, "could not create ID Address")
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
@@ -3,13 +3,14 @@ package aerrors
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func IsFatal(err ActorError) bool {
|
||||
return err != nil && err.IsFatal()
|
||||
}
|
||||
func RetCode(err ActorError) uint8 {
|
||||
func RetCode(err ActorError) exitcode.ExitCode {
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
@@ -25,12 +26,12 @@ type internalActorError interface {
|
||||
type ActorError interface {
|
||||
error
|
||||
IsFatal() bool
|
||||
RetCode() uint8
|
||||
RetCode() exitcode.ExitCode
|
||||
}
|
||||
|
||||
type actorError struct {
|
||||
fatal bool
|
||||
retCode uint8
|
||||
retCode exitcode.ExitCode
|
||||
|
||||
msg string
|
||||
frame xerrors.Frame
|
||||
@@ -41,7 +42,7 @@ func (e *actorError) IsFatal() bool {
|
||||
return e.fatal
|
||||
}
|
||||
|
||||
func (e *actorError) RetCode() uint8 {
|
||||
func (e *actorError) RetCode() exitcode.ExitCode {
|
||||
return e.retCode
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -31,5 +32,5 @@ func TestAbsorbeError(t *testing.T) {
|
||||
aw3 := Wrap(aw2, "creating miner in storage market")
|
||||
t.Logf("Verbose error: %+v", aw3)
|
||||
t.Logf("Normal error: %v", aw3)
|
||||
assert.Equal(t, uint8(35), RetCode(aw3))
|
||||
assert.Equal(t, exitcode.ExitCode(35), RetCode(aw3))
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// New creates a new non-fatal error
|
||||
func New(retCode uint8, message string) ActorError {
|
||||
func New(retCode exitcode.ExitCode, message string) ActorError {
|
||||
if retCode == 0 {
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
@@ -29,7 +30,7 @@ func New(retCode uint8, message string) ActorError {
|
||||
}
|
||||
|
||||
// Newf creates a new non-fatal error
|
||||
func Newf(retCode uint8, format string, args ...interface{}) ActorError {
|
||||
func Newf(retCode exitcode.ExitCode, format string, args ...interface{}) ActorError {
|
||||
if retCode == 0 {
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
@@ -48,6 +49,27 @@ func Newf(retCode uint8, format string, args ...interface{}) ActorError {
|
||||
}
|
||||
}
|
||||
|
||||
// todo: bit hacky
|
||||
|
||||
func NewfSkip(skip int, retCode exitcode.ExitCode, format string, args ...interface{}) ActorError {
|
||||
if retCode == 0 {
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
retCode: 0,
|
||||
|
||||
msg: "tried creating an error and setting RetCode to 0",
|
||||
frame: xerrors.Caller(skip),
|
||||
err: fmt.Errorf(format, args...),
|
||||
}
|
||||
}
|
||||
return &actorError{
|
||||
retCode: retCode,
|
||||
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
frame: xerrors.Caller(skip),
|
||||
}
|
||||
}
|
||||
|
||||
func Fatal(message string, args ...interface{}) ActorError {
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
@@ -95,7 +117,7 @@ func Wrapf(err ActorError, format string, args ...interface{}) ActorError {
|
||||
}
|
||||
|
||||
// Absorb takes and error and makes in not fatal ActorError
|
||||
func Absorb(err error, retCode uint8, msg string) ActorError {
|
||||
func Absorb(err error, retCode exitcode.ExitCode, msg string) ActorError {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -160,7 +182,7 @@ func HandleExternalError(err error, msg string) ActorError {
|
||||
}
|
||||
}
|
||||
|
||||
if xerrors.Is(err, &hamt.SerializationError{}) {
|
||||
if xerrors.Is(err, &cbor.SerializationError{}) {
|
||||
return &actorError{
|
||||
fatal: false,
|
||||
retCode: 253,
|
||||
@@ -171,7 +193,8 @@ func HandleExternalError(err error, msg string) ActorError {
|
||||
}
|
||||
|
||||
return &actorError{
|
||||
fatal: true,
|
||||
fatal: false,
|
||||
retCode: 219,
|
||||
|
||||
msg: msg,
|
||||
frame: xerrors.Caller(1),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
package actors
|
||||
|
||||
import "github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
|
||||
type ActorError = aerrors.ActorError
|
||||
@@ -1,46 +0,0 @@
|
||||
package actors
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type update struct {
|
||||
start uint64
|
||||
method interface{}
|
||||
}
|
||||
|
||||
func withUpdates(updates ...update) interface{} {
|
||||
sort.Slice(updates, func(i, j int) bool { // so we iterate from newest below
|
||||
return updates[i].start > updates[j].start
|
||||
})
|
||||
|
||||
// <script type="application/javascript">
|
||||
|
||||
typ := reflect.TypeOf(updates[0].method)
|
||||
|
||||
out := reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) {
|
||||
vmctx := args[1].Interface().(types.VMContext)
|
||||
|
||||
for _, u := range updates {
|
||||
if vmctx.BlockHeight() >= u.start {
|
||||
return reflect.ValueOf(u.method).Call(args)
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.ValueOf(notFound(vmctx)).Call([]reflect.Value{})
|
||||
})
|
||||
|
||||
return out.Interface()
|
||||
|
||||
// </script>
|
||||
}
|
||||
|
||||
func notFound(vmctx types.VMContext) func() ([]byte, ActorError) {
|
||||
return func() ([]byte, ActorError) {
|
||||
return nil, aerrors.Fatal("no code for method %d at height %d", vmctx.Message().Method, vmctx.BlockHeight())
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
package actors_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
hamt "github.com/ipfs/go-hamt-ipld"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
)
|
||||
|
||||
const testGasLimit = 10000
|
||||
|
||||
type HarnessInit struct {
|
||||
NAddrs uint64
|
||||
Addrs map[address.Address]types.BigInt
|
||||
Miner address.Address
|
||||
}
|
||||
|
||||
type HarnessStage int
|
||||
|
||||
const (
|
||||
HarnessPreInit HarnessStage = iota
|
||||
HarnessPostInit
|
||||
)
|
||||
|
||||
type HarnessOpt func(testing.TB, *Harness) error
|
||||
|
||||
type Harness struct {
|
||||
HI HarnessInit
|
||||
Stage HarnessStage
|
||||
Nonces map[address.Address]uint64
|
||||
GasCharges map[address.Address]types.BigInt
|
||||
Rand vm.Rand
|
||||
BlockHeight uint64
|
||||
|
||||
lastBalanceCheck map[address.Address]types.BigInt
|
||||
|
||||
ctx context.Context
|
||||
bs blockstore.Blockstore
|
||||
vm *vm.VM
|
||||
cs *store.ChainStore
|
||||
w *wallet.Wallet
|
||||
}
|
||||
|
||||
var HarnessMinerFunds = types.NewInt(1000000)
|
||||
|
||||
func HarnessAddr(addr *address.Address, value uint64) HarnessOpt {
|
||||
return func(t testing.TB, h *Harness) error {
|
||||
if h.Stage != HarnessPreInit {
|
||||
return nil
|
||||
}
|
||||
hi := &h.HI
|
||||
if addr.Empty() {
|
||||
k, err := h.w.GenerateKey(types.KTSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
*addr = k
|
||||
}
|
||||
hi.Addrs[*addr] = types.NewInt(value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func HarnessMiner(addr *address.Address) HarnessOpt {
|
||||
return func(_ testing.TB, h *Harness) error {
|
||||
if h.Stage != HarnessPreInit {
|
||||
return nil
|
||||
}
|
||||
hi := &h.HI
|
||||
if addr.Empty() {
|
||||
*addr = hi.Miner
|
||||
return nil
|
||||
}
|
||||
delete(hi.Addrs, hi.Miner)
|
||||
hi.Miner = *addr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func HarnessActor(actor *address.Address, creator *address.Address, code cid.Cid, params func() cbg.CBORMarshaler) HarnessOpt {
|
||||
return func(t testing.TB, h *Harness) error {
|
||||
if h.Stage != HarnessPostInit {
|
||||
return nil
|
||||
}
|
||||
if !actor.Empty() {
|
||||
return xerrors.New("actor address should be empty")
|
||||
}
|
||||
|
||||
ret, _ := h.CreateActor(t, *creator, code, params())
|
||||
if ret.ExitCode != 0 {
|
||||
return xerrors.Errorf("creating actor: %w", ret.ActorErr)
|
||||
}
|
||||
var err error
|
||||
*actor, err = address.NewFromBytes(ret.Return)
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func HarnessCtx(ctx context.Context) HarnessOpt {
|
||||
return func(t testing.TB, h *Harness) error {
|
||||
h.ctx = ctx
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewHarness(t *testing.T, options ...HarnessOpt) *Harness {
|
||||
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := &Harness{
|
||||
Stage: HarnessPreInit,
|
||||
Nonces: make(map[address.Address]uint64),
|
||||
Rand: &fakeRand{},
|
||||
HI: HarnessInit{
|
||||
NAddrs: 1,
|
||||
Miner: blsaddr(0),
|
||||
Addrs: map[address.Address]types.BigInt{
|
||||
blsaddr(0): HarnessMinerFunds,
|
||||
},
|
||||
},
|
||||
GasCharges: make(map[address.Address]types.BigInt),
|
||||
|
||||
lastBalanceCheck: make(map[address.Address]types.BigInt),
|
||||
w: w,
|
||||
ctx: context.Background(),
|
||||
bs: bstore.NewBlockstore(dstore.NewMapDatastore()),
|
||||
BlockHeight: 0,
|
||||
}
|
||||
for _, opt := range options {
|
||||
err := opt(t, h)
|
||||
if err != nil {
|
||||
t.Fatalf("Applying options: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
st, err := gen.MakeInitialStateTree(h.bs, h.HI.Addrs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateroot, err := st.Flush()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stateroot, err = gen.SetupStorageMarketActor(h.bs, stateroot, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
h.cs = store.NewChainStore(h.bs, nil)
|
||||
h.vm, err = vm.NewVM(stateroot, 1, h.Rand, h.HI.Miner, h.cs.Blockstore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.Stage = HarnessPostInit
|
||||
for _, opt := range options {
|
||||
err := opt(t, h)
|
||||
if err != nil {
|
||||
t.Fatalf("Applying options: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Harness) Apply(t testing.TB, msg types.Message) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
if msg.Nonce == 0 {
|
||||
msg.Nonce, _ = h.Nonces[msg.From]
|
||||
h.Nonces[msg.From] = msg.Nonce + 1
|
||||
}
|
||||
|
||||
ret, err := h.vm.ApplyMessage(h.ctx, &msg)
|
||||
if err != nil {
|
||||
t.Fatalf("Applying message: %+v", err)
|
||||
}
|
||||
|
||||
if ret != nil {
|
||||
if prev, ok := h.GasCharges[msg.From]; ok {
|
||||
h.GasCharges[msg.From] = types.BigAdd(prev, ret.GasUsed)
|
||||
} else {
|
||||
h.GasCharges[msg.From] = ret.GasUsed
|
||||
}
|
||||
}
|
||||
|
||||
stateroot, err := h.vm.Flush(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatalf("Flushing VM: %+v", err)
|
||||
}
|
||||
cst := hamt.CSTFromBstore(h.bs)
|
||||
state, err := state.LoadStateTree(cst, stateroot)
|
||||
if err != nil {
|
||||
t.Fatalf("Loading state tree: %+v", err)
|
||||
}
|
||||
return ret, state
|
||||
}
|
||||
|
||||
func (h *Harness) CreateActor(t testing.TB, from address.Address,
|
||||
code cid.Cid, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
|
||||
return h.Apply(t, types.Message{
|
||||
To: actors.InitAddress,
|
||||
From: from,
|
||||
Method: actors.IAMethods.Exec,
|
||||
Params: DumpObject(t,
|
||||
&actors.ExecParams{
|
||||
Code: code,
|
||||
Params: DumpObject(t, params),
|
||||
}),
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(testGasLimit),
|
||||
Value: types.NewInt(0),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Harness) SendFunds(t testing.TB, from address.Address, to address.Address,
|
||||
value types.BigInt) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
return h.Apply(t, types.Message{
|
||||
To: to,
|
||||
From: from,
|
||||
Method: 0,
|
||||
Value: value,
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(testGasLimit),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Harness) Invoke(t testing.TB, from address.Address, to address.Address,
|
||||
method uint64, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
return h.InvokeWithValue(t, from, to, method, types.NewInt(0), params)
|
||||
}
|
||||
|
||||
func (h *Harness) InvokeWithValue(t testing.TB, from address.Address, to address.Address,
|
||||
method uint64, value types.BigInt, params cbg.CBORMarshaler) (*vm.ApplyRet, *state.StateTree) {
|
||||
t.Helper()
|
||||
h.vm.SetBlockHeight(h.BlockHeight)
|
||||
return h.Apply(t, types.Message{
|
||||
To: to,
|
||||
From: from,
|
||||
Method: method,
|
||||
Value: value,
|
||||
Params: DumpObject(t, params),
|
||||
GasPrice: types.NewInt(1),
|
||||
GasLimit: types.NewInt(testGasLimit),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Harness) AssertBalance(t testing.TB, addr address.Address, amt uint64) {
|
||||
t.Helper()
|
||||
|
||||
b, err := h.vm.ActorBalance(addr)
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
if types.BigCmp(types.NewInt(amt), b) != 0 {
|
||||
t.Errorf("expected %s to have balanced of %d. Instead has %s", addr, amt, b)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Harness) AssertBalanceChange(t testing.TB, addr address.Address, amt int64) {
|
||||
t.Helper()
|
||||
lastBalance, ok := h.lastBalanceCheck[addr]
|
||||
if !ok {
|
||||
lastBalance, ok = h.HI.Addrs[addr]
|
||||
if !ok {
|
||||
lastBalance = types.NewInt(0)
|
||||
}
|
||||
}
|
||||
|
||||
var expected types.BigInt
|
||||
|
||||
if amt >= 0 {
|
||||
expected = types.BigAdd(lastBalance, types.NewInt(uint64(amt)))
|
||||
} else {
|
||||
expected = types.BigSub(lastBalance, types.NewInt(uint64(-amt)))
|
||||
}
|
||||
|
||||
h.lastBalanceCheck[addr] = expected
|
||||
|
||||
if gasUsed, ok := h.GasCharges[addr]; ok {
|
||||
expected = types.BigSub(expected, gasUsed)
|
||||
}
|
||||
|
||||
b, err := h.vm.ActorBalance(addr)
|
||||
if err != nil {
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
if types.BigCmp(expected, b) != 0 {
|
||||
t.Errorf("expected %s to have balanced of %d. Instead has %s", addr, amt, b)
|
||||
}
|
||||
}
|
||||
|
||||
func DumpObject(t testing.TB, obj cbg.CBORMarshaler) []byte {
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
t.Helper()
|
||||
b := new(bytes.Buffer)
|
||||
if err := obj.MarshalCBOR(b); err != nil {
|
||||
t.Fatalf("dumping params: %+v", err)
|
||||
}
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
type fakeRand struct{}
|
||||
|
||||
func (fr *fakeRand) GetRandomness(ctx context.Context, h int64) ([]byte, error) {
|
||||
out := make([]byte, 32)
|
||||
rand.New(rand.NewSource(h)).Read(out)
|
||||
return out, nil
|
||||
}
|
||||
@@ -7,10 +7,6 @@ import (
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
var (
|
||||
EmptyStructCBOR = []byte{0xa0}
|
||||
)
|
||||
|
||||
func SerializeParams(i cbg.CBORMarshaler) ([]byte, aerrors.ActorError) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := i.MarshalCBOR(buf); err != nil {
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
bls "github.com/filecoin-project/filecoin-ffi"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/minio/blake2b-simd"
|
||||
"github.com/multiformats/go-varint"
|
||||
"github.com/polydawn/refmt/obj/atlas"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(addressAtlasEntry)
|
||||
}
|
||||
|
||||
var addressAtlasEntry = atlas.BuildEntry(Address{}).Transform().
|
||||
TransformMarshal(atlas.MakeMarshalTransformFunc(
|
||||
func(a Address) (string, error) {
|
||||
return string(a.Bytes()), nil
|
||||
})).
|
||||
TransformUnmarshal(atlas.MakeUnmarshalTransformFunc(
|
||||
func(x string) (Address, error) {
|
||||
return NewFromBytes([]byte(x))
|
||||
})).
|
||||
Complete()
|
||||
|
||||
// Address is the go type that represents an address in the filecoin network.
|
||||
type Address struct{ str string }
|
||||
|
||||
// Undef is the type that represents an undefined address.
|
||||
var Undef = Address{}
|
||||
|
||||
// Network represents which network an address belongs to.
|
||||
type Network = byte
|
||||
|
||||
const (
|
||||
// Mainnet is the main network.
|
||||
Mainnet Network = iota
|
||||
// Testnet is the test network.
|
||||
Testnet
|
||||
)
|
||||
|
||||
// MainnetPrefix is the main network prefix.
|
||||
const MainnetPrefix = "f"
|
||||
|
||||
// TestnetPrefix is the main network prefix.
|
||||
const TestnetPrefix = "t"
|
||||
|
||||
// Protocol represents which protocol an address uses.
|
||||
type Protocol = byte
|
||||
|
||||
const (
|
||||
// ID represents the address ID protocol.
|
||||
ID Protocol = iota
|
||||
// SECP256K1 represents the address SECP256K1 protocol.
|
||||
SECP256K1
|
||||
// Actor represents the address Actor protocol.
|
||||
Actor
|
||||
// BLS represents the address BLS protocol.
|
||||
BLS
|
||||
|
||||
Unknown = Protocol(255)
|
||||
)
|
||||
|
||||
// Protocol returns the protocol used by the address.
|
||||
func (a Address) Protocol() Protocol {
|
||||
if len(a.str) == 0 {
|
||||
return Unknown
|
||||
}
|
||||
return a.str[0]
|
||||
}
|
||||
|
||||
// Payload returns the payload of the address.
|
||||
func (a Address) Payload() []byte {
|
||||
return []byte(a.str[1:])
|
||||
}
|
||||
|
||||
// Bytes returns the address as bytes.
|
||||
func (a Address) Bytes() []byte {
|
||||
return []byte(a.str)
|
||||
}
|
||||
|
||||
// String returns an address encoded as a string.
|
||||
func (a Address) String() string {
|
||||
str, err := encode(Testnet, a)
|
||||
if err != nil {
|
||||
panic(err) // I don't know if this one is okay
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Empty returns true if the address is empty, false otherwise.
|
||||
func (a Address) Empty() bool {
|
||||
return a == Undef
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals the cbor bytes into the address.
|
||||
func (a Address) Unmarshal(b []byte) error {
|
||||
return cbor.DecodeInto(b, &a)
|
||||
}
|
||||
|
||||
// Marshal marshals the address to cbor.
|
||||
func (a Address) Marshal() ([]byte, error) {
|
||||
return cbor.DumpObject(a)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json unmarshal interface.
|
||||
func (a *Address) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr, err := decode(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*a = addr
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json marshal interface.
|
||||
func (a Address) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + a.String() + `"`), nil
|
||||
}
|
||||
|
||||
// Format implements the Formatter interface.
|
||||
func (a Address) Format(f fmt.State, c rune) {
|
||||
switch c {
|
||||
case 'v':
|
||||
if a.Empty() {
|
||||
fmt.Fprint(f, UndefAddressString) //nolint: errcheck
|
||||
} else {
|
||||
fmt.Fprintf(f, "[%x - %x]", a.Protocol(), a.Payload()) // nolint: errcheck
|
||||
}
|
||||
case 's':
|
||||
fmt.Fprintf(f, "%s", a.String()) // nolint: errcheck
|
||||
default:
|
||||
fmt.Fprintf(f, "%"+string(c), a.Bytes()) // nolint: errcheck
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Address) Scan(value interface{}) error {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
a1, err := decode(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*a = a1
|
||||
|
||||
return nil
|
||||
default:
|
||||
return xerrors.New("non-string types unsupported")
|
||||
}
|
||||
}
|
||||
|
||||
// NewIDAddress returns an address using the ID protocol.
|
||||
func NewIDAddress(id uint64) (Address, error) {
|
||||
return newAddress(ID, varint.ToUvarint(id))
|
||||
}
|
||||
|
||||
// NewSecp256k1Address returns an address using the SECP256K1 protocol.
|
||||
func NewSecp256k1Address(pubkey []byte) (Address, error) {
|
||||
return newAddress(SECP256K1, addressHash(pubkey))
|
||||
}
|
||||
|
||||
// NewActorAddress returns an address using the Actor protocol.
|
||||
func NewActorAddress(data []byte) (Address, error) {
|
||||
return newAddress(Actor, addressHash(data))
|
||||
}
|
||||
|
||||
// NewBLSAddress returns an address using the BLS protocol.
|
||||
func NewBLSAddress(pubkey []byte) (Address, error) {
|
||||
return newAddress(BLS, pubkey)
|
||||
}
|
||||
|
||||
// NewFromString returns the address represented by the string `addr`.
|
||||
func NewFromString(addr string) (Address, error) {
|
||||
return decode(addr)
|
||||
}
|
||||
|
||||
// NewFromBytes return the address represented by the bytes `addr`.
|
||||
func NewFromBytes(addr []byte) (Address, error) {
|
||||
if len(addr) == 0 {
|
||||
return Undef, nil
|
||||
}
|
||||
if len(addr) == 1 {
|
||||
return Undef, ErrInvalidLength
|
||||
}
|
||||
return newAddress(addr[0], addr[1:])
|
||||
}
|
||||
|
||||
// Checksum returns the checksum of `ingest`.
|
||||
func Checksum(ingest []byte) []byte {
|
||||
return hash(ingest, checksumHashConfig)
|
||||
}
|
||||
|
||||
// ValidateChecksum returns true if the checksum of `ingest` is equal to `expected`>
|
||||
func ValidateChecksum(ingest, expect []byte) bool {
|
||||
digest := Checksum(ingest)
|
||||
return bytes.Equal(digest, expect)
|
||||
}
|
||||
|
||||
func addressHash(ingest []byte) []byte {
|
||||
return hash(ingest, payloadHashConfig)
|
||||
}
|
||||
|
||||
func newAddress(protocol Protocol, payload []byte) (Address, error) {
|
||||
switch protocol {
|
||||
case ID:
|
||||
_, n, err := varint.FromUvarint(payload)
|
||||
if err != nil {
|
||||
return Undef, xerrors.Errorf("could not decode: %v: %w", err, ErrInvalidPayload)
|
||||
}
|
||||
if n != len(payload) {
|
||||
return Undef, xerrors.Errorf("different varint length (v:%d != p:%d): %w",
|
||||
n, len(payload), ErrInvalidPayload)
|
||||
}
|
||||
case SECP256K1, Actor:
|
||||
if len(payload) != PayloadHashLength {
|
||||
return Undef, ErrInvalidPayload
|
||||
}
|
||||
case BLS:
|
||||
if len(payload) != bls.PublicKeyBytes {
|
||||
return Undef, ErrInvalidPayload
|
||||
}
|
||||
default:
|
||||
return Undef, ErrUnknownProtocol
|
||||
}
|
||||
explen := 1 + len(payload)
|
||||
buf := make([]byte, explen)
|
||||
|
||||
buf[0] = protocol
|
||||
copy(buf[1:], payload)
|
||||
|
||||
return Address{string(buf)}, nil
|
||||
}
|
||||
|
||||
func encode(network Network, addr Address) (string, error) {
|
||||
if addr == Undef {
|
||||
return UndefAddressString, nil
|
||||
}
|
||||
var ntwk string
|
||||
switch network {
|
||||
case Mainnet:
|
||||
ntwk = MainnetPrefix
|
||||
case Testnet:
|
||||
ntwk = TestnetPrefix
|
||||
default:
|
||||
return UndefAddressString, ErrUnknownNetwork
|
||||
}
|
||||
|
||||
var strAddr string
|
||||
switch addr.Protocol() {
|
||||
case SECP256K1, Actor, BLS:
|
||||
cksm := Checksum(append([]byte{addr.Protocol()}, addr.Payload()...))
|
||||
strAddr = ntwk + fmt.Sprintf("%d", addr.Protocol()) + AddressEncoding.WithPadding(-1).EncodeToString(append(addr.Payload(), cksm[:]...))
|
||||
case ID:
|
||||
i, n, err := varint.FromUvarint(addr.Payload())
|
||||
if err != nil {
|
||||
return UndefAddressString, xerrors.Errorf("could not decode varint: %w", err)
|
||||
}
|
||||
if n != len(addr.Payload()) {
|
||||
return UndefAddressString, xerrors.Errorf("payload contains additional bytes")
|
||||
}
|
||||
strAddr = fmt.Sprintf("%s%d%d", ntwk, addr.Protocol(), i)
|
||||
default:
|
||||
return UndefAddressString, ErrUnknownProtocol
|
||||
}
|
||||
return strAddr, nil
|
||||
}
|
||||
|
||||
func decode(a string) (Address, error) {
|
||||
if len(a) == 0 {
|
||||
return Undef, nil
|
||||
}
|
||||
if a == UndefAddressString {
|
||||
return Undef, nil
|
||||
}
|
||||
if len(a) > MaxAddressStringLength || len(a) < 3 {
|
||||
return Undef, ErrInvalidLength
|
||||
}
|
||||
|
||||
if string(a[0]) != MainnetPrefix && string(a[0]) != TestnetPrefix {
|
||||
return Undef, ErrUnknownNetwork
|
||||
}
|
||||
|
||||
var protocol Protocol
|
||||
switch a[1] {
|
||||
case '0':
|
||||
protocol = ID
|
||||
case '1':
|
||||
protocol = SECP256K1
|
||||
case '2':
|
||||
protocol = Actor
|
||||
case '3':
|
||||
protocol = BLS
|
||||
default:
|
||||
return Undef, ErrUnknownProtocol
|
||||
}
|
||||
|
||||
raw := a[2:]
|
||||
if protocol == ID {
|
||||
// 20 is length of math.MaxUint64 as a string
|
||||
if len(raw) > 20 {
|
||||
return Undef, ErrInvalidLength
|
||||
}
|
||||
id, err := strconv.ParseUint(raw, 10, 64)
|
||||
if err != nil {
|
||||
return Undef, ErrInvalidPayload
|
||||
}
|
||||
return newAddress(protocol, varint.ToUvarint(id))
|
||||
}
|
||||
|
||||
payloadcksm, err := AddressEncoding.WithPadding(-1).DecodeString(raw)
|
||||
if err != nil {
|
||||
return Undef, err
|
||||
}
|
||||
payload := payloadcksm[:len(payloadcksm)-ChecksumHashLength]
|
||||
cksm := payloadcksm[len(payloadcksm)-ChecksumHashLength:]
|
||||
|
||||
if protocol == SECP256K1 || protocol == Actor {
|
||||
if len(payload) != 20 {
|
||||
return Undef, ErrInvalidPayload
|
||||
}
|
||||
}
|
||||
|
||||
if !ValidateChecksum(append([]byte{protocol}, payload...), cksm) {
|
||||
return Undef, ErrInvalidChecksum
|
||||
}
|
||||
|
||||
return newAddress(protocol, payload)
|
||||
}
|
||||
|
||||
func hash(ingest []byte, cfg *blake2b.Config) []byte {
|
||||
hasher, err := blake2b.New(cfg)
|
||||
if err != nil {
|
||||
// If this happens sth is very wrong.
|
||||
panic(fmt.Sprintf("invalid address hash configuration: %v", err)) // ok
|
||||
}
|
||||
if _, err := hasher.Write(ingest); err != nil {
|
||||
// blake2bs Write implementation never returns an error in its current
|
||||
// setup. So if this happens sth went very wrong.
|
||||
panic(fmt.Sprintf("blake2b is unable to process hashes: %v", err)) // ok
|
||||
}
|
||||
return hasher.Sum(nil)
|
||||
}
|
||||
|
||||
func (a Address) MarshalCBOR(w io.Writer) error {
|
||||
if a == Undef {
|
||||
return fmt.Errorf("cannot marshal undefined address")
|
||||
}
|
||||
|
||||
abytes := a.Bytes()
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(abytes)))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Write(abytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Address) UnmarshalCBOR(br io.Reader) error {
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if maj != cbg.MajByteString {
|
||||
return fmt.Errorf("cbor type for address unmarshal was not byte string")
|
||||
}
|
||||
|
||||
if extra > 64 {
|
||||
return fmt.Errorf("too many bytes to unmarshal for an address")
|
||||
}
|
||||
|
||||
buf := make([]byte, int(extra))
|
||||
if _, err := io.ReadFull(br, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr, err := NewFromBytes(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addr == Undef {
|
||||
return fmt.Errorf("cbor input should not contain empty addresses")
|
||||
}
|
||||
|
||||
*a = addr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IDFromAddress(addr Address) (uint64, error) {
|
||||
if addr.Protocol() != ID {
|
||||
return 0, xerrors.Errorf("cannot get id from non id address")
|
||||
}
|
||||
|
||||
i, _, err := varint.FromUvarint(addr.Payload())
|
||||
return i, err
|
||||
}
|
||||
@@ -1,542 +0,0 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||
"github.com/multiformats/go-varint"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/lotus/lib/crypto"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
}
|
||||
|
||||
func TestRandomIDAddress(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
addr, err := NewIDAddress(uint64(rand.Int()))
|
||||
assert.NoError(err)
|
||||
assert.Equal(ID, addr.Protocol())
|
||||
|
||||
str, err := encode(Testnet, addr)
|
||||
assert.NoError(err)
|
||||
|
||||
maybe, err := decode(str)
|
||||
assert.NoError(err)
|
||||
assert.Equal(addr, maybe)
|
||||
|
||||
}
|
||||
|
||||
var allTestAddresses = []string{
|
||||
"t00",
|
||||
"t01",
|
||||
"t010",
|
||||
"t0150",
|
||||
"t0499",
|
||||
"t01024",
|
||||
"t01729",
|
||||
"t0999999",
|
||||
"t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq",
|
||||
"t12fiakbhe2gwd5cnmrenekasyn6v5tnaxaqizq6a",
|
||||
"t1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva",
|
||||
"t1xtwapqc6nh4si2hcwpr3656iotzmlwumogqbuaa",
|
||||
"t1xcbgdhkgkwht3hrrnui3jdopeejsoatkzmoltqy",
|
||||
"t17uoq6tp427uzv7fztkbsnn64iwotfrristwpryy",
|
||||
"t24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as6i",
|
||||
"t25nml2cfbljvn4goqtclhifepvfnicv6g7mfmmvq",
|
||||
"t2nuqrg7vuysaue2pistjjnt3fadsdzvyuatqtfei",
|
||||
"t24dd4ox4c2vpf5vk5wkadgyyn6qtuvgcpxxon64a",
|
||||
"t2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y",
|
||||
"t3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a",
|
||||
"t3wmuu6crofhqmm3v4enos73okk2l366ck6yc4owxwbdtkmpk42ohkqxfitcpa57pjdcftql4tojda2poeruwa",
|
||||
"t3s2q2hzhkpiknjgmf4zq3ejab2rh62qbndueslmsdzervrhapxr7dftie4kpnpdiv2n6tvkr743ndhrsw6d3a",
|
||||
"t3q22fijmmlckhl56rn5nkyamkph3mcfu5ed6dheq53c244hfmnq2i7efdma3cj5voxenwiummf2ajlsbxc65a",
|
||||
"t3u5zgwa4ael3vuocgc5mfgygo4yuqocrntuuhcklf4xzg5tcaqwbyfabxetwtj4tsam3pbhnwghyhijr5mixa",
|
||||
}
|
||||
|
||||
func TestVectorsIDAddress(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input uint64
|
||||
expected string
|
||||
}{
|
||||
{uint64(0), "t00"},
|
||||
{uint64(1), "t01"},
|
||||
{uint64(10), "t010"},
|
||||
{uint64(150), "t0150"},
|
||||
{uint64(499), "t0499"},
|
||||
{uint64(1024), "t01024"},
|
||||
{uint64(1729), "t01729"},
|
||||
{uint64(999999), "t0999999"},
|
||||
{math.MaxUint64, fmt.Sprintf("t0%s", strconv.FormatUint(math.MaxUint64, 10))},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(fmt.Sprintf("testing actorID address: %s", tc.expected), func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Round trip encoding and decoding from string
|
||||
addr, err := NewIDAddress(tc.input)
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.expected, addr.String())
|
||||
|
||||
maybeAddr, err := NewFromString(tc.expected)
|
||||
assert.NoError(err)
|
||||
assert.Equal(ID, maybeAddr.Protocol())
|
||||
id, _, err := varint.FromUvarint(maybeAddr.Payload())
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.input, id)
|
||||
|
||||
// Round trip to and from bytes
|
||||
maybeAddrBytes, err := NewFromBytes(maybeAddr.Bytes())
|
||||
assert.NoError(err)
|
||||
assert.Equal(maybeAddr, maybeAddrBytes)
|
||||
|
||||
// Round trip encoding and decoding json
|
||||
b, err := addr.MarshalJSON()
|
||||
assert.NoError(err)
|
||||
|
||||
var newAddr Address
|
||||
err = newAddr.UnmarshalJSON(b)
|
||||
assert.NoError(err)
|
||||
assert.Equal(addr, newAddr)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSecp256k1Address(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
sk, err := crypto.GenerateKey()
|
||||
assert.NoError(err)
|
||||
|
||||
addr, err := NewSecp256k1Address(crypto.PublicKey(sk))
|
||||
assert.NoError(err)
|
||||
assert.Equal(SECP256K1, addr.Protocol())
|
||||
|
||||
str, err := encode(Mainnet, addr)
|
||||
assert.NoError(err)
|
||||
|
||||
maybe, err := decode(str)
|
||||
assert.NoError(err)
|
||||
assert.Equal(addr, maybe)
|
||||
|
||||
}
|
||||
|
||||
func TestVectorSecp256k1Address(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input []byte
|
||||
expected string
|
||||
}{
|
||||
{[]byte{4, 148, 2, 250, 195, 126, 100, 50, 164, 22, 163, 160, 202, 84,
|
||||
38, 181, 24, 90, 179, 178, 79, 97, 52, 239, 162, 92, 228, 135, 200,
|
||||
45, 46, 78, 19, 191, 69, 37, 17, 224, 210, 36, 84, 33, 248, 97, 59,
|
||||
193, 13, 114, 250, 33, 102, 102, 169, 108, 59, 193, 57, 32, 211,
|
||||
255, 35, 63, 208, 188, 5},
|
||||
"t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq"},
|
||||
|
||||
{[]byte{4, 118, 135, 185, 16, 55, 155, 242, 140, 190, 58, 234, 103, 75,
|
||||
18, 0, 12, 107, 125, 186, 70, 255, 192, 95, 108, 148, 254, 42, 34,
|
||||
187, 204, 38, 2, 255, 127, 92, 118, 242, 28, 165, 93, 54, 149, 145,
|
||||
82, 176, 225, 232, 135, 145, 124, 57, 53, 118, 238, 240, 147, 246,
|
||||
30, 189, 58, 208, 111, 127, 218},
|
||||
"t12fiakbhe2gwd5cnmrenekasyn6v5tnaxaqizq6a"},
|
||||
{[]byte{4, 222, 253, 208, 16, 1, 239, 184, 110, 1, 222, 213, 206, 52,
|
||||
248, 71, 167, 58, 20, 129, 158, 230, 65, 188, 182, 11, 185, 41, 147,
|
||||
89, 111, 5, 220, 45, 96, 95, 41, 133, 248, 209, 37, 129, 45, 172,
|
||||
65, 99, 163, 150, 52, 155, 35, 193, 28, 194, 255, 53, 157, 229, 75,
|
||||
226, 135, 234, 98, 49, 155},
|
||||
"t1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva"},
|
||||
{[]byte{4, 3, 237, 18, 200, 20, 182, 177, 13, 46, 224, 157, 149, 180,
|
||||
104, 141, 178, 209, 128, 208, 169, 163, 122, 107, 106, 125, 182, 61,
|
||||
41, 129, 30, 233, 115, 4, 121, 216, 239, 145, 57, 233, 18, 73, 202,
|
||||
189, 57, 50, 145, 207, 229, 210, 119, 186, 118, 222, 69, 227, 224,
|
||||
133, 163, 118, 129, 191, 54, 69, 210},
|
||||
"t1xtwapqc6nh4si2hcwpr3656iotzmlwumogqbuaa"},
|
||||
{[]byte{4, 247, 150, 129, 154, 142, 39, 22, 49, 175, 124, 24, 151, 151,
|
||||
181, 69, 214, 2, 37, 147, 97, 71, 230, 1, 14, 101, 98, 179, 206, 158,
|
||||
254, 139, 16, 20, 65, 97, 169, 30, 208, 180, 236, 137, 8, 0, 37, 63,
|
||||
166, 252, 32, 172, 144, 251, 241, 251, 242, 113, 48, 164, 236, 195,
|
||||
228, 3, 183, 5, 118},
|
||||
"t1xcbgdhkgkwht3hrrnui3jdopeejsoatkzmoltqy"},
|
||||
{[]byte{4, 66, 131, 43, 248, 124, 206, 158, 163, 69, 185, 3, 80, 222,
|
||||
125, 52, 149, 133, 156, 164, 73, 5, 156, 94, 136, 221, 231, 66, 133,
|
||||
223, 251, 158, 192, 30, 186, 188, 95, 200, 98, 104, 207, 234, 235,
|
||||
167, 174, 5, 191, 184, 214, 142, 183, 90, 82, 104, 120, 44, 248, 111,
|
||||
200, 112, 43, 239, 138, 31, 224},
|
||||
"t17uoq6tp427uzv7fztkbsnn64iwotfrristwpryy"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(fmt.Sprintf("testing secp256k1 address: %s", tc.expected), func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Round trip encoding and decoding from string
|
||||
addr, err := NewSecp256k1Address(tc.input)
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.expected, addr.String())
|
||||
|
||||
maybeAddr, err := NewFromString(tc.expected)
|
||||
assert.NoError(err)
|
||||
assert.Equal(SECP256K1, maybeAddr.Protocol())
|
||||
assert.Equal(addressHash(tc.input), maybeAddr.Payload())
|
||||
|
||||
// Round trip to and from bytes
|
||||
maybeAddrBytes, err := NewFromBytes(maybeAddr.Bytes())
|
||||
assert.NoError(err)
|
||||
assert.Equal(maybeAddr, maybeAddrBytes)
|
||||
|
||||
// Round trip encoding and decoding json
|
||||
b, err := addr.MarshalJSON()
|
||||
assert.NoError(err)
|
||||
|
||||
var newAddr Address
|
||||
err = newAddr.UnmarshalJSON(b)
|
||||
assert.NoError(err)
|
||||
assert.Equal(addr, newAddr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomActorAddress(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
actorMsg := make([]byte, 20)
|
||||
rand.Read(actorMsg)
|
||||
|
||||
addr, err := NewActorAddress(actorMsg)
|
||||
assert.NoError(err)
|
||||
assert.Equal(Actor, addr.Protocol())
|
||||
|
||||
str, err := encode(Mainnet, addr)
|
||||
assert.NoError(err)
|
||||
|
||||
maybe, err := decode(str)
|
||||
assert.NoError(err)
|
||||
assert.Equal(addr, maybe)
|
||||
|
||||
}
|
||||
|
||||
func TestVectorActorAddress(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input []byte
|
||||
expected string
|
||||
}{
|
||||
{[]byte{118, 18, 129, 144, 205, 240, 104, 209, 65, 128, 68, 172, 192,
|
||||
62, 11, 103, 129, 151, 13, 96},
|
||||
"t24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as6i"},
|
||||
{[]byte{44, 175, 184, 226, 224, 107, 186, 152, 234, 101, 124, 92, 245,
|
||||
244, 32, 35, 170, 35, 232, 142},
|
||||
"t25nml2cfbljvn4goqtclhifepvfnicv6g7mfmmvq"},
|
||||
{[]byte{2, 44, 158, 14, 162, 157, 143, 64, 197, 106, 190, 195, 92, 141,
|
||||
88, 125, 160, 166, 76, 24},
|
||||
"t2nuqrg7vuysaue2pistjjnt3fadsdzvyuatqtfei"},
|
||||
{[]byte{223, 236, 3, 14, 32, 79, 15, 89, 216, 15, 29, 94, 233, 29, 253,
|
||||
6, 109, 127, 99, 189},
|
||||
"t24dd4ox4c2vpf5vk5wkadgyyn6qtuvgcpxxon64a"},
|
||||
{[]byte{61, 58, 137, 232, 221, 171, 84, 120, 50, 113, 108, 109, 70, 140,
|
||||
53, 96, 201, 244, 127, 216},
|
||||
"t2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(fmt.Sprintf("testing Actor address: %s", tc.expected), func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Round trip encoding and decoding from string
|
||||
addr, err := NewActorAddress(tc.input)
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.expected, addr.String())
|
||||
|
||||
maybeAddr, err := NewFromString(tc.expected)
|
||||
assert.NoError(err)
|
||||
assert.Equal(Actor, maybeAddr.Protocol())
|
||||
assert.Equal(addressHash(tc.input), maybeAddr.Payload())
|
||||
|
||||
// Round trip to and from bytes
|
||||
maybeAddrBytes, err := NewFromBytes(maybeAddr.Bytes())
|
||||
assert.NoError(err)
|
||||
assert.Equal(maybeAddr, maybeAddrBytes)
|
||||
|
||||
// Round trip encoding and decoding json
|
||||
b, err := addr.MarshalJSON()
|
||||
assert.NoError(err)
|
||||
|
||||
var newAddr Address
|
||||
err = newAddr.UnmarshalJSON(b)
|
||||
assert.NoError(err)
|
||||
assert.Equal(addr, newAddr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomBLSAddress(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
pk := ffi.PrivateKeyPublicKey(ffi.PrivateKeyGenerate())
|
||||
|
||||
addr, err := NewBLSAddress(pk[:])
|
||||
assert.NoError(err)
|
||||
assert.Equal(BLS, addr.Protocol())
|
||||
|
||||
str, err := encode(Mainnet, addr)
|
||||
assert.NoError(err)
|
||||
|
||||
maybe, err := decode(str)
|
||||
assert.NoError(err)
|
||||
assert.Equal(addr, maybe)
|
||||
|
||||
}
|
||||
|
||||
func TestVectorBLSAddress(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input []byte
|
||||
expected string
|
||||
}{
|
||||
{[]byte{173, 88, 223, 105, 110, 45, 78, 145, 234, 134, 200, 129, 233, 56,
|
||||
186, 78, 168, 27, 57, 94, 18, 121, 123, 132, 185, 207, 49, 75, 149, 70,
|
||||
112, 94, 131, 156, 122, 153, 214, 6, 178, 71, 221, 180, 249, 172, 122,
|
||||
52, 20, 221},
|
||||
"t3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a"},
|
||||
{[]byte{179, 41, 79, 10, 46, 41, 224, 198, 110, 188, 35, 93, 47, 237,
|
||||
202, 86, 151, 191, 120, 74, 246, 5, 199, 90, 246, 8, 230, 166, 61, 92,
|
||||
211, 142, 168, 92, 168, 152, 158, 14, 253, 233, 24, 139, 56, 47,
|
||||
147, 114, 70, 13},
|
||||
"t3wmuu6crofhqmm3v4enos73okk2l366ck6yc4owxwbdtkmpk42ohkqxfitcpa57pjdcftql4tojda2poeruwa"},
|
||||
{[]byte{150, 161, 163, 228, 234, 122, 20, 212, 153, 133, 230, 97, 178,
|
||||
36, 1, 212, 79, 237, 64, 45, 29, 9, 37, 178, 67, 201, 35, 88, 156,
|
||||
15, 188, 126, 50, 205, 4, 226, 158, 215, 141, 21, 211, 125, 58, 170,
|
||||
63, 230, 218, 51},
|
||||
"t3s2q2hzhkpiknjgmf4zq3ejab2rh62qbndueslmsdzervrhapxr7dftie4kpnpdiv2n6tvkr743ndhrsw6d3a"},
|
||||
{[]byte{134, 180, 84, 37, 140, 88, 148, 117, 247, 209, 111, 90, 172, 1,
|
||||
138, 121, 246, 193, 22, 157, 32, 252, 51, 146, 29, 216, 181, 206, 28,
|
||||
172, 108, 52, 143, 144, 163, 96, 54, 36, 246, 174, 185, 27, 100, 81,
|
||||
140, 46, 128, 149},
|
||||
"t3q22fijmmlckhl56rn5nkyamkph3mcfu5ed6dheq53c244hfmnq2i7efdma3cj5voxenwiummf2ajlsbxc65a"},
|
||||
{[]byte{167, 114, 107, 3, 128, 34, 247, 90, 56, 70, 23, 88, 83, 96, 206,
|
||||
230, 41, 7, 10, 45, 157, 40, 113, 41, 101, 229, 242, 110, 204, 64,
|
||||
133, 131, 130, 128, 55, 36, 237, 52, 242, 114, 3, 54, 240, 157, 182,
|
||||
49, 240, 116},
|
||||
"t3u5zgwa4ael3vuocgc5mfgygo4yuqocrntuuhcklf4xzg5tcaqwbyfabxetwtj4tsam3pbhnwghyhijr5mixa"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(fmt.Sprintf("testing bls address: %s", tc.expected), func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Round trip encoding and decoding from string
|
||||
addr, err := NewBLSAddress(tc.input)
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.expected, addr.String())
|
||||
|
||||
maybeAddr, err := NewFromString(tc.expected)
|
||||
assert.NoError(err)
|
||||
assert.Equal(BLS, maybeAddr.Protocol())
|
||||
assert.Equal(tc.input, maybeAddr.Payload())
|
||||
|
||||
// Round trip to and from bytes
|
||||
maybeAddrBytes, err := NewFromBytes(maybeAddr.Bytes())
|
||||
assert.NoError(err)
|
||||
assert.Equal(maybeAddr, maybeAddrBytes)
|
||||
|
||||
// Round trip encoding and decoding json
|
||||
b, err := addr.MarshalJSON()
|
||||
assert.NoError(err)
|
||||
|
||||
var newAddr Address
|
||||
err = newAddr.UnmarshalJSON(b)
|
||||
assert.NoError(err)
|
||||
assert.Equal(addr, newAddr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidStringAddresses(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expetErr error
|
||||
}{
|
||||
{"Q2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y", ErrUnknownNetwork},
|
||||
{"t4gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y", ErrUnknownProtocol},
|
||||
{"t2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr24y", ErrInvalidChecksum},
|
||||
{"t0banananananannnnnnnnn", ErrInvalidLength},
|
||||
{"t0banananananannnnnnnn", ErrInvalidPayload},
|
||||
{"t2gfvuyh7v2sx3patm1k23wdzmhyhtmqctasbr24y", base32.CorruptInputError(16)}, // '1' is not in base32 alphabet
|
||||
{"t2gfvuyh7v2sx3paTm1k23wdzmhyhtmqctasbr24y", base32.CorruptInputError(14)}, // 'T' is not in base32 alphabet
|
||||
{"t2", ErrInvalidLength},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(fmt.Sprintf("testing string address: %s", tc.expetErr), func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
_, err := NewFromString(tc.input)
|
||||
assert.Equal(tc.expetErr, err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestInvalidByteAddresses(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input []byte
|
||||
expetErr error
|
||||
}{
|
||||
// Unknown Protocol
|
||||
{[]byte{4, 4, 4}, ErrUnknownProtocol},
|
||||
|
||||
// ID protocol
|
||||
{[]byte{0}, ErrInvalidLength},
|
||||
|
||||
// SECP256K1 Protocol
|
||||
{append([]byte{1}, make([]byte, PayloadHashLength-1)...), ErrInvalidPayload},
|
||||
{append([]byte{1}, make([]byte, PayloadHashLength+1)...), ErrInvalidPayload},
|
||||
// Actor Protocol
|
||||
{append([]byte{2}, make([]byte, PayloadHashLength-1)...), ErrInvalidPayload},
|
||||
{append([]byte{2}, make([]byte, PayloadHashLength+1)...), ErrInvalidPayload},
|
||||
|
||||
// BLS Protocol
|
||||
{append([]byte{3}, make([]byte, ffi.PublicKeyBytes-1)...), ErrInvalidPayload},
|
||||
{append([]byte{3}, make([]byte, ffi.PrivateKeyBytes+1)...), ErrInvalidPayload},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(fmt.Sprintf("testing byte address: %s", tc.expetErr), func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
_, err := NewFromBytes(tc.input)
|
||||
assert.Equal(tc.expetErr, err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestChecksum(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
data := []byte("helloworld")
|
||||
bata := []byte("kittinmittins")
|
||||
|
||||
cksm := Checksum(data)
|
||||
assert.Len(cksm, ChecksumHashLength)
|
||||
|
||||
assert.True(ValidateChecksum(data, cksm))
|
||||
assert.False(ValidateChecksum(bata, cksm))
|
||||
|
||||
}
|
||||
|
||||
func TestAddressFormat(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
a, err := NewActorAddress([]byte("hello"))
|
||||
require.NoError(err)
|
||||
|
||||
assert.Equal("t2wvjry4bx6bwj6kkhcmvgu5zafqyi5cjzbtet3va", a.String())
|
||||
assert.Equal("02B5531C7037F06C9F2947132A6A77202C308E8939", fmt.Sprintf("%X", a))
|
||||
assert.Equal("[2 - b5531c7037f06c9f2947132a6a77202c308e8939]", fmt.Sprintf("%v", a))
|
||||
|
||||
assert.Equal("", fmt.Sprintf("%X", Undef))
|
||||
assert.Equal(UndefAddressString, Undef.String())
|
||||
assert.Equal(UndefAddressString, fmt.Sprintf("%v", Undef))
|
||||
}
|
||||
|
||||
func TestCborMarshal(t *testing.T) {
|
||||
for _, a := range allTestAddresses {
|
||||
addr, err := NewFromString(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := addr.MarshalCBOR(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
/*
|
||||
// Note: this is commented out because we're currently serializing addresses as cbor "text strings", not "byte strings".
|
||||
// This is to get around the restriction that refmt only allows string keys in maps.
|
||||
// if you change it to serialize to byte strings and uncomment this, the tests pass fine
|
||||
oldbytes, err := cbor.DumpObject(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(oldbytes, buf.Bytes()) {
|
||||
t.Fatalf("serialization doesnt match old serialization: %s", a)
|
||||
}
|
||||
*/
|
||||
|
||||
var out Address
|
||||
if err := out.UnmarshalCBOR(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if out != addr {
|
||||
t.Fatalf("failed to round trip %s", a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCborMarshal(b *testing.B) {
|
||||
addr, err := NewFromString("t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
if err := addr.MarshalCBOR(buf); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCborUnmarshal(b *testing.B) {
|
||||
addr, err := NewFromString("t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := addr.MarshalCBOR(buf); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var a Address
|
||||
if err := a.UnmarshalCBOR(bytes.NewReader(buf.Bytes())); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDEdgeCase(t *testing.T) {
|
||||
a, err := NewFromBytes([]byte{0, 0x80})
|
||||
_ = a.String()
|
||||
assert.Error(t, err)
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func blsaddr(n int64) Address {
|
||||
buf := make([]byte, 48)
|
||||
r := rand.New(rand.NewSource(n))
|
||||
r.Read(buf)
|
||||
|
||||
addr, err := NewBLSAddress(buf)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func makeActorAddresses(n int) [][]byte {
|
||||
var addrs [][]byte
|
||||
for i := 0; i < n; i++ {
|
||||
a, err := NewActorAddress([]byte(fmt.Sprintf("ACTOR ADDRESS %d", i)))
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
addrs = append(addrs, a.Bytes())
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
func makeBlsAddresses(n int64) [][]byte {
|
||||
var addrs [][]byte
|
||||
for i := int64(0); i < n; i++ {
|
||||
addrs = append(addrs, blsaddr(n).Bytes())
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func makeSecpAddresses(n int) [][]byte {
|
||||
var addrs [][]byte
|
||||
for i := 0; i < n; i++ {
|
||||
r := rand.New(rand.NewSource(int64(i)))
|
||||
buf := make([]byte, 32)
|
||||
r.Read(buf)
|
||||
|
||||
a, err := NewSecp256k1Address(buf)
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
|
||||
addrs = append(addrs, a.Bytes())
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func makeIDAddresses(n int) [][]byte {
|
||||
var addrs [][]byte
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
a, err := NewIDAddress(uint64(i))
|
||||
if err != nil {
|
||||
panic(err) // ok
|
||||
}
|
||||
|
||||
addrs = append(addrs, a.Bytes())
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func BenchmarkParseActorAddress(b *testing.B) {
|
||||
benchTestWithAddrs := func(a [][]byte) func(b *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := NewFromBytes(a[i%len(a)])
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b.Run("actor", benchTestWithAddrs(makeActorAddresses(20)))
|
||||
b.Run("bls", benchTestWithAddrs(makeBlsAddresses(20)))
|
||||
b.Run("secp256k1", benchTestWithAddrs(makeSecpAddresses(20)))
|
||||
b.Run("id", benchTestWithAddrs(makeIDAddresses(20)))
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
|
||||
"github.com/minio/blake2b-simd"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
var err error
|
||||
|
||||
TestAddress, err = NewActorAddress([]byte("satoshi"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
TestAddress2, err = NewActorAddress([]byte("nakamoto"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// TestAddress is an account with some initial funds in it.
|
||||
TestAddress Address
|
||||
// TestAddress2 is an account with some initial funds in it.
|
||||
TestAddress2 Address
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUnknownNetwork is returned when encountering an unknown network in an address.
|
||||
ErrUnknownNetwork = errors.New("unknown address network")
|
||||
|
||||
// ErrUnknownProtocol is returned when encountering an unknown protocol in an address.
|
||||
ErrUnknownProtocol = errors.New("unknown address protocol")
|
||||
// ErrInvalidPayload is returned when encountering an invalid address payload.
|
||||
ErrInvalidPayload = errors.New("invalid address payload")
|
||||
// ErrInvalidLength is returned when encountering an address of invalid length.
|
||||
ErrInvalidLength = errors.New("invalid address length")
|
||||
// ErrInvalidChecksum is returned when encountering an invalid address checksum.
|
||||
ErrInvalidChecksum = errors.New("invalid address checksum")
|
||||
)
|
||||
|
||||
// UndefAddressString is the string used to represent an empty address when encoded to a string.
|
||||
var UndefAddressString = "<empty>"
|
||||
|
||||
// PayloadHashLength defines the hash length taken over addresses using the Actor and SECP256K1 protocols.
|
||||
const PayloadHashLength = 20
|
||||
|
||||
// ChecksumHashLength defines the hash length used for calculating address checksums.
|
||||
const ChecksumHashLength = 4
|
||||
|
||||
// MaxAddressStringLength is the max length of an address encoded as a string
|
||||
// it include the network prefx, protocol, and bls publickey
|
||||
const MaxAddressStringLength = 2 + 84
|
||||
|
||||
var payloadHashConfig = &blake2b.Config{Size: PayloadHashLength}
|
||||
var checksumHashConfig = &blake2b.Config{Size: ChecksumHashLength}
|
||||
|
||||
const encodeStd = "abcdefghijklmnopqrstuvwxyz234567"
|
||||
|
||||
// AddressEncoding defines the base32 config used for address encoding and decoding.
|
||||
var AddressEncoding = base32.NewEncoding(encodeStd)
|
||||
@@ -1,20 +0,0 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NewForTestGetter returns a closure that returns an address unique to that invocation.
|
||||
// The address is unique wrt the closure returned, not globally.
|
||||
func NewForTestGetter() func() Address {
|
||||
i := 0
|
||||
return func() Address {
|
||||
s := fmt.Sprintf("address%d", i)
|
||||
i++
|
||||
newAddr, err := NewActorAddress([]byte(s))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return newAddr
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -21,10 +21,15 @@ func NewBadBlockCache() *BadBlockCache {
|
||||
}
|
||||
}
|
||||
|
||||
func (bts *BadBlockCache) Add(c cid.Cid) {
|
||||
bts.badBlocks.Add(c, nil)
|
||||
func (bts *BadBlockCache) Add(c cid.Cid, reason string) {
|
||||
bts.badBlocks.Add(c, reason)
|
||||
}
|
||||
|
||||
func (bts *BadBlockCache) Has(c cid.Cid) bool {
|
||||
return bts.badBlocks.Contains(c)
|
||||
func (bts *BadBlockCache) Has(c cid.Cid) (string, bool) {
|
||||
rval, ok := bts.badBlocks.Get(c)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return rval.(string), true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
var log = logging.Logger("beacon")
|
||||
|
||||
type Response struct {
|
||||
Entry types.BeaconEntry
|
||||
Err error
|
||||
}
|
||||
|
||||
type RandomBeacon interface {
|
||||
Entry(context.Context, uint64) <-chan Response
|
||||
VerifyEntry(types.BeaconEntry, types.BeaconEntry) error
|
||||
MaxBeaconRoundForEpoch(abi.ChainEpoch, types.BeaconEntry) uint64
|
||||
}
|
||||
|
||||
func ValidateBlockValues(b RandomBeacon, h *types.BlockHeader, prevEntry types.BeaconEntry) error {
|
||||
maxRound := b.MaxBeaconRoundForEpoch(h.Height, prevEntry)
|
||||
if maxRound == prevEntry.Round {
|
||||
if len(h.BeaconEntries) != 0 {
|
||||
return xerrors.Errorf("expected not to have any beacon entries in this block, got %d", len(h.BeaconEntries))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
last := h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
if last.Round != maxRound {
|
||||
return xerrors.Errorf("expected final beacon entry in block to be at round %d, got %d", maxRound, last.Round)
|
||||
}
|
||||
|
||||
for i, e := range h.BeaconEntries {
|
||||
if err := b.VerifyEntry(e, prevEntry); err != nil {
|
||||
return xerrors.Errorf("beacon entry %d (%d - %x (%d)) was invalid: %w", i, e.Round, e.Data, len(e.Data), err)
|
||||
}
|
||||
prevEntry = e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func BeaconEntriesForBlock(ctx context.Context, beacon RandomBeacon, round abi.ChainEpoch, prev types.BeaconEntry) ([]types.BeaconEntry, error) {
|
||||
start := time.Now()
|
||||
|
||||
maxRound := beacon.MaxBeaconRoundForEpoch(round, prev)
|
||||
if maxRound == prev.Round {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: this is a sketchy way to handle the genesis block not having a beacon entry
|
||||
if prev.Round == 0 {
|
||||
prev.Round = maxRound - 1
|
||||
}
|
||||
|
||||
cur := maxRound
|
||||
var out []types.BeaconEntry
|
||||
for cur > prev.Round {
|
||||
rch := beacon.Entry(ctx, cur)
|
||||
select {
|
||||
case resp := <-rch:
|
||||
if resp.Err != nil {
|
||||
return nil, xerrors.Errorf("beacon entry request returned error: %w", resp.Err)
|
||||
}
|
||||
|
||||
out = append(out, resp.Entry)
|
||||
cur = resp.Entry.Round - 1
|
||||
case <-ctx.Done():
|
||||
return nil, xerrors.Errorf("context timed out waiting on beacon entry to come back for round %d: %w", round, ctx.Err())
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugw("fetching beacon entries", "took", time.Since(start), "numEntries", len(out))
|
||||
reverse(out)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func reverse(arr []types.BeaconEntry) {
|
||||
for i := 0; i < len(arr)/2; i++ {
|
||||
arr[i], arr[len(arr)-(1+i)] = arr[len(arr)-(1+i)], arr[i]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package drand
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
dchain "github.com/drand/drand/chain"
|
||||
dclient "github.com/drand/drand/client"
|
||||
gclient "github.com/drand/drand/cmd/relay-gossip/client"
|
||||
dlog "github.com/drand/drand/log"
|
||||
"github.com/drand/kyber"
|
||||
kzap "github.com/go-kit/kit/log/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
)
|
||||
|
||||
var log = logging.Logger("drand")
|
||||
|
||||
var drandServers = []string{
|
||||
"https://pl-eu.testnet.drand.sh",
|
||||
"https://pl-us.testnet.drand.sh",
|
||||
"https://pl-sin.testnet.drand.sh",
|
||||
}
|
||||
|
||||
var drandChain *dchain.Info
|
||||
|
||||
func init() {
|
||||
|
||||
var err error
|
||||
drandChain, err = dchain.InfoFromJSON(bytes.NewReader([]byte(build.DrandChain)))
|
||||
if err != nil {
|
||||
panic("could not unmarshal chain info: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type drandPeer struct {
|
||||
addr string
|
||||
tls bool
|
||||
}
|
||||
|
||||
func (dp *drandPeer) Address() string {
|
||||
return dp.addr
|
||||
}
|
||||
|
||||
func (dp *drandPeer) IsTLS() bool {
|
||||
return dp.tls
|
||||
}
|
||||
|
||||
type DrandBeacon struct {
|
||||
client dclient.Client
|
||||
|
||||
pubkey kyber.Point
|
||||
|
||||
// seconds
|
||||
interval time.Duration
|
||||
|
||||
drandGenTime uint64
|
||||
filGenTime uint64
|
||||
filRoundTime uint64
|
||||
|
||||
cacheLk sync.Mutex
|
||||
localCache map[uint64]types.BeaconEntry
|
||||
}
|
||||
|
||||
func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub) (*DrandBeacon, error) {
|
||||
if genesisTs == 0 {
|
||||
panic("what are you doing this cant be zero")
|
||||
}
|
||||
|
||||
dlogger := dlog.NewKitLoggerFrom(kzap.NewZapSugarLogger(
|
||||
log.SugaredLogger.Desugar(), zapcore.InfoLevel))
|
||||
opts := []dclient.Option{
|
||||
dclient.WithChainInfo(drandChain),
|
||||
dclient.WithHTTPEndpoints(drandServers),
|
||||
dclient.WithCacheSize(1024),
|
||||
dclient.WithLogger(dlogger),
|
||||
}
|
||||
|
||||
if ps != nil {
|
||||
opts = append(opts, gclient.WithPubsub(ps))
|
||||
} else {
|
||||
log.Info("drand beacon without pubsub")
|
||||
}
|
||||
|
||||
client, err := dclient.New(opts...)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("creating drand client")
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Explicitly Watch until that is fixed in drand
|
||||
ch := client.Watch(context.Background())
|
||||
for range ch {
|
||||
}
|
||||
log.Error("dranch Watch bork")
|
||||
}()
|
||||
|
||||
db := &DrandBeacon{
|
||||
client: client,
|
||||
localCache: make(map[uint64]types.BeaconEntry),
|
||||
}
|
||||
|
||||
db.pubkey = drandChain.PublicKey
|
||||
db.interval = drandChain.Period
|
||||
db.drandGenTime = uint64(drandChain.GenesisTime)
|
||||
db.filRoundTime = interval
|
||||
db.filGenTime = genesisTs
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) Entry(ctx context.Context, round uint64) <-chan beacon.Response {
|
||||
out := make(chan beacon.Response, 1)
|
||||
if round != 0 {
|
||||
be := db.getCachedValue(round)
|
||||
if be != nil {
|
||||
out <- beacon.Response{Entry: *be}
|
||||
close(out)
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
start := time.Now()
|
||||
log.Infow("start fetching randomness", "round", round)
|
||||
resp, err := db.client.Get(ctx, round)
|
||||
|
||||
var br beacon.Response
|
||||
if err != nil {
|
||||
br.Err = xerrors.Errorf("drand failed Get request: %w", err)
|
||||
} else {
|
||||
br.Entry.Round = resp.Round()
|
||||
br.Entry.Data = resp.Signature()
|
||||
}
|
||||
log.Infow("done fetching randomness", "round", round, "took", time.Since(start))
|
||||
out <- br
|
||||
close(out)
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
func (db *DrandBeacon) cacheValue(e types.BeaconEntry) {
|
||||
db.cacheLk.Lock()
|
||||
defer db.cacheLk.Unlock()
|
||||
db.localCache[e.Round] = e
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) getCachedValue(round uint64) *types.BeaconEntry {
|
||||
db.cacheLk.Lock()
|
||||
defer db.cacheLk.Unlock()
|
||||
v, ok := db.localCache[round]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &v
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntry) error {
|
||||
if prev.Round == 0 {
|
||||
// TODO handle genesis better
|
||||
return nil
|
||||
}
|
||||
b := &dchain.Beacon{
|
||||
PreviousSig: prev.Data,
|
||||
Round: curr.Round,
|
||||
Signature: curr.Data,
|
||||
}
|
||||
err := dchain.VerifyBeacon(db.pubkey, b)
|
||||
if err == nil {
|
||||
db.cacheValue(curr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *DrandBeacon) MaxBeaconRoundForEpoch(filEpoch abi.ChainEpoch, prevEntry types.BeaconEntry) uint64 {
|
||||
// TODO: sometimes the genesis time for filecoin is zero and this goes negative
|
||||
latestTs := ((uint64(filEpoch) * db.filRoundTime) + db.filGenTime) - db.filRoundTime
|
||||
dround := (latestTs - db.drandGenTime) / uint64(db.interval.Seconds())
|
||||
return dround
|
||||
}
|
||||
|
||||
var _ beacon.RandomBeacon = (*DrandBeacon)(nil)
|
||||
@@ -0,0 +1,22 @@
|
||||
package drand
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
dchain "github.com/drand/drand/chain"
|
||||
dclient "github.com/drand/drand/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPrintGroupInfo(t *testing.T) {
|
||||
c, err := dclient.NewHTTPClient(drandServers[0], nil, nil)
|
||||
assert.NoError(t, err)
|
||||
cg := c.(interface {
|
||||
FetchChainInfo(groupHash []byte) (*dchain.Info, error)
|
||||
})
|
||||
chain, err := cg.FetchChainInfo(nil)
|
||||
assert.NoError(t, err)
|
||||
err = chain.ToJSON(os.Stdout)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/minio/blake2b-simd"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Mock beacon assumes that filecoin rounds are 1:1 mapped with the beacon rounds
|
||||
type mockBeacon struct {
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func NewMockBeacon(interval time.Duration) RandomBeacon {
|
||||
mb := &mockBeacon{interval: interval}
|
||||
|
||||
return mb
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) RoundTime() time.Duration {
|
||||
return mb.interval
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) entryForIndex(index uint64) types.BeaconEntry {
|
||||
buf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buf, index)
|
||||
rval := blake2b.Sum256(buf)
|
||||
return types.BeaconEntry{
|
||||
Round: index,
|
||||
Data: rval[:],
|
||||
}
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) Entry(ctx context.Context, index uint64) <-chan Response {
|
||||
e := mb.entryForIndex(index)
|
||||
out := make(chan Response, 1)
|
||||
out <- Response{Entry: e}
|
||||
return out
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) VerifyEntry(from types.BeaconEntry, to types.BeaconEntry) error {
|
||||
// TODO: cache this, especially for bls
|
||||
oe := mb.entryForIndex(from.Round)
|
||||
if !bytes.Equal(from.Data, oe.Data) {
|
||||
return xerrors.Errorf("mock beacon entry was invalid!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) IsEntryForEpoch(e types.BeaconEntry, epoch abi.ChainEpoch, nulls int) (bool, error) {
|
||||
return int64(e.Round) <= int64(epoch) && int64(epoch)-int64(nulls) >= int64(e.Round), nil
|
||||
}
|
||||
|
||||
func (mb *mockBeacon) MaxBeaconRoundForEpoch(epoch abi.ChainEpoch, prevEntry types.BeaconEntry) uint64 {
|
||||
return uint64(epoch)
|
||||
}
|
||||
|
||||
var _ RandomBeacon = (*mockBeacon)(nil)
|
||||
@@ -3,18 +3,18 @@ package blocksync
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/protocol"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
)
|
||||
@@ -25,11 +25,7 @@ type NewStreamFunc func(context.Context, peer.ID, ...protocol.ID) (inet.Stream,
|
||||
|
||||
const BlockSyncProtocolID = "/fil/sync/blk/0.0.1"
|
||||
|
||||
func init() {
|
||||
cbor.RegisterCborType(BlockSyncRequest{})
|
||||
cbor.RegisterCborType(BlockSyncResponse{})
|
||||
cbor.RegisterCborType(BSTipSet{})
|
||||
}
|
||||
const BlockSyncMaxRequestLength = 800
|
||||
|
||||
type BlockSyncService struct {
|
||||
cs *store.ChainStore
|
||||
@@ -55,8 +51,17 @@ func ParseBSOptions(optfield uint64) *BSOptions {
|
||||
}
|
||||
|
||||
const (
|
||||
BSOptBlocks = 1 << 0
|
||||
BSOptMessages = 1 << 1
|
||||
BSOptBlocks = 1 << iota
|
||||
BSOptMessages
|
||||
)
|
||||
|
||||
const (
|
||||
StatusOK = uint64(0)
|
||||
StatusPartial = uint64(101)
|
||||
StatusNotFound = uint64(201)
|
||||
StatusGoAway = uint64(202)
|
||||
StatusInternalError = uint64(203)
|
||||
StatusBadRequest = uint64(204)
|
||||
)
|
||||
|
||||
type BlockSyncResponse struct {
|
||||
@@ -86,35 +91,37 @@ func (bss *BlockSyncService) HandleStream(s inet.Stream) {
|
||||
ctx, span := trace.StartSpan(context.Background(), "blocksync.HandleStream")
|
||||
defer span.End()
|
||||
|
||||
defer s.Close()
|
||||
defer s.Close() //nolint:errcheck
|
||||
|
||||
var req BlockSyncRequest
|
||||
if err := cborutil.ReadCborRPC(bufio.NewReader(s), &req); err != nil {
|
||||
log.Warnf("failed to read block sync request: %s", err)
|
||||
return
|
||||
}
|
||||
log.Infof("block sync request for: %s %d", req.Start, req.RequestLength)
|
||||
log.Infow("block sync request", "start", req.Start, "len", req.RequestLength)
|
||||
|
||||
resp, err := bss.processRequest(ctx, &req)
|
||||
resp, err := bss.processRequest(ctx, s.Conn().RemotePeer(), &req)
|
||||
if err != nil {
|
||||
log.Warn("failed to process block sync request: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
writeDeadline := 60 * time.Second
|
||||
_ = s.SetDeadline(time.Now().Add(writeDeadline))
|
||||
if err := cborutil.WriteCborRPC(s, resp); err != nil {
|
||||
log.Warn("failed to write back response for handle stream: ", err)
|
||||
log.Warnw("failed to write back response for handle stream", "err", err, "peer", s.Conn().RemotePeer())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) processRequest(ctx context.Context, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
func (bss *BlockSyncService) processRequest(ctx context.Context, p peer.ID, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
_, span := trace.StartSpan(ctx, "blocksync.ProcessRequest")
|
||||
defer span.End()
|
||||
|
||||
opts := ParseBSOptions(req.Options)
|
||||
if len(req.Start) == 0 {
|
||||
return &BlockSyncResponse{
|
||||
Status: 204,
|
||||
Status: StatusBadRequest,
|
||||
Message: "no cids given in blocksync request",
|
||||
}, nil
|
||||
}
|
||||
@@ -122,35 +129,47 @@ func (bss *BlockSyncService) processRequest(ctx context.Context, req *BlockSyncR
|
||||
span.AddAttributes(
|
||||
trace.BoolAttribute("blocks", opts.IncludeBlocks),
|
||||
trace.BoolAttribute("messages", opts.IncludeMessages),
|
||||
trace.Int64Attribute("reqlen", int64(req.RequestLength)),
|
||||
)
|
||||
|
||||
chain, err := bss.collectChainSegment(types.NewTipSetKey(req.Start...), req.RequestLength, opts)
|
||||
reqlen := req.RequestLength
|
||||
if reqlen > BlockSyncMaxRequestLength {
|
||||
log.Warnw("limiting blocksync request length", "orig", req.RequestLength, "peer", p)
|
||||
reqlen = BlockSyncMaxRequestLength
|
||||
}
|
||||
|
||||
chain, err := collectChainSegment(bss.cs, types.NewTipSetKey(req.Start...), reqlen, opts)
|
||||
if err != nil {
|
||||
log.Warn("encountered error while responding to block sync request: ", err)
|
||||
return &BlockSyncResponse{
|
||||
Status: 203,
|
||||
Status: StatusInternalError,
|
||||
Message: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
status := StatusOK
|
||||
if reqlen < req.RequestLength {
|
||||
status = StatusPartial
|
||||
}
|
||||
|
||||
return &BlockSyncResponse{
|
||||
Chain: chain,
|
||||
Status: 0,
|
||||
Status: status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) collectChainSegment(start types.TipSetKey, length uint64, opts *BSOptions) ([]*BSTipSet, error) {
|
||||
func collectChainSegment(cs *store.ChainStore, start types.TipSetKey, length uint64, opts *BSOptions) ([]*BSTipSet, error) {
|
||||
var bstips []*BSTipSet
|
||||
cur := start
|
||||
for {
|
||||
var bst BSTipSet
|
||||
ts, err := bss.cs.LoadTipSet(cur)
|
||||
ts, err := cs.LoadTipSet(cur)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed loading tipset %s: %w", cur, err)
|
||||
}
|
||||
|
||||
if opts.IncludeMessages {
|
||||
bmsgs, bmincl, smsgs, smincl, err := bss.gatherMessages(ts)
|
||||
bmsgs, bmincl, smsgs, smincl, err := gatherMessages(cs, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("gather messages failed: %w", err)
|
||||
}
|
||||
@@ -175,7 +194,7 @@ func (bss *BlockSyncService) collectChainSegment(start types.TipSetKey, length u
|
||||
}
|
||||
}
|
||||
|
||||
func (bss *BlockSyncService) gatherMessages(ts *types.TipSet) ([]*types.Message, [][]uint64, []*types.SignedMessage, [][]uint64, error) {
|
||||
func gatherMessages(cs *store.ChainStore, ts *types.TipSet) ([]*types.Message, [][]uint64, []*types.SignedMessage, [][]uint64, error) {
|
||||
blsmsgmap := make(map[cid.Cid]uint64)
|
||||
secpkmsgmap := make(map[cid.Cid]uint64)
|
||||
var secpkmsgs []*types.SignedMessage
|
||||
@@ -183,7 +202,7 @@ func (bss *BlockSyncService) gatherMessages(ts *types.TipSet) ([]*types.Message,
|
||||
var secpkincl, blsincl [][]uint64
|
||||
|
||||
for _, b := range ts.Blocks() {
|
||||
bmsgs, smsgs, err := bss.cs.MessagesForBlock(b)
|
||||
bmsgs, smsgs, err := cs.MessagesForBlock(b)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
@@ -12,47 +12,52 @@ import (
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
bserv "github.com/ipfs/go-blockservice"
|
||||
"github.com/ipfs/go-cid"
|
||||
graphsync "github.com/ipfs/go-graphsync"
|
||||
gsnet "github.com/ipfs/go-graphsync/network"
|
||||
host "github.com/libp2p/go-libp2p-core/host"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
cborutil "github.com/filecoin-project/go-cbor-util"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
incrt "github.com/filecoin-project/lotus/lib/increadtimeout"
|
||||
"github.com/filecoin-project/lotus/lib/peermgr"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/peermgr"
|
||||
)
|
||||
|
||||
type BlockSync struct {
|
||||
bserv bserv.BlockService
|
||||
gsync graphsync.GraphExchange
|
||||
host host.Host
|
||||
|
||||
syncPeers *bsPeerTracker
|
||||
peerMgr *peermgr.PeerMgr
|
||||
}
|
||||
|
||||
func NewBlockSyncClient(bserv dtypes.ChainBlockService, h host.Host, pmgr peermgr.MaybePeerMgr) *BlockSync {
|
||||
func NewBlockSyncClient(bserv dtypes.ChainBlockService, h host.Host, pmgr peermgr.MaybePeerMgr, gs dtypes.Graphsync) *BlockSync {
|
||||
return &BlockSync{
|
||||
bserv: bserv,
|
||||
host: h,
|
||||
syncPeers: newPeerTracker(pmgr.Mgr),
|
||||
peerMgr: pmgr.Mgr,
|
||||
gsync: gs,
|
||||
}
|
||||
}
|
||||
|
||||
func (bs *BlockSync) processStatus(req *BlockSyncRequest, res *BlockSyncResponse) error {
|
||||
switch res.Status {
|
||||
case 101: // Partial Response
|
||||
case StatusPartial: // Partial Response
|
||||
return xerrors.Errorf("not handling partial blocksync responses yet")
|
||||
case 201: // req.Start not found
|
||||
case StatusNotFound: // req.Start not found
|
||||
return xerrors.Errorf("not found")
|
||||
case 202: // Go Away
|
||||
case StatusGoAway: // Go Away
|
||||
return xerrors.Errorf("not handling 'go away' blocksync responses yet")
|
||||
case 203: // Internal Error
|
||||
case StatusInternalError: // Internal Error
|
||||
return xerrors.Errorf("block sync peer errored: %s", res.Message)
|
||||
case 204:
|
||||
case StatusBadRequest:
|
||||
return xerrors.Errorf("block sync request invalid: %s", res.Message)
|
||||
default:
|
||||
return xerrors.Errorf("unrecognized response code: %d", res.Status)
|
||||
@@ -100,7 +105,7 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tsk types.TipSetKey, count i
|
||||
continue
|
||||
}
|
||||
|
||||
if res.Status == 0 {
|
||||
if res.Status == StatusOK || res.Status == StatusPartial {
|
||||
resp, err := bs.processBlocksResponse(req, res)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("success response from peer failed to process: %w", err)
|
||||
@@ -109,6 +114,7 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tsk types.TipSetKey, count i
|
||||
bs.host.ConnManager().TagPeer(p, "bsync", 25)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
oerr = bs.processStatus(req, res)
|
||||
if oerr != nil {
|
||||
log.Warnf("BlockSync peer %s response was an error: %s", p.String(), oerr)
|
||||
@@ -140,7 +146,7 @@ func (bs *BlockSync) GetFullTipSet(ctx context.Context, p peer.ID, tsk types.Tip
|
||||
|
||||
return bstsToFullTipSet(bts)
|
||||
case 101: // Partial Response
|
||||
return nil, xerrors.Errorf("partial responses are not handled")
|
||||
return nil, xerrors.Errorf("partial responses are not handled for single tipset fetching")
|
||||
case 201: // req.Start not found
|
||||
return nil, fmt.Errorf("not found")
|
||||
case 202: // Go Away
|
||||
@@ -180,7 +186,7 @@ func (bs *BlockSync) GetChainMessages(ctx context.Context, h *types.TipSet, coun
|
||||
req := &BlockSyncRequest{
|
||||
Start: h.Cids(),
|
||||
RequestLength: count,
|
||||
Options: BSOptMessages | BSOptBlocks,
|
||||
Options: BSOptMessages,
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -194,11 +200,16 @@ func (bs *BlockSync) GetChainMessages(ctx context.Context, h *types.TipSet, coun
|
||||
continue
|
||||
}
|
||||
|
||||
if res.Status == 0 {
|
||||
if res.Status == StatusOK {
|
||||
bs.syncPeers.logGlobalSuccess(time.Since(start))
|
||||
return res.Chain, nil
|
||||
}
|
||||
|
||||
if res.Status == StatusPartial {
|
||||
// TODO: track partial response sizes to ensure we don't overrequest too often
|
||||
return res.Chain, nil
|
||||
}
|
||||
|
||||
err = bs.processStatus(req, res)
|
||||
if err != nil {
|
||||
log.Warnf("BlockSync peer %s response was an error: %s", p.String(), err)
|
||||
@@ -228,29 +239,62 @@ func (bs *BlockSync) sendRequestToPeer(ctx context.Context, p peer.ID, req *Bloc
|
||||
}
|
||||
}()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("peer", p.Pretty()),
|
||||
)
|
||||
}
|
||||
|
||||
gsproto := string(gsnet.ProtocolGraphsync)
|
||||
supp, err := bs.host.Peerstore().SupportsProtocols(p, BlockSyncProtocolID, gsproto)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get protocols for peer: %w", err)
|
||||
}
|
||||
|
||||
if len(supp) == 0 {
|
||||
return nil, xerrors.Errorf("peer %s supports no known sync protocols", p)
|
||||
}
|
||||
|
||||
switch supp[0] {
|
||||
case BlockSyncProtocolID:
|
||||
res, err := bs.fetchBlocksBlockSync(ctx, p, req)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("blocksync req failed: %w", err)
|
||||
}
|
||||
return res, nil
|
||||
case gsproto:
|
||||
res, err := bs.fetchBlocksGraphSync(ctx, p, req)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("graphsync req failed: %w", err)
|
||||
}
|
||||
return res, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("peerstore somehow returned unexpected protocols: %v", supp)
|
||||
}
|
||||
|
||||
}
|
||||
func (bs *BlockSync) fetchBlocksBlockSync(ctx context.Context, p peer.ID, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "blockSyncFetch")
|
||||
defer span.End()
|
||||
|
||||
start := time.Now()
|
||||
s, err := bs.host.NewStream(inet.WithNoDial(ctx, "should already have connection"), p, BlockSyncProtocolID)
|
||||
if err != nil {
|
||||
bs.RemovePeer(p)
|
||||
return nil, xerrors.Errorf("failed to open stream to peer: %w", err)
|
||||
}
|
||||
s.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
defer s.SetDeadline(time.Time{})
|
||||
_ = s.SetWriteDeadline(time.Now().Add(5 * time.Second))
|
||||
|
||||
if err := cborutil.WriteCborRPC(s, req); err != nil {
|
||||
_ = s.SetWriteDeadline(time.Time{})
|
||||
bs.syncPeers.logFailure(p, time.Since(start))
|
||||
return nil, err
|
||||
}
|
||||
_ = s.SetWriteDeadline(time.Time{})
|
||||
|
||||
var res BlockSyncResponse
|
||||
if err := cborutil.ReadCborRPC(bufio.NewReader(s), &res); err != nil {
|
||||
r := incrt.New(s, 50<<10, 5*time.Second)
|
||||
if err := cborutil.ReadCborRPC(bufio.NewReader(r), &res); err != nil {
|
||||
bs.syncPeers.logFailure(p, time.Since(start))
|
||||
return nil, err
|
||||
}
|
||||
@@ -264,7 +308,6 @@ func (bs *BlockSync) sendRequestToPeer(ctx context.Context, p peer.ID, req *Bloc
|
||||
}
|
||||
|
||||
bs.syncPeers.logSuccess(p, time.Since(start))
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
@@ -455,7 +498,7 @@ func (bpt *bsPeerTracker) prefSortedPeers() []peer.ID {
|
||||
getPeerInitLat := func(p peer.ID) float64 {
|
||||
var res float64
|
||||
if bpt.pmgr != nil {
|
||||
if lat, ok := bpt.pmgr.GetPeerLatency(out[i]); ok {
|
||||
if lat, ok := bpt.pmgr.GetPeerLatency(p); ok {
|
||||
res = float64(lat)
|
||||
}
|
||||
}
|
||||
@@ -476,7 +519,7 @@ func (bpt *bsPeerTracker) prefSortedPeers() []peer.ID {
|
||||
failRateJ := float64(pj.failures) / float64(pj.failures+pj.successes)
|
||||
costJ = float64(pj.averageTime) + failRateJ*float64(bpt.avgGlobalTime)
|
||||
} else {
|
||||
costI = getPeerInitLat(out[i])
|
||||
costJ = getPeerInitLat(out[j])
|
||||
}
|
||||
|
||||
return costI < costJ
|
||||
@@ -518,26 +561,30 @@ func (bpt *bsPeerTracker) logSuccess(p peer.ID, dur time.Duration) {
|
||||
bpt.lk.Lock()
|
||||
defer bpt.lk.Unlock()
|
||||
|
||||
if pi, ok := bpt.peers[p]; !ok {
|
||||
var pi *peerStats
|
||||
var ok bool
|
||||
if pi, ok = bpt.peers[p]; !ok {
|
||||
log.Warnw("log success called on peer not in tracker", "peerid", p.String())
|
||||
return
|
||||
} else {
|
||||
pi.successes++
|
||||
|
||||
logTime(pi, dur)
|
||||
}
|
||||
|
||||
pi.successes++
|
||||
logTime(pi, dur)
|
||||
}
|
||||
|
||||
func (bpt *bsPeerTracker) logFailure(p peer.ID, dur time.Duration) {
|
||||
bpt.lk.Lock()
|
||||
defer bpt.lk.Unlock()
|
||||
if pi, ok := bpt.peers[p]; !ok {
|
||||
|
||||
var pi *peerStats
|
||||
var ok bool
|
||||
if pi, ok = bpt.peers[p]; !ok {
|
||||
log.Warn("log failure called on peer not in tracker", "peerid", p.String())
|
||||
return
|
||||
} else {
|
||||
pi.failures++
|
||||
logTime(pi, dur)
|
||||
}
|
||||
|
||||
pi.failures++
|
||||
logTime(pi, dur)
|
||||
}
|
||||
|
||||
func (bpt *bsPeerTracker) removePeer(p peer.ID) {
|
||||
|
||||
+110
-61
@@ -1,3 +1,5 @@
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
package blocksync
|
||||
|
||||
import (
|
||||
@@ -5,54 +7,60 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
|
||||
var lengthBufBlockSyncRequest = []byte{131}
|
||||
|
||||
func (t *BlockSyncRequest) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{131}); err != nil {
|
||||
if _, err := w.Write(lengthBufBlockSyncRequest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Start ([]cid.Cid) (slice)
|
||||
if len(t.Start) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Start was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Start)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Start))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Start {
|
||||
if err := cbg.WriteCid(w, v); err != nil {
|
||||
if err := cbg.WriteCidBuf(scratch, w, v); err != nil {
|
||||
return xerrors.Errorf("failed writing cid field t.Start: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// t.RequestLength (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.RequestLength))); err != nil {
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.RequestLength)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Options (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Options))); err != nil {
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Options)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -66,7 +74,7 @@ func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.Start ([]cid.Cid) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -78,9 +86,11 @@ func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Start = make([]cid.Cid, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
@@ -92,42 +102,54 @@ func (t *BlockSyncRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.RequestLength (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.RequestLength = uint64(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.RequestLength = uint64(extra)
|
||||
// t.Options (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Options = uint64(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Options = uint64(extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
var lengthBufBlockSyncResponse = []byte{131}
|
||||
|
||||
func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{131}); err != nil {
|
||||
if _, err := w.Write(lengthBufBlockSyncResponse); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Chain ([]*blocksync.BSTipSet) (slice)
|
||||
if len(t.Chain) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Chain was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Chain)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Chain))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Chain {
|
||||
@@ -137,7 +159,8 @@ func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
|
||||
// t.Status (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Status))); err != nil {
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Status)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -146,10 +169,10 @@ func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Value in field t.Message was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Message)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.Message))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Message)); err != nil {
|
||||
if _, err := io.WriteString(w, t.Message); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -157,8 +180,9 @@ func (t *BlockSyncResponse) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -172,7 +196,7 @@ func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.Chain ([]*blocksync.BSTipSet) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -184,9 +208,11 @@ func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Chain = make([]*BSTipSet, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v BSTipSet
|
||||
@@ -199,18 +225,22 @@ func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.Status (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
{
|
||||
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Status = uint64(extra)
|
||||
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Status = uint64(extra)
|
||||
// t.Message (string) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -220,21 +250,25 @@ func (t *BlockSyncResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var lengthBufBSTipSet = []byte{133}
|
||||
|
||||
func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{133}); err != nil {
|
||||
if _, err := w.Write(lengthBufBSTipSet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.Blocks ([]*types.BlockHeader) (slice)
|
||||
if len(t.Blocks) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Blocks was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Blocks)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.Blocks))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Blocks {
|
||||
@@ -248,7 +282,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.BlsMessages was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.BlsMessages)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.BlsMessages))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.BlsMessages {
|
||||
@@ -262,7 +296,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.BlsMsgIncludes was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.BlsMsgIncludes)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.BlsMsgIncludes))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.BlsMsgIncludes {
|
||||
@@ -270,11 +304,11 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field v was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(v)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(v))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range v {
|
||||
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, v); err != nil {
|
||||
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, uint64(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -285,7 +319,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.SecpkMessages was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.SecpkMessages)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.SecpkMessages))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.SecpkMessages {
|
||||
@@ -299,7 +333,7 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field t.SecpkMsgIncludes was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.SecpkMsgIncludes)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.SecpkMsgIncludes))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.SecpkMsgIncludes {
|
||||
@@ -307,11 +341,11 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
return xerrors.Errorf("Slice value in field v was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(v)))); err != nil {
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(v))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range v {
|
||||
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, v); err != nil {
|
||||
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, uint64(v)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -321,8 +355,9 @@ func (t *BSTipSet) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
scratch := make([]byte, 8)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -336,7 +371,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.Blocks ([]*types.BlockHeader) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -348,9 +383,11 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Blocks = make([]*types.BlockHeader, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.BlockHeader
|
||||
@@ -363,7 +400,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.BlsMessages ([]*types.Message) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -375,9 +412,11 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.BlsMessages = make([]*types.Message, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.Message
|
||||
@@ -390,7 +429,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.BlsMsgIncludes ([][]uint64) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -402,16 +441,18 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.BlsMsgIncludes = make([][]uint64, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
{
|
||||
var maj byte
|
||||
var extra uint64
|
||||
var err error
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -423,12 +464,14 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.BlsMsgIncludes[i] = make([]uint64, extra)
|
||||
}
|
||||
|
||||
for j := 0; j < int(extra); j++ {
|
||||
|
||||
maj, val, err := cbg.CborReadHeader(br)
|
||||
maj, val, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read uint64 for t.BlsMsgIncludes[i] slice: %w", err)
|
||||
}
|
||||
@@ -437,7 +480,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
return xerrors.Errorf("value read for array t.BlsMsgIncludes[i] was not a uint, instead got %d", maj)
|
||||
}
|
||||
|
||||
t.BlsMsgIncludes[i][j] = val
|
||||
t.BlsMsgIncludes[i][j] = uint64(val)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -445,7 +488,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.SecpkMessages ([]*types.SignedMessage) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -457,9 +500,11 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.SecpkMessages = make([]*types.SignedMessage, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v types.SignedMessage
|
||||
@@ -472,7 +517,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
|
||||
// t.SecpkMsgIncludes ([][]uint64) (slice)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -484,16 +529,18 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.SecpkMsgIncludes = make([][]uint64, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
{
|
||||
var maj byte
|
||||
var extra uint64
|
||||
var err error
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -505,12 +552,14 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.SecpkMsgIncludes[i] = make([]uint64, extra)
|
||||
}
|
||||
|
||||
for j := 0; j < int(extra); j++ {
|
||||
|
||||
maj, val, err := cbg.CborReadHeader(br)
|
||||
maj, val, err := cbg.CborReadHeaderBuf(br, scratch)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read uint64 for t.SecpkMsgIncludes[i] slice: %w", err)
|
||||
}
|
||||
@@ -519,7 +568,7 @@ func (t *BSTipSet) UnmarshalCBOR(r io.Reader) error {
|
||||
return xerrors.Errorf("value read for array t.SecpkMsgIncludes[i] was not a uint, instead got %d", maj)
|
||||
}
|
||||
|
||||
t.SecpkMsgIncludes[i][j] = val
|
||||
t.SecpkMsgIncludes[i][j] = uint64(val)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package blocksync
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-graphsync"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
store "github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
|
||||
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||
basicnode "github.com/ipld/go-ipld-prime/node/basic"
|
||||
ipldselector "github.com/ipld/go-ipld-prime/traversal/selector"
|
||||
selectorbuilder "github.com/ipld/go-ipld-prime/traversal/selector/builder"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// AMT selector recursion. An AMT has arity of 8 so this gives allows
|
||||
// us to retrieve trees with 8^10 (1,073,741,824) elements.
|
||||
amtRecursionDepth = uint32(10)
|
||||
|
||||
// some constants for looking up tuple encoded struct fields
|
||||
// field index of Parents field in a block header
|
||||
blockIndexParentsField = 5
|
||||
|
||||
// field index of Messages field in a block header
|
||||
blockIndexMessagesField = 10
|
||||
|
||||
// field index of AMT node in AMT head
|
||||
amtHeadNodeFieldIndex = 2
|
||||
|
||||
// field index of links array AMT node
|
||||
amtNodeLinksFieldIndex = 1
|
||||
|
||||
// field index of values array AMT node
|
||||
amtNodeValuesFieldIndex = 2
|
||||
|
||||
// maximum depth per traversal
|
||||
maxRequestLength = 50
|
||||
)
|
||||
|
||||
var amtSelector selectorbuilder.SelectorSpec
|
||||
|
||||
func init() {
|
||||
// builer for selectors
|
||||
ssb := selectorbuilder.NewSelectorSpecBuilder(basicnode.Style.Any)
|
||||
// amt selector -- needed to selector through a messages AMT
|
||||
amtSelector = ssb.ExploreIndex(amtHeadNodeFieldIndex,
|
||||
ssb.ExploreRecursive(ipldselector.RecursionLimitDepth(int(amtRecursionDepth)),
|
||||
ssb.ExploreUnion(
|
||||
ssb.ExploreIndex(amtNodeLinksFieldIndex,
|
||||
ssb.ExploreAll(ssb.ExploreRecursiveEdge())),
|
||||
ssb.ExploreIndex(amtNodeValuesFieldIndex,
|
||||
ssb.ExploreAll(ssb.Matcher())))))
|
||||
}
|
||||
|
||||
func selectorForRequest(req *BlockSyncRequest) ipld.Node {
|
||||
// builer for selectors
|
||||
ssb := selectorbuilder.NewSelectorSpecBuilder(basicnode.Style.Any)
|
||||
|
||||
bso := ParseBSOptions(req.Options)
|
||||
if bso.IncludeMessages {
|
||||
return ssb.ExploreRecursive(ipldselector.RecursionLimitDepth(int(req.RequestLength)),
|
||||
ssb.ExploreIndex(blockIndexParentsField,
|
||||
ssb.ExploreUnion(
|
||||
ssb.ExploreAll(
|
||||
ssb.ExploreIndex(blockIndexMessagesField,
|
||||
ssb.ExploreRange(0, 2, amtSelector),
|
||||
)),
|
||||
ssb.ExploreIndex(0, ssb.ExploreRecursiveEdge()),
|
||||
))).Node()
|
||||
}
|
||||
return ssb.ExploreRecursive(ipldselector.RecursionLimitDepth(int(req.RequestLength)), ssb.ExploreIndex(blockIndexParentsField,
|
||||
ssb.ExploreUnion(
|
||||
ssb.ExploreAll(
|
||||
ssb.Matcher(),
|
||||
),
|
||||
ssb.ExploreIndex(0, ssb.ExploreRecursiveEdge()),
|
||||
))).Node()
|
||||
}
|
||||
|
||||
func firstTipsetSelector(req *BlockSyncRequest) ipld.Node {
|
||||
// builer for selectors
|
||||
ssb := selectorbuilder.NewSelectorSpecBuilder(basicnode.Style.Any)
|
||||
|
||||
bso := ParseBSOptions(req.Options)
|
||||
if bso.IncludeMessages {
|
||||
return ssb.ExploreIndex(blockIndexMessagesField,
|
||||
ssb.ExploreRange(0, 2, amtSelector),
|
||||
).Node()
|
||||
}
|
||||
return ssb.Matcher().Node()
|
||||
|
||||
}
|
||||
|
||||
func (bs *BlockSync) executeGsyncSelector(ctx context.Context, p peer.ID, root cid.Cid, sel ipld.Node) error {
|
||||
extension := graphsync.ExtensionData{
|
||||
Name: "chainsync",
|
||||
Data: nil,
|
||||
}
|
||||
_, errs := bs.gsync.Request(ctx, p, cidlink.Link{Cid: root}, sel, extension)
|
||||
|
||||
for err := range errs {
|
||||
return xerrors.Errorf("failed to complete graphsync request: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback for interacting with other non-lotus nodes
|
||||
func (bs *BlockSync) fetchBlocksGraphSync(ctx context.Context, p peer.ID, req *BlockSyncRequest) (*BlockSyncResponse, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
immediateTsSelector := firstTipsetSelector(req)
|
||||
|
||||
// Do this because we can only request one root at a time
|
||||
for _, r := range req.Start {
|
||||
if err := bs.executeGsyncSelector(ctx, p, r, immediateTsSelector); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if req.RequestLength > maxRequestLength {
|
||||
req.RequestLength = maxRequestLength
|
||||
}
|
||||
|
||||
sel := selectorForRequest(req)
|
||||
|
||||
// execute the selector forreal
|
||||
if err := bs.executeGsyncSelector(ctx, p, req.Start[0], sel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now pull the data we fetched out of the chainstore (where it should now be persisted)
|
||||
tempcs := store.NewChainStore(bs.bserv.Blockstore(), datastore.NewMapDatastore(), nil)
|
||||
|
||||
opts := ParseBSOptions(req.Options)
|
||||
tsk := types.NewTipSetKey(req.Start...)
|
||||
chain, err := collectChainSegment(tempcs, tsk, req.RequestLength, opts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load chain data from chainstore after successful graphsync response (start = %v): %w", req.Start, err)
|
||||
}
|
||||
|
||||
return &BlockSyncResponse{Chain: chain}, nil
|
||||
}
|
||||
@@ -1,883 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
|
||||
func (t *AskRequest) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{129}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Miner (address.Address) (struct)
|
||||
if err := t.Miner.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *AskRequest) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 1 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.Miner (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Miner.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *AskResponse) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{129}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Ask (types.SignedStorageAsk) (struct)
|
||||
if err := t.Ask.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *AskResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 1 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.Ask (types.SignedStorageAsk) (struct)
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
t.Ask = new(types.SignedStorageAsk)
|
||||
if err := t.Ask.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Proposal) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{130}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.DealProposal (actors.StorageDealProposal) (struct)
|
||||
if err := t.DealProposal.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Piece (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Piece); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Piece: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Proposal) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 2 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.DealProposal (actors.StorageDealProposal) (struct)
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
t.DealProposal = new(actors.StorageDealProposal)
|
||||
if err := t.DealProposal.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// t.Piece (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Piece: %w", err)
|
||||
}
|
||||
|
||||
t.Piece = c
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Response) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{132}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.State (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.State))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Message (string) (string)
|
||||
if len(t.Message) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field t.Message was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Message)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Message)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Proposal (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Proposal); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Proposal: %w", err)
|
||||
}
|
||||
|
||||
// t.StorageDealSubmission (types.SignedMessage) (struct)
|
||||
if err := t.StorageDealSubmission.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Response) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 4 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.State (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.State = uint64(extra)
|
||||
// t.Message (string) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Message = string(sval)
|
||||
}
|
||||
// t.Proposal (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Proposal: %w", err)
|
||||
}
|
||||
|
||||
t.Proposal = c
|
||||
|
||||
}
|
||||
// t.StorageDealSubmission (types.SignedMessage) (struct)
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
t.StorageDealSubmission = new(types.SignedMessage)
|
||||
if err := t.StorageDealSubmission.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SignedResponse) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{130}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Response (deals.Response) (struct)
|
||||
if err := t.Response.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Signature (types.Signature) (struct)
|
||||
if err := t.Signature.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SignedResponse) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 2 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.Response (deals.Response) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Response.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.Signature (types.Signature) (struct)
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
t.Signature = new(types.Signature)
|
||||
if err := t.Signature.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ClientDealProposal) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{136}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Data (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Data); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Data: %w", err)
|
||||
}
|
||||
|
||||
// t.PricePerEpoch (types.BigInt) (struct)
|
||||
if err := t.PricePerEpoch.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.ProposalExpiration (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.ProposalExpiration))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Duration (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Duration))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.ProviderAddress (address.Address) (struct)
|
||||
if err := t.ProviderAddress.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Client (address.Address) (struct)
|
||||
if err := t.Client.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.MinerWorker (address.Address) (struct)
|
||||
if err := t.MinerWorker.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.MinerID (peer.ID) (string)
|
||||
if len(t.MinerID) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field t.MinerID was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.MinerID)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.MinerID)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ClientDealProposal) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 8 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.Data (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Data: %w", err)
|
||||
}
|
||||
|
||||
t.Data = c
|
||||
|
||||
}
|
||||
// t.PricePerEpoch (types.BigInt) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.PricePerEpoch.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.ProposalExpiration (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.ProposalExpiration = uint64(extra)
|
||||
// t.Duration (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Duration = uint64(extra)
|
||||
// t.ProviderAddress (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.ProviderAddress.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.Client (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Client.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.MinerWorker (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.MinerWorker.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.MinerID (peer.ID) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.MinerID = peer.ID(sval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ClientDeal) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{135}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.ProposalCid (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.ProposalCid); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.ProposalCid: %w", err)
|
||||
}
|
||||
|
||||
// t.Proposal (actors.StorageDealProposal) (struct)
|
||||
if err := t.Proposal.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.State (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.State))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Miner (peer.ID) (string)
|
||||
if len(t.Miner) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field t.Miner was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Miner)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Miner)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.MinerWorker (address.Address) (struct)
|
||||
if err := t.MinerWorker.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.DealID (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.DealID))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.PublishMessage (types.SignedMessage) (struct)
|
||||
if err := t.PublishMessage.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ClientDeal) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 7 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.ProposalCid (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.ProposalCid: %w", err)
|
||||
}
|
||||
|
||||
t.ProposalCid = c
|
||||
|
||||
}
|
||||
// t.Proposal (actors.StorageDealProposal) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Proposal.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.State (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.State = uint64(extra)
|
||||
// t.Miner (peer.ID) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Miner = peer.ID(sval)
|
||||
}
|
||||
// t.MinerWorker (address.Address) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.MinerWorker.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.DealID (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.DealID = uint64(extra)
|
||||
// t.PublishMessage (types.SignedMessage) (struct)
|
||||
|
||||
{
|
||||
|
||||
pb, err := br.PeekByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pb == cbg.CborNull[0] {
|
||||
var nbuf [1]byte
|
||||
if _, err := br.Read(nbuf[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
t.PublishMessage = new(types.SignedMessage)
|
||||
if err := t.PublishMessage.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *MinerDeal) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{135}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Client (peer.ID) (string)
|
||||
if len(t.Client) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field t.Client was too long")
|
||||
}
|
||||
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.Client)))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(t.Client)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Proposal (actors.StorageDealProposal) (struct)
|
||||
if err := t.Proposal.MarshalCBOR(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.ProposalCid (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.ProposalCid); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.ProposalCid: %w", err)
|
||||
}
|
||||
|
||||
// t.State (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.State))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Ref (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Ref); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Ref: %w", err)
|
||||
}
|
||||
|
||||
// t.DealID (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.DealID))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.SectorID (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.SectorID))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *MinerDeal) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 7 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.Client (peer.ID) (string)
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadString(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Client = peer.ID(sval)
|
||||
}
|
||||
// t.Proposal (actors.StorageDealProposal) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.Proposal.UnmarshalCBOR(br); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
// t.ProposalCid (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.ProposalCid: %w", err)
|
||||
}
|
||||
|
||||
t.ProposalCid = c
|
||||
|
||||
}
|
||||
// t.State (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.State = uint64(extra)
|
||||
// t.Ref (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Ref: %w", err)
|
||||
}
|
||||
|
||||
t.Ref = c
|
||||
|
||||
}
|
||||
// t.DealID (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.DealID = uint64(extra)
|
||||
// t.SectorID (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.SectorID = uint64(extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *StorageDataTransferVoucher) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{130}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Proposal (cid.Cid) (struct)
|
||||
|
||||
if err := cbg.WriteCid(w, t.Proposal); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.Proposal: %w", err)
|
||||
}
|
||||
|
||||
// t.DealID (uint64) (uint64)
|
||||
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.DealID))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *StorageDataTransferVoucher) UnmarshalCBOR(r io.Reader) error {
|
||||
br := cbg.GetPeeker(r)
|
||||
|
||||
maj, extra, err := cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 2 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.Proposal (cid.Cid) (struct)
|
||||
|
||||
{
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.Proposal: %w", err)
|
||||
}
|
||||
|
||||
t.Proposal = c
|
||||
|
||||
}
|
||||
// t.DealID (uint64) (uint64)
|
||||
|
||||
maj, extra, err = cbg.CborReadHeader(br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.DealID = uint64(extra)
|
||||
return nil
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/events"
|
||||
"github.com/filecoin-project/lotus/chain/market"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/statestore"
|
||||
"github.com/filecoin-project/lotus/node/impl/full"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/retrieval/discovery"
|
||||
)
|
||||
|
||||
var log = logging.Logger("deals")
|
||||
|
||||
type ClientDeal struct {
|
||||
ProposalCid cid.Cid
|
||||
Proposal actors.StorageDealProposal
|
||||
State api.DealState
|
||||
Miner peer.ID
|
||||
MinerWorker address.Address
|
||||
DealID uint64
|
||||
|
||||
PublishMessage *types.SignedMessage
|
||||
|
||||
s inet.Stream
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
sm *stmgr.StateManager
|
||||
chain *store.ChainStore
|
||||
h host.Host
|
||||
w *wallet.Wallet
|
||||
// dataTransfer
|
||||
// TODO: once the data transfer module is complete, the
|
||||
// client will listen to events on the data transfer module
|
||||
// Because we are using only a fake DAGService
|
||||
// implementation, there's no validation or events on the client side
|
||||
dataTransfer dtypes.ClientDataTransfer
|
||||
dag dtypes.ClientDAG
|
||||
discovery *discovery.Local
|
||||
events *events.Events
|
||||
fm *market.FundMgr
|
||||
|
||||
deals *statestore.StateStore
|
||||
conns map[cid.Cid]inet.Stream
|
||||
|
||||
incoming chan *ClientDeal
|
||||
updated chan clientDealUpdate
|
||||
|
||||
stop chan struct{}
|
||||
stopped chan struct{}
|
||||
}
|
||||
|
||||
type clientDealUpdate struct {
|
||||
newState api.DealState
|
||||
id cid.Cid
|
||||
err error
|
||||
mut func(*ClientDeal)
|
||||
}
|
||||
|
||||
type clientApi struct {
|
||||
full.ChainAPI
|
||||
full.StateAPI
|
||||
}
|
||||
|
||||
func NewClient(sm *stmgr.StateManager, chain *store.ChainStore, h host.Host, w *wallet.Wallet, dag dtypes.ClientDAG, dataTransfer dtypes.ClientDataTransfer, discovery *discovery.Local, fm *market.FundMgr, deals dtypes.ClientDealStore, chainapi full.ChainAPI, stateapi full.StateAPI) *Client {
|
||||
c := &Client{
|
||||
sm: sm,
|
||||
chain: chain,
|
||||
h: h,
|
||||
w: w,
|
||||
dataTransfer: dataTransfer,
|
||||
dag: dag,
|
||||
discovery: discovery,
|
||||
fm: fm,
|
||||
events: events.NewEvents(context.TODO(), &clientApi{chainapi, stateapi}),
|
||||
|
||||
deals: deals,
|
||||
conns: map[cid.Cid]inet.Stream{},
|
||||
|
||||
incoming: make(chan *ClientDeal, 16),
|
||||
updated: make(chan clientDealUpdate, 16),
|
||||
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) Run(ctx context.Context) {
|
||||
go func() {
|
||||
defer close(c.stopped)
|
||||
|
||||
for {
|
||||
select {
|
||||
case deal := <-c.incoming:
|
||||
c.onIncoming(deal)
|
||||
case update := <-c.updated:
|
||||
c.onUpdated(ctx, update)
|
||||
case <-c.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Client) onIncoming(deal *ClientDeal) {
|
||||
log.Info("incoming deal")
|
||||
|
||||
if _, ok := c.conns[deal.ProposalCid]; ok {
|
||||
log.Errorf("tracking deal connection: already tracking connection for deal %s", deal.ProposalCid)
|
||||
return
|
||||
}
|
||||
c.conns[deal.ProposalCid] = deal.s
|
||||
|
||||
if err := c.deals.Begin(deal.ProposalCid, deal); err != nil {
|
||||
// We may have re-sent the proposal
|
||||
log.Errorf("deal tracking failed: %s", err)
|
||||
c.failDeal(deal.ProposalCid, err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
c.updated <- clientDealUpdate{
|
||||
newState: api.DealUnknown,
|
||||
id: deal.ProposalCid,
|
||||
err: nil,
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Client) onUpdated(ctx context.Context, update clientDealUpdate) {
|
||||
log.Infof("Client deal %s updated state to %s", update.id, api.DealStates[update.newState])
|
||||
var deal ClientDeal
|
||||
err := c.deals.Mutate(update.id, func(d *ClientDeal) error {
|
||||
d.State = update.newState
|
||||
if update.mut != nil {
|
||||
update.mut(d)
|
||||
}
|
||||
deal = *d
|
||||
return nil
|
||||
})
|
||||
if update.err != nil {
|
||||
log.Errorf("deal %s failed: %s", update.id, update.err)
|
||||
c.failDeal(update.id, update.err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
c.failDeal(update.id, err)
|
||||
return
|
||||
}
|
||||
|
||||
switch update.newState {
|
||||
case api.DealUnknown: // new
|
||||
c.handle(ctx, deal, c.new, api.DealAccepted)
|
||||
case api.DealAccepted:
|
||||
c.handle(ctx, deal, c.accepted, api.DealStaged)
|
||||
case api.DealStaged:
|
||||
c.handle(ctx, deal, c.staged, api.DealSealing)
|
||||
case api.DealSealing:
|
||||
c.handle(ctx, deal, c.sealing, api.DealNoUpdate)
|
||||
// TODO: DealComplete -> watch for faults, expiration, etc.
|
||||
}
|
||||
}
|
||||
|
||||
type ClientDealProposal struct {
|
||||
Data cid.Cid
|
||||
|
||||
PricePerEpoch types.BigInt
|
||||
ProposalExpiration uint64
|
||||
Duration uint64
|
||||
|
||||
ProviderAddress address.Address
|
||||
Client address.Address
|
||||
MinerWorker address.Address
|
||||
MinerID peer.ID
|
||||
}
|
||||
|
||||
func (c *Client) Start(ctx context.Context, p ClientDealProposal) (cid.Cid, error) {
|
||||
if err := c.fm.EnsureAvailable(ctx, p.Client, types.BigMul(p.PricePerEpoch, types.NewInt(p.Duration))); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("adding market funds failed: %w", err)
|
||||
}
|
||||
|
||||
commP, pieceSize, err := c.commP(ctx, p.Data)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("computing commP failed: %w", err)
|
||||
}
|
||||
|
||||
dealProposal := &actors.StorageDealProposal{
|
||||
PieceRef: commP,
|
||||
PieceSize: uint64(pieceSize),
|
||||
Client: p.Client,
|
||||
Provider: p.ProviderAddress,
|
||||
ProposalExpiration: p.ProposalExpiration,
|
||||
Duration: p.Duration,
|
||||
StoragePricePerEpoch: p.PricePerEpoch,
|
||||
StorageCollateral: types.NewInt(uint64(pieceSize)), // TODO: real calc
|
||||
}
|
||||
|
||||
if err := api.SignWith(ctx, c.w.Sign, p.Client, dealProposal); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("signing deal proposal failed: %w", err)
|
||||
}
|
||||
|
||||
proposalNd, err := cborutil.AsIpld(dealProposal)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("getting proposal node failed: %w", err)
|
||||
}
|
||||
|
||||
s, err := c.h.NewStream(ctx, p.MinerID, DealProtocolID)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("connecting to storage provider failed: %w", err)
|
||||
}
|
||||
|
||||
proposal := &Proposal{
|
||||
DealProposal: dealProposal,
|
||||
Piece: p.Data,
|
||||
}
|
||||
|
||||
if err := cborutil.WriteCborRPC(s, proposal); err != nil {
|
||||
s.Reset()
|
||||
return cid.Undef, xerrors.Errorf("sending proposal to storage provider failed: %w", err)
|
||||
}
|
||||
|
||||
deal := &ClientDeal{
|
||||
ProposalCid: proposalNd.Cid(),
|
||||
Proposal: *dealProposal,
|
||||
State: api.DealUnknown,
|
||||
Miner: p.MinerID,
|
||||
MinerWorker: p.MinerWorker,
|
||||
|
||||
s: s,
|
||||
}
|
||||
|
||||
c.incoming <- deal
|
||||
|
||||
return deal.ProposalCid, c.discovery.AddPeer(p.Data, discovery.RetrievalPeer{
|
||||
Address: dealProposal.Provider,
|
||||
ID: deal.Miner,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) QueryAsk(ctx context.Context, p peer.ID, a address.Address) (*types.SignedStorageAsk, error) {
|
||||
s, err := c.h.NewStream(ctx, p, AskProtocolID)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to open stream to miner: %w", err)
|
||||
}
|
||||
|
||||
req := &AskRequest{
|
||||
Miner: a,
|
||||
}
|
||||
if err := cborutil.WriteCborRPC(s, req); err != nil {
|
||||
return nil, xerrors.Errorf("failed to send ask request: %w", err)
|
||||
}
|
||||
|
||||
var out AskResponse
|
||||
if err := cborutil.ReadCborRPC(s, &out); err != nil {
|
||||
return nil, xerrors.Errorf("failed to read ask response: %w", err)
|
||||
}
|
||||
|
||||
if out.Ask == nil {
|
||||
return nil, xerrors.Errorf("got no ask back")
|
||||
}
|
||||
|
||||
if out.Ask.Ask.Miner != a {
|
||||
return nil, xerrors.Errorf("got back ask for wrong miner")
|
||||
}
|
||||
|
||||
if err := c.checkAskSignature(out.Ask); err != nil {
|
||||
return nil, xerrors.Errorf("ask was not properly signed")
|
||||
}
|
||||
|
||||
return out.Ask, nil
|
||||
}
|
||||
|
||||
func (c *Client) List() ([]ClientDeal, error) {
|
||||
var out []ClientDeal
|
||||
if err := c.deals.List(&out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetDeal(d cid.Cid) (*ClientDeal, error) {
|
||||
var out ClientDeal
|
||||
if err := c.deals.Get(d, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func (c *Client) Stop() {
|
||||
close(c.stop)
|
||||
<-c.stopped
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
)
|
||||
|
||||
type clientHandlerFunc func(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error)
|
||||
|
||||
func (c *Client) handle(ctx context.Context, deal ClientDeal, cb clientHandlerFunc, next api.DealState) {
|
||||
go func() {
|
||||
mut, err := cb(ctx, deal)
|
||||
if err != nil {
|
||||
next = api.DealError
|
||||
}
|
||||
|
||||
if err == nil && next == api.DealNoUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case c.updated <- clientDealUpdate{
|
||||
newState: next,
|
||||
id: deal.ProposalCid,
|
||||
err: err,
|
||||
mut: mut,
|
||||
}:
|
||||
case <-c.stop:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Client) new(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error) {
|
||||
resp, err := c.readStorageDealResp(deal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: verify StorageDealSubmission
|
||||
|
||||
if err := c.disconnect(deal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* data transfer happens */
|
||||
if resp.State != api.DealAccepted {
|
||||
return nil, xerrors.Errorf("deal wasn't accepted (State=%d)", resp.State)
|
||||
}
|
||||
|
||||
return func(info *ClientDeal) {
|
||||
info.PublishMessage = resp.StorageDealSubmission
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) accepted(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error) {
|
||||
log.Infow("DEAL ACCEPTED!")
|
||||
|
||||
pubmsg := deal.PublishMessage.Message
|
||||
pw, err := stmgr.GetMinerWorker(ctx, c.sm, nil, deal.Proposal.Provider)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting miner worker failed: %w", err)
|
||||
}
|
||||
|
||||
if pubmsg.From != pw {
|
||||
return nil, xerrors.Errorf("deal wasn't published by storage provider: from=%s, provider=%s", pubmsg.From, deal.Proposal.Provider)
|
||||
}
|
||||
|
||||
if pubmsg.To != actors.StorageMarketAddress {
|
||||
return nil, xerrors.Errorf("deal publish message wasn't set to StorageMarket actor (to=%s)", pubmsg.To)
|
||||
}
|
||||
|
||||
if pubmsg.Method != actors.SMAMethods.PublishStorageDeals {
|
||||
return nil, xerrors.Errorf("deal publish message called incorrect method (method=%s)", pubmsg.Method)
|
||||
}
|
||||
|
||||
var params actors.PublishStorageDealsParams
|
||||
if err := params.UnmarshalCBOR(bytes.NewReader(pubmsg.Params)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dealIdx := -1
|
||||
for i, storageDeal := range params.Deals {
|
||||
// TODO: make it less hacky
|
||||
sd := storageDeal
|
||||
eq, err := cborutil.Equals(&deal.Proposal, &sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if eq {
|
||||
dealIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if dealIdx == -1 {
|
||||
return nil, xerrors.Errorf("deal publish didn't contain our deal (message cid: %s)", deal.PublishMessage.Cid())
|
||||
}
|
||||
|
||||
// TODO: timeout
|
||||
_, ret, err := c.sm.WaitForMessage(ctx, deal.PublishMessage.Cid())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("waiting for deal publish message: %w", err)
|
||||
}
|
||||
if ret.ExitCode != 0 {
|
||||
return nil, xerrors.Errorf("deal publish failed: exit=%d", ret.ExitCode)
|
||||
}
|
||||
|
||||
var res actors.PublishStorageDealResponse
|
||||
if err := res.UnmarshalCBOR(bytes.NewReader(ret.Return)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(info *ClientDeal) {
|
||||
info.DealID = res.DealIDs[dealIdx]
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) staged(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error) {
|
||||
// TODO: Maybe wait for pre-commit
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *Client) sealing(ctx context.Context, deal ClientDeal) (func(*ClientDeal), error) {
|
||||
checkFunc := func(ts *types.TipSet) (done bool, more bool, err error) {
|
||||
sd, err := stmgr.GetStorageDeal(ctx, c.sm, deal.DealID, ts)
|
||||
if err != nil {
|
||||
// TODO: This may be fine for some errors
|
||||
return false, false, xerrors.Errorf("failed to look up deal on chain: %w", err)
|
||||
}
|
||||
|
||||
if sd.ActivationEpoch > 0 {
|
||||
select {
|
||||
case c.updated <- clientDealUpdate{
|
||||
newState: api.DealComplete,
|
||||
id: deal.ProposalCid,
|
||||
}:
|
||||
case <-c.stop:
|
||||
}
|
||||
|
||||
return true, false, nil
|
||||
}
|
||||
|
||||
return false, true, nil
|
||||
}
|
||||
|
||||
called := func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (more bool, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
select {
|
||||
case c.updated <- clientDealUpdate{
|
||||
newState: api.DealComplete,
|
||||
id: deal.ProposalCid,
|
||||
err: xerrors.Errorf("handling applied event: %w", err),
|
||||
}:
|
||||
case <-c.stop:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if msg == nil {
|
||||
log.Error("timed out waiting for deal activation... what now?")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
sd, err := stmgr.GetStorageDeal(ctx, c.sm, deal.DealID, ts)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("failed to look up deal on chain: %w", err)
|
||||
}
|
||||
|
||||
if sd.ActivationEpoch == 0 {
|
||||
return false, xerrors.Errorf("deal wasn't active: deal=%d, parentState=%s, h=%d", deal.DealID, ts.ParentState(), ts.Height())
|
||||
}
|
||||
|
||||
log.Infof("Storage deal %d activated at epoch %d", deal.DealID, sd.ActivationEpoch)
|
||||
|
||||
select {
|
||||
case c.updated <- clientDealUpdate{
|
||||
newState: api.DealComplete,
|
||||
id: deal.ProposalCid,
|
||||
}:
|
||||
case <-c.stop:
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
revert := func(ctx context.Context, ts *types.TipSet) error {
|
||||
log.Warn("deal activation reverted; TODO: actually handle this!")
|
||||
// TODO: Just go back to DealSealing?
|
||||
return nil
|
||||
}
|
||||
|
||||
matchEvent := func(msg *types.Message) (bool, error) {
|
||||
if msg.To != deal.Proposal.Provider {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if msg.Method != actors.MAMethods.ProveCommitSector {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var params actors.SectorProveCommitInfo
|
||||
if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, dealID := range params.DealIDs {
|
||||
if dealID == deal.DealID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return found, nil
|
||||
}
|
||||
|
||||
if err := c.events.Called(checkFunc, called, revert, 3, build.SealRandomnessLookbackLimit, matchEvent); err != nil {
|
||||
return nil, xerrors.Errorf("failed to set up called handler")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"runtime"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
files "github.com/ipfs/go-ipfs-files"
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/datatransfer"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/padreader"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
"github.com/filecoin-project/lotus/lib/statestore"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
func (c *Client) failDeal(id cid.Cid, cerr error) {
|
||||
if cerr == nil {
|
||||
_, f, l, _ := runtime.Caller(1)
|
||||
cerr = xerrors.Errorf("unknown error (fail called at %s:%d)", f, l)
|
||||
}
|
||||
|
||||
s, ok := c.conns[id]
|
||||
if ok {
|
||||
_ = s.Reset()
|
||||
delete(c.conns, id)
|
||||
}
|
||||
|
||||
// TODO: store in some sort of audit log
|
||||
log.Errorf("deal %s failed: %+v", id, cerr)
|
||||
}
|
||||
|
||||
func (c *Client) commP(ctx context.Context, data cid.Cid) ([]byte, uint64, error) {
|
||||
root, err := c.dag.Get(ctx, data)
|
||||
if err != nil {
|
||||
log.Errorf("failed to get file root for deal: %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
n, err := unixfile.NewUnixfsFile(ctx, c.dag, root)
|
||||
if err != nil {
|
||||
log.Errorf("cannot open unixfs file: %s", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
uf, ok := n.(files.File)
|
||||
if !ok {
|
||||
// TODO: we probably got directory, how should we handle this in unixfs mode?
|
||||
return nil, 0, xerrors.New("unsupported unixfs type")
|
||||
}
|
||||
|
||||
s, err := uf.Size()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
pr, psize := padreader.New(uf, uint64(s))
|
||||
|
||||
commp, err := sectorbuilder.GeneratePieceCommitment(pr, psize)
|
||||
if err != nil {
|
||||
return nil, 0, xerrors.Errorf("generating CommP: %w", err)
|
||||
}
|
||||
|
||||
return commp[:], psize, nil
|
||||
}
|
||||
|
||||
func (c *Client) readStorageDealResp(deal ClientDeal) (*Response, error) {
|
||||
s, ok := c.conns[deal.ProposalCid]
|
||||
if !ok {
|
||||
// TODO: Try to re-establish the connection using query protocol
|
||||
return nil, xerrors.Errorf("no connection to miner")
|
||||
}
|
||||
|
||||
var resp SignedResponse
|
||||
if err := cborutil.ReadCborRPC(s, &resp); err != nil {
|
||||
log.Errorw("failed to read Response message", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := resp.Verify(deal.MinerWorker); err != nil {
|
||||
return nil, xerrors.Errorf("verifying response signature failed", err)
|
||||
}
|
||||
|
||||
if resp.Response.Proposal != deal.ProposalCid {
|
||||
return nil, xerrors.Errorf("miner responded to a wrong proposal: %s != %s", resp.Response.Proposal, deal.ProposalCid)
|
||||
}
|
||||
|
||||
return &resp.Response, nil
|
||||
}
|
||||
|
||||
func (c *Client) disconnect(deal ClientDeal) error {
|
||||
s, ok := c.conns[deal.ProposalCid]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := s.Close()
|
||||
delete(c.conns, deal.ProposalCid)
|
||||
return err
|
||||
}
|
||||
|
||||
var _ datatransfer.RequestValidator = &ClientRequestValidator{}
|
||||
|
||||
// ClientRequestValidator validates data transfer requests for the client
|
||||
// in a storage market
|
||||
type ClientRequestValidator struct {
|
||||
deals *statestore.StateStore
|
||||
}
|
||||
|
||||
// NewClientRequestValidator returns a new client request validator for the
|
||||
// given datastore
|
||||
func NewClientRequestValidator(deals dtypes.ClientDealStore) *ClientRequestValidator {
|
||||
crv := &ClientRequestValidator{
|
||||
deals: deals,
|
||||
}
|
||||
return crv
|
||||
}
|
||||
|
||||
// ValidatePush validates a push request received from the peer that will send data
|
||||
// Will always error because clients should not accept push requests from a provider
|
||||
// in a storage deal (i.e. send data to client).
|
||||
func (c *ClientRequestValidator) ValidatePush(
|
||||
sender peer.ID,
|
||||
voucher datatransfer.Voucher,
|
||||
baseCid cid.Cid,
|
||||
Selector ipld.Node) error {
|
||||
return ErrNoPushAccepted
|
||||
}
|
||||
|
||||
// ValidatePull validates a pull request received from the peer that will receive data
|
||||
// Will succeed only if:
|
||||
// - voucher has correct type
|
||||
// - voucher references an active deal
|
||||
// - referenced deal matches the receiver (miner)
|
||||
// - referenced deal matches the given base CID
|
||||
// - referenced deal is in an acceptable state
|
||||
func (c *ClientRequestValidator) ValidatePull(
|
||||
receiver peer.ID,
|
||||
voucher datatransfer.Voucher,
|
||||
baseCid cid.Cid,
|
||||
Selector ipld.Node) error {
|
||||
dealVoucher, ok := voucher.(*StorageDataTransferVoucher)
|
||||
if !ok {
|
||||
return xerrors.Errorf("voucher type %s: %w", voucher.Identifier(), ErrWrongVoucherType)
|
||||
}
|
||||
|
||||
var deal ClientDeal
|
||||
err := c.deals.Get(dealVoucher.Proposal, &deal)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Proposal CID %s: %w", dealVoucher.Proposal.String(), ErrNoDeal)
|
||||
}
|
||||
if deal.Miner != receiver {
|
||||
return xerrors.Errorf("Deal Peer %s, Data Transfer Peer %s: %w", deal.Miner.String(), receiver.String(), ErrWrongPeer)
|
||||
}
|
||||
if !bytes.Equal(deal.Proposal.PieceRef, baseCid.Bytes()) {
|
||||
return xerrors.Errorf("Deal Payload CID %s, Data Transfer CID %s: %w", string(deal.Proposal.PieceRef), baseCid.String(), ErrWrongPiece)
|
||||
}
|
||||
for _, state := range DataTransferStates {
|
||||
if deal.State == state {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return xerrors.Errorf("Deal State %s: %w", deal.State, ErrInacceptableDealState)
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
datastore "github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/namespace"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/datatransfer"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/statestore"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/storage"
|
||||
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
||||
)
|
||||
|
||||
var ProviderDsPrefix = "/deals/provider"
|
||||
|
||||
type MinerDeal struct {
|
||||
Client peer.ID
|
||||
Proposal actors.StorageDealProposal
|
||||
ProposalCid cid.Cid
|
||||
State api.DealState
|
||||
|
||||
Ref cid.Cid
|
||||
|
||||
DealID uint64
|
||||
SectorID uint64 // Set when State >= DealStaged
|
||||
|
||||
s inet.Stream
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
pricePerByteBlock types.BigInt // how much we want for storing one byte for one block
|
||||
minPieceSize uint64
|
||||
|
||||
ask *types.SignedStorageAsk
|
||||
askLk sync.Mutex
|
||||
|
||||
secb *sectorblocks.SectorBlocks
|
||||
sminer *storage.Miner
|
||||
full api.FullNode
|
||||
|
||||
// TODO: This will go away once storage market module + CAR
|
||||
// is implemented
|
||||
dag dtypes.StagingDAG
|
||||
|
||||
// dataTransfer is the manager of data transfers used by this storage provider
|
||||
dataTransfer dtypes.ProviderDataTransfer
|
||||
|
||||
deals *statestore.StateStore
|
||||
ds dtypes.MetadataDS
|
||||
|
||||
conns map[cid.Cid]inet.Stream
|
||||
|
||||
actor address.Address
|
||||
|
||||
incoming chan MinerDeal
|
||||
updated chan minerDealUpdate
|
||||
stop chan struct{}
|
||||
stopped chan struct{}
|
||||
}
|
||||
|
||||
type minerDealUpdate struct {
|
||||
newState api.DealState
|
||||
id cid.Cid
|
||||
err error
|
||||
mut func(*MinerDeal)
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrDataTransferFailed means a data transfer for a deal failed
|
||||
ErrDataTransferFailed = errors.New("deal data transfer failed")
|
||||
)
|
||||
|
||||
func NewProvider(ds dtypes.MetadataDS, sminer *storage.Miner, secb *sectorblocks.SectorBlocks, dag dtypes.StagingDAG, dataTransfer dtypes.ProviderDataTransfer, fullNode api.FullNode) (*Provider, error) {
|
||||
addr, err := ds.Get(datastore.NewKey("miner-address"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
minerAddress, err := address.NewFromBytes(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h := &Provider{
|
||||
sminer: sminer,
|
||||
dag: dag,
|
||||
dataTransfer: dataTransfer,
|
||||
full: fullNode,
|
||||
secb: secb,
|
||||
|
||||
pricePerByteBlock: types.NewInt(3), // TODO: allow setting
|
||||
minPieceSize: 256, // TODO: allow setting (BUT KEEP MIN 256! (because of how we fill sectors up))
|
||||
|
||||
conns: map[cid.Cid]inet.Stream{},
|
||||
|
||||
incoming: make(chan MinerDeal),
|
||||
updated: make(chan minerDealUpdate),
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
|
||||
actor: minerAddress,
|
||||
|
||||
deals: statestore.New(namespace.Wrap(ds, datastore.NewKey(ProviderDsPrefix))),
|
||||
ds: ds,
|
||||
}
|
||||
|
||||
if err := h.tryLoadAsk(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if h.ask == nil {
|
||||
// TODO: we should be fine with this state, and just say it means 'not actively accepting deals'
|
||||
// for now... lets just set a price
|
||||
if err := h.SetPrice(types.NewInt(500_000_000), 1000000); err != nil {
|
||||
return nil, xerrors.Errorf("failed setting a default price: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// register a data transfer event handler -- this will move deals from
|
||||
// accepted to staged
|
||||
h.dataTransfer.SubscribeToEvents(h.onDataTransferEvent)
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (p *Provider) Run(ctx context.Context) {
|
||||
// TODO: restore state
|
||||
|
||||
go func() {
|
||||
defer log.Warn("quitting deal provider loop")
|
||||
defer close(p.stopped)
|
||||
|
||||
for {
|
||||
select {
|
||||
case deal := <-p.incoming: // DealAccepted
|
||||
p.onIncoming(deal)
|
||||
case update := <-p.updated: // DealStaged
|
||||
p.onUpdated(ctx, update)
|
||||
case <-p.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *Provider) onIncoming(deal MinerDeal) {
|
||||
log.Info("incoming deal")
|
||||
|
||||
p.conns[deal.ProposalCid] = deal.s
|
||||
|
||||
if err := p.deals.Begin(deal.ProposalCid, &deal); err != nil {
|
||||
// This can happen when client re-sends proposal
|
||||
p.failDeal(deal.ProposalCid, err)
|
||||
log.Errorf("deal tracking failed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
p.updated <- minerDealUpdate{
|
||||
newState: api.DealAccepted,
|
||||
id: deal.ProposalCid,
|
||||
err: nil,
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *Provider) onUpdated(ctx context.Context, update minerDealUpdate) {
|
||||
log.Infof("Deal %s updated state to %s", update.id, api.DealStates[update.newState])
|
||||
if update.err != nil {
|
||||
log.Errorf("deal %s (newSt: %d) failed: %+v", update.id, update.newState, update.err)
|
||||
p.failDeal(update.id, update.err)
|
||||
return
|
||||
}
|
||||
var deal MinerDeal
|
||||
err := p.deals.Mutate(update.id, func(d *MinerDeal) error {
|
||||
d.State = update.newState
|
||||
if update.mut != nil {
|
||||
update.mut(d)
|
||||
}
|
||||
deal = *d
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
p.failDeal(update.id, err)
|
||||
return
|
||||
}
|
||||
|
||||
switch update.newState {
|
||||
case api.DealAccepted:
|
||||
p.handle(ctx, deal, p.accept, api.DealNoUpdate)
|
||||
case api.DealStaged:
|
||||
p.handle(ctx, deal, p.staged, api.DealSealing)
|
||||
case api.DealSealing:
|
||||
p.handle(ctx, deal, p.sealing, api.DealComplete)
|
||||
case api.DealComplete:
|
||||
p.handle(ctx, deal, p.complete, api.DealNoUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
// onDataTransferEvent is the function called when an event occurs in a data
|
||||
// transfer -- it reads the voucher to verify this even occurred in a storage
|
||||
// market deal, then, based on the data transfer event that occurred, it generates
|
||||
// and update message for the deal -- either moving to staged for a completion
|
||||
// event or moving to error if a data transfer error occurs
|
||||
func (p *Provider) onDataTransferEvent(event datatransfer.Event, channelState datatransfer.ChannelState) {
|
||||
voucher, ok := channelState.Voucher().(*StorageDataTransferVoucher)
|
||||
// if this event is for a transfer not related to storage, ignore
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// data transfer events for opening and progress do not affect deal state
|
||||
var next api.DealState
|
||||
var err error
|
||||
var mut func(*MinerDeal)
|
||||
switch event {
|
||||
case datatransfer.Complete:
|
||||
next = api.DealStaged
|
||||
mut = func(deal *MinerDeal) {
|
||||
deal.DealID = voucher.DealID
|
||||
}
|
||||
case datatransfer.Error:
|
||||
next = api.DealFailed
|
||||
err = ErrDataTransferFailed
|
||||
default:
|
||||
// the only events we care about are complete and error
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case p.updated <- minerDealUpdate{
|
||||
newState: next,
|
||||
id: voucher.Proposal,
|
||||
err: err,
|
||||
mut: mut,
|
||||
}:
|
||||
case <-p.stop:
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) newDeal(s inet.Stream, proposal Proposal) (MinerDeal, error) {
|
||||
proposalNd, err := cborutil.AsIpld(proposal.DealProposal)
|
||||
if err != nil {
|
||||
return MinerDeal{}, err
|
||||
}
|
||||
|
||||
return MinerDeal{
|
||||
Client: s.Conn().RemotePeer(),
|
||||
Proposal: *proposal.DealProposal,
|
||||
ProposalCid: proposalNd.Cid(),
|
||||
State: api.DealUnknown,
|
||||
|
||||
Ref: proposal.Piece,
|
||||
|
||||
s: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) HandleStream(s inet.Stream) {
|
||||
log.Info("Handling storage deal proposal!")
|
||||
|
||||
proposal, err := p.readProposal(s)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
|
||||
deal, err := p.newDeal(s, proposal)
|
||||
if err != nil {
|
||||
log.Errorf("%+v", err)
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
|
||||
p.incoming <- deal
|
||||
}
|
||||
|
||||
func (p *Provider) Stop() {
|
||||
close(p.stop)
|
||||
<-p.stopped
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
datastore "github.com/ipfs/go-datastore"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func (p *Provider) SetPrice(price types.BigInt, ttlsecs int64) error {
|
||||
p.askLk.Lock()
|
||||
defer p.askLk.Unlock()
|
||||
|
||||
var seqno uint64
|
||||
if p.ask != nil {
|
||||
seqno = p.ask.Ask.SeqNo + 1
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
ask := &types.StorageAsk{
|
||||
Price: price,
|
||||
Timestamp: uint64(now),
|
||||
Expiry: uint64(now + ttlsecs),
|
||||
Miner: p.actor,
|
||||
SeqNo: seqno,
|
||||
MinPieceSize: p.minPieceSize,
|
||||
}
|
||||
|
||||
ssa, err := p.signAsk(ask)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.saveAsk(ssa)
|
||||
}
|
||||
|
||||
func (p *Provider) getAsk(m address.Address) *types.SignedStorageAsk {
|
||||
p.askLk.Lock()
|
||||
defer p.askLk.Unlock()
|
||||
if m != p.actor {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p.ask
|
||||
}
|
||||
|
||||
func (p *Provider) HandleAskStream(s inet.Stream) {
|
||||
defer s.Close()
|
||||
var ar AskRequest
|
||||
if err := cborutil.ReadCborRPC(s, &ar); err != nil {
|
||||
log.Errorf("failed to read AskRequest from incoming stream: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := p.processAskRequest(&ar)
|
||||
|
||||
if err := cborutil.WriteCborRPC(s, resp); err != nil {
|
||||
log.Errorf("failed to write ask response: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) processAskRequest(ar *AskRequest) *AskResponse {
|
||||
return &AskResponse{
|
||||
Ask: p.getAsk(ar.Miner),
|
||||
}
|
||||
}
|
||||
|
||||
var bestAskKey = datastore.NewKey("latest-ask")
|
||||
|
||||
func (p *Provider) tryLoadAsk() error {
|
||||
p.askLk.Lock()
|
||||
defer p.askLk.Unlock()
|
||||
|
||||
err := p.loadAsk()
|
||||
if err != nil {
|
||||
if xerrors.Is(err, datastore.ErrNotFound) {
|
||||
log.Warn("no previous ask found, miner will not accept deals until a price is set")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) loadAsk() error {
|
||||
askb, err := p.ds.Get(datastore.NewKey("latest-ask"))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load most recent ask from disk: %w", err)
|
||||
}
|
||||
|
||||
var ssa types.SignedStorageAsk
|
||||
if err := cborutil.ReadCborRPC(bytes.NewReader(askb), &ssa); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ask = &ssa
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) signAsk(a *types.StorageAsk) (*types.SignedStorageAsk, error) {
|
||||
b, err := cborutil.Dump(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
worker, err := p.getWorker(p.actor)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get worker to sign ask: %w", err)
|
||||
}
|
||||
|
||||
sig, err := p.full.WalletSign(context.TODO(), worker, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.SignedStorageAsk{
|
||||
Ask: a,
|
||||
Signature: sig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) saveAsk(a *types.SignedStorageAsk) error {
|
||||
b, err := cborutil.Dump(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.ds.Put(bestAskKey, b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ask = a
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) checkAskSignature(ask *types.SignedStorageAsk) error {
|
||||
tss := c.sm.ChainStore().GetHeaviestTipSet().ParentState()
|
||||
|
||||
w, err := stmgr.GetMinerWorkerRaw(context.TODO(), c.sm, tss, ask.Ask.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get worker for miner in ask", err)
|
||||
}
|
||||
|
||||
sigb, err := cborutil.Dump(ask.Ask)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to re-serialize ask")
|
||||
}
|
||||
|
||||
return ask.Signature.Verify(w, sigb)
|
||||
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
ipldfree "github.com/ipld/go-ipld-prime/impl/free"
|
||||
"github.com/ipld/go-ipld-prime/traversal/selector"
|
||||
"github.com/ipld/go-ipld-prime/traversal/selector/builder"
|
||||
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/padreader"
|
||||
"github.com/filecoin-project/lotus/storage/sectorblocks"
|
||||
)
|
||||
|
||||
type providerHandlerFunc func(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error)
|
||||
|
||||
func (p *Provider) handle(ctx context.Context, deal MinerDeal, cb providerHandlerFunc, next api.DealState) {
|
||||
go func() {
|
||||
mut, err := cb(ctx, deal)
|
||||
|
||||
if err == nil && next == api.DealNoUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case p.updated <- minerDealUpdate{
|
||||
newState: next,
|
||||
id: deal.ProposalCid,
|
||||
err: err,
|
||||
mut: mut,
|
||||
}:
|
||||
case <-p.stop:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// ACCEPTED
|
||||
func (p *Provider) accept(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
|
||||
head, err := p.full.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if head.Height() >= deal.Proposal.ProposalExpiration {
|
||||
return nil, xerrors.Errorf("deal proposal already expired")
|
||||
}
|
||||
|
||||
// TODO: check StorageCollateral
|
||||
|
||||
minPrice := types.BigDiv(types.BigMul(p.ask.Ask.Price, types.NewInt(deal.Proposal.PieceSize)), types.NewInt(1<<30))
|
||||
if deal.Proposal.StoragePricePerEpoch.LessThan(minPrice) {
|
||||
return nil, xerrors.Errorf("storage price per epoch less than asking price: %s < %s", deal.Proposal.StoragePricePerEpoch, minPrice)
|
||||
}
|
||||
|
||||
if deal.Proposal.PieceSize < p.ask.Ask.MinPieceSize {
|
||||
return nil, xerrors.Errorf("piece size less than minimum required size: %d < %d", deal.Proposal.PieceSize, p.ask.Ask.MinPieceSize)
|
||||
}
|
||||
|
||||
// check market funds
|
||||
clientMarketBalance, err := p.full.StateMarketBalance(ctx, deal.Proposal.Client, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting client market balance failed: %w", err)
|
||||
}
|
||||
|
||||
// This doesn't guarantee that the client won't withdraw / lock those funds
|
||||
// but it's a decent first filter
|
||||
if clientMarketBalance.Available.LessThan(deal.Proposal.TotalStoragePrice()) {
|
||||
return nil, xerrors.New("clientMarketBalance.Available too small")
|
||||
}
|
||||
|
||||
waddr, err := p.full.StateMinerWorker(ctx, deal.Proposal.Provider, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: check StorageCollateral (may be too large (or too small))
|
||||
if err := p.full.MarketEnsureAvailable(ctx, waddr, deal.Proposal.StorageCollateral); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("publishing deal")
|
||||
|
||||
params, err := actors.SerializeParams(&actors.PublishStorageDealsParams{
|
||||
Deals: []actors.StorageDealProposal{deal.Proposal},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("serializing PublishStorageDeals params failed: ", err)
|
||||
}
|
||||
|
||||
// TODO: We may want this to happen after fetching data
|
||||
smsg, err := p.full.MpoolPushMessage(ctx, &types.Message{
|
||||
To: actors.StorageMarketAddress,
|
||||
From: waddr,
|
||||
Value: types.NewInt(0),
|
||||
GasPrice: types.NewInt(0),
|
||||
GasLimit: types.NewInt(1000000),
|
||||
Method: actors.SMAMethods.PublishStorageDeals,
|
||||
Params: params,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := p.full.StateWaitMsg(ctx, smsg.Cid())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.Receipt.ExitCode != 0 {
|
||||
return nil, xerrors.Errorf("publishing deal failed: exit %d", r.Receipt.ExitCode)
|
||||
}
|
||||
var resp actors.PublishStorageDealResponse
|
||||
if err := resp.UnmarshalCBOR(bytes.NewReader(r.Receipt.Return)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(resp.DealIDs) != 1 {
|
||||
return nil, xerrors.Errorf("got unexpected number of DealIDs from SMA")
|
||||
}
|
||||
|
||||
log.Infof("fetching data for a deal %d", resp.DealIDs[0])
|
||||
err = p.sendSignedResponse(&Response{
|
||||
State: api.DealAccepted,
|
||||
|
||||
Proposal: deal.ProposalCid,
|
||||
StorageDealSubmission: smsg,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.disconnect(deal); err != nil {
|
||||
log.Warnf("closing client connection: %+v", err)
|
||||
}
|
||||
|
||||
ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder())
|
||||
|
||||
// this is the selector for "get the whole DAG"
|
||||
// TODO: support storage deals with custom payload selectors
|
||||
allSelector := ssb.ExploreRecursive(selector.RecursionLimitNone(),
|
||||
ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node()
|
||||
|
||||
// initiate a pull data transfer. This will complete asynchronously and the
|
||||
// completion of the data transfer will trigger a change in deal state
|
||||
// (see onDataTransferEvent)
|
||||
_, err = p.dataTransfer.OpenPullDataChannel(ctx,
|
||||
deal.Client,
|
||||
&StorageDataTransferVoucher{Proposal: deal.ProposalCid, DealID: resp.DealIDs[0]},
|
||||
deal.Ref,
|
||||
allSelector,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to open pull data channel: %w", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// STAGED
|
||||
|
||||
func (p *Provider) staged(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
root, err := p.dag.Get(ctx, deal.Ref)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get file root for deal: %s", err)
|
||||
}
|
||||
|
||||
// TODO: abstract this away into ReadSizeCloser + implement different modes
|
||||
n, err := unixfile.NewUnixfsFile(ctx, p.dag, root)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("cannot open unixfs file: %s", err)
|
||||
}
|
||||
|
||||
uf, ok := n.(sectorblocks.UnixfsReader)
|
||||
if !ok {
|
||||
// we probably got directory, unsupported for now
|
||||
return nil, xerrors.Errorf("unsupported unixfs file type")
|
||||
}
|
||||
|
||||
// TODO: uf.Size() is user input, not trusted
|
||||
// This won't be useful / here after we migrate to putting CARs into sectors
|
||||
size, err := uf.Size()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting unixfs file size: %w", err)
|
||||
}
|
||||
if padreader.PaddedSize(uint64(size)) != deal.Proposal.PieceSize {
|
||||
return nil, xerrors.Errorf("deal.Proposal.PieceSize didn't match padded unixfs file size")
|
||||
}
|
||||
|
||||
sectorID, err := p.secb.AddUnixfsPiece(ctx, uf, deal.DealID)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("AddPiece failed: %s", err)
|
||||
}
|
||||
log.Warnf("New Sector: %d (deal %d)", sectorID, deal.DealID)
|
||||
|
||||
return func(deal *MinerDeal) {
|
||||
deal.SectorID = sectorID
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SEALING
|
||||
|
||||
func (p *Provider) sealing(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
// TODO: consider waiting for seal to happen
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *Provider) complete(ctx context.Context, deal MinerDeal) (func(*MinerDeal), error) {
|
||||
// TODO: observe sector lifecycle, status, expiration..
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"runtime"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/datatransfer"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/ipld/go-ipld-prime"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/statestore"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
inet "github.com/libp2p/go-libp2p-core/network"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func (p *Provider) failDeal(id cid.Cid, cerr error) {
|
||||
if err := p.deals.End(id); err != nil {
|
||||
log.Warnf("deals.End: %s", err)
|
||||
}
|
||||
|
||||
if cerr == nil {
|
||||
_, f, l, _ := runtime.Caller(1)
|
||||
cerr = xerrors.Errorf("unknown error (fail called at %s:%d)", f, l)
|
||||
}
|
||||
|
||||
log.Warnf("deal %s failed: %s", id, cerr)
|
||||
|
||||
err := p.sendSignedResponse(&Response{
|
||||
State: api.DealFailed,
|
||||
Message: cerr.Error(),
|
||||
Proposal: id,
|
||||
})
|
||||
|
||||
s, ok := p.conns[id]
|
||||
if ok {
|
||||
_ = s.Reset()
|
||||
delete(p.conns, id)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("notifying client about deal failure: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) readProposal(s inet.Stream) (proposal Proposal, err error) {
|
||||
if err := cborutil.ReadCborRPC(s, &proposal); err != nil {
|
||||
log.Errorw("failed to read proposal message", "error", err)
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
if err := proposal.DealProposal.Verify(); err != nil {
|
||||
return proposal, xerrors.Errorf("verifying StorageDealProposal: %w", err)
|
||||
}
|
||||
|
||||
if proposal.DealProposal.Provider != p.actor {
|
||||
log.Errorf("proposal with wrong ProviderAddress: %s", proposal.DealProposal.Provider)
|
||||
return proposal, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Provider) sendSignedResponse(resp *Response) error {
|
||||
s, ok := p.conns[resp.Proposal]
|
||||
if !ok {
|
||||
return xerrors.New("couldn't send response: not connected")
|
||||
}
|
||||
|
||||
msg, err := cborutil.Dump(resp)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("serializing response: %w", err)
|
||||
}
|
||||
|
||||
worker, err := p.getWorker(p.actor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, err := p.full.WalletSign(context.TODO(), worker, msg)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to sign response message: %w", err)
|
||||
}
|
||||
|
||||
signedResponse := &SignedResponse{
|
||||
Response: *resp,
|
||||
Signature: sig,
|
||||
}
|
||||
|
||||
err = cborutil.WriteCborRPC(s, signedResponse)
|
||||
if err != nil {
|
||||
// Assume client disconnected
|
||||
s.Close()
|
||||
delete(p.conns, resp.Proposal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Provider) disconnect(deal MinerDeal) error {
|
||||
s, ok := p.conns[deal.ProposalCid]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := s.Close()
|
||||
delete(p.conns, deal.ProposalCid)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Provider) getWorker(miner address.Address) (address.Address, error) {
|
||||
getworker := &types.Message{
|
||||
To: miner,
|
||||
From: miner,
|
||||
Method: actors.MAMethods.GetWorkerAddr,
|
||||
}
|
||||
r, err := p.full.StateCall(context.TODO(), getworker, nil)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("getting worker address: %w", err)
|
||||
}
|
||||
|
||||
if r.ExitCode != 0 {
|
||||
return address.Undef, xerrors.Errorf("getWorker call failed: %d", r.ExitCode)
|
||||
}
|
||||
|
||||
return address.NewFromBytes(r.Return)
|
||||
}
|
||||
|
||||
var _ datatransfer.RequestValidator = &ProviderRequestValidator{}
|
||||
|
||||
// ProviderRequestValidator validates data transfer requests for the provider
|
||||
// in a storage market
|
||||
type ProviderRequestValidator struct {
|
||||
deals *statestore.StateStore
|
||||
}
|
||||
|
||||
// NewProviderRequestValidator returns a new client request validator for the
|
||||
// given datastore
|
||||
func NewProviderRequestValidator(deals dtypes.ProviderDealStore) *ProviderRequestValidator {
|
||||
return &ProviderRequestValidator{
|
||||
deals: deals,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidatePush validates a push request received from the peer that will send data
|
||||
// Will succeed only if:
|
||||
// - voucher has correct type
|
||||
// - voucher references an active deal
|
||||
// - referenced deal matches the client
|
||||
// - referenced deal matches the given base CID
|
||||
// - referenced deal is in an acceptable state
|
||||
func (m *ProviderRequestValidator) ValidatePush(
|
||||
sender peer.ID,
|
||||
voucher datatransfer.Voucher,
|
||||
baseCid cid.Cid,
|
||||
Selector ipld.Node) error {
|
||||
dealVoucher, ok := voucher.(*StorageDataTransferVoucher)
|
||||
if !ok {
|
||||
return xerrors.Errorf("voucher type %s: %w", voucher.Identifier(), ErrWrongVoucherType)
|
||||
}
|
||||
|
||||
var deal MinerDeal
|
||||
err := m.deals.Get(dealVoucher.Proposal, &deal)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Proposal CID %s: %w", dealVoucher.Proposal.String(), ErrNoDeal)
|
||||
}
|
||||
if deal.Client != sender {
|
||||
return xerrors.Errorf("Deal Peer %s, Data Transfer Peer %s: %w", deal.Client.String(), sender.String(), ErrWrongPeer)
|
||||
}
|
||||
|
||||
if !bytes.Equal(deal.Proposal.PieceRef, baseCid.Bytes()) {
|
||||
return xerrors.Errorf("Deal Payload CID %s, Data Transfer CID %s: %w", string(deal.Proposal.PieceRef), baseCid.String(), ErrWrongPiece)
|
||||
}
|
||||
for _, state := range DataTransferStates {
|
||||
if deal.State == state {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return xerrors.Errorf("Deal State %s: %w", deal.State, ErrInacceptableDealState)
|
||||
}
|
||||
|
||||
// ValidatePull validates a pull request received from the peer that will receive data.
|
||||
// Will always error because providers should not accept pull requests from a client
|
||||
// in a storage deal (i.e. send data to client).
|
||||
func (m *ProviderRequestValidator) ValidatePull(
|
||||
receiver peer.ID,
|
||||
voucher datatransfer.Voucher,
|
||||
baseCid cid.Cid,
|
||||
Selector ipld.Node) error {
|
||||
return ErrNoPullAccepted
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
package deals_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/namespace"
|
||||
dss "github.com/ipfs/go-datastore/sync"
|
||||
blocksutil "github.com/ipfs/go-ipfs-blocksutil"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/deals"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/filecoin-project/lotus/lib/statestore"
|
||||
)
|
||||
|
||||
var blockGenerator = blocksutil.NewBlockGenerator()
|
||||
|
||||
type wrongDTType struct {
|
||||
}
|
||||
|
||||
func (wrongDTType) ToBytes() ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func (wrongDTType) FromBytes([]byte) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (wrongDTType) Identifier() string {
|
||||
return "WrongDTTYPE"
|
||||
}
|
||||
|
||||
func uniqueStorageDealProposal() (actors.StorageDealProposal, error) {
|
||||
clientAddr, err := address.NewIDAddress(uint64(rand.Int()))
|
||||
if err != nil {
|
||||
return actors.StorageDealProposal{}, err
|
||||
}
|
||||
providerAddr, err := address.NewIDAddress(uint64(rand.Int()))
|
||||
if err != nil {
|
||||
return actors.StorageDealProposal{}, err
|
||||
}
|
||||
return actors.StorageDealProposal{
|
||||
PieceRef: blockGenerator.Next().Cid().Bytes(),
|
||||
Client: clientAddr,
|
||||
Provider: providerAddr,
|
||||
ProposerSignature: &types.Signature{
|
||||
Data: []byte("foo bar cat dog"),
|
||||
Type: types.KTBLS,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newClientDeal(minerID peer.ID, state api.DealState) (deals.ClientDeal, error) {
|
||||
newProposal, err := uniqueStorageDealProposal()
|
||||
if err != nil {
|
||||
return deals.ClientDeal{}, err
|
||||
}
|
||||
proposalNd, err := cborutil.AsIpld(&newProposal)
|
||||
if err != nil {
|
||||
return deals.ClientDeal{}, err
|
||||
}
|
||||
minerAddr, err := address.NewIDAddress(uint64(rand.Int()))
|
||||
if err != nil {
|
||||
return deals.ClientDeal{}, err
|
||||
}
|
||||
|
||||
return deals.ClientDeal{
|
||||
Proposal: newProposal,
|
||||
ProposalCid: proposalNd.Cid(),
|
||||
Miner: minerID,
|
||||
MinerWorker: minerAddr,
|
||||
State: state,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newMinerDeal(clientID peer.ID, state api.DealState) (deals.MinerDeal, error) {
|
||||
newProposal, err := uniqueStorageDealProposal()
|
||||
if err != nil {
|
||||
return deals.MinerDeal{}, err
|
||||
}
|
||||
proposalNd, err := cborutil.AsIpld(&newProposal)
|
||||
if err != nil {
|
||||
return deals.MinerDeal{}, err
|
||||
}
|
||||
ref, err := cid.Cast(newProposal.PieceRef)
|
||||
if err != nil {
|
||||
return deals.MinerDeal{}, err
|
||||
}
|
||||
|
||||
return deals.MinerDeal{
|
||||
Proposal: newProposal,
|
||||
ProposalCid: proposalNd.Cid(),
|
||||
Client: clientID,
|
||||
State: state,
|
||||
Ref: ref,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestClientRequestValidation(t *testing.T) {
|
||||
ds := dss.MutexWrap(datastore.NewMapDatastore())
|
||||
state := statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client")))
|
||||
|
||||
crv := deals.NewClientRequestValidator(state)
|
||||
minerID := peer.ID("fakepeerid")
|
||||
block := blockGenerator.Next()
|
||||
t.Run("ValidatePush fails", func(t *testing.T) {
|
||||
if !xerrors.Is(crv.ValidatePush(minerID, wrongDTType{}, block.Cid(), nil), deals.ErrNoPushAccepted) {
|
||||
t.Fatal("Push should fail for the client request validator for storage deals")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePull fails deal not found", func(t *testing.T) {
|
||||
proposal, err := uniqueStorageDealProposal()
|
||||
if err != nil {
|
||||
t.Fatal("error creating proposal")
|
||||
}
|
||||
proposalNd, err := cborutil.AsIpld(&proposal)
|
||||
if err != nil {
|
||||
t.Fatal("error serializing proposal")
|
||||
}
|
||||
pieceRef, err := cid.Cast(proposal.PieceRef)
|
||||
if err != nil {
|
||||
t.Fatal("unable to construct piece cid")
|
||||
}
|
||||
if !xerrors.Is(crv.ValidatePull(minerID, &deals.StorageDataTransferVoucher{proposalNd.Cid(), 1}, pieceRef, nil), deals.ErrNoDeal) {
|
||||
t.Fatal("Pull should fail if there is no deal stored")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePull fails wrong client", func(t *testing.T) {
|
||||
otherMiner := peer.ID("otherminer")
|
||||
clientDeal, err := newClientDeal(otherMiner, api.DealAccepted)
|
||||
if err != nil {
|
||||
t.Fatal("error creating client deal")
|
||||
}
|
||||
if err := state.Begin(clientDeal.ProposalCid, &clientDeal); err != nil {
|
||||
t.Fatal("deal tracking failed")
|
||||
}
|
||||
pieceRef, err := cid.Cast(clientDeal.Proposal.PieceRef)
|
||||
if err != nil {
|
||||
t.Fatal("unable to construct piece cid")
|
||||
}
|
||||
if !xerrors.Is(crv.ValidatePull(minerID, &deals.StorageDataTransferVoucher{clientDeal.ProposalCid, 1}, pieceRef, nil), deals.ErrWrongPeer) {
|
||||
t.Fatal("Pull should fail if miner address is incorrect")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePull fails wrong piece ref", func(t *testing.T) {
|
||||
clientDeal, err := newClientDeal(minerID, api.DealAccepted)
|
||||
if err != nil {
|
||||
t.Fatal("error creating client deal")
|
||||
}
|
||||
if err := state.Begin(clientDeal.ProposalCid, &clientDeal); err != nil {
|
||||
t.Fatal("deal tracking failed")
|
||||
}
|
||||
if !xerrors.Is(crv.ValidatePull(minerID, &deals.StorageDataTransferVoucher{clientDeal.ProposalCid, 1}, blockGenerator.Next().Cid(), nil), deals.ErrWrongPiece) {
|
||||
t.Fatal("Pull should fail if piece ref is incorrect")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePull fails wrong deal state", func(t *testing.T) {
|
||||
clientDeal, err := newClientDeal(minerID, api.DealComplete)
|
||||
if err != nil {
|
||||
t.Fatal("error creating client deal")
|
||||
}
|
||||
if err := state.Begin(clientDeal.ProposalCid, &clientDeal); err != nil {
|
||||
t.Fatal("deal tracking failed")
|
||||
}
|
||||
pieceRef, err := cid.Cast(clientDeal.Proposal.PieceRef)
|
||||
if err != nil {
|
||||
t.Fatal("unable to construct piece cid")
|
||||
}
|
||||
if !xerrors.Is(crv.ValidatePull(minerID, &deals.StorageDataTransferVoucher{clientDeal.ProposalCid, 1}, pieceRef, nil), deals.ErrInacceptableDealState) {
|
||||
t.Fatal("Pull should fail if deal is in a state that cannot be data transferred")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePull succeeds", func(t *testing.T) {
|
||||
clientDeal, err := newClientDeal(minerID, api.DealAccepted)
|
||||
if err != nil {
|
||||
t.Fatal("error creating client deal")
|
||||
}
|
||||
if err := state.Begin(clientDeal.ProposalCid, &clientDeal); err != nil {
|
||||
t.Fatal("deal tracking failed")
|
||||
}
|
||||
pieceRef, err := cid.Cast(clientDeal.Proposal.PieceRef)
|
||||
if err != nil {
|
||||
t.Fatal("unable to construct piece cid")
|
||||
}
|
||||
if crv.ValidatePull(minerID, &deals.StorageDataTransferVoucher{clientDeal.ProposalCid, 1}, pieceRef, nil) != nil {
|
||||
t.Fatal("Pull should should succeed when all parameters are correct")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestProviderRequestValidation(t *testing.T) {
|
||||
ds := dss.MutexWrap(datastore.NewMapDatastore())
|
||||
state := statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client")))
|
||||
|
||||
mrv := deals.NewProviderRequestValidator(state)
|
||||
clientID := peer.ID("fakepeerid")
|
||||
block := blockGenerator.Next()
|
||||
t.Run("ValidatePull fails", func(t *testing.T) {
|
||||
if !xerrors.Is(mrv.ValidatePull(clientID, wrongDTType{}, block.Cid(), nil), deals.ErrNoPullAccepted) {
|
||||
t.Fatal("Pull should fail for the provider request validator for storage deals")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ValidatePush fails deal not found", func(t *testing.T) {
|
||||
proposal, err := uniqueStorageDealProposal()
|
||||
if err != nil {
|
||||
t.Fatal("error creating proposal")
|
||||
}
|
||||
proposalNd, err := cborutil.AsIpld(&proposal)
|
||||
if err != nil {
|
||||
t.Fatal("error serializing proposal")
|
||||
}
|
||||
pieceRef, err := cid.Cast(proposal.PieceRef)
|
||||
if err != nil {
|
||||
t.Fatal("unable to construct piece cid")
|
||||
}
|
||||
if !xerrors.Is(mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{proposalNd.Cid(), 1}, pieceRef, nil), deals.ErrNoDeal) {
|
||||
t.Fatal("Push should fail if there is no deal stored")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePush fails wrong miner", func(t *testing.T) {
|
||||
otherClient := peer.ID("otherclient")
|
||||
minerDeal, err := newMinerDeal(otherClient, api.DealAccepted)
|
||||
if err != nil {
|
||||
t.Fatal("error creating client deal")
|
||||
}
|
||||
if err := state.Begin(minerDeal.ProposalCid, &minerDeal); err != nil {
|
||||
t.Fatal("deal tracking failed")
|
||||
}
|
||||
pieceRef, err := cid.Cast(minerDeal.Proposal.PieceRef)
|
||||
if err != nil {
|
||||
t.Fatal("unable to construct piece cid")
|
||||
}
|
||||
if !xerrors.Is(mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{minerDeal.ProposalCid, 1}, pieceRef, nil), deals.ErrWrongPeer) {
|
||||
t.Fatal("Push should fail if miner address is incorrect")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePush fails wrong piece ref", func(t *testing.T) {
|
||||
minerDeal, err := newMinerDeal(clientID, api.DealAccepted)
|
||||
if err != nil {
|
||||
t.Fatal("error creating client deal")
|
||||
}
|
||||
if err := state.Begin(minerDeal.ProposalCid, &minerDeal); err != nil {
|
||||
t.Fatal("deal tracking failed")
|
||||
}
|
||||
if !xerrors.Is(mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{minerDeal.ProposalCid, 1}, blockGenerator.Next().Cid(), nil), deals.ErrWrongPiece) {
|
||||
t.Fatal("Push should fail if piece ref is incorrect")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePush fails wrong deal state", func(t *testing.T) {
|
||||
minerDeal, err := newMinerDeal(clientID, api.DealComplete)
|
||||
if err != nil {
|
||||
t.Fatal("error creating client deal")
|
||||
}
|
||||
if err := state.Begin(minerDeal.ProposalCid, &minerDeal); err != nil {
|
||||
t.Fatal("deal tracking failed")
|
||||
}
|
||||
pieceRef, err := cid.Cast(minerDeal.Proposal.PieceRef)
|
||||
if err != nil {
|
||||
t.Fatal("unable to construct piece cid")
|
||||
}
|
||||
if !xerrors.Is(mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{minerDeal.ProposalCid, 1}, pieceRef, nil), deals.ErrInacceptableDealState) {
|
||||
t.Fatal("Push should fail if deal is in a state that cannot be data transferred")
|
||||
}
|
||||
})
|
||||
t.Run("ValidatePush succeeds", func(t *testing.T) {
|
||||
minerDeal, err := newMinerDeal(clientID, api.DealAccepted)
|
||||
if err != nil {
|
||||
t.Fatal("error creating client deal")
|
||||
}
|
||||
if err := state.Begin(minerDeal.ProposalCid, &minerDeal); err != nil {
|
||||
t.Fatal("deal tracking failed")
|
||||
}
|
||||
pieceRef, err := cid.Cast(minerDeal.Proposal.PieceRef)
|
||||
if err != nil {
|
||||
t.Fatal("unable to construct piece cid")
|
||||
}
|
||||
if mrv.ValidatePush(clientID, &deals.StorageDataTransferVoucher{minerDeal.ProposalCid, 1}, pieceRef, nil) != nil {
|
||||
t.Fatal("Push should should succeed when all parameters are correct")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package deals
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/cborutil"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrWrongVoucherType means the voucher was not the correct type can validate against
|
||||
ErrWrongVoucherType = errors.New("cannot validate voucher type.")
|
||||
|
||||
// ErrNoPushAccepted just means clients do not accept pushes for storage deals
|
||||
ErrNoPushAccepted = errors.New("client should not receive data for a storage deal.")
|
||||
|
||||
// ErrNoPullAccepted just means providers do not accept pulls for storage deals
|
||||
ErrNoPullAccepted = errors.New("provider should not send data for a storage deal.")
|
||||
|
||||
// ErrNoDeal means no active deal was found for this vouchers proposal cid
|
||||
ErrNoDeal = errors.New("no deal found for this proposal.")
|
||||
|
||||
// ErrWrongPeer means that the other peer for this data transfer request does not match
|
||||
// the other peer for the deal
|
||||
ErrWrongPeer = errors.New("data Transfer peer id and Deal peer id do not match.")
|
||||
|
||||
// ErrWrongPiece means that the pieceref for this data transfer request does not match
|
||||
// the one specified in the deal
|
||||
ErrWrongPiece = errors.New("base CID for deal does not match CID for piece.")
|
||||
|
||||
// ErrInacceptableDealState means the deal for this transfer is not in a deal state
|
||||
// where transfer can be performed
|
||||
ErrInacceptableDealState = errors.New("deal is not a in a state where deals are accepted.")
|
||||
|
||||
// DataTransferStates are the states in which it would make sense to actually start a data transfer
|
||||
DataTransferStates = []api.DealState{api.DealAccepted, api.DealUnknown}
|
||||
)
|
||||
|
||||
const DealProtocolID = "/fil/storage/mk/1.0.1"
|
||||
const AskProtocolID = "/fil/storage/ask/1.0.1"
|
||||
|
||||
type Proposal struct {
|
||||
DealProposal *actors.StorageDealProposal
|
||||
|
||||
Piece cid.Cid // Used for retrieving from the client
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
State api.DealState
|
||||
|
||||
// DealProposalRejected
|
||||
Message string
|
||||
Proposal cid.Cid
|
||||
|
||||
// DealAccepted
|
||||
StorageDealSubmission *types.SignedMessage
|
||||
}
|
||||
|
||||
// TODO: Do we actually need this to be signed?
|
||||
type SignedResponse struct {
|
||||
Response Response
|
||||
|
||||
Signature *types.Signature
|
||||
}
|
||||
|
||||
func (r *SignedResponse) Verify(addr address.Address) error {
|
||||
b, err := cborutil.Dump(&r.Response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.Signature.Verify(addr, b)
|
||||
}
|
||||
|
||||
type AskRequest struct {
|
||||
Miner address.Address
|
||||
}
|
||||
|
||||
type AskResponse struct {
|
||||
Ask *types.SignedStorageAsk
|
||||
}
|
||||
|
||||
// StorageDataTransferVoucher is the voucher type for data transfers
|
||||
// used by the storage market
|
||||
type StorageDataTransferVoucher struct {
|
||||
Proposal cid.Cid
|
||||
DealID uint64
|
||||
}
|
||||
|
||||
// ToBytes converts the StorageDataTransferVoucher to raw bytes
|
||||
func (dv *StorageDataTransferVoucher) ToBytes() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
err := dv.MarshalCBOR(&buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// FromBytes converts the StorageDataTransferVoucher to raw bytes
|
||||
func (dv *StorageDataTransferVoucher) FromBytes(raw []byte) error {
|
||||
r := bytes.NewReader(raw)
|
||||
return dv.UnmarshalCBOR(r)
|
||||
}
|
||||
|
||||
// Identifier is the unique string identifier for a StorageDataTransferVoucher
|
||||
func (dv *StorageDataTransferVoucher) Identifier() string {
|
||||
return "StorageDataTransferVoucher"
|
||||
}
|
||||
+26
-17
@@ -5,21 +5,22 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
var log = logging.Logger("events")
|
||||
|
||||
// `curH`-`ts.Height` = `confidence`
|
||||
type HeightHandler func(ctx context.Context, ts *types.TipSet, curH uint64) error
|
||||
// HeightHandler `curH`-`ts.Height` = `confidence`
|
||||
type HeightHandler func(ctx context.Context, ts *types.TipSet, curH abi.ChainEpoch) error
|
||||
type RevertHandler func(ctx context.Context, ts *types.TipSet) error
|
||||
|
||||
type heightHandler struct {
|
||||
@@ -30,17 +31,18 @@ type heightHandler struct {
|
||||
revert RevertHandler
|
||||
}
|
||||
|
||||
type eventApi interface {
|
||||
ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
|
||||
type eventAPI interface {
|
||||
ChainNotify(context.Context) (<-chan []*api.HeadChange, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
|
||||
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error)
|
||||
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error)
|
||||
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
|
||||
|
||||
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error) // optional / for CalledMsg
|
||||
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) // optional / for CalledMsg
|
||||
}
|
||||
|
||||
type Events struct {
|
||||
api eventApi
|
||||
api eventAPI
|
||||
|
||||
tsc *tipSetCache
|
||||
lk sync.Mutex
|
||||
@@ -52,7 +54,7 @@ type Events struct {
|
||||
calledEvents
|
||||
}
|
||||
|
||||
func NewEvents(ctx context.Context, api eventApi) *Events {
|
||||
func NewEvents(ctx context.Context, api eventAPI) *Events {
|
||||
gcConfidence := 2 * build.ForkLengthThreshold
|
||||
|
||||
tsc := newTSCache(gcConfidence, api.ChainGetTipSetByHeight)
|
||||
@@ -65,11 +67,11 @@ func NewEvents(ctx context.Context, api eventApi) *Events {
|
||||
heightEvents: heightEvents{
|
||||
tsc: tsc,
|
||||
ctx: ctx,
|
||||
gcConfidence: uint64(gcConfidence),
|
||||
gcConfidence: gcConfidence,
|
||||
|
||||
heightTriggers: map[uint64]*heightHandler{},
|
||||
htTriggerHeights: map[uint64][]uint64{},
|
||||
htHeights: map[uint64][]uint64{},
|
||||
htTriggerHeights: map[abi.ChainEpoch][]uint64{},
|
||||
htHeights: map[abi.ChainEpoch][]uint64{},
|
||||
},
|
||||
|
||||
calledEvents: calledEvents{
|
||||
@@ -80,9 +82,9 @@ func NewEvents(ctx context.Context, api eventApi) *Events {
|
||||
|
||||
confQueue: map[triggerH]map[msgH][]*queuedEvent{},
|
||||
revertQueue: map[msgH][]triggerH{},
|
||||
triggers: map[triggerId]*callHandler{},
|
||||
matchers: map[triggerId][]MatchFunc{},
|
||||
timeouts: map[uint64]map[triggerId]int{},
|
||||
triggers: map[triggerID]*callHandler{},
|
||||
matchers: map[triggerID][]MatchFunc{},
|
||||
timeouts: map[abi.ChainEpoch]map[triggerID]int{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -141,6 +143,8 @@ func (e *Events) listenHeadChangesOnce(ctx context.Context) error {
|
||||
}
|
||||
|
||||
e.readyOnce.Do(func() {
|
||||
e.at = cur[0].Val.Height()
|
||||
|
||||
e.ready.Done()
|
||||
})
|
||||
|
||||
@@ -160,6 +164,11 @@ func (e *Events) listenHeadChangesOnce(ctx context.Context) error {
|
||||
if err := e.headChange(rev, app); err != nil {
|
||||
log.Warnf("headChange failed: %s", err)
|
||||
}
|
||||
|
||||
// sync with fake chainstore (for tests)
|
||||
if fcs, ok := e.api.(interface{ notifDone() }); ok {
|
||||
fcs.notifDone()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -5,27 +5,28 @@ import (
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
const NoTimeout = math.MaxUint64
|
||||
const NoTimeout = math.MaxInt64
|
||||
|
||||
type triggerId = uint64
|
||||
type triggerID = uint64
|
||||
|
||||
// msgH is the block height at which a message was present / event has happened
|
||||
type msgH = uint64
|
||||
type msgH = abi.ChainEpoch
|
||||
|
||||
// triggerH is the block height at which the listener will be notified about the
|
||||
// message (msgH+confidence)
|
||||
type triggerH = uint64
|
||||
type triggerH = abi.ChainEpoch
|
||||
|
||||
// CalledHandler arguments:
|
||||
// `ts` is the tipset, in which the `msg` is included.
|
||||
// `curH`-`ts.Height` = `confidence`
|
||||
type CalledHandler func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (more bool, err error)
|
||||
type CalledHandler func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error)
|
||||
|
||||
// CheckFunc is used for atomicity guarantees. If the condition the callbacks
|
||||
// wait for has already happened in tipset `ts`
|
||||
@@ -39,7 +40,7 @@ type MatchFunc func(msg *types.Message) (bool, error)
|
||||
|
||||
type callHandler struct {
|
||||
confidence int
|
||||
timeout uint64
|
||||
timeout abi.ChainEpoch
|
||||
|
||||
disabled bool // TODO: GC after gcConfidence reached
|
||||
|
||||
@@ -48,26 +49,28 @@ type callHandler struct {
|
||||
}
|
||||
|
||||
type queuedEvent struct {
|
||||
trigger triggerId
|
||||
trigger triggerID
|
||||
|
||||
h uint64
|
||||
h abi.ChainEpoch
|
||||
msg *types.Message
|
||||
|
||||
called bool
|
||||
}
|
||||
|
||||
type calledEvents struct {
|
||||
cs eventApi
|
||||
cs eventAPI
|
||||
tsc *tipSetCache
|
||||
ctx context.Context
|
||||
gcConfidence uint64
|
||||
|
||||
at abi.ChainEpoch
|
||||
|
||||
lk sync.Mutex
|
||||
|
||||
ctr triggerId
|
||||
ctr triggerID
|
||||
|
||||
triggers map[triggerId]*callHandler
|
||||
matchers map[triggerId][]MatchFunc
|
||||
triggers map[triggerID]*callHandler
|
||||
matchers map[triggerID][]MatchFunc
|
||||
|
||||
// maps block heights to events
|
||||
// [triggerH][msgH][event]
|
||||
@@ -76,21 +79,23 @@ type calledEvents struct {
|
||||
// [msgH][triggerH]
|
||||
revertQueue map[msgH][]triggerH
|
||||
|
||||
// [timeoutH+confidence][triggerId]{calls}
|
||||
timeouts map[uint64]map[triggerId]int
|
||||
// [timeoutH+confidence][triggerID]{calls}
|
||||
timeouts map[abi.ChainEpoch]map[triggerID]int
|
||||
}
|
||||
|
||||
func (e *calledEvents) headChangeCalled(rev, app []*types.TipSet) error {
|
||||
for _, ts := range rev {
|
||||
e.handleReverts(ts)
|
||||
e.at = ts.Height()
|
||||
}
|
||||
|
||||
for _, ts := range app {
|
||||
// called triggers
|
||||
|
||||
e.checkNewCalls(ts)
|
||||
e.applyWithConfidence(ts)
|
||||
e.applyTimeouts(ts)
|
||||
for ; e.at <= ts.Height(); e.at++ {
|
||||
e.applyWithConfidence(ts, e.at)
|
||||
e.applyTimeouts(ts)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -121,7 +126,13 @@ func (e *calledEvents) handleReverts(ts *types.TipSet) {
|
||||
}
|
||||
|
||||
func (e *calledEvents) checkNewCalls(ts *types.TipSet) {
|
||||
e.messagesForTs(ts, func(msg *types.Message) {
|
||||
pts, err := e.cs.ChainGetTipSet(e.ctx, ts.Parents()) // we actually care about messages in the parent tipset here
|
||||
if err != nil {
|
||||
log.Errorf("getting parent tipset in checkNewCalls: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
e.messagesForTs(pts, func(msg *types.Message) {
|
||||
// TODO: provide receipts
|
||||
|
||||
for tid, matchFns := range e.matchers {
|
||||
@@ -129,7 +140,7 @@ func (e *calledEvents) checkNewCalls(ts *types.TipSet) {
|
||||
for _, matchFn := range matchFns {
|
||||
ok, err := matchFn(msg)
|
||||
if err != nil {
|
||||
log.Warnf("event matcher failed: %s")
|
||||
log.Errorf("event matcher failed: %s", err)
|
||||
continue
|
||||
}
|
||||
matched = ok
|
||||
@@ -147,22 +158,21 @@ func (e *calledEvents) checkNewCalls(ts *types.TipSet) {
|
||||
})
|
||||
}
|
||||
|
||||
func (e *calledEvents) queueForConfidence(triggerId uint64, msg *types.Message, ts *types.TipSet) {
|
||||
trigger := e.triggers[triggerId]
|
||||
func (e *calledEvents) queueForConfidence(trigID uint64, msg *types.Message, ts *types.TipSet) {
|
||||
trigger := e.triggers[trigID]
|
||||
|
||||
// messages are not applied in the tipset they are included in
|
||||
appliedH := ts.Height() + 1
|
||||
appliedH := ts.Height()
|
||||
|
||||
triggerH := appliedH + uint64(trigger.confidence)
|
||||
triggerH := appliedH + abi.ChainEpoch(trigger.confidence)
|
||||
|
||||
byOrigH, ok := e.confQueue[triggerH]
|
||||
if !ok {
|
||||
byOrigH = map[uint64][]*queuedEvent{}
|
||||
byOrigH = map[abi.ChainEpoch][]*queuedEvent{}
|
||||
e.confQueue[triggerH] = byOrigH
|
||||
}
|
||||
|
||||
byOrigH[appliedH] = append(byOrigH[appliedH], &queuedEvent{
|
||||
trigger: triggerId,
|
||||
trigger: trigID,
|
||||
h: appliedH,
|
||||
msg: msg,
|
||||
})
|
||||
@@ -170,8 +180,8 @@ func (e *calledEvents) queueForConfidence(triggerId uint64, msg *types.Message,
|
||||
e.revertQueue[appliedH] = append(e.revertQueue[appliedH], triggerH)
|
||||
}
|
||||
|
||||
func (e *calledEvents) applyWithConfidence(ts *types.TipSet) {
|
||||
byOrigH, ok := e.confQueue[ts.Height()]
|
||||
func (e *calledEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch) {
|
||||
byOrigH, ok := e.confQueue[height]
|
||||
if !ok {
|
||||
return // no triggers at thin height
|
||||
}
|
||||
@@ -179,7 +189,7 @@ func (e *calledEvents) applyWithConfidence(ts *types.TipSet) {
|
||||
for origH, events := range byOrigH {
|
||||
triggerTs, err := e.tsc.get(origH)
|
||||
if err != nil {
|
||||
log.Errorf("events: applyWithConfidence didn't find tipset for event; wanted %d; current %d", origH, ts.Height())
|
||||
log.Errorf("events: applyWithConfidence didn't find tipset for event; wanted %d; current %d", origH, height)
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
@@ -192,15 +202,15 @@ func (e *calledEvents) applyWithConfidence(ts *types.TipSet) {
|
||||
continue
|
||||
}
|
||||
|
||||
rec, err := e.cs.StateGetReceipt(e.ctx, event.msg.Cid(), ts)
|
||||
rec, err := e.cs.StateGetReceipt(e.ctx, event.msg.Cid(), ts.Key())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
more, err := trigger.handle(event.msg, rec, triggerTs, ts.Height())
|
||||
more, err := trigger.handle(event.msg, rec, triggerTs, height)
|
||||
if err != nil {
|
||||
log.Errorf("chain trigger (call %s.%d() @H %d, called @ %d) failed: %s", event.msg.To, event.msg.Method, origH, ts.Height(), err)
|
||||
log.Errorf("chain trigger (call %s.%d() @H %d, called @ %d) failed: %s", event.msg.To, event.msg.Method, origH, height, err)
|
||||
continue // don't revert failed calls
|
||||
}
|
||||
|
||||
@@ -222,18 +232,18 @@ func (e *calledEvents) applyTimeouts(ts *types.TipSet) {
|
||||
return // nothing to do
|
||||
}
|
||||
|
||||
for triggerId, calls := range triggers {
|
||||
for triggerID, calls := range triggers {
|
||||
if calls > 0 {
|
||||
continue // don't timeout if the method was called
|
||||
}
|
||||
trigger := e.triggers[triggerId]
|
||||
trigger := e.triggers[triggerID]
|
||||
if trigger.disabled {
|
||||
continue
|
||||
}
|
||||
|
||||
timeoutTs, err := e.tsc.get(ts.Height() - uint64(trigger.confidence))
|
||||
timeoutTs, err := e.tsc.get(ts.Height() - abi.ChainEpoch(trigger.confidence))
|
||||
if err != nil {
|
||||
log.Errorf("events: applyTimeouts didn't find tipset for event; wanted %d; current %d", ts.Height()-uint64(trigger.confidence), ts.Height())
|
||||
log.Errorf("events: applyTimeouts didn't find tipset for event; wanted %d; current %d", ts.Height()-abi.ChainEpoch(trigger.confidence), ts.Height())
|
||||
}
|
||||
|
||||
more, err := trigger.handle(nil, nil, timeoutTs, ts.Height())
|
||||
@@ -304,7 +314,7 @@ func (e *calledEvents) messagesForTs(ts *types.TipSet, consume func(*types.Messa
|
||||
// containing the message. The tipset passed as the argument is the tipset
|
||||
// that is being dropped. Note that the message dropped may be re-applied
|
||||
// in a different tipset in small amount of time.
|
||||
func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHandler, confidence int, timeout uint64, mf MatchFunc) error {
|
||||
func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf MatchFunc) error {
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
|
||||
@@ -322,7 +332,7 @@ func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHand
|
||||
|
||||
e.triggers[id] = &callHandler{
|
||||
confidence: confidence,
|
||||
timeout: timeout + uint64(confidence),
|
||||
timeout: timeout + abi.ChainEpoch(confidence),
|
||||
|
||||
disabled: !more,
|
||||
|
||||
@@ -333,15 +343,15 @@ func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHand
|
||||
e.matchers[id] = append(e.matchers[id], mf)
|
||||
|
||||
if timeout != NoTimeout {
|
||||
if e.timeouts[timeout+uint64(confidence)] == nil {
|
||||
e.timeouts[timeout+uint64(confidence)] = map[uint64]int{}
|
||||
if e.timeouts[timeout+abi.ChainEpoch(confidence)] == nil {
|
||||
e.timeouts[timeout+abi.ChainEpoch(confidence)] = map[uint64]int{}
|
||||
}
|
||||
e.timeouts[timeout+uint64(confidence)][id] = 0
|
||||
e.timeouts[timeout+abi.ChainEpoch(confidence)][id] = 0
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *calledEvents) CalledMsg(ctx context.Context, hnd CalledHandler, rev RevertHandler, confidence int, timeout uint64, msg store.ChainMsg) error {
|
||||
func (e *calledEvents) CalledMsg(ctx context.Context, hnd CalledHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, msg types.ChainMsg) error {
|
||||
return e.Called(e.CheckMsg(ctx, msg, hnd), hnd, rev, confidence, timeout, e.MatchMsg(msg.VMMessage()))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@@ -12,14 +13,14 @@ import (
|
||||
type heightEvents struct {
|
||||
lk sync.Mutex
|
||||
tsc *tipSetCache
|
||||
gcConfidence uint64
|
||||
gcConfidence abi.ChainEpoch
|
||||
|
||||
ctr triggerId
|
||||
ctr triggerID
|
||||
|
||||
heightTriggers map[triggerId]*heightHandler
|
||||
heightTriggers map[triggerID]*heightHandler
|
||||
|
||||
htTriggerHeights map[triggerH][]triggerId
|
||||
htHeights map[msgH][]triggerId
|
||||
htTriggerHeights map[triggerH][]triggerID
|
||||
htHeights map[msgH][]triggerID
|
||||
|
||||
ctx context.Context
|
||||
}
|
||||
@@ -35,7 +36,7 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
// TODO: log error if h below gcconfidence
|
||||
// revert height-based triggers
|
||||
|
||||
revert := func(h uint64, ts *types.TipSet) {
|
||||
revert := func(h abi.ChainEpoch, ts *types.TipSet) {
|
||||
for _, tid := range e.htHeights[h] {
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightRevert")
|
||||
|
||||
@@ -80,7 +81,7 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
|
||||
// height triggers
|
||||
|
||||
apply := func(h uint64, ts *types.TipSet) error {
|
||||
apply := func(h abi.ChainEpoch, ts *types.TipSet) error {
|
||||
for _, tid := range e.htTriggerHeights[h] {
|
||||
hnd := e.heightTriggers[tid]
|
||||
if hnd.called {
|
||||
@@ -88,7 +89,7 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
}
|
||||
hnd.called = true
|
||||
|
||||
triggerH := h - uint64(hnd.confidence)
|
||||
triggerH := h - abi.ChainEpoch(hnd.confidence)
|
||||
|
||||
incTs, err := e.tsc.getNonNull(triggerH)
|
||||
if err != nil {
|
||||
@@ -139,13 +140,13 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
// specified height, `RevertHandler` will be called.
|
||||
//
|
||||
// ts passed to handlers is the tipset at the specified, or above, if lower tipsets were null
|
||||
func (e *heightEvents) ChainAt(hnd HeightHandler, rev RevertHandler, confidence int, h uint64) error {
|
||||
func (e *heightEvents) ChainAt(hnd HeightHandler, rev RevertHandler, confidence int, h abi.ChainEpoch) error {
|
||||
|
||||
e.lk.Lock() // Tricky locking, check your locks if you modify this function!
|
||||
|
||||
bestH := e.tsc.best().Height()
|
||||
|
||||
if bestH >= h+uint64(confidence) {
|
||||
if bestH >= h+abi.ChainEpoch(confidence) {
|
||||
ts, err := e.tsc.getNonNull(h)
|
||||
if err != nil {
|
||||
log.Warnf("events.ChainAt: calling HandleFunc with nil tipset, not found in cache: %s", err)
|
||||
@@ -168,11 +169,11 @@ func (e *heightEvents) ChainAt(hnd HeightHandler, rev RevertHandler, confidence
|
||||
|
||||
defer e.lk.Unlock()
|
||||
|
||||
if bestH >= h+uint64(confidence)+e.gcConfidence {
|
||||
if bestH >= h+abi.ChainEpoch(confidence)+e.gcConfidence {
|
||||
return nil
|
||||
}
|
||||
|
||||
triggerAt := h + uint64(confidence)
|
||||
triggerAt := h + abi.ChainEpoch(confidence)
|
||||
|
||||
id := e.ctr
|
||||
e.ctr++
|
||||
|
||||
+261
-75
@@ -3,18 +3,20 @@ package events
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/multiformats/go-multihash"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@@ -31,28 +33,36 @@ type fakeMsg struct {
|
||||
|
||||
type fakeCS struct {
|
||||
t *testing.T
|
||||
h uint64
|
||||
h abi.ChainEpoch
|
||||
tsc *tipSetCache
|
||||
|
||||
msgs map[cid.Cid]fakeMsg
|
||||
blkMsgs map[cid.Cid]cid.Cid
|
||||
|
||||
sync sync.Mutex
|
||||
|
||||
tipsets map[types.TipSetKey]*types.TipSet
|
||||
|
||||
sub func(rev, app []*types.TipSet)
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error) {
|
||||
func (fcs *fakeCS) ChainGetTipSet(ctx context.Context, key types.TipSetKey) (*types.TipSet, error) {
|
||||
return fcs.tipsets[key], nil
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) StateGetReceipt(context.Context, cid.Cid, types.TipSetKey) (*types.MessageReceipt, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error) {
|
||||
func (fcs *fakeCS) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) {
|
||||
panic("Not Implemented")
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error) {
|
||||
func (fcs *fakeCS) ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) {
|
||||
panic("Not Implemented")
|
||||
}
|
||||
|
||||
func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
|
||||
func (fcs *fakeCS) makeTs(t *testing.T, parents []cid.Cid, h abi.ChainEpoch, msgcid cid.Cid) *types.TipSet {
|
||||
a, _ := address.NewFromString("t00")
|
||||
b, _ := address.NewFromString("t02")
|
||||
var ts, err = types.NewTipSet([]*types.BlockHeader{
|
||||
@@ -60,50 +70,59 @@ func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
|
||||
Height: h,
|
||||
Miner: a,
|
||||
|
||||
Parents: parents,
|
||||
|
||||
Ticket: &types.Ticket{VRFProof: []byte{byte(h % 2)}},
|
||||
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: msgcid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
|
||||
BlockSig: &types.Signature{Type: types.KTBLS},
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS},
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
},
|
||||
{
|
||||
Height: h,
|
||||
Miner: b,
|
||||
|
||||
Parents: parents,
|
||||
|
||||
Ticket: &types.Ticket{VRFProof: []byte{byte((h + 1) % 2)}},
|
||||
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: msgcid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
|
||||
BlockSig: &types.Signature{Type: types.KTBLS},
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS},
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
},
|
||||
})
|
||||
|
||||
if fcs.tipsets == nil {
|
||||
fcs.tipsets = map[types.TipSetKey]*types.TipSet{}
|
||||
}
|
||||
fcs.tipsets[ts.Key()] = ts
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
return ts
|
||||
}
|
||||
|
||||
func (fcs *fakeCS) ChainNotify(context.Context) (<-chan []*store.HeadChange, error) {
|
||||
out := make(chan []*store.HeadChange, 1)
|
||||
out <- []*store.HeadChange{{Type: store.HCCurrent, Val: fcs.tsc.best()}}
|
||||
func (fcs *fakeCS) ChainNotify(context.Context) (<-chan []*api.HeadChange, error) {
|
||||
out := make(chan []*api.HeadChange, 1)
|
||||
out <- []*api.HeadChange{{Type: store.HCCurrent, Val: fcs.tsc.best()}}
|
||||
|
||||
fcs.sub = func(rev, app []*types.TipSet) {
|
||||
notif := make([]*store.HeadChange, len(rev)+len(app))
|
||||
notif := make([]*api.HeadChange, len(rev)+len(app))
|
||||
|
||||
for i, r := range rev {
|
||||
notif[i] = &store.HeadChange{
|
||||
notif[i] = &api.HeadChange{
|
||||
Type: store.HCRevert,
|
||||
Val: r,
|
||||
}
|
||||
}
|
||||
for i, r := range app {
|
||||
notif[i+len(rev)] = &store.HeadChange{
|
||||
notif[i+len(rev)] = &api.HeadChange{
|
||||
Type: store.HCApply,
|
||||
Val: r,
|
||||
}
|
||||
@@ -177,7 +196,7 @@ func (fcs *fakeCS) advance(rev, app int, msgs map[int]cid.Cid, nulls ...int) { /
|
||||
continue
|
||||
}
|
||||
|
||||
ts := makeTs(fcs.t, fcs.h, mc)
|
||||
ts := fcs.makeTs(fcs.t, fcs.tsc.best().Key().Cids(), fcs.h, mc)
|
||||
require.NoError(fcs.t, fcs.tsc.add(ts))
|
||||
|
||||
if hasMsgs {
|
||||
@@ -187,11 +206,19 @@ func (fcs *fakeCS) advance(rev, app int, msgs map[int]cid.Cid, nulls ...int) { /
|
||||
apps = append(apps, ts)
|
||||
}
|
||||
|
||||
fcs.sync.Lock()
|
||||
|
||||
fcs.sub(revs, apps)
|
||||
time.Sleep(100 * time.Millisecond) // TODO: :c
|
||||
|
||||
fcs.sync.Lock()
|
||||
fcs.sync.Unlock() //nolint:staticcheck
|
||||
}
|
||||
|
||||
var _ eventApi = &fakeCS{}
|
||||
func (fcs *fakeCS) notifDone() {
|
||||
fcs.sync.Unlock()
|
||||
}
|
||||
|
||||
var _ eventAPI = &fakeCS{}
|
||||
|
||||
func TestAt(t *testing.T) {
|
||||
fcs := &fakeCS{
|
||||
@@ -199,14 +226,14 @@ func TestAt(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
@@ -264,14 +291,14 @@ func TestAtDoubleTrigger(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
@@ -306,15 +333,15 @@ func TestAtNullTrigger(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
require.Equal(t, uint64(6), ts.Height())
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, abi.ChainEpoch(6), ts.Height())
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
return nil
|
||||
@@ -340,14 +367,14 @@ func TestAtNullConf(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
@@ -379,7 +406,7 @@ func TestAtStart(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -388,7 +415,7 @@ func TestAtStart(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 8, int(curH))
|
||||
applied = true
|
||||
@@ -413,7 +440,7 @@ func TestAtStartConfidence(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -422,7 +449,7 @@ func TestAtStartConfidence(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 5, int(ts.Height()))
|
||||
require.Equal(t, 11, int(curH))
|
||||
applied = true
|
||||
@@ -443,15 +470,15 @@ func TestAtChained(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 10, int(ts.Height()))
|
||||
applied = true
|
||||
return nil
|
||||
@@ -477,7 +504,7 @@ func TestAtChainedConfidence(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -486,8 +513,8 @@ func TestAtChainedConfidence(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
return events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
require.Equal(t, 10, int(ts.Height()))
|
||||
applied = true
|
||||
return nil
|
||||
@@ -511,7 +538,7 @@ func TestAtChainedConfidenceNull(t *testing.T) {
|
||||
h: 1,
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -520,7 +547,7 @@ func TestAtChainedConfidenceNull(t *testing.T) {
|
||||
var applied bool
|
||||
var reverted bool
|
||||
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH uint64) error {
|
||||
err := events.ChainAt(func(_ context.Context, ts *types.TipSet, curH abi.ChainEpoch) error {
|
||||
applied = true
|
||||
require.Equal(t, 6, int(ts.Height()))
|
||||
return nil
|
||||
@@ -534,7 +561,7 @@ func TestAtChainedConfidenceNull(t *testing.T) {
|
||||
require.Equal(t, false, reverted)
|
||||
}
|
||||
|
||||
func matchAddrMethod(to address.Address, m uint64) func(msg *types.Message) (bool, error) {
|
||||
func matchAddrMethod(to address.Address, m abi.MethodNum) func(msg *types.Message) (bool, error) {
|
||||
return func(msg *types.Message) (bool, error) {
|
||||
return to == msg.To && m == msg.Method, nil
|
||||
}
|
||||
@@ -549,7 +576,7 @@ func TestCalled(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -560,11 +587,11 @@ func TestCalled(t *testing.T) {
|
||||
var applied, reverted bool
|
||||
var appliedMsg *types.Message
|
||||
var appliedTs *types.TipSet
|
||||
var appliedH uint64
|
||||
var appliedH abi.ChainEpoch
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
require.Equal(t, false, applied)
|
||||
applied = true
|
||||
appliedMsg = msg
|
||||
@@ -610,12 +637,12 @@ func TestCalled(t *testing.T) {
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
require.Equal(t, uint64(7), appliedTs.Height())
|
||||
require.Equal(t, abi.ChainEpoch(7), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(10), appliedH)
|
||||
require.Equal(t, abi.ChainEpoch(10), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(1), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
require.Equal(t, abi.MethodNum(5), appliedMsg.Method)
|
||||
|
||||
// revert some blocks, keep the message
|
||||
|
||||
@@ -625,7 +652,7 @@ func TestCalled(t *testing.T) {
|
||||
|
||||
// revert the message
|
||||
|
||||
fcs.advance(2, 1, nil) // H=7, we reverted ts with the msg
|
||||
fcs.advance(2, 1, nil) // H=7, we reverted ts with the msg execution, but not the msg itself
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
@@ -639,24 +666,31 @@ func TestCalled(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
fcs.advance(0, 5, map[int]cid.Cid{ // (confidence=3)
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // (n2msg confidence=1)
|
||||
0: n2msg,
|
||||
})
|
||||
|
||||
require.Equal(t, true, applied) // msg from H=7, which had reverted execution
|
||||
require.Equal(t, false, reverted)
|
||||
require.Equal(t, abi.ChainEpoch(10), appliedH)
|
||||
applied = false
|
||||
|
||||
fcs.advance(0, 2, nil) // (confidence=3)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
require.Equal(t, uint64(9), appliedTs.Height())
|
||||
require.Equal(t, abi.ChainEpoch(9), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(12), appliedH)
|
||||
require.Equal(t, abi.ChainEpoch(12), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(2), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
require.Equal(t, abi.MethodNum(5), appliedMsg.Method)
|
||||
|
||||
// revert and apply at different height
|
||||
|
||||
fcs.advance(4, 6, map[int]cid.Cid{ // (confidence=3)
|
||||
fcs.advance(8, 6, map[int]cid.Cid{ // (confidence=3)
|
||||
1: n2msg,
|
||||
})
|
||||
|
||||
@@ -668,12 +702,12 @@ func TestCalled(t *testing.T) {
|
||||
reverted = false
|
||||
applied = false
|
||||
|
||||
require.Equal(t, uint64(11), appliedTs.Height())
|
||||
require.Equal(t, abi.ChainEpoch(7), appliedTs.Height())
|
||||
require.Equal(t, "bafkqaaa", appliedTs.Blocks()[0].Messages.String())
|
||||
require.Equal(t, uint64(14), appliedH)
|
||||
require.Equal(t, abi.ChainEpoch(10), appliedH)
|
||||
require.Equal(t, t0123, appliedMsg.To)
|
||||
require.Equal(t, uint64(2), appliedMsg.Nonce)
|
||||
require.Equal(t, uint64(5), appliedMsg.Method)
|
||||
require.Equal(t, abi.MethodNum(5), appliedMsg.Method)
|
||||
|
||||
// call method again
|
||||
|
||||
@@ -694,7 +728,7 @@ func TestCalled(t *testing.T) {
|
||||
}),
|
||||
})
|
||||
|
||||
fcs.advance(1, 4, nil) // H=19, but message reverted
|
||||
fcs.advance(2, 5, nil) // H=19, but message reverted
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
@@ -754,7 +788,7 @@ func TestCalledTimeout(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -765,11 +799,11 @@ func TestCalledTimeout(t *testing.T) {
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
called = true
|
||||
require.Nil(t, msg)
|
||||
require.Equal(t, uint64(20), ts.Height())
|
||||
require.Equal(t, uint64(23), curH)
|
||||
require.Equal(t, abi.ChainEpoch(20), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(23), curH)
|
||||
return false, nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
t.Fatal("revert on timeout")
|
||||
@@ -794,17 +828,17 @@ func TestCalledTimeout(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events = NewEvents(context.Background(), fcs)
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return true, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
called = true
|
||||
require.Nil(t, msg)
|
||||
require.Equal(t, uint64(20), ts.Height())
|
||||
require.Equal(t, uint64(23), curH)
|
||||
require.Equal(t, abi.ChainEpoch(20), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(23), curH)
|
||||
return false, nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
t.Fatal("revert on timeout")
|
||||
@@ -828,7 +862,7 @@ func TestCalledOrder(t *testing.T) {
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
@@ -839,14 +873,14 @@ func TestCalledOrder(t *testing.T) {
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH uint64) (bool, error) {
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
switch at {
|
||||
case 0:
|
||||
require.Equal(t, uint64(1), msg.Nonce)
|
||||
require.Equal(t, uint64(4), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(4), ts.Height())
|
||||
case 1:
|
||||
require.Equal(t, uint64(2), msg.Nonce)
|
||||
require.Equal(t, uint64(5), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(5), ts.Height())
|
||||
default:
|
||||
t.Fatal("apply should only get called twice, at: ", at)
|
||||
}
|
||||
@@ -855,9 +889,9 @@ func TestCalledOrder(t *testing.T) {
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
switch at {
|
||||
case 2:
|
||||
require.Equal(t, uint64(5), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(5), ts.Height())
|
||||
case 3:
|
||||
require.Equal(t, uint64(4), ts.Height())
|
||||
require.Equal(t, abi.ChainEpoch(4), ts.Height())
|
||||
default:
|
||||
t.Fatal("revert should only get called twice, at: ", at)
|
||||
}
|
||||
@@ -881,3 +915,155 @@ func TestCalledOrder(t *testing.T) {
|
||||
|
||||
fcs.advance(9, 1, nil)
|
||||
}
|
||||
|
||||
func TestCalledNull(t *testing.T) {
|
||||
fcs := &fakeCS{
|
||||
t: t,
|
||||
h: 1,
|
||||
|
||||
msgs: map[cid.Cid]fakeMsg{},
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
t0123, err := address.NewFromString("t0123")
|
||||
require.NoError(t, err)
|
||||
|
||||
more := true
|
||||
var applied, reverted bool
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
require.Equal(t, false, applied)
|
||||
applied = true
|
||||
return more, nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 20, matchAddrMethod(t0123, 5))
|
||||
require.NoError(t, err)
|
||||
|
||||
// create few blocks to make sure nothing get's randomly called
|
||||
|
||||
fcs.advance(0, 4, nil) // H=5
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg at H=6; H=8 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional blocks so we are above confidence threshold, but with null tipset at the height
|
||||
// of application
|
||||
|
||||
fcs.advance(0, 3, nil, 10) // H=11 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
applied = false
|
||||
|
||||
fcs.advance(5, 1, nil, 10)
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, true, reverted)
|
||||
}
|
||||
|
||||
func TestRemoveTriggersOnMessage(t *testing.T) {
|
||||
fcs := &fakeCS{
|
||||
t: t,
|
||||
h: 1,
|
||||
|
||||
msgs: map[cid.Cid]fakeMsg{},
|
||||
blkMsgs: map[cid.Cid]cid.Cid{},
|
||||
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
|
||||
}
|
||||
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
|
||||
|
||||
events := NewEvents(context.Background(), fcs)
|
||||
|
||||
t0123, err := address.NewFromString("t0123")
|
||||
require.NoError(t, err)
|
||||
|
||||
more := true
|
||||
var applied, reverted bool
|
||||
|
||||
err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) {
|
||||
return false, true, nil
|
||||
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
|
||||
require.Equal(t, false, applied)
|
||||
fmt.Println(msg == nil)
|
||||
fmt.Println(curH)
|
||||
applied = true
|
||||
return more, nil
|
||||
}, func(_ context.Context, ts *types.TipSet) error {
|
||||
reverted = true
|
||||
return nil
|
||||
}, 3, 20, matchAddrMethod(t0123, 5))
|
||||
require.NoError(t, err)
|
||||
|
||||
// create few blocks to make sure nothing get's randomly called
|
||||
|
||||
fcs.advance(0, 4, nil) // H=5
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg occurs at H=5, applied at H=6; H=8 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 1},
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// revert applied TS & message TS
|
||||
fcs.advance(3, 1, nil) // H=6 (tipset message applied in reverted, AND message reverted)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional blocks so we are above confidence threshold, but message not applied
|
||||
// as it was reverted
|
||||
fcs.advance(0, 5, nil) // H=11 (confidence=3, apply)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create blocks with message again (but below confidence threshold)
|
||||
|
||||
fcs.advance(0, 3, map[int]cid.Cid{ // msg occurs at H=12, applied at H=13; H=15 (confidence=2)
|
||||
0: fcs.fakeMsgs(fakeMsg{
|
||||
bmsgs: []*types.Message{
|
||||
{To: t0123, From: t0123, Method: 5, Nonce: 2},
|
||||
},
|
||||
}),
|
||||
})
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// revert applied height TS, but don't remove message trigger
|
||||
fcs.advance(2, 1, nil) // H=13 (tipset message applied in reverted, by tipset with message not reverted)
|
||||
require.Equal(t, false, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
|
||||
// create additional blocks so we are above confidence threshold
|
||||
fcs.advance(0, 4, nil) // H=18 (confidence=3, apply)
|
||||
|
||||
require.Equal(t, true, applied)
|
||||
require.Equal(t, false, reverted)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ package events
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type tsByHFunc func(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
|
||||
type tsByHFunc func(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
|
||||
// tipSetCache implements a simple ring-buffer cache to keep track of recent
|
||||
// tipsets
|
||||
@@ -20,7 +21,7 @@ type tipSetCache struct {
|
||||
storage tsByHFunc
|
||||
}
|
||||
|
||||
func newTSCache(cap int, storage tsByHFunc) *tipSetCache {
|
||||
func newTSCache(cap abi.ChainEpoch, storage tsByHFunc) *tipSetCache {
|
||||
return &tipSetCache{
|
||||
cache: make([]*types.TipSet, cap),
|
||||
start: 0,
|
||||
@@ -77,7 +78,7 @@ func (tsc *tipSetCache) revert(ts *types.TipSet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) getNonNull(height uint64) (*types.TipSet, error) {
|
||||
func (tsc *tipSetCache) getNonNull(height abi.ChainEpoch) (*types.TipSet, error) {
|
||||
for {
|
||||
ts, err := tsc.get(height)
|
||||
if err != nil {
|
||||
@@ -90,10 +91,10 @@ func (tsc *tipSetCache) getNonNull(height uint64) (*types.TipSet, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) get(height uint64) (*types.TipSet, error) {
|
||||
func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) {
|
||||
if tsc.len == 0 {
|
||||
log.Warnf("tipSetCache.get: cache is empty, requesting from storage (h=%d)", height)
|
||||
return tsc.storage(context.TODO(), height, nil)
|
||||
return tsc.storage(context.TODO(), height, types.EmptyTSK)
|
||||
}
|
||||
|
||||
headH := tsc.cache[tsc.start].Height()
|
||||
@@ -113,7 +114,7 @@ func (tsc *tipSetCache) get(height uint64) (*types.TipSet, error) {
|
||||
|
||||
if height < tail.Height() {
|
||||
log.Warnf("tipSetCache.get: requested tipset not in cache, requesting from storage (h=%d; tail=%d)", height, tail.Height())
|
||||
return tsc.storage(context.TODO(), height, tail)
|
||||
return tsc.storage(context.TODO(), height, tail.Key())
|
||||
}
|
||||
|
||||
return tsc.cache[normalModulo(tsc.start-int(headH-height), clen)], nil
|
||||
|
||||
@@ -4,19 +4,21 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func TestTsCache(t *testing.T) {
|
||||
tsc := newTSCache(50, func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) {
|
||||
tsc := newTSCache(50, func(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) {
|
||||
t.Fatal("storage call")
|
||||
return &types.TipSet{}, nil
|
||||
})
|
||||
|
||||
h := uint64(75)
|
||||
h := abi.ChainEpoch(75)
|
||||
|
||||
a, _ := address.NewFromString("t00")
|
||||
|
||||
@@ -27,8 +29,8 @@ func TestTsCache(t *testing.T) {
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: dummyCid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
BlockSig: &types.Signature{Type: types.KTBLS},
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS},
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -54,12 +56,12 @@ func TestTsCache(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTsCacheNulls(t *testing.T) {
|
||||
tsc := newTSCache(50, func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) {
|
||||
tsc := newTSCache(50, func(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) {
|
||||
t.Fatal("storage call")
|
||||
return &types.TipSet{}, nil
|
||||
})
|
||||
|
||||
h := uint64(75)
|
||||
h := abi.ChainEpoch(75)
|
||||
|
||||
a, _ := address.NewFromString("t00")
|
||||
add := func() {
|
||||
@@ -69,8 +71,8 @@ func TestTsCacheNulls(t *testing.T) {
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: dummyCid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
BlockSig: &types.Signature{Type: types.KTBLS},
|
||||
BLSAggregate: types.Signature{Type: types.KTBLS},
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -5,15 +5,14 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func (e *calledEvents) CheckMsg(ctx context.Context, smsg store.ChainMsg, hnd CalledHandler) CheckFunc {
|
||||
func (e *calledEvents) CheckMsg(ctx context.Context, smsg types.ChainMsg, hnd CalledHandler) CheckFunc {
|
||||
msg := smsg.VMMessage()
|
||||
|
||||
return func(ts *types.TipSet) (done bool, more bool, err error) {
|
||||
fa, err := e.cs.StateGetActor(ctx, msg.From, ts)
|
||||
fa, err := e.cs.StateGetActor(ctx, msg.From, ts.Key())
|
||||
if err != nil {
|
||||
return false, true, err
|
||||
}
|
||||
@@ -23,7 +22,7 @@ func (e *calledEvents) CheckMsg(ctx context.Context, smsg store.ChainMsg, hnd Ca
|
||||
return false, true, nil
|
||||
}
|
||||
|
||||
rec, err := e.cs.StateGetReceipt(ctx, smsg.VMMessage().Cid(), ts)
|
||||
rec, err := e.cs.StateGetReceipt(ctx, smsg.VMMessage().Cid(), ts.Key())
|
||||
if err != nil {
|
||||
return false, true, xerrors.Errorf("getting receipt in CheckMsg: %w", err)
|
||||
}
|
||||
@@ -37,7 +36,7 @@ func (e *calledEvents) CheckMsg(ctx context.Context, smsg store.ChainMsg, hnd Ca
|
||||
func (e *calledEvents) MatchMsg(inmsg *types.Message) MatchFunc {
|
||||
return func(msg *types.Message) (bool, error) {
|
||||
if msg.From == inmsg.From && msg.Nonce == inmsg.Nonce && !inmsg.Equals(msg) {
|
||||
return false, xerrors.Errorf("matching msg %s from %s, nonce %d: got duplicate origin/nonce msg %s", inmsg.Cid(), inmsg.From, inmsg.Nonce, msg.Nonce)
|
||||
return false, xerrors.Errorf("matching msg %s from %s, nonce %d: got duplicate origin/nonce msg %d", inmsg.Cid(), inmsg.From, inmsg.Nonce, msg.Nonce)
|
||||
}
|
||||
|
||||
return inmsg.Equals(msg), nil
|
||||
|
||||
+245
-263
@@ -3,38 +3,42 @@ package gen
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
commcid "github.com/filecoin-project/go-fil-commcid"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
block "github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-blockservice"
|
||||
"github.com/ipfs/go-car"
|
||||
"github.com/ipfs/go-cid"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
format "github.com/ipfs/go-ipld-format"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/ipfs/go-merkledag"
|
||||
peer "github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/ipld/go-car"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/address"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
"github.com/filecoin-project/lotus/lib/sectorbuilder"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
|
||||
block "github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-cid"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
)
|
||||
|
||||
var log = logging.Logger("gen")
|
||||
@@ -48,16 +52,20 @@ type ChainGen struct {
|
||||
|
||||
cs *store.ChainStore
|
||||
|
||||
beacon beacon.RandomBeacon
|
||||
|
||||
sm *stmgr.StateManager
|
||||
|
||||
genesis *types.BlockHeader
|
||||
CurTipset *store.FullTipSet
|
||||
|
||||
Timestamper func(*types.TipSet, uint64) uint64
|
||||
Timestamper func(*types.TipSet, abi.ChainEpoch) uint64
|
||||
|
||||
GetMessages func(*ChainGen) ([]*types.SignedMessage, error)
|
||||
|
||||
w *wallet.Wallet
|
||||
|
||||
eppProvs map[address.Address]ElectionPoStProver
|
||||
eppProvs map[address.Address]WinningPoStProver
|
||||
Miners []address.Address
|
||||
receivers []address.Address
|
||||
banker address.Address
|
||||
@@ -80,7 +88,11 @@ func (m mybs) Get(c cid.Cid) (block.Block, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func NewGenerator() (*ChainGen, error) {
|
||||
func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
|
||||
saminer.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
|
||||
mr := repo.NewMemory(nil)
|
||||
lr, err := mr.Lock(repo.StorageMiner)
|
||||
if err != nil {
|
||||
@@ -92,7 +104,7 @@ func NewGenerator() (*ChainGen, error) {
|
||||
return nil, xerrors.Errorf("failed to get metadata datastore: %w", err)
|
||||
}
|
||||
|
||||
bds, err := lr.Datastore("/blocks")
|
||||
bds, err := lr.Datastore("/chain")
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get blocks datastore: %w", err)
|
||||
}
|
||||
@@ -109,77 +121,86 @@ func NewGenerator() (*ChainGen, error) {
|
||||
return nil, xerrors.Errorf("creating memrepo wallet failed: %w", err)
|
||||
}
|
||||
|
||||
banker, err := w.GenerateKey(types.KTSecp256k1)
|
||||
banker, err := w.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to generate banker key: %w", err)
|
||||
}
|
||||
|
||||
receievers := make([]address.Address, msgsPerBlock)
|
||||
for r := range receievers {
|
||||
receievers[r], err = w.GenerateKey(types.KTBLS)
|
||||
receievers[r], err = w.GenerateKey(crypto.SigTypeBLS)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to generate receiver key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
maddr1, err := address.NewFromString("t0300")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maddr1 := genesis2.MinerAddress(0)
|
||||
|
||||
m1temp, err := ioutil.TempDir("", "preseal")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
genm1, err := seed.PreSeal(maddr1, 1024, 0, 1, m1temp, []byte("some randomness"))
|
||||
genm1, k1, err := seed.PreSeal(maddr1, abi.RegisteredProof_StackedDRG2KiBPoSt, 0, numSectors, m1temp, []byte("some randomness"), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maddr2, err := address.NewFromString("t0301")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maddr2 := genesis2.MinerAddress(1)
|
||||
|
||||
m2temp, err := ioutil.TempDir("", "preseal")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
genm2, err := seed.PreSeal(maddr2, 1024, 0, 1, m2temp, []byte("some randomness"))
|
||||
genm2, k2, err := seed.PreSeal(maddr2, abi.RegisteredProof_StackedDRG2KiBPoSt, 0, numSectors, m2temp, []byte("some randomness"), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mk1, err := w.Import(&genm1.Key)
|
||||
mk1, err := w.Import(k1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mk2, err := w.Import(&genm2.Key)
|
||||
mk2, err := w.Import(k2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
minercfg := &GenMinerCfg{
|
||||
PeerIDs: []peer.ID{"peerID1", "peerID2"},
|
||||
PreSeals: map[string]genesis.GenesisMiner{
|
||||
maddr1.String(): *genm1,
|
||||
maddr2.String(): *genm2,
|
||||
sys := vm.Syscalls(&genFakeVerifier{})
|
||||
|
||||
tpl := genesis.Template{
|
||||
Accounts: []genesis.Actor{
|
||||
{
|
||||
Type: genesis.TAccount,
|
||||
Balance: types.FromFil(40000),
|
||||
Meta: (&genesis.AccountMeta{Owner: mk1}).ActorMeta(),
|
||||
},
|
||||
{
|
||||
Type: genesis.TAccount,
|
||||
Balance: types.FromFil(40000),
|
||||
Meta: (&genesis.AccountMeta{Owner: mk2}).ActorMeta(),
|
||||
},
|
||||
{
|
||||
Type: genesis.TAccount,
|
||||
Balance: types.FromFil(50000),
|
||||
Meta: (&genesis.AccountMeta{Owner: banker}).ActorMeta(),
|
||||
},
|
||||
},
|
||||
MinerAddrs: []address.Address{maddr1, maddr2},
|
||||
Miners: []genesis.Miner{
|
||||
*genm1,
|
||||
*genm2,
|
||||
},
|
||||
NetworkName: "",
|
||||
Timestamp: uint64(time.Now().Add(-500 * build.BlockDelay * time.Second).Unix()),
|
||||
}
|
||||
|
||||
genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{
|
||||
mk1: types.FromFil(40000),
|
||||
mk2: types.FromFil(40000),
|
||||
banker: types.FromFil(50000),
|
||||
}, minercfg, 100000)
|
||||
genb, err := genesis2.MakeGenesisBlock(context.TODO(), bs, sys, tpl)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("make genesis block failed: %w", err)
|
||||
}
|
||||
|
||||
cs := store.NewChainStore(bs, ds)
|
||||
cs := store.NewChainStore(bs, ds, sys)
|
||||
|
||||
genfb := &types.FullBlock{Header: genb.Genesis}
|
||||
gents := store.NewFullTipSet([]*types.FullBlock{genfb})
|
||||
@@ -188,29 +209,35 @@ func NewGenerator() (*ChainGen, error) {
|
||||
return nil, xerrors.Errorf("set genesis failed: %w", err)
|
||||
}
|
||||
|
||||
if len(minercfg.MinerAddrs) == 0 {
|
||||
return nil, xerrors.Errorf("MakeGenesisBlock failed to set miner address")
|
||||
}
|
||||
|
||||
mgen := make(map[address.Address]ElectionPoStProver)
|
||||
for _, m := range minercfg.MinerAddrs {
|
||||
mgen[m] = &eppProvider{}
|
||||
mgen := make(map[address.Address]WinningPoStProver)
|
||||
for i := range tpl.Miners {
|
||||
mgen[genesis2.MinerAddress(uint64(i))] = &wppProvider{}
|
||||
}
|
||||
|
||||
sm := stmgr.NewStateManager(cs)
|
||||
|
||||
miners := []address.Address{maddr1, maddr2}
|
||||
|
||||
beac := beacon.NewMockBeacon(time.Second)
|
||||
//beac, err := drand.NewDrandBeacon(tpl.Timestamp, build.BlockDelay)
|
||||
//if err != nil {
|
||||
//return nil, xerrors.Errorf("creating drand beacon: %w", err)
|
||||
//}
|
||||
|
||||
gen := &ChainGen{
|
||||
bs: bs,
|
||||
cs: cs,
|
||||
sm: sm,
|
||||
msgsPerBlock: msgsPerBlock,
|
||||
genesis: genb.Genesis,
|
||||
beacon: beac,
|
||||
w: w,
|
||||
|
||||
Miners: minercfg.MinerAddrs,
|
||||
eppProvs: mgen,
|
||||
banker: banker,
|
||||
receivers: receievers,
|
||||
GetMessages: getRandomMessages,
|
||||
Miners: miners,
|
||||
eppProvs: mgen,
|
||||
banker: banker,
|
||||
receivers: receievers,
|
||||
|
||||
CurTipset: gents,
|
||||
|
||||
@@ -221,6 +248,18 @@ func NewGenerator() (*ChainGen, error) {
|
||||
return gen, nil
|
||||
}
|
||||
|
||||
func NewGenerator() (*ChainGen, error) {
|
||||
return NewGeneratorWithSectors(1)
|
||||
}
|
||||
|
||||
func (cg *ChainGen) SetStateManager(sm *stmgr.StateManager) {
|
||||
cg.sm = sm
|
||||
}
|
||||
|
||||
func (cg *ChainGen) ChainStore() *store.ChainStore {
|
||||
return cg.cs
|
||||
}
|
||||
|
||||
func (cg *ChainGen) Genesis() *types.BlockHeader {
|
||||
return cg.genesis
|
||||
}
|
||||
@@ -232,46 +271,76 @@ func (cg *ChainGen) GenesisCar() ([]byte, error) {
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
|
||||
if err := car.WriteCar(context.TODO(), dserv, []cid.Cid{cg.Genesis().Cid()}, out); err != nil {
|
||||
if err := car.WriteCarWithWalker(context.TODO(), dserv, []cid.Cid{cg.Genesis().Cid()}, out, CarWalkFunc); err != nil {
|
||||
return nil, xerrors.Errorf("genesis car write car failed: %w", err)
|
||||
}
|
||||
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round int64) (*types.EPostProof, *types.Ticket, error) {
|
||||
func CarWalkFunc(nd format.Node) (out []*format.Link, err error) {
|
||||
for _, link := range nd.Links() {
|
||||
if link.Cid.Prefix().MhType == uint64(commcid.FC_SEALED_V1) || link.Cid.Prefix().MhType == uint64(commcid.FC_UNSEALED_V1) {
|
||||
continue
|
||||
}
|
||||
out = append(out, link)
|
||||
}
|
||||
|
||||
lastTicket := pts.MinTicket()
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round abi.ChainEpoch) ([]types.BeaconEntry, *types.ElectionProof, *types.Ticket, error) {
|
||||
mc := &mca{w: cg.w, sm: cg.sm, pv: ffiwrapper.ProofVerifier, bcn: cg.beacon}
|
||||
|
||||
mbi, err := mc.MinerGetBaseInfo(ctx, m, round, pts.Key())
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("get miner base info: %w", err)
|
||||
}
|
||||
|
||||
prev := mbi.PrevBeaconEntry
|
||||
|
||||
entries, err := beacon.BeaconEntriesForBlock(ctx, cg.beacon, round, prev)
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("get beacon entries for block: %w", err)
|
||||
}
|
||||
|
||||
rbase := prev
|
||||
if len(entries) > 0 {
|
||||
rbase = entries[len(entries)-1]
|
||||
}
|
||||
|
||||
eproof, err := IsRoundWinner(ctx, pts, round, m, rbase, mbi, mc)
|
||||
if err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("checking round winner failed: %w", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := m.MarshalCBOR(buf); err != nil {
|
||||
return nil, nil, nil, xerrors.Errorf("failed to cbor marshal address: %w", err)
|
||||
}
|
||||
|
||||
if len(entries) == 0 {
|
||||
buf.Write(pts.MinTicket().VRFProof)
|
||||
}
|
||||
|
||||
ticketRand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_TicketProduction, round-build.TicketRandomnessLookback, buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
st := pts.ParentState()
|
||||
|
||||
worker, err := stmgr.GetMinerWorkerRaw(ctx, cg.sm, st, m)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("get miner worker: %w", err)
|
||||
return nil, nil, nil, xerrors.Errorf("get miner worker: %w", err)
|
||||
}
|
||||
|
||||
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, m, DSepTicket, lastTicket.VRFProof)
|
||||
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, ticketRand)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("compute VRF: %w", err)
|
||||
return nil, nil, nil, xerrors.Errorf("compute VRF: %w", err)
|
||||
}
|
||||
|
||||
tick := &types.Ticket{
|
||||
VRFProof: vrfout,
|
||||
}
|
||||
|
||||
eproofin, err := IsRoundWinner(ctx, pts, round, m, cg.eppProvs[m], &mca{w: cg.w, sm: cg.sm})
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("checking round winner failed: %w", err)
|
||||
}
|
||||
if eproofin == nil {
|
||||
return nil, tick, nil
|
||||
}
|
||||
eproof, err := ComputeProof(ctx, cg.eppProvs[m], eproofin)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("computing proof: %w", err)
|
||||
}
|
||||
|
||||
return eproof, tick, nil
|
||||
return entries, eproof, &types.Ticket{VRFProof: vrfout}, nil
|
||||
}
|
||||
|
||||
type MinedTipSet struct {
|
||||
@@ -292,20 +361,26 @@ func (cg *ChainGen) NextTipSet() (*MinedTipSet, error) {
|
||||
func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Address) (*MinedTipSet, error) {
|
||||
var blks []*types.FullBlock
|
||||
|
||||
msgs, err := cg.getRandomMessages()
|
||||
msgs, err := cg.GetMessages(cg)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get random messages: %w", err)
|
||||
}
|
||||
|
||||
for round := int64(base.Height() + 1); len(blks) == 0; round++ {
|
||||
for round := base.Height() + 1; len(blks) == 0; round++ {
|
||||
for _, m := range miners {
|
||||
proof, t, err := cg.nextBlockProof(context.TODO(), base, m, round)
|
||||
bvals, et, ticket, err := cg.nextBlockProof(context.TODO(), base, m, round)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("next block proof: %w", err)
|
||||
}
|
||||
|
||||
if proof != nil {
|
||||
fblk, err := cg.makeBlock(base, m, proof, t, uint64(round), msgs)
|
||||
if et != nil {
|
||||
// TODO: maybe think about passing in more real parameters to this?
|
||||
wpost, err := cg.eppProvs[m].ComputeProof(context.TODO(), nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fblk, err := cg.makeBlock(base, m, ticket, et, bvals, round, wpost, msgs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("making a block for next tipset failed: %w", err)
|
||||
}
|
||||
@@ -327,16 +402,28 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof *types.EPostProof, ticket *types.Ticket, height uint64, msgs []*types.SignedMessage) (*types.FullBlock, error) {
|
||||
func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, vrfticket *types.Ticket,
|
||||
eticket *types.ElectionProof, bvals []types.BeaconEntry, height abi.ChainEpoch,
|
||||
wpost []abi.PoStProof, msgs []*types.SignedMessage) (*types.FullBlock, error) {
|
||||
|
||||
var ts uint64
|
||||
if cg.Timestamper != nil {
|
||||
ts = cg.Timestamper(parents, height-parents.Height())
|
||||
} else {
|
||||
ts = parents.MinTimestamp() + ((height - parents.Height()) * build.BlockDelay)
|
||||
ts = parents.MinTimestamp() + uint64((height-parents.Height())*build.BlockDelay)
|
||||
}
|
||||
|
||||
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, ticket, eproof, msgs, height, ts)
|
||||
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, &api.BlockTemplate{
|
||||
Miner: m,
|
||||
Parents: parents.Key(),
|
||||
Ticket: vrfticket,
|
||||
Eproof: eticket,
|
||||
BeaconValues: bvals,
|
||||
Messages: msgs,
|
||||
Epoch: height,
|
||||
Timestamp: ts,
|
||||
WinningPoStProof: wpost,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -344,7 +431,7 @@ func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof *
|
||||
return fblk, err
|
||||
}
|
||||
|
||||
// This function is awkward. It's used to deal with messages made when
|
||||
// ResyncBankerNonce is used for dealing with messages made when
|
||||
// simulating forks
|
||||
func (cg *ChainGen) ResyncBankerNonce(ts *types.TipSet) error {
|
||||
act, err := cg.sm.GetActor(cg.banker, ts)
|
||||
@@ -356,7 +443,15 @@ func (cg *ChainGen) ResyncBankerNonce(ts *types.TipSet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cg *ChainGen) getRandomMessages() ([]*types.SignedMessage, error) {
|
||||
func (cg *ChainGen) Banker() address.Address {
|
||||
return cg.banker
|
||||
}
|
||||
|
||||
func (cg *ChainGen) Wallet() *wallet.Wallet {
|
||||
return cg.w
|
||||
}
|
||||
|
||||
func getRandomMessages(cg *ChainGen) ([]*types.SignedMessage, error) {
|
||||
msgs := make([]*types.SignedMessage, cg.msgsPerBlock)
|
||||
for m := range msgs {
|
||||
msg := types.Message{
|
||||
@@ -369,7 +464,7 @@ func (cg *ChainGen) getRandomMessages() ([]*types.SignedMessage, error) {
|
||||
|
||||
Method: 0,
|
||||
|
||||
GasLimit: types.NewInt(10000),
|
||||
GasLimit: 10000,
|
||||
GasPrice: types.NewInt(0),
|
||||
}
|
||||
|
||||
@@ -395,240 +490,127 @@ func (cg *ChainGen) YieldRepo() (repo.Repo, error) {
|
||||
}
|
||||
|
||||
type MiningCheckAPI interface {
|
||||
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
|
||||
ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error)
|
||||
|
||||
StateMinerPower(context.Context, address.Address, *types.TipSet) (api.MinerPower, error)
|
||||
MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error)
|
||||
|
||||
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
|
||||
|
||||
StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error)
|
||||
|
||||
StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error)
|
||||
|
||||
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
WalletSign(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
}
|
||||
|
||||
type mca struct {
|
||||
w *wallet.Wallet
|
||||
sm *stmgr.StateManager
|
||||
w *wallet.Wallet
|
||||
sm *stmgr.StateManager
|
||||
pv ffiwrapper.Verifier
|
||||
bcn beacon.RandomBeacon
|
||||
}
|
||||
|
||||
func (mca mca) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, lb int64) ([]byte, error) {
|
||||
return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), int64(lb))
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) {
|
||||
mpow, tpow, err := stmgr.GetPower(ctx, mca.sm, ts, maddr)
|
||||
func (mca mca) ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) {
|
||||
pts, err := mca.sm.ChainStore().LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return api.MinerPower{}, err
|
||||
return nil, xerrors.Errorf("loading tipset key: %w", err)
|
||||
}
|
||||
|
||||
return api.MinerPower{
|
||||
MinerPower: mpow,
|
||||
TotalPower: tpow,
|
||||
}, err
|
||||
return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), personalization, randEpoch, entropy)
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerWorker(ctx context.Context, maddr address.Address, ts *types.TipSet) (address.Address, error) {
|
||||
return stmgr.GetMinerWorkerRaw(ctx, mca.sm, ts.ParentState(), maddr)
|
||||
func (mca mca) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) {
|
||||
return stmgr.MinerGetBaseInfo(ctx, mca.sm, mca.bcn, tsk, epoch, maddr, mca.pv)
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerSectorSize(ctx context.Context, maddr address.Address, ts *types.TipSet) (uint64, error) {
|
||||
return stmgr.GetMinerSectorSize(ctx, mca.sm, ts, maddr)
|
||||
}
|
||||
|
||||
func (mca mca) StateMinerProvingSet(ctx context.Context, maddr address.Address, ts *types.TipSet) ([]*api.ChainSectorInfo, error) {
|
||||
return stmgr.GetMinerProvingSet(ctx, mca.sm, ts, maddr)
|
||||
}
|
||||
|
||||
func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*types.Signature, error) {
|
||||
func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*crypto.Signature, error) {
|
||||
return mca.w.Sign(ctx, a, v)
|
||||
}
|
||||
|
||||
type ElectionPoStProver interface {
|
||||
GenerateCandidates(context.Context, sectorbuilder.SortedPublicSectorInfo, []byte) ([]sectorbuilder.EPostCandidate, error)
|
||||
ComputeProof(context.Context, sectorbuilder.SortedPublicSectorInfo, []byte, []sectorbuilder.EPostCandidate) ([]byte, error)
|
||||
type WinningPoStProver interface {
|
||||
GenerateCandidates(context.Context, abi.PoStRandomness, uint64) ([]uint64, error)
|
||||
ComputeProof(context.Context, []abi.SectorInfo, abi.PoStRandomness) ([]abi.PoStProof, error)
|
||||
}
|
||||
|
||||
type eppProvider struct{}
|
||||
type wppProvider struct{}
|
||||
|
||||
func (epp *eppProvider) GenerateCandidates(ctx context.Context, _ sectorbuilder.SortedPublicSectorInfo, eprand []byte) ([]sectorbuilder.EPostCandidate, error) {
|
||||
return []sectorbuilder.EPostCandidate{
|
||||
{
|
||||
SectorID: 1,
|
||||
PartialTicket: [32]byte{},
|
||||
Ticket: [32]byte{},
|
||||
SectorChallengeIndex: 1,
|
||||
},
|
||||
}, nil
|
||||
func (wpp *wppProvider) GenerateCandidates(ctx context.Context, _ abi.PoStRandomness, _ uint64) ([]uint64, error) {
|
||||
return []uint64{0}, nil
|
||||
}
|
||||
|
||||
func (epp *eppProvider) ComputeProof(ctx context.Context, _ sectorbuilder.SortedPublicSectorInfo, eprand []byte, winners []sectorbuilder.EPostCandidate) ([]byte, error) {
|
||||
|
||||
return []byte("valid proof"), nil
|
||||
func (wpp *wppProvider) ComputeProof(context.Context, []abi.SectorInfo, abi.PoStRandomness) ([]abi.PoStProof, error) {
|
||||
return []abi.PoStProof{{
|
||||
ProofBytes: []byte("valid proof"),
|
||||
}}, nil
|
||||
}
|
||||
|
||||
type ProofInput struct {
|
||||
sectors sectorbuilder.SortedPublicSectorInfo
|
||||
hvrf []byte
|
||||
winners []sectorbuilder.EPostCandidate
|
||||
vrfout []byte
|
||||
}
|
||||
func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch,
|
||||
miner address.Address, brand types.BeaconEntry, mbi *api.MiningBaseInfo, a MiningCheckAPI) (*types.ElectionProof, error) {
|
||||
|
||||
func IsRoundWinner(ctx context.Context, ts *types.TipSet, round int64, miner address.Address, epp ElectionPoStProver, a MiningCheckAPI) (*ProofInput, error) {
|
||||
r, err := a.ChainGetRandomness(ctx, ts.Key(), round-build.EcRandomnessLookback)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("chain get randomness: %w", err)
|
||||
buf := new(bytes.Buffer)
|
||||
if err := miner.MarshalCBOR(buf); err != nil {
|
||||
return nil, xerrors.Errorf("failed to cbor marshal address: %w")
|
||||
}
|
||||
|
||||
mworker, err := a.StateMinerWorker(ctx, miner, ts)
|
||||
electionRand, err := store.DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get miner worker: %w", err)
|
||||
return nil, xerrors.Errorf("failed to draw randomness: %w", err)
|
||||
}
|
||||
|
||||
vrfout, err := ComputeVRF(ctx, a.WalletSign, mworker, miner, DSepElectionPost, r)
|
||||
vrfout, err := ComputeVRF(ctx, a.WalletSign, mbi.WorkerKey, electionRand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to compute VRF: %w", err)
|
||||
}
|
||||
|
||||
pset, err := a.StateMinerProvingSet(ctx, miner, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load proving set for miner: %w", err)
|
||||
}
|
||||
if len(pset) == 0 {
|
||||
// TODO: wire in real power
|
||||
if !types.IsTicketWinner(vrfout, mbi.MinerPower, mbi.NetworkPower) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var sinfos []ffi.PublicSectorInfo
|
||||
for _, s := range pset {
|
||||
var commRa [32]byte
|
||||
copy(commRa[:], s.CommR)
|
||||
sinfos = append(sinfos, ffi.PublicSectorInfo{
|
||||
SectorID: s.SectorID,
|
||||
CommR: commRa,
|
||||
})
|
||||
}
|
||||
sectors := sectorbuilder.NewSortedPublicSectorInfo(sinfos)
|
||||
|
||||
hvrf := sha256.Sum256(vrfout)
|
||||
candidates, err := epp.GenerateCandidates(ctx, sectors, hvrf[:])
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to generate electionPoSt candidates: %w", err)
|
||||
}
|
||||
|
||||
pow, err := a.StateMinerPower(ctx, miner, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to check power: %w", err)
|
||||
}
|
||||
|
||||
ssize, err := a.StateMinerSectorSize(ctx, miner, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to look up miners sector size: %w", err)
|
||||
}
|
||||
|
||||
var winners []sectorbuilder.EPostCandidate
|
||||
for _, c := range candidates {
|
||||
if types.IsTicketWinner(c.PartialTicket[:], ssize, uint64(len(sinfos)), pow.TotalPower) {
|
||||
winners = append(winners, c)
|
||||
}
|
||||
}
|
||||
|
||||
// no winners, sad
|
||||
if len(winners) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &ProofInput{
|
||||
sectors: sectors,
|
||||
hvrf: hvrf[:],
|
||||
winners: winners,
|
||||
vrfout: vrfout,
|
||||
}, nil
|
||||
return &types.ElectionProof{VRFProof: vrfout}, nil
|
||||
}
|
||||
|
||||
func ComputeProof(ctx context.Context, epp ElectionPoStProver, pi *ProofInput) (*types.EPostProof, error) {
|
||||
proof, err := epp.ComputeProof(ctx, pi.sectors, pi.hvrf, pi.winners)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to compute snark for election proof: %w", err)
|
||||
}
|
||||
type SignFunc func(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
|
||||
ept := types.EPostProof{
|
||||
Proof: proof,
|
||||
PostRand: pi.vrfout,
|
||||
}
|
||||
for _, win := range pi.winners {
|
||||
part := make([]byte, 32)
|
||||
copy(part, win.PartialTicket[:])
|
||||
ept.Candidates = append(ept.Candidates, types.EPostTicket{
|
||||
Partial: part,
|
||||
SectorID: win.SectorID,
|
||||
ChallengeIndex: win.SectorChallengeIndex,
|
||||
})
|
||||
}
|
||||
|
||||
return &ept, nil
|
||||
}
|
||||
|
||||
type SignFunc func(context.Context, address.Address, []byte) (*types.Signature, error)
|
||||
|
||||
const (
|
||||
DSepTicket = 1
|
||||
DSepElectionPost = 2
|
||||
)
|
||||
|
||||
func hashVRFBase(personalization uint64, miner address.Address, input []byte) ([]byte, error) {
|
||||
if miner.Protocol() != address.ID {
|
||||
return nil, xerrors.Errorf("miner address for compute VRF must be an ID address")
|
||||
}
|
||||
|
||||
var persbuf [8]byte
|
||||
binary.LittleEndian.PutUint64(persbuf[:], personalization)
|
||||
|
||||
h := sha256.New()
|
||||
h.Write(persbuf[:])
|
||||
h.Write([]byte{0})
|
||||
h.Write(input)
|
||||
h.Write([]byte{0})
|
||||
h.Write(miner.Bytes())
|
||||
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
func VerifyVRF(ctx context.Context, worker, miner address.Address, p uint64, input, vrfproof []byte) error {
|
||||
func VerifyVRF(ctx context.Context, worker address.Address, vrfBase, vrfproof []byte) error {
|
||||
_, span := trace.StartSpan(ctx, "VerifyVRF")
|
||||
defer span.End()
|
||||
|
||||
vrfBase, err := hashVRFBase(p, miner, input)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("computing vrf base failed: %w", err)
|
||||
}
|
||||
|
||||
sig := &types.Signature{
|
||||
Type: types.KTBLS,
|
||||
sig := &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: vrfproof,
|
||||
}
|
||||
|
||||
if err := sig.Verify(worker, vrfBase); err != nil {
|
||||
if err := sigs.Verify(sig, worker, vrfBase); err != nil {
|
||||
return xerrors.Errorf("vrf was invalid: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ComputeVRF(ctx context.Context, sign SignFunc, worker, miner address.Address, p uint64, input []byte) ([]byte, error) {
|
||||
sigInput, err := hashVRFBase(p, miner, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func ComputeVRF(ctx context.Context, sign SignFunc, worker address.Address, sigInput []byte) ([]byte, error) {
|
||||
sig, err := sign(ctx, worker, sigInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sig.Type != types.KTBLS {
|
||||
if sig.Type != crypto.SigTypeBLS {
|
||||
return nil, fmt.Errorf("miner worker address was not a BLS key")
|
||||
}
|
||||
|
||||
return sig.Data, nil
|
||||
}
|
||||
|
||||
type genFakeVerifier struct{}
|
||||
|
||||
var _ ffiwrapper.Verifier = (*genFakeVerifier)(nil)
|
||||
|
||||
func (m genFakeVerifier) VerifySeal(svi abi.SealVerifyInfo) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (m genFakeVerifier) VerifyWinningPoSt(ctx context.Context, info abi.WinningPoStVerifyInfo) (bool, error) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (m genFakeVerifier) VerifyWindowPoSt(ctx context.Context, info abi.WindowPoStVerifyInfo) (bool, error) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (m genFakeVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, proof abi.RegisteredProof, id abi.ActorID, randomness abi.PoStRandomness, u uint64) ([]uint64, error) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
+23
-12
@@ -3,18 +3,28 @@ package gen
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
build.SectorSizes = []uint64{1024}
|
||||
build.MinimumMinerPower = 1024
|
||||
miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
|
||||
abi.RegisteredProof_StackedDRG2KiBSeal: {},
|
||||
}
|
||||
power.ConsensusMinerMinPower = big.NewInt(2048)
|
||||
verifreg.MinVerifiedDealSize = big.NewInt(256)
|
||||
}
|
||||
|
||||
func testGeneration(t testing.TB, n int, msgs int) {
|
||||
g, err := NewGenerator()
|
||||
func testGeneration(t testing.TB, n int, msgs int, sectors int) {
|
||||
g, err := NewGeneratorWithSectors(sectors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("%+v", err)
|
||||
}
|
||||
|
||||
g.msgsPerBlock = msgs
|
||||
@@ -22,30 +32,31 @@ func testGeneration(t testing.TB, n int, msgs int) {
|
||||
for i := 0; i < n; i++ {
|
||||
mts, err := g.NextTipSet()
|
||||
if err != nil {
|
||||
t.Fatalf("error at H:%d, %s", i, err)
|
||||
t.Fatalf("error at H:%d, %+v", i, err)
|
||||
}
|
||||
_ = mts
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainGeneration(t *testing.T) {
|
||||
testGeneration(t, 10, 20)
|
||||
testGeneration(t, 10, 20, 1)
|
||||
testGeneration(t, 10, 20, 25)
|
||||
}
|
||||
|
||||
func BenchmarkChainGeneration(b *testing.B) {
|
||||
b.Run("0-messages", func(b *testing.B) {
|
||||
testGeneration(b, b.N, 0)
|
||||
testGeneration(b, b.N, 0, 1)
|
||||
})
|
||||
|
||||
b.Run("10-messages", func(b *testing.B) {
|
||||
testGeneration(b, b.N, 10)
|
||||
testGeneration(b, b.N, 10, 1)
|
||||
})
|
||||
|
||||
b.Run("100-messages", func(b *testing.B) {
|
||||
testGeneration(b, b.N, 100)
|
||||
testGeneration(b, b.N, 100, 1)
|
||||
})
|
||||
|
||||
b.Run("1000-messages", func(b *testing.B) {
|
||||
testGeneration(b, b.N, 1000)
|
||||
testGeneration(b, b.N, 1000, 1)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,370 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/filecoin-project/go-amt-ipld/v2"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/account"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
)
|
||||
|
||||
const AccountStart = 100
|
||||
const MinerStart = 1000
|
||||
const MaxAccounts = MinerStart - AccountStart
|
||||
|
||||
var log = logging.Logger("genesis")
|
||||
|
||||
type GenesisBootstrap struct {
|
||||
Genesis *types.BlockHeader
|
||||
}
|
||||
|
||||
/*
|
||||
From a list of parameters, create a genesis block / initial state
|
||||
|
||||
The process:
|
||||
- Bootstrap state (MakeInitialStateTree)
|
||||
- Create empty state
|
||||
- Create system actor
|
||||
- Make init actor
|
||||
- Create accounts mappings
|
||||
- Set NextID to MinerStart
|
||||
- Setup Reward (1.4B fil)
|
||||
- Setup Cron
|
||||
- Create empty power actor
|
||||
- Create empty market
|
||||
- Create verified registry
|
||||
- Setup burnt fund address
|
||||
- Initialize account / msig balances
|
||||
- Instantiate early vm with genesis syscalls
|
||||
- Create miners
|
||||
- Each:
|
||||
- power.CreateMiner, set msg value to PowerBalance
|
||||
- market.AddFunds with correct value
|
||||
- market.PublishDeals for related sectors
|
||||
- Set precommits
|
||||
- Commit presealed sectors
|
||||
|
||||
Data Types:
|
||||
|
||||
PreSeal :{
|
||||
CommR CID
|
||||
CommD CID
|
||||
SectorID SectorNumber
|
||||
Deal market.DealProposal # Start at 0, self-deal!
|
||||
}
|
||||
|
||||
Genesis: {
|
||||
Accounts: [ # non-miner, non-singleton actors, max len = MaxAccounts
|
||||
{
|
||||
Type: "account" / "multisig",
|
||||
Value: "attofil",
|
||||
[Meta: {msig settings, account key..}]
|
||||
},...
|
||||
],
|
||||
Miners: [
|
||||
{
|
||||
Owner, Worker Addr # ID
|
||||
MarketBalance, PowerBalance TokenAmount
|
||||
SectorSize uint64
|
||||
PreSeals []PreSeal
|
||||
},...
|
||||
],
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template genesis.Template) (*state.StateTree, error) {
|
||||
// Create empty state tree
|
||||
|
||||
cst := cbor.NewCborStore(bs)
|
||||
_, err := cst.Put(context.TODO(), []struct{}{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("putting empty object: %w", err)
|
||||
}
|
||||
|
||||
state, err := state.NewStateTree(cst)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("making new state tree: %w", err)
|
||||
}
|
||||
|
||||
emptyobject, err := cst.Put(context.TODO(), []struct{}{})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed putting empty object: %w", err)
|
||||
}
|
||||
|
||||
// Create system actor
|
||||
|
||||
sysact, err := SetupSystemActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup init actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.SystemActorAddr, sysact); err != nil {
|
||||
return nil, xerrors.Errorf("set init actor: %w", err)
|
||||
}
|
||||
|
||||
// Create init actor
|
||||
|
||||
initact, err := SetupInitActor(bs, template.NetworkName, template.Accounts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup init actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.InitActorAddr, initact); err != nil {
|
||||
return nil, xerrors.Errorf("set init actor: %w", err)
|
||||
}
|
||||
|
||||
// Setup reward
|
||||
rewact, err := SetupRewardActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup init actor: %w", err)
|
||||
}
|
||||
|
||||
err = state.SetActor(builtin.RewardActorAddr, rewact)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("set network account actor: %w", err)
|
||||
}
|
||||
|
||||
// Setup cron
|
||||
cronact, err := SetupCronActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup cron actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.CronActorAddr, cronact); err != nil {
|
||||
return nil, xerrors.Errorf("set cron actor: %w", err)
|
||||
}
|
||||
|
||||
// Create empty power actor
|
||||
spact, err := SetupStoragePowerActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.StoragePowerActorAddr, spact); err != nil {
|
||||
return nil, xerrors.Errorf("set storage market actor: %w", err)
|
||||
}
|
||||
|
||||
// Create empty market actor
|
||||
marketact, err := SetupStorageMarketActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.StorageMarketActorAddr, marketact); err != nil {
|
||||
return nil, xerrors.Errorf("set market actor: %w", err)
|
||||
}
|
||||
|
||||
// Create verified registry
|
||||
verifact, err := SetupVerifiedRegistryActor(bs)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage market actor: %w", err)
|
||||
}
|
||||
if err := state.SetActor(builtin.VerifiedRegistryActorAddr, verifact); err != nil {
|
||||
return nil, xerrors.Errorf("set market actor: %w", err)
|
||||
}
|
||||
|
||||
// Setup burnt-funds
|
||||
err = state.SetActor(builtin.BurntFundsActorAddr, &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Balance: types.NewInt(0),
|
||||
Head: emptyobject,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("set burnt funds account actor: %w", err)
|
||||
}
|
||||
|
||||
// Create accounts
|
||||
for id, info := range template.Accounts {
|
||||
if info.Type != genesis.TAccount {
|
||||
return nil, xerrors.New("unsupported account type") // TODO: msigs
|
||||
}
|
||||
|
||||
ida, err := address.NewIDAddress(uint64(AccountStart + id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ainfo genesis.AccountMeta
|
||||
if err := json.Unmarshal(info.Meta, &ainfo); err != nil {
|
||||
return nil, xerrors.Errorf("unmarshaling account meta: %w", err)
|
||||
}
|
||||
|
||||
st, err := cst.Put(ctx, &account.State{Address: ainfo.Owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = state.SetActor(ida, &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Balance: info.Balance,
|
||||
Head: st,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setting account from actmap: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
vregroot, err := address.NewIDAddress(80)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vrst, err := cst.Put(ctx, &account.State{Address: RootVerifierAddr})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = state.SetActor(vregroot, &types.Actor{
|
||||
Code: builtin.AccountActorCodeID,
|
||||
Balance: types.NewInt(0),
|
||||
Head: vrst,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setting account from actmap: %w", err)
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot cid.Cid, template genesis.Template) (cid.Cid, error) {
|
||||
verifNeeds := make(map[address.Address]abi.PaddedPieceSize)
|
||||
var sum abi.PaddedPieceSize
|
||||
for _, m := range template.Miners {
|
||||
for _, s := range m.Sectors {
|
||||
amt := s.Deal.PieceSize
|
||||
verifNeeds[s.Deal.Client] += amt
|
||||
sum += amt
|
||||
}
|
||||
}
|
||||
|
||||
verifier, err := address.NewIDAddress(80)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
vm, err := vm.NewVM(stateroot, 0, &fakeRand{}, cs.Blockstore(), &fakedSigSyscalls{cs.VMSys()})
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err)
|
||||
}
|
||||
|
||||
_, err = doExecValue(ctx, vm, builtin.VerifiedRegistryActorAddr, RootVerifierAddr, types.NewInt(0), builtin.MethodsVerifiedRegistry.AddVerifier, mustEnc(&verifreg.AddVerifierParams{
|
||||
Address: verifier,
|
||||
Allowance: abi.NewStoragePower(int64(sum)), // eh, close enough
|
||||
|
||||
}))
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to failed to create verifier: %w", err)
|
||||
}
|
||||
|
||||
for c, amt := range verifNeeds {
|
||||
_, err := doExecValue(ctx, vm, builtin.VerifiedRegistryActorAddr, verifier, types.NewInt(0), builtin.MethodsVerifiedRegistry.AddVerifiedClient, mustEnc(&verifreg.AddVerifiedClientParams{
|
||||
Address: c,
|
||||
Allowance: abi.NewStoragePower(int64(amt)),
|
||||
}))
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to add verified client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return vm.Flush(ctx)
|
||||
}
|
||||
|
||||
func MakeGenesisBlock(ctx context.Context, bs bstore.Blockstore, sys runtime.Syscalls, template genesis.Template) (*GenesisBootstrap, error) {
|
||||
st, err := MakeInitialStateTree(ctx, bs, template)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("make initial state tree failed: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err := st.Flush(ctx)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("flush state tree failed: %w", err)
|
||||
}
|
||||
|
||||
// temp chainstore
|
||||
cs := store.NewChainStore(bs, datastore.NewMapDatastore(), sys)
|
||||
|
||||
// Verify PreSealed Data
|
||||
stateroot, err = VerifyPreSealedData(ctx, cs, stateroot, template)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to verify presealed data: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err = SetupStorageMiners(ctx, cs, stateroot, template.Miners)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup storage miners failed: %w", err)
|
||||
}
|
||||
|
||||
cst := cbor.NewCborStore(bs)
|
||||
|
||||
emptyroot, err := amt.FromArray(ctx, cst, nil)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("amt build failed: %w", err)
|
||||
}
|
||||
|
||||
mm := &types.MsgMeta{
|
||||
BlsMessages: emptyroot,
|
||||
SecpkMessages: emptyroot,
|
||||
}
|
||||
mmb, err := mm.ToStorageBlock()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("serializing msgmeta failed: %w", err)
|
||||
}
|
||||
if err := bs.Put(mmb); err != nil {
|
||||
return nil, xerrors.Errorf("putting msgmeta block to blockstore: %w", err)
|
||||
}
|
||||
|
||||
log.Infof("Empty Genesis root: %s", emptyroot)
|
||||
|
||||
genesisticket := &types.Ticket{
|
||||
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
|
||||
}
|
||||
|
||||
b := &types.BlockHeader{
|
||||
Miner: builtin.SystemActorAddr,
|
||||
Ticket: genesisticket,
|
||||
Parents: []cid.Cid{},
|
||||
Height: 0,
|
||||
ParentWeight: types.NewInt(0),
|
||||
ParentStateRoot: stateroot,
|
||||
Messages: mmb.Cid(),
|
||||
ParentMessageReceipts: emptyroot,
|
||||
BLSAggregate: nil,
|
||||
BlockSig: nil,
|
||||
Timestamp: template.Timestamp,
|
||||
ElectionProof: new(types.ElectionProof),
|
||||
BeaconEntries: []types.BeaconEntry{
|
||||
{
|
||||
Round: 0,
|
||||
Data: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sb, err := b.ToStorageBlock()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("serializing block header failed: %w", err)
|
||||
}
|
||||
|
||||
if err := bs.Put(sb); err != nil {
|
||||
return nil, xerrors.Errorf("putting header to blockstore: %w", err)
|
||||
}
|
||||
|
||||
return &GenesisBootstrap{
|
||||
Genesis: b,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
)
|
||||
|
||||
func MinerAddress(genesisIndex uint64) address.Address {
|
||||
maddr, err := address.NewIDAddress(MinerStart + genesisIndex)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return maddr
|
||||
}
|
||||
|
||||
type fakedSigSyscalls struct {
|
||||
runtime.Syscalls
|
||||
}
|
||||
|
||||
func (fss *fakedSigSyscalls) VerifySignature(signature crypto.Signature, signer address.Address, plaintext []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, miners []genesis.Miner) (cid.Cid, error) {
|
||||
vm, err := vm.NewVM(sroot, 0, &fakeRand{}, cs.Blockstore(), &fakedSigSyscalls{cs.VMSys()})
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err)
|
||||
}
|
||||
|
||||
if len(miners) == 0 {
|
||||
return cid.Undef, xerrors.New("no genesis miners")
|
||||
}
|
||||
|
||||
for i, m := range miners {
|
||||
// Create miner through power actor
|
||||
i := i
|
||||
m := m
|
||||
|
||||
spt, err := ffiwrapper.SealProofTypeFromSectorSize(m.SectorSize)
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
var maddr address.Address
|
||||
{
|
||||
constructorParams := &power.CreateMinerParams{
|
||||
Owner: m.Worker,
|
||||
Worker: m.Worker,
|
||||
Peer: []byte(m.PeerId),
|
||||
SealProofType: spt,
|
||||
}
|
||||
|
||||
params := mustEnc(constructorParams)
|
||||
rval, err := doExecValue(ctx, vm, builtin.StoragePowerActorAddr, m.Owner, m.PowerBalance, builtin.MethodsPower.CreateMiner, params)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
|
||||
var ma power.CreateMinerReturn
|
||||
if err := ma.UnmarshalCBOR(bytes.NewReader(rval)); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("unmarshaling CreateMinerReturn: %w", err)
|
||||
}
|
||||
|
||||
expma := MinerAddress(uint64(i))
|
||||
if ma.IDAddress != expma {
|
||||
return cid.Undef, xerrors.Errorf("miner assigned wrong address: %s != %s", ma.IDAddress, expma)
|
||||
}
|
||||
maddr = ma.IDAddress
|
||||
}
|
||||
|
||||
// Add market funds
|
||||
|
||||
{
|
||||
params := mustEnc(&maddr)
|
||||
_, err := doExecValue(ctx, vm, builtin.StorageMarketActorAddr, m.Worker, m.MarketBalance, builtin.MethodsMarket.AddBalance, params)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
params := mustEnc(&m.Worker)
|
||||
_, err := doExecValue(ctx, vm, builtin.StorageMarketActorAddr, m.Worker, big.Zero(), builtin.MethodsMarket.AddBalance, params)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Publish preseal deals
|
||||
|
||||
var dealIDs []abi.DealID
|
||||
{
|
||||
publish := func(params *market.PublishStorageDealsParams) error {
|
||||
fmt.Printf("publishing %d storage deals on miner %s with worker %s\n", len(params.Deals), params.Deals[0].Proposal.Provider, m.Worker)
|
||||
|
||||
ret, err := doExecValue(ctx, vm, builtin.StorageMarketActorAddr, m.Worker, big.Zero(), builtin.MethodsMarket.PublishStorageDeals, mustEnc(params))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create genesis miner: %w", err)
|
||||
}
|
||||
var ids market.PublishStorageDealsReturn
|
||||
if err := ids.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
||||
return xerrors.Errorf("unmarsahling publishStorageDeals result: %w", err)
|
||||
}
|
||||
|
||||
dealIDs = append(dealIDs, ids.IDs...)
|
||||
return nil
|
||||
}
|
||||
|
||||
params := &market.PublishStorageDealsParams{}
|
||||
for _, preseal := range m.Sectors {
|
||||
preseal.Deal.VerifiedDeal = true
|
||||
params.Deals = append(params.Deals, market.ClientDealProposal{
|
||||
Proposal: preseal.Deal,
|
||||
ClientSignature: crypto.Signature{Type: crypto.SigTypeBLS}, // TODO: do we want to sign these? Or do we want to fake signatures for genesis setup?
|
||||
})
|
||||
|
||||
if len(params.Deals) == cbg.MaxLength {
|
||||
if err := publish(params); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
params = &market.PublishStorageDealsParams{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(params.Deals) > 0 {
|
||||
if err := publish(params); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Commit sectors
|
||||
for pi, preseal := range m.Sectors {
|
||||
preseal := preseal
|
||||
// TODO: Maybe check seal (Can just be snark inputs, doesn't go into the genesis file)
|
||||
|
||||
// check deals, get dealWeight
|
||||
var dealWeight market.VerifyDealsOnSectorProveCommitReturn
|
||||
{
|
||||
params := &market.VerifyDealsOnSectorProveCommitParams{
|
||||
DealIDs: []abi.DealID{dealIDs[pi]},
|
||||
SectorExpiry: preseal.Deal.EndEpoch,
|
||||
}
|
||||
|
||||
ret, err := doExecValue(ctx, vm, builtin.StorageMarketActorAddr, maddr, big.Zero(), builtin.MethodsMarket.VerifyDealsOnSectorProveCommit, mustEnc(params))
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to verify preseal deals miner: %w", err)
|
||||
}
|
||||
if err := dealWeight.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
||||
return cid.Undef, xerrors.Errorf("unmarshaling market onProveCommit result: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// update power claims
|
||||
{
|
||||
err = vm.MutateState(ctx, builtin.StoragePowerActorAddr, func(cst cbor.IpldStore, st *power.State) error {
|
||||
weight := &power.SectorStorageWeightDesc{
|
||||
SectorSize: m.SectorSize,
|
||||
Duration: preseal.Deal.Duration(),
|
||||
DealWeight: dealWeight.DealWeight,
|
||||
VerifiedDealWeight: dealWeight.VerifiedDealWeight,
|
||||
}
|
||||
|
||||
qapower := power.QAPowerForWeight(weight)
|
||||
|
||||
err := st.AddToClaim(&state.AdtStore{cst}, maddr, types.NewInt(uint64(weight.SectorSize)), qapower)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("add to claim: %w", err)
|
||||
}
|
||||
fmt.Println("Added weight to claim: ", st.TotalRawBytePower, st.TotalQualityAdjPower)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("register power claim in power actor: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Put sectors to miner sector sets
|
||||
{
|
||||
newSectorInfo := &miner.SectorOnChainInfo{
|
||||
Info: miner.SectorPreCommitInfo{
|
||||
RegisteredProof: preseal.ProofType,
|
||||
SectorNumber: preseal.SectorID,
|
||||
SealedCID: preseal.CommR,
|
||||
SealRandEpoch: 0,
|
||||
DealIDs: []abi.DealID{dealIDs[pi]},
|
||||
Expiration: preseal.Deal.EndEpoch,
|
||||
},
|
||||
ActivationEpoch: 0,
|
||||
DealWeight: dealWeight.DealWeight,
|
||||
VerifiedDealWeight: dealWeight.VerifiedDealWeight,
|
||||
}
|
||||
|
||||
err = vm.MutateState(ctx, maddr, func(cst cbor.IpldStore, st *miner.State) error {
|
||||
store := &state.AdtStore{cst}
|
||||
|
||||
if err = st.PutSector(store, newSectorInfo); err != nil {
|
||||
return xerrors.Errorf("failed to put sector: %v", err)
|
||||
}
|
||||
|
||||
if err := st.AddNewSectors(newSectorInfo.Info.SectorNumber); err != nil {
|
||||
return xerrors.Errorf("failed to add NewSector: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return cid.Cid{}, xerrors.Errorf("put to sset: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: to avoid division by zero, we set the initial power actor power to 1, this adjusts that back down so the accounting is accurate.
|
||||
err = vm.MutateState(ctx, builtin.StoragePowerActorAddr, func(cst cbor.IpldStore, st *power.State) error {
|
||||
st.TotalQualityAdjPower = big.Sub(st.TotalQualityAdjPower, big.NewInt(1))
|
||||
return nil
|
||||
})
|
||||
|
||||
c, err := vm.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("flushing vm: %w", err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// TODO: copied from actors test harness, deduplicate or remove from here
|
||||
type fakeRand struct{}
|
||||
|
||||
func (fr *fakeRand) GetRandomness(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||
out := make([]byte, 32)
|
||||
_, _ = rand.New(rand.NewSource(int64(randEpoch))).Read(out)
|
||||
return out, nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/system"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
bstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
)
|
||||
|
||||
func SetupSystemActor(bs bstore.Blockstore) (*types.Actor, error) {
|
||||
var st system.State
|
||||
|
||||
cst := cbor.NewCborStore(bs)
|
||||
|
||||
statecid, err := cst.Put(context.TODO(), &st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
act := &types.Actor{
|
||||
Code: builtin.SystemActorCodeID,
|
||||
Head: statecid,
|
||||
}
|
||||
|
||||
return act, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user