Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0ddb10deb | ||
|
|
bb244f2e0b | ||
|
|
7c015840a6 | ||
|
|
21148c90db | ||
|
|
67864334a3 | ||
|
|
2c8357beb0 | ||
|
|
08cec487cf | ||
|
|
6b9aeca71d | ||
|
|
153470c3d2 | ||
|
|
f77dc42ef0 | ||
|
|
490f70f071 | ||
|
|
0c1e29390a | ||
|
|
bf9d0bca40 | ||
|
|
8e45d27a06 | ||
|
|
e996c27619 | ||
|
|
3c65595406 | ||
|
|
79e141e71d | ||
|
|
eb2bbfebe3 | ||
|
|
45dc9697bf | ||
|
|
3a1fc8e7da | ||
|
|
438d5ce78e | ||
|
|
90827afa47 | ||
|
|
e786a0507e | ||
|
|
cd08788792 | ||
|
|
143b6d7a37 | ||
|
|
5db02ca059 | ||
|
|
d8a5812f76 | ||
|
|
daf0b57528 | ||
|
|
b7c6143cef | ||
|
|
6907797783 | ||
|
|
aa09b799b3 | ||
|
|
f751291f1a | ||
|
|
b7f771b258 | ||
|
|
93e1c80569 | ||
|
|
dfc039276d | ||
|
|
8b9e9fede4 | ||
|
|
186c4990dd | ||
|
|
e4044151f0 | ||
|
|
b089cc4fc3 | ||
|
|
a63bc10d26 | ||
|
|
b093d8a6e4 | ||
|
|
567d0546ec | ||
|
|
a75d67a42c | ||
|
|
4f5fa3be3c | ||
|
|
0216697b98 | ||
|
|
768ecbc397 | ||
|
|
785a54692b | ||
|
|
41b8b6d416 | ||
|
|
aef4cc829b | ||
|
|
f7b90cb5c1 | ||
|
|
db34b828e4 | ||
|
|
ff12fd3a61 | ||
|
|
52cb19cc5c | ||
|
|
3a3a4fc413 | ||
|
|
e922791012 | ||
|
|
992cc3ffbf | ||
|
|
9e2129338a | ||
|
|
0b7793a927 | ||
|
|
c69d1db1eb | ||
|
|
179458efe9 | ||
|
|
42135ec7cc | ||
|
|
dc1f482a7d | ||
|
|
91804d5746 | ||
|
|
b4e9bc50f8 | ||
|
|
0853b99fca | ||
|
|
a6ddd167c6 | ||
|
|
386910589d | ||
|
|
212400b635 | ||
|
|
56acfe30af | ||
|
|
c9bf20ec56 | ||
|
|
6379d5129f | ||
|
|
baa1ec098d | ||
|
|
3c2d854a9e | ||
|
|
fea430a553 | ||
|
|
4659b9207c | ||
|
|
31efe8a690 | ||
|
|
7ef1b62b41 | ||
|
|
727bb84592 | ||
|
|
30fccaa0bd | ||
|
|
95b128b7bc | ||
|
|
dbee23c895 | ||
|
|
591f8f561a | ||
|
|
5831a6ba4a | ||
|
|
41db98d49f | ||
|
|
b280e29409 | ||
|
|
570edcbbb9 | ||
|
|
6a02237f6f | ||
|
|
074eac16cc | ||
|
|
91da70fb7d | ||
|
|
7a38cd9286 | ||
|
|
4c607d92f9 | ||
|
|
daaa725e3b | ||
|
|
a85e9953c9 | ||
|
|
3118bd1039 | ||
|
|
ed05eaf1d1 | ||
|
|
52e93371bd | ||
|
|
41dfd010b3 | ||
|
|
b0f57d74e5 | ||
|
|
d113813b33 | ||
|
|
1da59fa2fe | ||
|
|
1cf556c3a2 | ||
|
|
003eae81ce | ||
|
|
f518e34131 | ||
|
|
82ac0a24a0 | ||
|
|
3846170302 | ||
|
|
a875e9ba73 | ||
|
|
43bbde1e6b | ||
|
|
14754f1b18 | ||
|
|
08207912a5 | ||
|
|
e6902304d5 | ||
|
|
c298d8ca3e | ||
|
|
b4719c9daf | ||
|
|
f44ba40450 | ||
|
|
8385f1d2a1 | ||
|
|
37c9c397d3 | ||
|
|
c3e12a69cc | ||
|
|
d1a68df21a | ||
|
|
0789a2927a | ||
|
|
3e450b8818 | ||
|
|
14bfd00c0e | ||
|
|
7b7a5b0b21 | ||
|
|
8e52bf30c1 | ||
|
|
165735d01a | ||
|
|
8f2d6ac042 | ||
|
|
714635c75c | ||
|
|
b0293fe0e5 | ||
|
|
2293ecd8e8 | ||
|
|
7db4ddaae7 | ||
|
|
3708a7ac30 | ||
|
|
17b7dcef6f | ||
|
|
982a3774f1 | ||
|
|
22d75f4843 | ||
|
|
4630216c38 | ||
|
|
07dcb074f8 | ||
|
|
a63f5c8bf2 | ||
|
|
c19ee58f46 | ||
|
|
525c584fcf | ||
|
|
a8bd8d13a9 | ||
|
|
ac51b8eabc | ||
|
|
77a19774cf | ||
|
|
aa8ac05d6b | ||
|
|
c7e7f89026 | ||
|
|
0e8abc1973 | ||
|
|
f33c02e56b | ||
|
|
737ee90e69 | ||
|
|
b42171dc04 | ||
|
|
c664f9e30c | ||
|
|
a9826dce27 | ||
|
|
1cecf3c9cb | ||
|
|
b9015f8f29 | ||
|
|
8bcd917c12 | ||
|
|
55dbbf5f0c | ||
|
|
1ba427f638 | ||
|
|
667836fbe5 | ||
|
|
88df721dcc | ||
|
|
502f2ba2ef | ||
|
|
2d79b34a1d | ||
|
|
d6e309aa01 | ||
|
|
e2ba650a8c | ||
|
|
b094e0913d | ||
|
|
5366821144 | ||
|
|
81b1dd12f8 | ||
|
|
15667db6a9 | ||
|
|
7c484cae13 | ||
|
|
bdd1c36802 | ||
|
|
1dbfa9caf6 | ||
|
|
de79bf57e5 | ||
|
|
7739051725 | ||
|
|
928ac2191b | ||
|
|
548865e8dd | ||
|
|
0eacb9b9c8 | ||
|
|
87fc8db5a9 | ||
|
|
2495c52b05 | ||
|
|
b3966ab4eb | ||
|
|
0c0fd00788 | ||
|
|
95965615a4 | ||
|
|
1105c24e8c | ||
|
|
b509f1f098 | ||
|
|
cbf69f6ded | ||
|
|
ee62dac67b | ||
|
|
add699d238 | ||
|
|
16f8a35e76 | ||
|
|
263a85c2a6 | ||
|
|
d29509b233 | ||
|
|
d61612e05b | ||
|
|
5a23c2bb90 | ||
|
|
5993257eb1 | ||
|
|
e1a056db46 | ||
|
|
95d4483593 | ||
|
|
a9bf24695d | ||
|
|
ccf8844689 | ||
|
|
d1759a4335 | ||
|
|
927ef041f8 | ||
|
|
01ea39adad | ||
|
|
371e90a40a | ||
|
|
80624845c4 | ||
|
|
151cd76f75 | ||
|
|
1858c0e290 | ||
|
|
a7ab55dc00 | ||
|
|
68269b23fb | ||
|
|
b096887b16 | ||
|
|
b3e73e4dad | ||
|
|
6c3acb8e21 | ||
|
|
2a4ad207a5 | ||
|
|
36cb7d9451 | ||
|
|
de1c041900 | ||
|
|
b7cda68c1c | ||
|
|
342134f853 | ||
|
|
abb0ea01a7 | ||
|
|
57337c1ae0 | ||
|
|
c7aac60c6f | ||
|
|
b026c70f79 | ||
|
|
1ac87279d7 | ||
|
|
3506b9e8f2 | ||
|
|
4510fa7dcb | ||
|
|
c05eb1251b | ||
|
|
e74c374783 | ||
|
|
12875a9664 | ||
|
|
ece154d73b | ||
|
|
69204eea04 | ||
|
|
0030e208a0 | ||
|
|
5579001366 | ||
|
|
2dde022a00 | ||
|
|
862a2ce695 | ||
|
|
94f7205943 | ||
|
|
5bdc1862d9 | ||
|
|
62769e3b11 | ||
|
|
fcc3103123 | ||
|
|
0beb48f11d | ||
|
|
28bf9688f1 | ||
|
|
da86f445c8 | ||
|
|
2d1494314e | ||
|
|
8c586d7f7c | ||
|
|
5ca128e8bb | ||
|
|
c143fa922e | ||
|
|
192fb159d2 | ||
|
|
47d9a69802 | ||
|
|
3350bd422f | ||
|
|
d494778435 | ||
|
|
88f5bcdb63 | ||
|
|
f0143e351d | ||
|
|
113ea6dc4b | ||
|
|
1e6cc6f836 | ||
|
|
e467b130c2 | ||
|
|
7ff5844aa2 | ||
|
|
518b6f1d41 | ||
|
|
d7076778e2 | ||
|
|
473a192b85 | ||
|
|
15d90c24ed | ||
|
|
52746911d8 | ||
|
|
ea242b5bad | ||
|
|
c37241c733 | ||
|
|
263ae6f99f | ||
|
|
11ff756b2e | ||
|
|
10615b53ad | ||
|
|
2e5b492edd | ||
|
|
90e60f7a98 | ||
|
|
0d7654d2c9 | ||
|
|
92402e9f23 | ||
|
|
adcb8f3b15 | ||
|
|
56c35ff1e9 | ||
|
|
e397a079c1 | ||
|
|
4bcf159381 | ||
|
|
b50b1e9ac4 | ||
|
|
c5f81d35f5 | ||
|
|
6812c293b1 | ||
|
|
34dc70ca2a | ||
|
|
81a5521845 | ||
|
|
f7cfb74a3f | ||
|
|
f34631facc | ||
|
|
35c8c5885b | ||
|
|
8c9321ce7b | ||
|
|
2e2b9009fc | ||
|
|
eaff65c00f | ||
|
|
931cf9342e | ||
|
|
e23c3b3e77 | ||
|
|
230f81661c | ||
|
|
d95fcd55bf | ||
|
|
3983dadf3a | ||
|
|
f312ba376c | ||
|
|
13e9d7c71c | ||
|
|
8c326d7bad | ||
|
|
04b358f340 | ||
|
|
23cd561f2e | ||
|
|
3733fa9cc2 | ||
|
|
a94ab050bf | ||
|
|
1527ed46bc | ||
|
|
43d48f37b6 | ||
|
|
2c9edb1d1a | ||
|
|
a1aed960a9 | ||
|
|
22461f30e9 | ||
|
|
7b1a82a3cc | ||
|
|
0d06527507 | ||
|
|
2b5bcfdb7f | ||
|
|
9133f819e0 | ||
|
|
73c161a295 | ||
|
|
b46b26f1ae | ||
|
|
18eadd338e | ||
|
|
21b1059ba5 | ||
|
|
c25ed49f0d | ||
|
|
2ecf8914a1 | ||
|
|
f9ea2e834c | ||
|
|
85a4f8325f | ||
|
|
60b612247d | ||
|
|
d92c6a2097 | ||
|
|
45166e760b | ||
|
|
e90e71456b | ||
|
|
ce58b119bf | ||
|
|
2d8c9bba99 | ||
|
|
c8720fef02 | ||
|
|
73a644f320 | ||
|
|
3960f719db | ||
|
|
2484fbc1f7 | ||
|
|
5f6166435c | ||
|
|
af9a71ac7c | ||
|
|
ee44312c00 | ||
|
|
4214ab12ef | ||
|
|
3bb5d4eb8f | ||
|
|
dcff06bd91 | ||
|
|
3235dbfaee | ||
|
|
9911ab1000 | ||
|
|
4bf8cb455b | ||
|
|
cbb7c191e6 | ||
|
|
b2714e19d5 | ||
|
|
498644acab | ||
|
|
e8a6ea1dc8 | ||
|
|
07c636bf86 | ||
|
|
a52352b13a | ||
|
|
14dcc477f1 | ||
|
|
bfda864c1b | ||
|
|
bf41240ee1 | ||
|
|
b914e95f34 | ||
|
|
d6562bccfc | ||
|
|
46a93bb78a | ||
|
|
0b8a211e70 | ||
|
|
130fc6fce1 | ||
|
|
65e3540d03 | ||
|
|
feb9d3c557 | ||
|
|
234030218c | ||
|
|
6da26dcefc | ||
|
|
c24d4e11d5 | ||
|
|
eb0a62e9e9 | ||
|
|
742c85bf81 | ||
|
|
79f348a01c | ||
|
|
a9403b42f0 | ||
|
|
1a59b73adc | ||
|
|
26a5832f92 | ||
|
|
380e16d465 | ||
|
|
b83b5405c6 | ||
|
|
57c984cea1 | ||
|
|
563fa1e31a | ||
|
|
cb3c53664d | ||
|
|
3c994d94aa | ||
|
|
32d94d4e85 | ||
|
|
49346f5679 | ||
|
|
1323d8fb20 | ||
|
|
6f22cffb6b | ||
|
|
e1e5af8661 | ||
|
|
18f39be3ba | ||
|
|
bfd69f5381 | ||
|
|
5e27023ba7 | ||
|
|
5a321f970e | ||
|
|
f7e288589b | ||
|
|
66bfadd7f0 | ||
|
|
cfc10e9b52 | ||
|
|
be2ecf6236 | ||
|
|
57bf5c2143 | ||
|
|
9dca65634d | ||
|
|
2744bb4e5a | ||
|
|
2fec0c2440 | ||
|
|
faf8a47684 | ||
|
|
c9124bd1a9 | ||
|
|
db6b7b91d8 | ||
|
|
eefdcb0454 | ||
|
|
94c5076710 | ||
|
|
66e8418125 | ||
|
|
677afbcb91 | ||
|
|
7f6c942b81 | ||
|
|
e09cdc45a7 | ||
|
|
9ee80fd1db | ||
|
|
77166f88ac | ||
|
|
e0777fed15 | ||
|
|
075bb3b314 | ||
|
|
a768dd84fa | ||
|
|
4a1655b225 | ||
|
|
9e707e18e1 | ||
|
|
fa7e52dbda | ||
|
|
1ce5c3bf9c | ||
|
|
8d51c5a971 | ||
|
|
d06f38757d | ||
|
|
b885d69aaa | ||
|
|
8426a62d15 | ||
|
|
555c402ba3 | ||
|
|
0c809d3a5f | ||
|
|
ca697c9aa2 | ||
|
|
73b2b5f384 | ||
|
|
0340189baa | ||
|
|
823bd54484 | ||
|
|
756e5ac0b8 | ||
|
|
7c99169fb4 | ||
|
|
2380d252e1 | ||
|
|
f2f6c209ea | ||
|
|
ff734ed080 | ||
|
|
c4ca3660de | ||
|
|
b584f44661 | ||
|
|
f63d1b114a | ||
|
|
e5fb4a6c04 | ||
|
|
1d50dadc18 | ||
|
|
1f7d584517 | ||
|
|
08e046066d | ||
|
|
c17a0c4fed | ||
|
|
28e3c8bd3f | ||
|
|
00fb675452 | ||
|
|
83a4d7dc1c | ||
|
|
8433aae1af | ||
|
|
9549087ba9 | ||
|
|
b17fcfc4d1 | ||
|
|
3144da86f3 | ||
|
|
90427bc3af | ||
|
|
8fae68a5ae | ||
|
|
3b2c3a3f6b | ||
|
|
d9c6a32b8e | ||
|
|
00e32d1ede | ||
|
|
7d6eb250ea | ||
|
|
22c0884f5f | ||
|
|
e00cb29804 | ||
|
|
973476930e | ||
|
|
4e19d8d562 | ||
|
|
595695a459 | ||
|
|
7f41d30ce8 | ||
|
|
18652bc388 | ||
|
|
4f2722e7cf | ||
|
|
57d87ef24b | ||
|
|
4273fb067c | ||
|
|
ef6620ad8e | ||
|
|
ee20d30480 | ||
|
|
2fd12daf9f | ||
|
|
351f615ce6 | ||
|
|
5e8b2c7860 | ||
|
|
8be8f043bd | ||
|
|
8d6bff36f2 | ||
|
|
ee59479e25 | ||
|
|
11394aa4aa | ||
|
|
8ccee51887 | ||
|
|
5c823e9b42 | ||
|
|
ca76822fde | ||
|
|
fe74311209 | ||
|
|
742321dd08 | ||
|
|
69f0bdd486 | ||
|
|
6ffcf611a6 | ||
|
|
256d12773b | ||
|
|
5be5f255d1 | ||
|
|
b812d3ec26 | ||
|
|
640407aae7 | ||
|
|
d3742048f9 | ||
|
|
a61ba7b09c | ||
|
|
b1fb707a3a | ||
|
|
e09a25ca68 | ||
|
|
a968784908 | ||
|
|
0202192fc0 | ||
|
|
dbe4891a39 | ||
|
|
5a3b4ab34c | ||
|
|
acf6b07794 | ||
|
|
b397955f24 | ||
|
|
4a90c0f13b | ||
|
|
21244704fc | ||
|
|
80aedcd276 | ||
|
|
07f40b9488 | ||
|
|
a1a23d8be5 | ||
|
|
ef94509eac | ||
|
|
3b617f4528 | ||
|
|
25052fe48f | ||
|
|
389f71251c | ||
|
|
5867e34ec9 | ||
|
|
f8afb699cd | ||
|
|
6969202a03 | ||
|
|
54c6a3a454 | ||
|
|
b75ff37448 | ||
|
|
4cf5c8f656 | ||
|
|
c65d72fbca | ||
|
|
fff1c0ae57 | ||
|
|
de4a847078 | ||
|
|
66e793531d | ||
|
|
297c9e2f7a | ||
|
|
0740274b7c | ||
|
|
950ded454d | ||
|
|
9d0cc2c9d7 | ||
|
|
0bc7456b7f | ||
|
|
b8bb673b75 | ||
|
|
2e7ba05173 | ||
|
|
f9595b58ee | ||
|
|
e49c77e29c | ||
|
|
01c786c141 | ||
|
|
c0b07286aa | ||
|
|
9afb63d2e4 | ||
|
|
c31e5596e8 | ||
|
|
29b2d27f00 | ||
|
|
d45b4a7a1f | ||
|
|
dfac7c5f29 | ||
|
|
e163d556fb | ||
|
|
c57c20c6e4 | ||
|
|
05a3710288 | ||
|
|
ae63a4b33b | ||
|
|
ce82f2827b | ||
|
|
e7d73cbe56 | ||
|
|
9bd312881d | ||
|
|
ca1cd741c3 | ||
|
|
50b217817e | ||
|
|
6ba42e3e6b | ||
|
|
ac8937245c | ||
|
|
e68c59f984 | ||
|
|
64d2b7a122 | ||
|
|
41bce7925a | ||
|
|
3829d6bd9a | ||
|
|
123a976f62 | ||
|
|
7ee46ad4e0 | ||
|
|
adb62a3fff | ||
|
|
9e34cce1f2 |
+18
-14
@@ -92,6 +92,9 @@ jobs:
|
||||
- run: sudo apt-get install npm
|
||||
- run:
|
||||
command: make buildall
|
||||
- run:
|
||||
name: check tag and version output match
|
||||
command: ./scripts/version-check.sh ./lotus
|
||||
- store_artifacts:
|
||||
path: lotus
|
||||
- store_artifacts:
|
||||
@@ -348,7 +351,7 @@ jobs:
|
||||
build-macos:
|
||||
description: build darwin lotus binary
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
xcode: "12.5.0"
|
||||
working_directory: ~/go/src/github.com/filecoin-project/lotus
|
||||
steps:
|
||||
- prepare:
|
||||
@@ -367,11 +370,6 @@ jobs:
|
||||
name: Install Rust
|
||||
command: |
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
- run:
|
||||
name: Install jq
|
||||
command: |
|
||||
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
|
||||
- run:
|
||||
name: Install hwloc
|
||||
command: |
|
||||
@@ -388,6 +386,9 @@ jobs:
|
||||
- run:
|
||||
command: make build
|
||||
no_output_timeout: 30m
|
||||
- run:
|
||||
name: check tag and version output match
|
||||
command: ./scripts/version-check.sh ./lotus
|
||||
- store_artifacts:
|
||||
path: lotus
|
||||
- store_artifacts:
|
||||
@@ -810,11 +811,21 @@ workflows:
|
||||
suite: itest-deadlines
|
||||
target: "./itests/deadlines_test.go"
|
||||
|
||||
- test:
|
||||
name: test-itest-deals_512mb
|
||||
suite: itest-deals_512mb
|
||||
target: "./itests/deals_512mb_test.go"
|
||||
|
||||
- test:
|
||||
name: test-itest-deals_concurrent
|
||||
suite: itest-deals_concurrent
|
||||
target: "./itests/deals_concurrent_test.go"
|
||||
|
||||
- test:
|
||||
name: test-itest-deals_max_staging_deals
|
||||
suite: itest-deals_max_staging_deals
|
||||
target: "./itests/deals_max_staging_deals_test.go"
|
||||
|
||||
- test:
|
||||
name: test-itest-deals_offline
|
||||
suite: itest-deals_offline
|
||||
@@ -978,14 +989,7 @@ workflows:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||
- build-lotus-soup
|
||||
- build-macos:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- /.*/
|
||||
tags:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||
- build-macos
|
||||
- build-appimage:
|
||||
filters:
|
||||
branches:
|
||||
|
||||
+8
-14
@@ -92,6 +92,9 @@ jobs:
|
||||
- run: sudo apt-get install npm
|
||||
- run:
|
||||
command: make buildall
|
||||
- run:
|
||||
name: check tag and version output match
|
||||
command: ./scripts/version-check.sh ./lotus
|
||||
- store_artifacts:
|
||||
path: lotus
|
||||
- store_artifacts:
|
||||
@@ -348,7 +351,7 @@ jobs:
|
||||
build-macos:
|
||||
description: build darwin lotus binary
|
||||
macos:
|
||||
xcode: "10.0.0"
|
||||
xcode: "12.5.0"
|
||||
working_directory: ~/go/src/github.com/filecoin-project/lotus
|
||||
steps:
|
||||
- prepare:
|
||||
@@ -367,11 +370,6 @@ jobs:
|
||||
name: Install Rust
|
||||
command: |
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
- run:
|
||||
name: Install jq
|
||||
command: |
|
||||
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
|
||||
- run:
|
||||
name: Install hwloc
|
||||
command: |
|
||||
@@ -388,6 +386,9 @@ jobs:
|
||||
- run:
|
||||
command: make build
|
||||
no_output_timeout: 30m
|
||||
- run:
|
||||
name: check tag and version output match
|
||||
command: ./scripts/version-check.sh ./lotus
|
||||
- store_artifacts:
|
||||
path: lotus
|
||||
- store_artifacts:
|
||||
@@ -843,14 +844,7 @@ workflows:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||
- build-lotus-soup
|
||||
- build-macos:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- /.*/
|
||||
tags:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+(-rc\d+)?$/
|
||||
- build-macos
|
||||
- build-appimage:
|
||||
filters:
|
||||
branches:
|
||||
|
||||
+72
-18
@@ -1,19 +1,73 @@
|
||||
comment: off
|
||||
ignore:
|
||||
- "**/cbor_gen.go"
|
||||
- "api/test/**/*"
|
||||
- "api/test/*"
|
||||
- "gen/**/*"
|
||||
- "gen/*"
|
||||
- "cmd/lotus-shed/*"
|
||||
- "cmd/tvx/*"
|
||||
- "cmd/lotus-pcr/*"
|
||||
- "cmd/tvx/*"
|
||||
- "cmd/lotus-chainwatch/*"
|
||||
- "cmd/lotus-health/*"
|
||||
- "cmd/lotus-fountain/*"
|
||||
- "cmd/lotus-townhall/*"
|
||||
- "cmd/lotus-stats/*"
|
||||
- "cmd/lotus-pcr/*"
|
||||
github_checks:
|
||||
annotations: false
|
||||
# Auto generated
|
||||
- "^.*_gen.go$"
|
||||
- "^.*/mock_full.go$"
|
||||
# Old actors.
|
||||
- "^chain/actors/builtin/[^/]*/(message|state|v)[0-4]\\.go$" # We test the latest version only.
|
||||
# Tests
|
||||
- "api/test/**"
|
||||
- "conformance/**"
|
||||
# Generators
|
||||
- "gen/**"
|
||||
- "chain/actors/agen/**"
|
||||
# Non-critical utilities
|
||||
- "api/docgen/**"
|
||||
- "api/docgen-openrpc/**"
|
||||
coverage:
|
||||
status:
|
||||
patch: off
|
||||
project:
|
||||
tools-and-tests:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
informational: true
|
||||
paths:
|
||||
- "testplans"
|
||||
- "tools"
|
||||
- "system"
|
||||
- "snap"
|
||||
- "lotuspond"
|
||||
- "conformance"
|
||||
- "scripts"
|
||||
- "gen"
|
||||
- "build"
|
||||
markets:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
informational: false
|
||||
paths:
|
||||
- "markets"
|
||||
- "paychmgr"
|
||||
miner:
|
||||
target: auto
|
||||
threshold: 1.5%
|
||||
informational: false
|
||||
paths:
|
||||
- "miner"
|
||||
- "storage"
|
||||
chain:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
informational: false
|
||||
paths:
|
||||
- "chain"
|
||||
node:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
informational: false
|
||||
paths:
|
||||
- "node"
|
||||
- "blockstore"
|
||||
- "metrics"
|
||||
- "lib"
|
||||
- "genesis"
|
||||
- "gateway"
|
||||
- "api"
|
||||
- "journal"
|
||||
cli:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
informational: true
|
||||
paths:
|
||||
- "cli"
|
||||
- "cmd"
|
||||
|
||||
@@ -21,35 +21,45 @@ body:
|
||||
required: true
|
||||
- label: I did not make any code changes to lotus.
|
||||
required: false
|
||||
- type: dropdown
|
||||
id: component-and-area
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Lotus component
|
||||
description: Please select the lotus component you are filing a bug for
|
||||
options:
|
||||
- lotus daemon - chain sync
|
||||
- lotus miner - mining and block production
|
||||
- lotus miner/worker - sealing
|
||||
- lotus miner - proving(WindowPoSt)
|
||||
- lotus miner/market - storage deal
|
||||
- lotus miner/market - retrieval deal
|
||||
- lotus client
|
||||
- lotus JSON-RPC API
|
||||
- lotus message management (mpool)
|
||||
- Other
|
||||
- label: lotus daemon - chain sync
|
||||
required: false
|
||||
- label: lotus miner - mining and block production
|
||||
required: false
|
||||
- label: lotus miner/worker - sealing
|
||||
required: false
|
||||
- label: lotus miner - proving(WindowPoSt)
|
||||
required: false
|
||||
- label: lotus miner/market - storage deal
|
||||
required: false
|
||||
- label: lotus miner/market - retrieval deal
|
||||
required: false
|
||||
- label: lotus miner/market - data transfer
|
||||
required: false
|
||||
- label: lotus client
|
||||
required: false
|
||||
- label: lotus JSON-RPC API
|
||||
required: false
|
||||
- label: lotus message management (mpool)
|
||||
required: false
|
||||
- label: Other
|
||||
required: false
|
||||
- type: textarea
|
||||
id: version
|
||||
attributes:
|
||||
label: Lotus Version
|
||||
render: text
|
||||
description: Enter the output of `lotus version` and `lotus-miner version` if applicable.
|
||||
placeholder: |
|
||||
e.g.
|
||||
Daemon:1.11.0-rc2+debug+git.0519cd371.dirty+api1.3.0
|
||||
Local: lotus version 1.11.0-rc2+debug+git.0519cd371.dirty
|
||||
validations:
|
||||
reuiqred: true
|
||||
required: true
|
||||
- type: textarea
|
||||
id: Description
|
||||
attributes:
|
||||
@@ -62,19 +72,18 @@ body:
|
||||
* For sealing issues, include the output of `lotus-miner sectors status --log <sectorId>` for the failed sector(s).
|
||||
* For proving issues, include the output of `lotus-miner proving` info.
|
||||
* For deal making issues, include the output of `lotus client list-deals -v` and/or `lotus-miner storage-deals|retrieval-deals|data-transfers list [-v]` commands for the deal(s) in question.
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: extraInfo
|
||||
attributes:
|
||||
label: Logging Information
|
||||
render: text
|
||||
description: |
|
||||
Please provide debug logs of the problem, remember you can get set log level control for:
|
||||
* lotus: use `lotus log list` to get all log systems available and set level by `lotus log set-level`. An example can be found [here](https://docs.filecoin.io/get-started/lotus/configuration-and-advanced-usage/#log-level-control).
|
||||
* lotus-miner:`lotus-miner log list` to get all log systems available and set level by `lotus-miner log set-level
|
||||
If you don't provide detailed logs when you raise the issue it will almost certainly be the first request I make before furthur diagnosing the problem.
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@@ -87,6 +96,5 @@ body:
|
||||
2. Do '...'
|
||||
3. See error '...'
|
||||
...
|
||||
render: bash
|
||||
validations:
|
||||
required: false
|
||||
required: false
|
||||
|
||||
@@ -15,24 +15,33 @@ body:
|
||||
required: true
|
||||
- label: I **have** a specific, actionable, and well motivated improvement to propose.
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: component
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Lotus component
|
||||
description: Please select the lotus component you are propoing improvement for
|
||||
description: Please select the lotus component you are filing an improvement request for
|
||||
options:
|
||||
- lotus daemon - chain sync
|
||||
- lotus miner - mining and block production
|
||||
- lotus miner/worker - sealing
|
||||
- lotus miner - proving(WindowPoSt)
|
||||
- lotus miner/market - storage deal
|
||||
- lotus miner/market - retrieval deal
|
||||
- lotus client
|
||||
- lotus JSON-RPC API
|
||||
- lotus message management (mpool)
|
||||
- Other
|
||||
- label: lotus daemon - chain sync
|
||||
required: false
|
||||
- label: lotus miner - mining and block production
|
||||
required: false
|
||||
- label: lotus miner/worker - sealing
|
||||
required: false
|
||||
- label: lotus miner - proving(WindowPoSt)
|
||||
required: false
|
||||
- label: lotus miner/market - storage deal
|
||||
required: false
|
||||
- label: lotus miner/market - retrieval deal
|
||||
required: false
|
||||
- label: lotus miner/market - data transfer
|
||||
required: false
|
||||
- label: lotus client
|
||||
required: false
|
||||
- label: lotus JSON-RPC API
|
||||
required: false
|
||||
- label: lotus message management (mpool)
|
||||
required: false
|
||||
- label: Other
|
||||
required: false
|
||||
- type: textarea
|
||||
id: request
|
||||
attributes:
|
||||
|
||||
@@ -13,24 +13,33 @@ body:
|
||||
required: true
|
||||
- label: I **have** a specific, actionable, and well motivated feature request to propose.
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: component
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Lotus component
|
||||
description: Please select the lotus component you are requesting a new feature for
|
||||
description: Please select the lotus component you are filing a new feature request for
|
||||
options:
|
||||
- lotus daemon - chain sync
|
||||
- lotus miner - mining and block production
|
||||
- lotus miner/worker - sealing
|
||||
- lotus miner - proving(WindowPoSt)
|
||||
- lotus miner/market - storage deal
|
||||
- lotus miner/market - retrieval deal
|
||||
- lotus client
|
||||
- lotus JSON-RPC API
|
||||
- lotus message management (mpool)
|
||||
- Other
|
||||
- label: lotus daemon - chain sync
|
||||
required: false
|
||||
- label: lotus miner - mining and block production
|
||||
required: false
|
||||
- label: lotus miner/worker - sealing
|
||||
required: false
|
||||
- label: lotus miner - proving(WindowPoSt)
|
||||
required: false
|
||||
- label: lotus miner/market - storage deal
|
||||
required: false
|
||||
- label: lotus miner/market - retrieval deal
|
||||
required: false
|
||||
- label: lotus miner/market - data transfer
|
||||
required: false
|
||||
- label: lotus client
|
||||
required: false
|
||||
- label: lotus JSON-RPC API
|
||||
required: false
|
||||
- label: lotus message management (mpool)
|
||||
required: false
|
||||
- label: Other
|
||||
required: false
|
||||
- type: textarea
|
||||
id: request
|
||||
attributes:
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
name: "M1 Bug Report For Deal Making"
|
||||
description: "File a bug report around deal making for the M1 releases"
|
||||
labels: [need/triage, kind/bug, M1-release]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Please check off the following boxes before continuing to file a bug report!
|
||||
options:
|
||||
- label: This is **not** a question or a support request. If you have any lotus related questions, please ask in the [lotus forum](https://github.com/filecoin-project/lotus/discussions).
|
||||
required: true
|
||||
- label: I **am** reporting a bug w.r.t one of the [M1 tags](https://github.com/filecoin-project/lotus/discussions/6852#discussioncomment-1043951). If not, choose another issue option [here](https://github.com/filecoin-project/lotus/issues/new/choose).
|
||||
required: true
|
||||
- label: I **am** reporting a bug around deal making. If not, create a [M1 Bug Report For Non Deal Making Issue](https://github.com/filecoin-project/lotus/issues/new?assignees=&labels=need%2Ftriage%2Ckind%2Fbug%2CM1-release&template=m1_bug_report_non_deal.yml).
|
||||
required: true
|
||||
- label: I have my log level set as instructed [here](https://github.com/filecoin-project/lotus/discussions/6852#discussioncomment-1043678) and have logs available for troubleshooting.
|
||||
required: true
|
||||
- label: The deal is coming from one of the M1 clients(communitcated in the coordination slack channel).
|
||||
required: true
|
||||
- label: I **have** searched on the [issue tracker](https://github.com/filecoin-project/lotus/issues) and the [lotus forum](https://github.com/filecoin-project/lotus/discussions), and there is no existing related issue or discussion.
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: lotus-componets
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Lotus Component
|
||||
description: Please select the lotus component you are filing a bug for
|
||||
options:
|
||||
- lotus miner market subsystem - storage deal
|
||||
- lotus miner market subsystem - retrieval deal
|
||||
- lotus miner - storage deal
|
||||
- lotus miner - retrieval deal
|
||||
- type: textarea
|
||||
id: version
|
||||
attributes:
|
||||
label: Lotus Tag and Version
|
||||
description: Enter the lotus tag, output of `lotus version` and `lotus-miner version`.
|
||||
validations:
|
||||
reuiqred: true
|
||||
- type: textarea
|
||||
id: Description
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: |
|
||||
This is where you get to tell us what went wrong, when doing so, please try to provide a clear and concise description of the bug with all related information:
|
||||
* What you were doding when you experienced the bug?
|
||||
* Any *error* messages you saw, *where* you saw them, and what you believe may have caused them (if you have any ideas).
|
||||
* What is the expected behaviour?
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: deal-status
|
||||
attributes:
|
||||
label: Deal Status
|
||||
description: What's the status of the deal?
|
||||
placeholder: |
|
||||
Please share the output of `lotus-miner storage-deals|retrieval-deals list [-v]` commands for the deal(s) in question.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: data-transfer-status
|
||||
attributes:
|
||||
label: Data Transfer Status
|
||||
description: What's the status of the data transfer?
|
||||
placeholder: |
|
||||
Please share the output of `lotus-miner data-transfers list -v` commands for the deal(s) in question.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logging
|
||||
attributes:
|
||||
label: Logging Information
|
||||
description: Please link to the whole of the miner logs on your side of the transaction. You can upload the logs to a [gist](https://gist.github.com).
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: RepoSteps
|
||||
attributes:
|
||||
label: Repo Steps (optional)
|
||||
description: "Steps to reproduce the behavior"
|
||||
value: |
|
||||
1. Run '...'
|
||||
2. Do '...'
|
||||
3. See error '...'
|
||||
...
|
||||
render: bash
|
||||
validations:
|
||||
required: false
|
||||
@@ -1,81 +0,0 @@
|
||||
name: "M1 Bug Report For Non Deal Making Issue"
|
||||
description: "File a bug report around non deal making issue for the M1 releases"
|
||||
labels: [need/triage, kind/bug, M1-release]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Please check off the following boxes before continuing to file a bug report!
|
||||
options:
|
||||
- label: This is **not** a question or a support request. If you have any lotus related questions, please ask in the [lotus forum](https://github.com/filecoin-project/lotus/discussions).
|
||||
required: true
|
||||
- label: I **am** reporting a bug w.r.t one of the [M1 tags](https://github.com/filecoin-project/lotus/discussions/6852#discussioncomment-1043951). If not, choose another issue option [here](https://github.com/filecoin-project/lotus/issues/new/choose).
|
||||
required: true
|
||||
- label: I am **not** reporting a bug around deal making. If yes, create a [M1 Bug Report For Deal Making](https://github.com/filecoin-project/lotus/issues/new?assignees=&labels=need%2Ftriage%2Ckind%2Fbug%2CM1-release&template=m1_bug_report_deal.yml).
|
||||
required: true
|
||||
- label: I **have** searched on the [issue tracker](https://github.com/filecoin-project/lotus/issues) and the [lotus forum](https://github.com/filecoin-project/lotus/discussions), and there is no existing related issue or discussion.
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: component-and-area
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Lotus component
|
||||
description: Please select the lotus component you are filing a bug for
|
||||
options:
|
||||
- lotus daemon - chain sync **with** splitstore enabled
|
||||
- lotus daemon - chain sync **without** splitstore enabled
|
||||
- lotus miner - mining and block production
|
||||
- lotus miner/worker - sealing
|
||||
- lotus miner - proving(WindowPoSt)
|
||||
- lotus client
|
||||
- lotus JSON-RPC API
|
||||
- lotus message management (mpool)
|
||||
- Other
|
||||
- type: textarea
|
||||
id: version
|
||||
attributes:
|
||||
label: Lotus Tag and Version
|
||||
description: Enter the lotus tag, output of `lotus version` and `lotus-miner version`.
|
||||
validations:
|
||||
reuiqred: true
|
||||
- type: textarea
|
||||
id: Description
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: |
|
||||
This is where you get to tell us what went wrong, when doing so, please try to provide a clear and concise description of the bug with all related information:
|
||||
* What you were doding when you experienced the bug?
|
||||
* Any *error* messages you saw, *where* you saw them, and what you believe may have caused them (if you have any ideas).
|
||||
* What is the expected behaviour?
|
||||
* For sealing issues, include the output of `lotus-miner sectors status --log <sectorId>` for the failed sector(s).
|
||||
* For proving issues, include the output of `lotus-miner proving` info.
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: extraInfo
|
||||
attributes:
|
||||
label: Logging Information
|
||||
description: |
|
||||
Please provide debug logs of the problem, remember you can get set log level control for:
|
||||
* lotus: use `lotus log list` to get all log systems available and set level by `lotus log set-level`. An example can be found [here](https://docs.filecoin.io/get-started/lotus/configuration-and-advanced-usage/#log-level-control).
|
||||
* lotus-miner:`lotus-miner log list` to get all log systems available and set level by `lotus-miner log set-level
|
||||
If you don't provide detailed logs when you raise the issue it will almost certainly be the first request I make before furthur diagnosing the problem.
|
||||
render: bash
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: RepoSteps
|
||||
attributes:
|
||||
label: Repo Steps
|
||||
description: "Steps to reproduce the behavior"
|
||||
value: |
|
||||
1. Run '...'
|
||||
2. Do '...'
|
||||
3. See error '...'
|
||||
...
|
||||
render: bash
|
||||
validations:
|
||||
required: false
|
||||
@@ -29,5 +29,5 @@ jobs:
|
||||
days-before-pr-close: 2
|
||||
remove-stale-when-updated: true
|
||||
enable-statistics: true
|
||||
|
||||
|
||||
|
||||
|
||||
+422
-170
@@ -1,186 +1,445 @@
|
||||
# Lotus changelog
|
||||
|
||||
# 1.11.1-rc2 / 2021-08-01
|
||||
# v1.11.3 / 2021-09-29
|
||||
|
||||
> Note: for discussion about this release, please comment [here](https://github.com/filecoin-project/lotus/discussions/6904)
|
||||
lotus v1.11.3 is a feature release that's **highly recommended to ALL lotus users to upgrade**, including node
|
||||
operators, storage providers and clients. It includes many improvements and bug fixes that result in perf
|
||||
improvements in different area, like deal making, sealing and so on.
|
||||
|
||||
This is the second release candidate for the **highly-recommended** but optional Lotus v1.11.1 release that introduces many deal making and datastore improvements and new features along with other bug fixes. A more organized and detailed release note will be shared in the next few days with future RCs, highlights are:
|
||||
- **Experimental** [lotus-miner market subsystem](https://docs.filecoin.io/mine/lotus/split-markets-miners/#frontmatter-title)
|
||||
- **Under Testing** [splistore](https://github.com/filecoin-project/lotus/blob/master/blockstore/splitstore/README.md)
|
||||
## Highlights
|
||||
|
||||
- 🌟🌟Introduce `MaxStagingDealsBytes - reject new deals if our staging deals area is full ([filecoin-project/lotus#7276](https://github.com/filecoin-project/lotus/pull/7276))
|
||||
- Set `MaxStagingDealsBytes` under the [Dealmaking] section of the markets' subsystem's `config.toml` to reject new incoming deals when the `deal-staging` directory of market subsystem's repo gets too large.
|
||||
- 🌟🌟miner: Command to list/remove expired sectors locally ([filecoin-project/lotus#7140](https://github.com/filecoin-project/lotus/pull/7140))
|
||||
- run `./lotus-miner sectors expired -h` for more details.
|
||||
- 🚀update to ffi to update-bellperson-proofs-v9-0-2 ([filecoin-project/lotus#7369](https://github.com/filecoin-project/lotus/pull/7369))
|
||||
- MinerX fellows(early testers of lotus releases) have reported faster WindowPoSt computation!
|
||||
- 🌟dealpublisher: Fully validate deals before publishing ([filecoin-project/lotus#7234](https://github.com/filecoin-project/lotus/pull/7234))
|
||||
- This excludes the expired deals before sending out a PSD message which reduces the chances of PSD message failure due to invalid deals.
|
||||
- 🌟Simple alert system; FD limit alerts ([filecoin-project/lotus#7108](https://github.com/filecoin-project/lotus/pull/7108))
|
||||
|
||||
## New Features
|
||||
|
||||
- feat(ci): include version/cli checks in tagged releases ([filecoin-project/lotus#7331](https://github.com/filecoin-project/lotus/pull/7331))
|
||||
- Show deal sizes is sealing sectors ([filecoin-project/lotus#7261](https://github.com/filecoin-project/lotus/pull/7261))
|
||||
- config for disabling NAT port mapping ([filecoin-project/lotus#7204](https://github.com/filecoin-project/lotus/pull/7204))
|
||||
- Add optional mined block list to miner info ([filecoin-project/lotus#7202](https://github.com/filecoin-project/lotus/pull/7202))
|
||||
- Shed: Create a verifreg command for when VRK isn't a multisig ([filecoin-project/lotus#7099](https://github.com/filecoin-project/lotus/pull/7099))
|
||||
|
||||
## Improvements
|
||||
|
||||
- build macOS CI ([filecoin-project/lotus#7307](https://github.com/filecoin-project/lotus/pull/7307))
|
||||
- itests: remove cid equality comparison ([filecoin-project/lotus#7292](https://github.com/filecoin-project/lotus/pull/7292))
|
||||
- Add partition info to the 'sectors status' command ([filecoin-project/lotus#7246](https://github.com/filecoin-project/lotus/pull/7246))
|
||||
- chain: Cleanup consensus logic ([filecoin-project/lotus#7255](https://github.com/filecoin-project/lotus/pull/7255))
|
||||
- builder: Handle chainstore config in ConfigFullNode ([filecoin-project/lotus#7232](https://github.com/filecoin-project/lotus/pull/7232))
|
||||
- gateway: check tipsets in ChainGetPath ([filecoin-project/lotus#7230](https://github.com/filecoin-project/lotus/pull/7230))
|
||||
- Refactor events subsystem ([filecoin-project/lotus#7000](https://github.com/filecoin-project/lotus/pull/7000))
|
||||
- test: re-enable disabled tests ([filecoin-project/lotus#7211](https://github.com/filecoin-project/lotus/pull/7211))
|
||||
- Reduce lotus-miner startup spam ([filecoin-project/lotus#7205](https://github.com/filecoin-project/lotus/pull/7205))
|
||||
- Catch deal slashed because sector was terminated ([filecoin-project/lotus#7201](https://github.com/filecoin-project/lotus/pull/7201))
|
||||
- Insert miner and network power data as gibibytes to avoid int64 overflows ([filecoin-project/lotus#7194](https://github.com/filecoin-project/lotus/pull/7194))
|
||||
- sealing: Check piece CIDs after AddPiece ([filecoin-project/lotus#7185](https://github.com/filecoin-project/lotus/pull/7185))
|
||||
- markets: OnDealExpiredOrSlashed - get deal by proposal instead of deal ID ([filecoin-project/lotus#5431](https://github.com/filecoin-project/lotus/pull/5431))
|
||||
- Incoming: improve a log message ([filecoin-project/lotus#7181](https://github.com/filecoin-project/lotus/pull/7181))
|
||||
- journal: make current log file have a fixed named (#7112) ([filecoin-project/lotus#7112](https://github.com/filecoin-project/lotus/pull/7112))
|
||||
- call string.Repeat always with positive int ([filecoin-project/lotus#7104](https://github. com/filecoin-project/lotus/pull/7104))
|
||||
- itests: support larger sector sizes; add large deal test. ([filecoin-project/lotus#7148](https://github.com/filecoin-project/lotus/pull/7148))
|
||||
- Ignore nil throttler ([filecoin-project/lotus#7169](https://github.com/filecoin-project/lotus/pull/7169))
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- fix: escape periods to match actual periods in version
|
||||
- fix bug for CommittedCapacitySectorLifetime ([filecoin-project/lotus#7337](https://github.com/filecoin-project/lotus/pull/7337))
|
||||
- fix a panic in HandleRecoverDealIDs ([filecoin-project/lotus#7336](https://github.com/filecoin-project/lotus/pull/7336))
|
||||
- fix index out of range ([filecoin-project/lotus#7273](https://github.com/filecoin-project/lotus/pull/7273))
|
||||
- fix: correctly handle null blocks when detecting an expensive fork ([filecoin-project/lotus#7210](https://github.com/filecoin-project/lotus/pull/7210))
|
||||
- fix: make lotus soup use the correct dependencies ([filecoin-project/lotus#7221](https://github.com/filecoin-project/lotus/pull/7221))
|
||||
- fix: init restore adds empty storage.json ([filecoin-project/lotus#7025](https://github.com/filecoin-project/lotus/pull/7025))
|
||||
- fix: disable broken testground integration test ([filecoin-project/lotus#7187](https://github.com/filecoin-project/lotus/pull/7187))
|
||||
- fix TestDealPublisher ([filecoin-project/lotus#7173](https://github.com/filecoin-project/lotus/pull/7173))
|
||||
- fix: make TestTimedCacheBlockstoreSimple pass reliably ([filecoin-project/lotus#7174](https://github.com/filecoin-project/lotus/pull/7174))
|
||||
- Fix throttling bug ([filecoin-project/lotus#7177](https://github.com/filecoin-project/lotus/pull/7177))
|
||||
- sealing: Fix sector state accounting with FinalizeEarly ([filecoin-project/lotus#7256](https://github.com/filecoin-project/lotus/pull/7256))
|
||||
- docker entrypoint.sh missing variable escape character ([filecoin-project/lotus#7291](https://github.com/filecoin-project/lotus/pull/7291))
|
||||
- sealing: Fix retry loop in SubmitCommitAggregate ([filecoin-project/lotus#7245](https://github.com/filecoin-project/lotus/pull/7245))
|
||||
- sectors expired: Handle precomitted and unproven sectors correctly ([filecoin-project/lotus#7236](https://github.com/filecoin-project/lotus/pull/7236))
|
||||
- stores: Fix reserved disk usage log spam ([filecoin-project/lotus#7233](https://github.com/filecoin-project/lotus/pull/7233))
|
||||
|
||||
|
||||
## Dependency Updates
|
||||
|
||||
- github.com/filecoin-project/go-fil-markets (v1.8.1 -> v1.12.0):
|
||||
- github.com/filecoin-project/go-data-transfer (v1.7.8 -> v1.10.1):
|
||||
- update to ffi to update-bellperson-proofs-v9-0-2 ([filecoin-project/lotus#7369](https://github.com/filecoin-project/lotus/pull/7369))
|
||||
- fix(deps): use go-graphsync v0.9.3 with hotfix
|
||||
- Update to unified go-graphsync v0.9.0 ([filecoin-project/lotus#7197](https://github.com/filecoin-project/lotus/pull/7197))
|
||||
|
||||
## Others
|
||||
|
||||
- v1.11.3-rc2 ([filecoin-project/lotus#7371](https://github.com/filecoin-project/lotus/pull/7371))
|
||||
- v1.11.3-rc1 ([filecoin-project/lotus#7299](https://github.com/filecoin-project/lotus/pull/7299))
|
||||
- Increase threshold from 0.5% to 1% ([filecoin-project/lotus#7262](https://github.com/filecoin-project/lotus/pull/7262))
|
||||
- ci: exclude cruft from code coverage ([filecoin-project/lotus#7189](https://github.com/filecoin-project/lotus/pull/7189))
|
||||
- Bump version to v1.11.3-dev ([filecoin-project/lotus#7180](https://github.com/filecoin-project/lotus/pull/7180))
|
||||
- test: disable flaky TestBatchDealInput ([filecoin-project/lotus#7176](https://github.com/filecoin-project/lotus/pull/7176))
|
||||
- Turn off patch ([filecoin-project/lotus#7172](https://github.com/filecoin-project/lotus/pull/7172))
|
||||
- test: disable flaky TestSimultaneousTransferLimit ([filecoin-project/lotus#7153](https://github.com/filecoin-project/lotus/pull/7153))
|
||||
|
||||
|
||||
## Contributors
|
||||
|
||||
| Contributor | Commits | Lines ± | Files Changed |
|
||||
|-------------|---------|---------|---------------|
|
||||
| @magik6k | 39 | +3311/-1825 | 179 |
|
||||
| @Stebalien | 23 | +1935/-1417 | 84 |
|
||||
| @dirkmc | 12 | +921/-732 | 111 |
|
||||
| @dirkmc | 12 | +663/-790 | 30 |
|
||||
| @hannahhoward | 3 | +482/-275 | 46 |
|
||||
| @travisperson | 1 | +317/-65 | 5 |
|
||||
| @jennijuju | 11 | +223/-126 | 24 |
|
||||
| @hannahhoward | 7 | +257/-55 | 16 |
|
||||
| @nonsense| 9 | +258/-37 | 19 |
|
||||
| @raulk | 4 | +127/-36 | 13 |
|
||||
| @raulk | 1 | +43/-60 | 15 |
|
||||
| @arajasek | 4 | +74/-8 | 10 |
|
||||
| @Frank | 2 | +68/-8 | 3 |
|
||||
| @placer14| 2 | +52/-1 | 4 |
|
||||
| @ldoublewood | 2 | +15/-13 | 3 |
|
||||
| @lanzafame | 1 | +16/-2 | 1 |
|
||||
| @aarshkshah1992 | 2 | +11/-6 | 2 |
|
||||
| @ZenGround0 | 2 | +7/-6 | 2 |
|
||||
| @ognots | 1 | +0/-10 | 2 |
|
||||
| @KAYUII | 2 | +4/-4 | 2 |
|
||||
| @lanzafame | 1 | +6/-0 | 1 |
|
||||
| @jacobheun | 1 | +3/-3 | 1 |
|
||||
| @frank | 1 | +4/-0 | 1 |
|
||||
|
||||
|
||||
# v1.11.2 / 2021-09-06
|
||||
|
||||
lotus v1.11.2 is a feature release that's **highly recommended ALL lotus users to upgrade**, including node operators,
|
||||
storage providers and clients.
|
||||
|
||||
## Highlights
|
||||
- 🌟🌟🌟 Introduce Dagstore and CARv2 for deal-making (#6671) ([filecoin-project/lotus#6671](https://github.com/filecoin-project/lotus/pull/6671))
|
||||
- **[lotus miner markets' Dagstore](https://docs.filecoin.io/mine/lotus/dagstore/#conceptual-overview)** is a
|
||||
component of the `markets` subsystem in lotus-miner. It is a sharded store to hold large IPLD graphs efficiently,
|
||||
packaged as location-transparent attachable CAR files and it replaces the former Badger staging blockstore. It
|
||||
is designed to provide high efficiency and throughput, and minimize resource utilization during deal-making operations.
|
||||
The dagstore also leverages the indexing features of [CARv2](https://github.com/ipld/ipld/blob/master/specs/transport/car/carv2/index.md) to enable plan CAR files to act as read and write
|
||||
blockstores, which are served as the direct medium for data exchanges in markets for both storage and retrieval
|
||||
deal making without requiring intermediate buffers.
|
||||
- In the future, lotus will leverage and interact with Dagstore a lot for new features and improvements for deal
|
||||
making, therefore, it's highly recommended to lotus users to go through [Lotus Miner: About the markets dagstore](https://docs.filecoin.io/mine/lotus/dagstore/#conceptual-overview) thoroughly to learn more about Dagstore's
|
||||
conceptual overview, terminology, directory structure, configuration and so on.
|
||||
- **Note**:
|
||||
- When you first start your lotus-miner or market subsystem with this release, a one-time/first-time **dagstore migration** will be triggered which replaces the former Badger staging blockstore with dagstore. We highly
|
||||
recommend storage providers to read this [section](https://docs.filecoin.io/mine/lotus/dagstore/#first-time-migration) to learn more about
|
||||
what the process does, what to expect and how monitor it.
|
||||
- It is highly recommended to **wait all ongoing data transfer to finish or cancel inbound storage deals that
|
||||
are still transferring**, using the `lotus-miner data-transfers cancel` command before upgrade your market nodes. Reason being that the new dagstore changes attributes in the internal deal state objects, and the paths to the staging CARs where the deal data was being placed will be lost.
|
||||
- ‼️Having your dags initialized will become important in the near feature for you to provide a better storage
|
||||
and retrieval service. We'd suggest you to start [forced bulk initialization] soon if possible as this process
|
||||
places relatively high IP workload on your storage system and is better to be carried out gradually and over a
|
||||
longer timeframe. Read how to do properly perform a force bulk initialization [here](https://docs.filecoin.io/mine/lotus/dagstore/#forcing-bulk-initialization).
|
||||
- ⏮ Rollback Alert(from v1.11.2-rcX to any version lower): If a storages deal is initiated with M1/v1.11.2(-rcX)
|
||||
release, it needs to get to the `StorageDealAwaitingPrecommit` state before you can do a version rollback or the markets process may panic.
|
||||
- 💙 **Special thanks to [MinerX fellows for testing and providing valuable feedbacks](https://github.com/filecoin-project/lotus/discussions/6852) for Dagstore in the past month!**
|
||||
- 🌟🌟 rpcenc: Support reader redirect ([filecoin-project/lotus#6952](https://github.com/filecoin-project/lotus/pull/6952))
|
||||
- This allows market processes to send piece bytes directly to workers involved on `AddPiece`.
|
||||
- Extending sectors: more practical and flexible tools ([filecoin-project/lotus#6097](https://github.com/filecoin-project/lotus/pull/6097))
|
||||
- `lotus-miner sectors check-expire` to inspect expiring sectors.
|
||||
- `lotus-miner sectors renew` for renewing expiring sectors, see the command help menu for customizable option
|
||||
like `extension`, `new-expiration` and so on.
|
||||
- ‼️ MpoolReplaceCmd ( lotus mpool replace`) now takes FIL for fee-limit ([filecoin-project/lotus#6927](https://github.com/filecoin-project/lotus/pull/6927))
|
||||
- Drop townhall/chainwatch ([filecoin-project/lotus#6912](https://github.com/filecoin-project/lotus/pull/6912))
|
||||
- ChainWatch is no longer supported by lotus.
|
||||
- Configurable CC Sector Expiration ([filecoin-project/lotus#6803](https://github.com/filecoin-project/lotus/pull/6803))
|
||||
- Set `CommittedCapacitySectorLifetime` in lotus-miner/config.toml to specify the default expiration for a new CC
|
||||
sector, value must be between 180-540 days inclusive.
|
||||
|
||||
## New Features
|
||||
- api/command for encoding actor params ([filecoin-project/lotus#7150](https://github.com/filecoin-project/lotus/pull/7150))
|
||||
- shed: Support raw encoding in cid id ([filecoin-project/lotus#7149](https://github.com/filecoin-project/lotus/pull/7149))
|
||||
- feat(miner deals): create subdir to miner repo for staged deals ([filecoin-project/lotus#6853](https://github.com/filecoin-project/lotus/pull/6853))
|
||||
- Support --actor in miner actor control list ([filecoin-project/lotus#7027](https://github.com/filecoin-project/lotus/pull/7027))
|
||||
- Shed: Include network name in genesis-verify ([filecoin-project/lotus#7019](https://github.com/filecoin-project/lotus/pull/7019))
|
||||
- feat: add ChainGetTipSetAfterHeight ([filecoin-project/lotus#6990](https://github.com/filecoin-project/lotus/pull/6990))
|
||||
- lotus-shed splitstore clear command ([filecoin-project/lotus#6967](https://github.com/filecoin-project/lotus/pull/6967))
|
||||
|
||||
## Improvements
|
||||
- improve get api error messages ([filecoin-project/lotus#7088](https://github.com/filecoin-project/lotus/pull/7088))
|
||||
- Strict major minor version checking on v0 and v1 apis ([filecoin-project/lotus#7038](https://github.com/filecoin-project/lotus/pull/7038))
|
||||
- make lotus-miner net commands hit markets subsystem. ([filecoin-project/lotus#7042](https://github.com/filecoin-project/lotus/pull/7042))
|
||||
- Test with latest actors version ([filecoin-project/lotus#6998](https://github.com/filecoin-project/lotus/pull/6998))
|
||||
- Reduce splitstore memory usage during chain walks ([filecoin-project/lotus#6949](https://github.com/filecoin-project/lotus/pull/6949))
|
||||
- Remove forgotten non-functioning config from the pre-mainnet days ([filecoin-project/lotus#6970](https://github.com/filecoin-project/lotus/pull/6970))
|
||||
- add explicit error msg if repo dir does not exist ([filecoin-project/lotus#6909](https://github.com/filecoin-project/lotus/pull/6909))
|
||||
- Test/pledge batching msg prop ([filecoin-project/lotus#6537](https://github.com/filecoin-project/lotus/pull/6537))
|
||||
- reasonable max value for initial sector expiration ([filecoin-project/lotus#6099](https://github.com/filecoin-project/lotus/pull/6099))
|
||||
- support MARKETS_API_INFO env var, and markets-repo, markets-api-url CLI flags. ([filecoin-project/lotus#6936](https://github.com/filecoin-project/lotus/pull/6936))
|
||||
- Improve formatting of workers CLI ([filecoin-project/lotus#6942](https://github.com/filecoin-project/lotus/pull/6942))
|
||||
- make: set default GOCC earlier ([filecoin-project/lotus#6932](https://github.com/filecoin-project/lotus/pull/6932))
|
||||
- Moving GC Followup ([filecoin-project/lotus#6905](https://github.com/filecoin-project/lotus/pull/6905))
|
||||
- Log more call context during errors ([filecoin-project/lotus#6918](https://github.com/filecoin-project/lotus/pull/6918))
|
||||
- polish(errors): better state tree errors ([filecoin-project/lotus#6923](https://github.com/filecoin-project/lotus/pull/6923))
|
||||
- adding an RuntimeSubsystems API to storage miner; fix `lotus-miner info` ([filecoin-project/lotus#6906](https://github.com/filecoin-project/lotus/pull/6906))
|
||||
- Reduce entropy in the chain package ([filecoin-project/lotus#6889](https://github.com/filecoin-project/lotus/pull/6889))
|
||||
- make: Allow setting Go compiler with GOCC ([filecoin-project/lotus#6911](https://github.com/filecoin-project/lotus/pull/6911))
|
||||
|
||||
## Bug Fixes
|
||||
- sealing: Fix RecoverDealIDs loop with changed PieceCID ([filecoin-project/lotus#7117](https://github.com/filecoin-project/lotus/pull/7117))
|
||||
- Fix error handling in SectorAddPieceToAny api impl ([filecoin-project/lotus#7135](https://github.com/filecoin-project/lotus/pull/7135))
|
||||
- add rice box to required binaries ([filecoin-project/lotus#7125](https://github.com/filecoin-project/lotus/pull/7125))
|
||||
- fix(miner): always create miner deal staging directory (#7098) ([filecoin-project/lotus#7098](https://github.com/filecoin-project/lotus/pull/7098))
|
||||
- fix build after merging #6097. (#7096) ([filecoin-project/lotus#7096](https://github.com/filecoin-project/lotus/pull/7096))
|
||||
- fix: don't check for t_aux when proving ([filecoin-project/lotus#7011](https://github.com/filecoin-project/lotus/pull/7011))
|
||||
- fix: vet actors shims ([filecoin-project/lotus#6999](https://github.com/filecoin-project/lotus/pull/6999))
|
||||
- fix: more logging in maybeStartBatch error ([filecoin-project/lotus#6996](https://github.com/filecoin-project/lotus/pull/6996))
|
||||
- fix flaky TestDealPublisher and re-enable ([filecoin-project/lotus#6991](https://github.com/filecoin-project/lotus/pull/6991))
|
||||
- fix skipCount ([filecoin-project/lotus#6940](https://github.com/filecoin-project/lotus/pull/6940))
|
||||
- fix bug in MpoolPending message exclusion ([filecoin-project/lotus#6945](https://github.com/filecoin-project/lotus/pull/6945))
|
||||
- PreCommitPolicy: Don't try to align expirations on proving period boundaries ([filecoin-project/lotus#7018](https://github.com/filecoin-project/lotus/pull/7018))
|
||||
- make: fix version check when using gotip ([filecoin-project/lotus#6916](https://github.com/filecoin-project/lotus/pull/6916))
|
||||
- fix ticket check ([filecoin-project/lotus#6882](https://github.com/filecoin-project/lotus/pull/6882))
|
||||
|
||||
## Dependency Updates
|
||||
- github.com/filecoin-project/go-data-transfer (v1.7.2 -> v1.7.8):
|
||||
- github.com/filecoin-project/go-fil-markets (v1.6.2 -> v1.8.1):
|
||||
- update go-libp2p-pubsub to v0.5.4 ([filecoin-project/lotus#6958](https://github.com/filecoin-project/lotus/pull/6958))
|
||||
- integrate the proof patch: tag proofs-v9-revert-deps-hotfix
|
||||
- Update markets, dt and graphsync ([filecoin-project/lotus#7160](https://github.com/filecoin-project/lotus/pull/7160))
|
||||
- Remove replace directive for multihash dep (#7113) ([filecoin-project/lotus#7113](https://github.com/filecoin-project/lotus/pull/7113))
|
||||
- upgrade upstream dependencies. ([filecoin-project/lotus#7115](https://github.com/filecoin-project/lotus/pull/7115))
|
||||
- Update to latest FFI ([filecoin-project/lotus#7110](https://github.com/filecoin-project/lotus/pull/7110))
|
||||
- Update state machine deps for logging ([filecoin-project/lotus#6941](https://github.com/filecoin-project/lotus/pull/6941))
|
||||
- Update deps for more logging in data transfer and markets ([filecoin-project/lotus#6937](https://github.com/filecoin-project/lotus/pull/6937))
|
||||
- Update to branches with improved logging ([filecoin-project/lotus#6919](https://github.com/filecoin-project/lotus/pull/6919))
|
||||
- update go-libp2p-pubsub to v0.5.3 ([filecoin-project/lotus#6907](https://github.com/filecoin-project/lotus/pull/6907))
|
||||
|
||||
## Others
|
||||
- Fix nits and see if codecov works now with auto ([filecoin-project/lotus#7151](https://github.com/filecoin-project/lotus/pull/7151))
|
||||
- Codecov Projects ([filecoin-project/lotus#7147](https://github.com/filecoin-project/lotus/pull/7147))
|
||||
- remove m1 templates and make area selection multi-optionable ([filecoin-project/lotus#7121](https://github.com/filecoin-project/lotus/pull/7121))
|
||||
- release -> master ([filecoin-project/lotus#7105](https://github.com/filecoin-project/lotus/pull/7105))
|
||||
- Lotus release process - how we make releases ([filecoin-project/lotus#6944](https://github.com/filecoin-project/lotus/pull/6944))
|
||||
- codecov: fix mock name ([filecoin-project/lotus#7039](https://github.com/filecoin-project/lotus/pull/7039))
|
||||
- codecov: fix regexes ([filecoin-project/lotus#7037](https://github.com/filecoin-project/lotus/pull/7037))
|
||||
- chore: disable flaky test ([filecoin-project/lotus#6957](https://github.com/filecoin-project/lotus/pull/6957))
|
||||
- set buildtype in nerpa and butterfly ([filecoin-project/lotus#6085](https://github.com/filecoin-project/lotus/pull/6085))
|
||||
- release v1.11.1 backport -> master ([filecoin-project/lotus#6929](https://github.com/filecoin-project/lotus/pull/6929))
|
||||
- chore: fixup issue templates ([filecoin-project/lotus#6899](https://github.com/filecoin-project/lotus/pull/6899))
|
||||
- bump master version to v1.11.2-dev ([filecoin-project/lotus#6903](https://github.com/filecoin-project/lotus/pull/6903))
|
||||
- releases -> master for v1.11.0 ([filecoin-project/lotus#6894](https://github.com/filecoin-project/lotus/pull/6894))
|
||||
|
||||
- github.com/filecoin-project/lotus:
|
||||
- Merge branch 'releases' into release/v1.11.1
|
||||
- Update to proof v8.0.3 ([filecoin-project/lotus#6890](https://github.com/filecoin-project/lotus/pull/6890))
|
||||
- lotus-shed: initial export cmd for markets related metadata ([filecoin-project/lotus#6840](https://github.com/filecoin-project/lotus/pull/6840))
|
||||
- add a very verbose -vv flag to lotus and lotus-miner. ([filecoin-project/lotus#6888](https://github.com/filecoin-project/lotus/pull/6888))
|
||||
- Update RELEASE_ISSUE_TEMPLATE.md ([filecoin-project/lotus#6880](https://github.com/filecoin-project/lotus/pull/6880))
|
||||
- Moving GC for badger ([filecoin-project/lotus#6854](https://github.com/filecoin-project/lotus/pull/6854))
|
||||
- Add github actions for staled pr ([filecoin-project/lotus#6879](https://github.com/filecoin-project/lotus/pull/6879))
|
||||
- Add allocated sectorid vis ([filecoin-project/lotus#4638](https://github.com/filecoin-project/lotus/pull/4638))
|
||||
- rename `cmd/lotus{-storage=>}-miner` to match binary. ([filecoin-project/lotus#6886](https://github.com/filecoin-project/lotus/pull/6886))
|
||||
- update to go-fil-market v1.6.0 ([filecoin-project/lotus#6885](https://github.com/filecoin-project/lotus/pull/6885))
|
||||
- Bump go-multihash, adjust test for supported version ([filecoin-project/lotus#6674](https://github.com/filecoin-project/lotus/pull/6674))
|
||||
- Fix padding of deals, which only partially shipped in #5988 ([filecoin-project/lotus#6683](https://github.com/filecoin-project/lotus/pull/6683))
|
||||
- fix racy TestSimultanenousTransferLimit. ([filecoin-project/lotus#6862](https://github.com/filecoin-project/lotus/pull/6862))
|
||||
- Improve splitstore warmup ([filecoin-project/lotus#6867](https://github.com/filecoin-project/lotus/pull/6867))
|
||||
- ValidateBlock: Assert that block header height's are greater than parents ([filecoin-project/lotus#6872](https://github.com/filecoin-project/lotus/pull/6872))
|
||||
- feat: Don't panic when api impl is nil ([filecoin-project/lotus#6857](https://github.com/filecoin-project/lotus/pull/6857))
|
||||
- splitstore shed utils ([filecoin-project/lotus#6811](https://github.com/filecoin-project/lotus/pull/6811))
|
||||
- Fix links in issue templates
|
||||
- Update issue templates and add templates for M1 ([filecoin-project/lotus#6856](https://github.com/filecoin-project/lotus/pull/6856))
|
||||
- Splitstore: support on-disk marksets using badger ([filecoin-project/lotus#6833](https://github.com/filecoin-project/lotus/pull/6833))
|
||||
- Config UX improvements ([filecoin-project/lotus#6848](https://github.com/filecoin-project/lotus/pull/6848))
|
||||
- fix deal concurrency test failures by upgrading graphsync and others ([filecoin-project/lotus#6724](https://github.com/filecoin-project/lotus/pull/6724))
|
||||
- Update issue templates to forms ([filecoin-project/lotus#6798](https://github.com/filecoin-project/lotus/pull/6798))
|
||||
- Nerpa v13 upgrade ([filecoin-project/lotus#6837](https://github.com/filecoin-project/lotus/pull/6837))
|
||||
- add docker-compose file ([filecoin-project/lotus#6544](https://github.com/filecoin-project/lotus/pull/6544))
|
||||
- fix warmup by decoupling state from message receipt walk ([filecoin-project/lotus#6841](https://github.com/filecoin-project/lotus/pull/6841))
|
||||
- add a command for compacting sector numbers bitfield ([filecoin-project/lotus#4640](https://github.com/filecoin-project/lotus/pull/4640))
|
||||
- PriceListByVersion ([filecoin-project/lotus#6766](https://github.com/filecoin-project/lotus/pull/6766))
|
||||
- easy way to make install app ([filecoin-project/lotus#5183](https://github.com/filecoin-project/lotus/pull/5183))
|
||||
- api: Separate the Net interface from Common ([filecoin-project/lotus#6627](https://github.com/filecoin-project/lotus/pull/6627))
|
||||
- cache loaded block messages ([filecoin-project/lotus#6760](https://github.com/filecoin-project/lotus/pull/6760))
|
||||
- fix: on randomness change, use new rand ([filecoin-project/lotus#6805](https://github.com/filecoin-project/lotus/pull/6805))
|
||||
- Splitstore: add retention policy option for keeping messages in the hotstore ([filecoin-project/lotus#6775](https://github.com/filecoin-project/lotus/pull/6775))
|
||||
- Introduce the LOTUS_CHAIN_BADGERSTORE_DISABLE_FSYNC envvar ([filecoin-project/lotus#6817](https://github.com/filecoin-project/lotus/pull/6817))
|
||||
- add StateReadState to gateway api ([filecoin-project/lotus#6818](https://github.com/filecoin-project/lotus/pull/6818))
|
||||
- add SealProof in SectorBuilder ([filecoin-project/lotus#6815](https://github.com/filecoin-project/lotus/pull/6815))
|
||||
- release -> master ([filecoin-project/lotus#6828](https://github.com/filecoin-project/lotus/pull/6828))
|
||||
- sealing: Handle preCommitParams errors more correctly ([filecoin-project/lotus#6763](https://github.com/filecoin-project/lotus/pull/6763))
|
||||
- fix: always check if StateSearchMessage returns nil ([filecoin-project/lotus#6802](https://github.com/filecoin-project/lotus/pull/6802))
|
||||
- ClientFindData: always fetch peer id from chain ([filecoin-project/lotus#6807](https://github.com/filecoin-project/lotus/pull/6807))
|
||||
- test: fix flaky window post tests ([filecoin-project/lotus#6804](https://github.com/filecoin-project/lotus/pull/6804))
|
||||
- ([filecoin-project/lotus#6800](https://github.com/filecoin-project/lotus/pull/6800))
|
||||
- fixes #6786 segfault ([filecoin-project/lotus#6787](https://github.com/filecoin-project/lotus/pull/6787))
|
||||
- Splitstore: add support for protecting out of chain references in the blockstore ([filecoin-project/lotus#6777](https://github.com/filecoin-project/lotus/pull/6777))
|
||||
- Resurrect CODEOWNERS, but for maintainers group ([filecoin-project/lotus#6773](https://github.com/filecoin-project/lotus/pull/6773))
|
||||
- update go-libp2p-pubsub to v0.5.0 ([filecoin-project/lotus#6764](https://github.com/filecoin-project/lotus/pull/6764))
|
||||
- Implement exposed splitstore ([filecoin-project/lotus#6762](https://github.com/filecoin-project/lotus/pull/6762))
|
||||
- Add ChainGetMessagesInTipset API ([filecoin-project/lotus#6642](https://github.com/filecoin-project/lotus/pull/6642))
|
||||
- test: handle null blocks in TestForkRefuseCall ([filecoin-project/lotus#6758](https://github.com/filecoin-project/lotus/pull/6758))
|
||||
- Master disclaimer ([filecoin-project/lotus#6757](https://github.com/filecoin-project/lotus/pull/6757))
|
||||
- Splitstore code reorg ([filecoin-project/lotus#6756](https://github.com/filecoin-project/lotus/pull/6756))
|
||||
- Create stale.yml ([filecoin-project/lotus#6747](https://github.com/filecoin-project/lotus/pull/6747))
|
||||
- Splitstore: Some small fixes ([filecoin-project/lotus#6754](https://github.com/filecoin-project/lotus/pull/6754))
|
||||
- ([filecoin-project/lotus#6746](https://github.com/filecoin-project/lotus/pull/6746))
|
||||
- Handle the --color flag via proper global state ([filecoin-project/lotus#6743](https://github.com/filecoin-project/lotus/pull/6743))
|
||||
- Config for collateral from miner available balance ([filecoin-project/lotus#6629](https://github.com/filecoin-project/lotus/pull/6629))
|
||||
- Support standalone miner-market process ([filecoin-project/lotus#6356](https://github.com/filecoin-project/lotus/pull/6356))
|
||||
- Splitstore Enhanchements ([filecoin-project/lotus#6474](https://github.com/filecoin-project/lotus/pull/6474))
|
||||
- ([filecoin-project/lotus#6739](https://github.com/filecoin-project/lotus/pull/6739))
|
||||
- Add more deal details to lotus-miner info ([filecoin-project/lotus#6708](https://github.com/filecoin-project/lotus/pull/6708))
|
||||
- Release template: Update all testnet infra at once ([filecoin-project/lotus#6710](https://github.com/filecoin-project/lotus/pull/6710))
|
||||
- Fix Lotus shed
|
||||
- Fix bugs in sectors extend --v1-sectors ([filecoin-project/lotus#6066](https://github.com/filecoin-project/lotus/pull/6066))
|
||||
- add election backtest ([filecoin-project/lotus#5950](https://github.com/filecoin-project/lotus/pull/5950))
|
||||
- Envvar to disable slash filter ([filecoin-project/lotus#6620](https://github.com/filecoin-project/lotus/pull/6620))
|
||||
- Release Template: remove binary validation step ([filecoin-project/lotus#6709](https://github.com/filecoin-project/lotus/pull/6709))
|
||||
- Config for deal publishing control addresses ([filecoin-project/lotus#6697](https://github.com/filecoin-project/lotus/pull/6697))
|
||||
- Reset of the interop network ([filecoin-project/lotus#6689](https://github.com/filecoin-project/lotus/pull/6689))
|
||||
- Enable color by default only if os.Stdout is a TTY ([filecoin-project/lotus#6696](https://github.com/filecoin-project/lotus/pull/6696))
|
||||
- Stop outputing ANSI color on non-TTY ([filecoin-project/lotus#6694](https://github.com/filecoin-project/lotus/pull/6694))
|
||||
- add dollar sign ([filecoin-project/lotus#6690](https://github.com/filecoin-project/lotus/pull/6690))
|
||||
- get-actor cli spelling fix ([filecoin-project/lotus#6681](https://github.com/filecoin-project/lotus/pull/6681))
|
||||
- fix "lotus-seed genesis car" error "merkledag: not found" ([filecoin-project/lotus#6688](https://github.com/filecoin-project/lotus/pull/6688))
|
||||
- polish(statetree): accept a context in statetree diff for timeouts ([filecoin-project/lotus#6639](https://github.com/filecoin-project/lotus/pull/6639))
|
||||
- Add helptext to lotus chain export ([filecoin-project/lotus#6672](https://github.com/filecoin-project/lotus/pull/6672))
|
||||
- Get retrieval pricing input should not error out on a deal state fetch ([filecoin-project/lotus#6679](https://github.com/filecoin-project/lotus/pull/6679))
|
||||
- Fix more CID double-encoding as hex ([filecoin-project/lotus#6680](https://github.com/filecoin-project/lotus/pull/6680))
|
||||
- add an incremental nonce itest. ([filecoin-project/lotus#6663](https://github.com/filecoin-project/lotus/pull/6663))
|
||||
- storage: Fix FinalizeSector with sectors in stoage paths ([filecoin-project/lotus#6653](https://github.com/filecoin-project/lotus/pull/6653))
|
||||
- Fix tiny error in check-client-datacap ([filecoin-project/lotus#6664](https://github.com/filecoin-project/lotus/pull/6664))
|
||||
- Fix: precommit_batch method used the wrong cfg.CommitBatchWait ([filecoin-project/lotus#6658](https://github.com/filecoin-project/lotus/pull/6658))
|
||||
- fix ticket expiration check ([filecoin-project/lotus#6635](https://github.com/filecoin-project/lotus/pull/6635))
|
||||
- commit batch: AggregateAboveBaseFee config ([filecoin-project/lotus#6650](https://github.com/filecoin-project/lotus/pull/6650))
|
||||
- commit batch: Initialize the FailedSectors map ([filecoin-project/lotus#6647](https://github.com/filecoin-project/lotus/pull/6647))
|
||||
- Fast-path retry submitting commit aggregate if commit is still valid ([filecoin-project/lotus#6638](https://github.com/filecoin-project/lotus/pull/6638))
|
||||
- remove precommit check in handleCommitFailed ([filecoin-project/lotus#6634](https://github.com/filecoin-project/lotus/pull/6634))
|
||||
- Reuse timers in sealing batch logic ([filecoin-project/lotus#6636](https://github.com/filecoin-project/lotus/pull/6636))
|
||||
- shed tool to estimate aggregate network fees ([filecoin-project/lotus#6631](https://github.com/filecoin-project/lotus/pull/6631))
|
||||
- fix prove commit aggregate send token amount ([filecoin-project/lotus#6625](https://github.com/filecoin-project/lotus/pull/6625))
|
||||
- Update version.go to 1.11.1 ([filecoin-project/lotus#6621](https://github.com/filecoin-project/lotus/pull/6621))
|
||||
- github.com/filecoin-project/go-data-transfer (v1.6.0 -> v1.7.0):
|
||||
- release: v1.7.0
|
||||
- Fire a transfer queued event when a transfer is queued in Graphsync (#221) ([filecoin-project/go-data-transfer#221](https://github.com/filecoin-project/go-data-transfer/pull/221))
|
||||
- feat: pass ChannelID to ValidatePush & ValidatePull (#220) ([filecoin-project/go-data-transfer#220](https://github.com/filecoin-project/go-data-transfer/pull/220))
|
||||
- release: v1.6.1 ([filecoin-project/go-data-transfer#218](https://github.com/filecoin-project/go-data-transfer/pull/218))
|
||||
- Remove CID lists (#217) ([filecoin-project/go-data-transfer#217](https://github.com/filecoin-project/go-data-transfer/pull/217))
|
||||
- Merge v1.6.0 ([filecoin-project/go-data-transfer#214](https://github.com/filecoin-project/go-data-transfer/pull/214))
|
||||
- Remove restart ack timeout (#211) ([filecoin-project/go-data-transfer#211](https://github.com/filecoin-project/go-data-transfer/pull/211))
|
||||
- feat: use different extension names to fit multiple hooks data in same graphsync message (#204) ([filecoin-project/go-data-transfer#204](https://github.com/filecoin-project/go-data-transfer/pull/204))
|
||||
- fix: map race in GS transport (#208) ([filecoin-project/go-data-transfer#208](https://github.com/filecoin-project/go-data-transfer/pull/208))
|
||||
- refactor: simplify graphsync transport (#203) ([filecoin-project/go-data-transfer#203](https://github.com/filecoin-project/go-data-transfer/pull/203))
|
||||
- release: v1.5.0 (#200) ([filecoin-project/go-data-transfer#200](https://github.com/filecoin-project/go-data-transfer/pull/200))
|
||||
- github.com/filecoin-project/go-fil-markets (v1.5.0 -> v1.6.0):
|
||||
- release: v1.6.0
|
||||
- support padding out smaller files (#536) ([filecoin-project/go-fil-markets#536](https://github.com/filecoin-project/go-fil-markets/pull/536))
|
||||
- On overloaded CI 10 seconds just isn't enough (#587) ([filecoin-project/go-fil-markets#587](https://github.com/filecoin-project/go-fil-markets/pull/587))
|
||||
- Do not hex-encode CIDs in logs (#561) ([filecoin-project/go-fil-markets#561](https://github.com/filecoin-project/go-fil-markets/pull/561))
|
||||
- remove wrong peer check in push deal validation (#585) ([filecoin-project/go-fil-markets#585](https://github.com/filecoin-project/go-fil-markets/pull/585))
|
||||
- fix: circleci docs-gen task (#574) ([filecoin-project/go-fil-markets#574](https://github.com/filecoin-project/go-fil-markets/pull/574))
|
||||
- Storage market request queued event and validation interface changes (#555) ([filecoin-project/go-fil-markets#555](https://github.com/filecoin-project/go-fil-markets/pull/555))
|
||||
- build(deps): bump ws from 6.2.1 to 6.2.2 (#554) ([filecoin-project/go-fil-markets#554](https://github.com/filecoin-project/go-fil-markets/pull/554))
|
||||
- release: v1.5.0 ([filecoin-project/go-fil-markets#553](https://github.com/filecoin-project/go-fil-markets/pull/553))
|
||||
- github.com/filecoin-project/go-padreader (v0.0.0-20200903213702-ed5fae088b20 -> v0.0.0-20210723183308-812a16dc01b1):
|
||||
- New method to pad harder (#6) ([filecoin-project/go-padreader#6](https://github.com/filecoin-project/go-padreader/pull/6))
|
||||
- Create SECURITY.md (#5) ([filecoin-project/go-padreader#5](https://github.com/filecoin-project/go-padreader/pull/5))
|
||||
- github.com/filecoin-project/go-state-types (v0.1.1-0.20210506134452-99b279731c48 -> v0.1.1-0.20210722133031-ad9bfe54c124):
|
||||
- Add version 6.5 (#30) ([filecoin-project/go-state-types#30](https://github.com/filecoin-project/go-state-types/pull/30))
|
||||
- rename file
|
||||
- github.com/filecoin-project/specs-actors/v5 (v5.0.1 -> v5.0.3):
|
||||
- Adjust code for subtle change in go-multihash 0.0.15 (#1463) ([filecoin-project/specs-actors#1463](https://github.com/filecoin-project/specs-actors/pull/1463))
|
||||
- Bump go state types (#1464) ([filecoin-project/specs-actors#1464](https://github.com/filecoin-project/specs-actors/pull/1464))
|
||||
- Create CODEOWNERS (#1465) ([filecoin-project/specs-actors#1465](https://github.com/filecoin-project/specs-actors/pull/1465))
|
||||
- Test deterministic offset (#1462) ([filecoin-project/specs-actors#1462](https://github.com/filecoin-project/specs-actors/pull/1462))
|
||||
|
||||
Contributors
|
||||
|
||||
| Contributor | Commits | Lines ± | Files Changed |
|
||||
|-------------|---------|---------|---------------|
|
||||
| vyzo | 295 | +8700/-5936 | 397 |
|
||||
| Anton Evangelatov | 94 | +4680/-2965 | 277 |
|
||||
| Łukasz Magiera | 37 | +3851/-1611 | 146 |
|
||||
| Mike Greenberg | 1 | +2310/-578 | 8 |
|
||||
| dirkmc | 7 | +1154/-726 | 29 |
|
||||
| Jennifer Wang | 9 | +485/-341 | 26 |
|
||||
| Peter Rabbitson | 18 | +469/-273 | 64 |
|
||||
| Cory Schwartz | 5 | +576/-135 | 14 |
|
||||
| hunjixin | 7 | +404/-82 | 19 |
|
||||
| ZenGround0 | 17 | +284/-135 | 44 |
|
||||
| Dirk McCormick | 17 | +348/-47 | 17 |
|
||||
| Raúl Kripalani | 18 | +254/-97 | 62 |
|
||||
| tchardin | 1 | +261/-33 | 4 |
|
||||
| Jakub Sztandera | 4 | +254/-16 | 4 |
|
||||
| Aarsh Shah | 2 | +196/-40 | 28 |
|
||||
| whyrusleeping | 3 | +150/-9 | 8 |
|
||||
| Whyrusleeping | 2 | +87/-66 | 10 |
|
||||
| Aayush Rajasekaran | 10 | +81/-53 | 13 |
|
||||
| zgfzgf | 2 | +104/-4 | 2 |
|
||||
| aarshkshah1992 | 4 | +73/-7 | 6 |
|
||||
| llifezou | 4 | +59/-20 | 4 |
|
||||
| Steven Allen | 7 | +47/-17 | 9 |
|
||||
| johnli-helloworld | 3 | +46/-15 | 5 |
|
||||
| frrist | 1 | +28/-23 | 2 |
|
||||
| Jennifer | 4 | +31/-2 | 4 |
|
||||
| wangchao | 1 | +1/-27 | 1 |
|
||||
| Jiaying Wang | 2 | +7/-21 | 2 |
|
||||
| hannahhoward | 3 | +21/-2 | 3 |
|
||||
| chadwick2143 | 1 | +15/-1 | 1 |
|
||||
| Jerry | 2 | +9/-4 | 2 |
|
||||
| @magik6k | 23 | +5040/-8389 | 114 |
|
||||
| @aarshkshah1992 | 11 | +4859/-1078 | 101 |
|
||||
| @raulk | 5 | +4170/-1662 | 104 |
|
||||
| @vyzo | 30 | +1092/-702 | 49 |
|
||||
| @nonsense | 6 | +630/-472 | 19 |
|
||||
| @ZenGround0 | 31 | +556/-274 | 74 |
|
||||
| @He Weidong | 16 | +680/-128 | 16 |
|
||||
| @raulk | 16 | +444/-277 | 49 |
|
||||
| @Stebalien | 11 | +403/-259 | 48 |
|
||||
| @jennijuju| 17 | +276/-281 | 42 |
|
||||
| @dirkmc | 5 | +204/-138 | 20 |
|
||||
| @placer14 | 7 | +178/-77 | 17 |
|
||||
| @BlocksOnAChain | 1 | +138/-0 | 1 |
|
||||
| @Frrist | 1 | +63/-56 | 2 |
|
||||
| @arajasek | 7 | +74/-42 | 13 |
|
||||
| @frrist | 2 | +67/-6 | 6 |
|
||||
| @hannahhoward | 2 | +13/-11 | 3 |
|
||||
| @coryschwartz | 1 | +16/-6 | 3 |
|
||||
| @whyrusleeping | 1 | +7/-7 | 1 |
|
||||
| @hunjixin | 1 | +8/-6 | 1 |
|
||||
| @aarshkshah1992 | 1 | +6/-6 | 2 |
|
||||
| @dirkmc | 2 | +8/-0 | 2 |
|
||||
| @mx | 2 | +6/-1 | 2 |
|
||||
| @travisperson | 1 | +3/-2 | 1 |
|
||||
| @jennijuju | 2 | +2/-2 | 2 |
|
||||
| @ribasushi | 1 | +1/-2 | 2 |
|
||||
|
||||
# 1.11.1 / 2021-08-16
|
||||
|
||||
> Note: for discussion about this release, please comment [here](https://github.com/filecoin-project/lotus/discussions/6904)
|
||||
|
||||
This is a **highly recommended** but optional Lotus v1.11.1 release that introduces many deal making and datastore improvements and new features along with other bug fixes.
|
||||
|
||||
## Highlights
|
||||
- ⭐️⭐️⭐️[**lotus-miner market subsystem**](https://docs.filecoin.io/mine/lotus/split-markets-miners/#frontmatter-title) is introduced in this release! It is **highly recommended** for storage providers to run markets processes on a separate machine! Doing so, only this machine needs to exposes public ports for deal making. This also means that the other miner operations can now be completely isolated by from the deal making processes and storage providers can stop and restarts the markets process without affecting an ongoing Winning/Window PoSt!
|
||||
- More details on the concepts, architecture and how to split the market process can be found [here](https://docs.filecoin.io/mine/lotus/split-markets-miners/#concepts).
|
||||
- Base on your system setup(running on separate machines, same machine and so on), please see the suggested practice by community members [here](https://github.com/filecoin-project/lotus/discussions/7047#discussion-3515335).
|
||||
- Note: if you are running lotus-worker on a different machine, you will need to set `MARKETS_API_INFO` for certain CLI to work properly. This will be improved by #7072.
|
||||
- Huge thanks to MinerX fellows for [helping testing the implementation, reporting the issues so they were fixed by now and providing feedbacks](https://github.com/filecoin-project/lotus/discussions/6861) to user docs in the past three weeks!
|
||||
- Config for collateral from miner available balance ([filecoin-project/lotus#6629](https://github.com/filecoin-project/lotus/pull/6629))
|
||||
- Better control your sector collateral payment by setting `CollateralFromMinerBalance`, `AvailableBalanceBuffer` and `DisableCollateralFallback`.
|
||||
- `CollateralFromMinerBalance`: whether to use available miner balance for sector collateral instead of sending it with each message, default is `false`.
|
||||
- `AvailableBalanceBuffer`: minimum available balance to keep in the miner actor before sending it with messages, default is 0FIL.
|
||||
- `DisableCollateralFallback`: whether to send collateral with messages even if there is no available balance in the miner actor, default is `false`.
|
||||
- Config for deal publishing control addresses ([filecoin-project/lotus#6697](https://github.com/filecoin-project/lotus/pull/6697))
|
||||
- Set `DealPublishControl` to set the wallet used for sending `PublishStorageDeals` messages, instructions [here](https://docs.filecoin.io/mine/lotus/miner-addresses/#control-addresses).
|
||||
- Config UX improvements ([filecoin-project/lotus#6848](https://github.com/filecoin-project/lotus/pull/6848))
|
||||
- You can now preview the the default and updated node config by running `lotus/lotus-miner config default/updated`
|
||||
|
||||
## New Features
|
||||
- ⭐️⭐️⭐️ Support standalone miner-market process ([filecoin-project/lotus#6356](https://github.com/filecoin-project/lotus/pull/6356))
|
||||
- **⭐️⭐️ Experimental** [Splitstore]((https://github.com/filecoin-project/lotus/blob/master/blockstore/splitstore/README.md)) (more details coming in v1.11.2! Stay tuned! Join the discussion [here](https://github.com/filecoin-project/lotus/discussions/5788) if you have questions!) :
|
||||
- Improve splitstore warmup ([filecoin-project/lotus#6867](https://github.com/filecoin-project/lotus/pull/6867))
|
||||
- Moving GC for badger ([filecoin-project/lotus#6854](https://github.com/filecoin-project/lotus/pull/6854))
|
||||
- splitstore shed utils ([filecoin-project/lotus#6811](https://github.com/filecoin-project/lotus/pull/6811))
|
||||
- fix warmup by decoupling state from message receipt walk ([filecoin-project/lotus#6841](https://github.com/filecoin-project/lotus/pull/6841))
|
||||
- Splitstore: support on-disk marksets using badger ([filecoin-project/lotus#6833](https://github.com/filecoin-project/lotus/pull/6833))
|
||||
- cache loaded block messages ([filecoin-project/lotus#6760](https://github.com/filecoin-project/lotus/pull/6760))
|
||||
- Splitstore: add retention policy option for keeping messages in the hotstore ([filecoin-project/lotus#6775](https://github.com/filecoin-project/lotus/pull/6775))
|
||||
- Introduce the LOTUS_CHAIN_BADGERSTORE_DISABLE_FSYNC envvar ([filecoin-project/lotus#6817](https://github.com/filecoin-project/lotus/pull/6817))
|
||||
- Splitstore: add support for protecting out of chain references in the blockstore ([filecoin-project/lotus#6777](https://github.com/filecoin-project/lotus/pull/6777))
|
||||
- Implement exposed splitstore ([filecoin-project/lotus#6762](https://github.com/filecoin-project/lotus/pull/6762))
|
||||
- Splitstore code reorg ([filecoin-project/lotus#6756](https://github.com/filecoin-project/lotus/pull/6756))
|
||||
- Splitstore: Some small fixes ([filecoin-project/lotus#6754](https://github.com/filecoin-project/lotus/pull/6754))
|
||||
- Splitstore Enhanchements ([filecoin-project/lotus#6474](https://github.com/filecoin-project/lotus/pull/6474))
|
||||
- lotus-shed: initial export cmd for markets related metadata ([filecoin-project/lotus#6840](https://github.com/filecoin-project/lotus/pull/6840))
|
||||
- add a very verbose -vv flag to lotus and lotus-miner. ([filecoin-project/lotus#6888](https://github.com/filecoin-project/lotus/pull/6888))
|
||||
- Add allocated sectorid vis ([filecoin-project/lotus#4638](https://github.com/filecoin-project/lotus/pull/4638))
|
||||
- add a command for compacting sector numbers bitfield ([filecoin-project/lotus#4640](https://github.com/filecoin-project/lotus/pull/4640))
|
||||
- Run `lotus-miner actor compact-allocated` to compact sector number allocations to reduce the size of the allocated sector number bitfield.
|
||||
- Add ChainGetMessagesInTipset API ([filecoin-project/lotus#6642](https://github.com/filecoin-project/lotus/pull/6642))
|
||||
- Handle the --color flag via proper global state ([filecoin-project/lotus#6743](https://github.com/filecoin-project/lotus/pull/6743))
|
||||
- Enable color by default only if os.Stdout is a TTY ([filecoin-project/lotus#6696](https://github.com/filecoin-project/lotus/pull/6696))
|
||||
- Stop outputing ANSI color on non-TTY ([filecoin-project/lotus#6694](https://github.com/filecoin-project/lotus/pull/6694))
|
||||
- Envvar to disable slash filter ([filecoin-project/lotus#6620](https://github.com/filecoin-project/lotus/pull/6620))
|
||||
- commit batch: AggregateAboveBaseFee config ([filecoin-project/lotus#6650](https://github.com/filecoin-project/lotus/pull/6650))
|
||||
- shed tool to estimate aggregate network fees ([filecoin-project/lotus#6631](https://github.com/filecoin-project/lotus/pull/6631))
|
||||
|
||||
## Bug Fixes
|
||||
- Fix padding of deals, which only partially shipped in #5988 ([filecoin-project/lotus#6683](https://github.com/filecoin-project/lotus/pull/6683))
|
||||
- fix deal concurrency test failures by upgrading graphsync and others ([filecoin-project/lotus#6724](https://github.com/filecoin-project/lotus/pull/6724))
|
||||
- fix: on randomness change, use new rand ([filecoin-project/lotus#6805](https://github.com/filecoin-project/lotus/pull/6805)) - fix: always check if StateSearchMessage returns nil ([filecoin-project/lotus#6802](https://github.com/filecoin-project/lotus/pull/6802))
|
||||
- test: fix flaky window post tests ([filecoin-project/lotus#6804](https://github.com/filecoin-project/lotus/pull/6804))
|
||||
- wrap close(wait) with sync.Once to avoid panic ([filecoin-project/lotus#6800](https://github.com/filecoin-project/lotus/pull/6800))
|
||||
- fixes #6786 segfault ([filecoin-project/lotus#6787](https://github.com/filecoin-project/lotus/pull/6787))
|
||||
- ClientRetrieve stops on cancel([filecoin-project/lotus#6739](https://github.com/filecoin-project/lotus/pull/6739))
|
||||
- Fix bugs in sectors extend --v1-sectors ([filecoin-project/lotus#6066](https://github.com/filecoin-project/lotus/pull/6066))
|
||||
- fix "lotus-seed genesis car" error "merkledag: not found" ([filecoin-project/lotus#6688](https://github.com/filecoin-project/lotus/pull/6688))
|
||||
- Get retrieval pricing input should not error out on a deal state fetch ([filecoin-project/lotus#6679](https://github.com/filecoin-project/lotus/pull/6679))
|
||||
- Fix more CID double-encoding as hex ([filecoin-project/lotus#6680](https://github.com/filecoin-project/lotus/pull/6680))
|
||||
- storage: Fix FinalizeSector with sectors in stoage paths ([filecoin-project/lotus#6653](https://github.com/filecoin-project/lotus/pull/6653))
|
||||
- Fix tiny error in check-client-datacap ([filecoin-project/lotus#6664](https://github.com/filecoin-project/lotus/pull/6664))
|
||||
- Fix: precommit_batch method used the wrong cfg.CommitBatchWait ([filecoin-project/lotus#6658](https://github.com/filecoin-project/lotus/pull/6658))
|
||||
- fix ticket expiration check ([filecoin-project/lotus#6635](https://github.com/filecoin-project/lotus/pull/6635))
|
||||
- remove precommit check in handleCommitFailed ([filecoin-project/lotus#6634](https://github.com/filecoin-project/lotus/pull/6634))
|
||||
- fix prove commit aggregate send token amount ([filecoin-project/lotus#6625](https://github.com/filecoin-project/lotus/pull/6625))
|
||||
|
||||
## Improvements
|
||||
- Eliminate inefficiency in markets logging ([filecoin-project/lotus#6895](https://github.com/filecoin-project/lotus/pull/6895))
|
||||
- rename `cmd/lotus{-storage=>}-miner` to match binary. ([filecoin-project/lotus#6886](https://github.com/filecoin-project/lotus/pull/6886))
|
||||
- fix racy TestSimultanenousTransferLimit. ([filecoin-project/lotus#6862](https://github.com/filecoin-project/lotus/pull/6862))
|
||||
- ValidateBlock: Assert that block header height's are greater than parents ([filecoin-project/lotus#6872](https://github.com/filecoin-project/lotus/pull/6872))
|
||||
- feat: Don't panic when api impl is nil ([filecoin-project/lotus#6857](https://github.com/filecoin-project/lotus/pull/6857))
|
||||
- add docker-compose file ([filecoin-project/lotus#6544](https://github.com/filecoin-project/lotus/pull/6544))
|
||||
- easy way to make install app ([filecoin-project/lotus#5183](https://github.com/filecoin-project/lotus/pull/5183))
|
||||
- api: Separate the Net interface from Common ([filecoin-project/lotus#6627](https://github.com/filecoin-project/lotus/pull/6627)) - add StateReadState to gateway api ([filecoin-project/lotus#6818](https://github.com/filecoin-project/lotus/pull/6818))
|
||||
- add SealProof in SectorBuilder ([filecoin-project/lotus#6815](https://github.com/filecoin-project/lotus/pull/6815))
|
||||
- sealing: Handle preCommitParams errors more correctly ([filecoin-project/lotus#6763](https://github.com/filecoin-project/lotus/pull/6763))
|
||||
- ClientFindData: always fetch peer id from chain ([filecoin-project/lotus#6807](https://github.com/filecoin-project/lotus/pull/6807))
|
||||
- test: handle null blocks in TestForkRefuseCall ([filecoin-project/lotus#6758](https://github.com/filecoin-project/lotus/pull/6758))
|
||||
- Add more deal details to lotus-miner info ([filecoin-project/lotus#6708](https://github.com/filecoin-project/lotus/pull/6708))
|
||||
- add election backtest ([filecoin-project/lotus#5950](https://github.com/filecoin-project/lotus/pull/5950))
|
||||
- add dollar sign ([filecoin-project/lotus#6690](https://github.com/filecoin-project/lotus/pull/6690))
|
||||
- get-actor cli spelling fix ([filecoin-project/lotus#6681](https://github.com/filecoin-project/lotus/pull/6681))
|
||||
- polish(statetree): accept a context in statetree diff for timeouts ([filecoin-project/lotus#6639](https://github.com/filecoin-project/lotus/pull/6639))
|
||||
- Add helptext to lotus chain export ([filecoin-project/lotus#6672](https://github.com/filecoin-project/lotus/pull/6672))
|
||||
- add an incremental nonce itest. ([filecoin-project/lotus#6663](https://github.com/filecoin-project/lotus/pull/6663))
|
||||
- commit batch: Initialize the FailedSectors map ([filecoin-project/lotus#6647](https://github.com/filecoin-project/lotus/pull/6647))
|
||||
- Fast-path retry submitting commit aggregate if commit is still valid ([filecoin-project/lotus#6638](https://github.com/filecoin-project/lotus/pull/6638))
|
||||
- Reuse timers in sealing batch logic ([filecoin-project/lotus#6636](https://github.com/filecoin-project/lotus/pull/6636))
|
||||
|
||||
## Dependency Updates
|
||||
- Update to proof v8.0.3 ([filecoin-project/lotus#6890](https://github.com/filecoin-project/lotus/pull/6890))
|
||||
- update to go-fil-market v1.6.0 ([filecoin-project/lotus#6885](https://github.com/filecoin-project/lotus/pull/6885))
|
||||
- Bump go-multihash, adjust test for supported version ([filecoin-project/lotus#6674](https://github.com/filecoin-project/lotus/pull/6674))
|
||||
- github.com/filecoin-project/go-data-transfer (v1.6.0 -> v1.7.2):
|
||||
- github.com/filecoin-project/go-fil-markets (v1.5.0 -> v1.6.2):
|
||||
- github.com/filecoin-project/go-padreader (v0.0.0-20200903213702-ed5fae088b20 -> v0.0.0-20210723183308-812a16dc01b1)
|
||||
- github.com/filecoin-project/go-state-types (v0.1.1-0.20210506134452-99b279731c48 -> v0.1.1-0.20210810190654-139e0e79e69e)
|
||||
- github.com/filecoin-project/go-statemachine (v0.0.0-20200925024713-05bd7c71fbfe -> v1.0.1)
|
||||
- update go-libp2p-pubsub to v0.5.0 ([filecoin-project/lotus#6764](https://github.com/filecoin-project/lotus/pull/6764))
|
||||
|
||||
## Others
|
||||
- Master->v1.11.1 ([filecoin-project/lotus#7051](https://github.com/filecoin-project/lotus/pull/7051))
|
||||
- v1.11.1-rc2 ([filecoin-project/lotus#6966](https://github.com/filecoin-project/lotus/pull/6966))
|
||||
- Backport master -> v1.11.1 ([filecoin-project/lotus#6965](https://github.com/filecoin-project/lotus/pull/6965))
|
||||
- Fixes in master -> release ([filecoin-project/lotus#6933](https://github.com/filecoin-project/lotus/pull/6933))
|
||||
- Add changelog for v1.11.1-rc1 and bump the version ([filecoin-project/lotus#6900](https://github.com/filecoin-project/lotus/pull/6900))
|
||||
- Fix merge release -> v1.11.1 ([filecoin-project/lotus#6897](https://github.com/filecoin-project/lotus/pull/6897))
|
||||
- Update RELEASE_ISSUE_TEMPLATE.md ([filecoin-project/lotus#6880](https://github.com/filecoin-project/lotus/pull/6880))
|
||||
- Add github actions for staled pr ([filecoin-project/lotus#6879](https://github.com/filecoin-project/lotus/pull/6879))
|
||||
- Update issue templates and add templates for M1 ([filecoin-project/lotus#6856](https://github.com/filecoin-project/lotus/pull/6856))
|
||||
- Fix links in issue templates
|
||||
- Update issue templates to forms ([filecoin-project/lotus#6798](https://github.com/filecoin-project/lotus/pull/6798)
|
||||
- Nerpa v13 upgrade ([filecoin-project/lotus#6837](https://github.com/filecoin-project/lotus/pull/6837))
|
||||
- add docker-compose file ([filecoin-project/lotus#6544](https://github.com/filecoin-project/lotus/pull/6544))
|
||||
- release -> master ([filecoin-project/lotus#6828](https://github.com/filecoin-project/lotus/pull/6828))
|
||||
- Resurrect CODEOWNERS, but for maintainers group ([filecoin-project/lotus#6773](https://github.com/filecoin-project/lotus/pull/6773))
|
||||
- Master disclaimer ([filecoin-project/lotus#6757](https://github.com/filecoin-project/lotus/pull/6757))
|
||||
- Create stale.yml ([filecoin-project/lotus#6747](https://github.com/filecoin-project/lotus/pull/6747))
|
||||
- Release template: Update all testnet infra at once ([filecoin-project/lotus#6710](https://github.com/filecoin-project/lotus/pull/6710))
|
||||
- Release Template: remove binary validation step ([filecoin-project/lotus#6709](https://github.com/filecoin-project/lotus/pull/6709))
|
||||
- Reset of the interop network ([filecoin-project/lotus#6689](https://github.com/filecoin-project/lotus/pull/6689))
|
||||
- Update version.go to 1.11.1 ([filecoin-project/lotus#6621](https://github.com/filecoin-project/lotus/pull/6621))
|
||||
|
||||
## Contributors
|
||||
|
||||
| Contributor | Commits | Lines ± | Files Changed |
|
||||
|-------------|---------|---------|---------------|
|
||||
| @vyzo | 313 | +8928/-6010 | 415 |
|
||||
| @nonsense | 103 | +6041/-4041 | 304 |
|
||||
| @magik6k | 37 | +3851/-1611 | 146 |
|
||||
| @ZenGround0 | 24 | +1693/-1394 | 95 |
|
||||
| @placer14 | 1 | +2310/-578 | 8 |
|
||||
| @dirkmc | 7 | +1154/-726 | 29 |
|
||||
| @raulk | 44 | +969/-616 | 141 |
|
||||
| @jennijuju | 15 | +682/-354 | 47 |
|
||||
| @ribasushi | 18 | +469/-273 | 64 |
|
||||
| @coryschwartz | 5 | +576/-135 | 14 |
|
||||
| @hunjixin | 7 | +404/-82 | 19 |
|
||||
| @dirkmc | 17 | +348/-47 | 17 |
|
||||
| @tchardin | 2 | +262/-34 | 5 |
|
||||
| @aarshkshah1992 | 9 | +233/-63 | 44 |
|
||||
| @Kubuxu | 4 | +254/-16 | 4 |
|
||||
| @hannahhoward | 6 | +163/-75 | 8 |
|
||||
| @whyrusleeping | 4 | +157/-16 | 9 |
|
||||
| @Whyrusleeping | 2 | +87/-66 | 10 |
|
||||
| @arajasek | 10 | +81/-53 | 13 |
|
||||
| @zgfzgf | 2 | +104/-4 | 2 |
|
||||
| @aarshkshah1992 | 6 | +85/-19 | 10 |
|
||||
| @llifezou | 4 | +59/-20 | 4 |
|
||||
| @Stebalien | 7 | +47/-17 | 9 |
|
||||
| @johnli-helloworld | 3 | +46/-15 | 5 |
|
||||
| @frrist | 1 | +28/-23 | 2 |
|
||||
| @hannahhoward | 4 | +46/-5 | 11 |
|
||||
| @Jennifer | 4 | +31/-2 | 4 |
|
||||
| @wangchao | 1 | +1/-27 | 1 |
|
||||
| @jennijuju | 2 | +7/-21 | 2 |
|
||||
| @chadwick2143 | 1 | +15/-1 | 1 |
|
||||
| @Jerry | 2 | +9/-4 | 2 |
|
||||
| Steve Loeppky | 2 | +12/-0 | 2 |
|
||||
| David Dias | 1 | +9/-0 | 1 |
|
||||
| dependabot[bot] | 1 | +3/-3 | 1 |
|
||||
| zhoutian527 | 1 | +2/-2 | 1 |
|
||||
| xloem | 1 | +4/-0 | 1 |
|
||||
| Travis Person | 2 | +2/-2 | 3 |
|
||||
| @travisperson| 2 | +2/-2 | 3 |
|
||||
| Liviu Damian | 2 | +2/-2 | 2 |
|
||||
| Jim Pick | 2 | +2/-2 | 2 |
|
||||
| @jimpick | 2 | +2/-2 | 2 |
|
||||
| Frank | 1 | +3/-0 | 1 |
|
||||
| turuslan | 1 | +1/-1 | 1 |
|
||||
| Kirk Baird | 1 | +0/-0 | 1 |
|
||||
|
||||
|
||||
|
||||
# 1.11.0 / 2021-07-22
|
||||
|
||||
This is a **highly recommended** release of Lotus that have many bug fixes, improvements and new features.
|
||||
@@ -434,13 +693,6 @@ Contributors
|
||||
| @zhoutian527 | 1 | +2/-2 | 1 |
|
||||
| @ribasushi| 1 | +1/-1 | 1 |
|
||||
|
||||
|
||||
<<<<<<< HEAD
|
||||
||||||| merged common ancestors
|
||||
>>>>>>>>> Temporary merge branch 2
|
||||
=======
|
||||
>>>>>>> releases
|
||||
>>>>>>> releases
|
||||
# 1.10.0 / 2021-06-23
|
||||
|
||||
This is a mandatory release of Lotus that introduces Filecoin network v13, codenamed the HyperDrive upgrade. The
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [`lotus` Release Flow](#lotus-release-flow)
|
||||
- [Purpose](#purpose)
|
||||
- [High-level Summary](#high-level-summary)
|
||||
- [Motivation and Requirements](#motivation-and-requirements)
|
||||
- [Adopted Conventions](#adopted-conventions)
|
||||
- [Major Releases](#major-releases)
|
||||
- [Mandatory Releases](#mandatory-releases)
|
||||
- [Feature Releases](#feature-releases)
|
||||
- [Examples Scenarios](#examples-scenarios)
|
||||
- [Release Cycle](#release-cycle)
|
||||
- [Patch Releases](#patch-releases)
|
||||
- [Performing a Release](#performing-a-release)
|
||||
- [Security Fix Policy](#security-fix-policy)
|
||||
- [FAQ](#faq)
|
||||
- [Why aren't Go major versions used more?](#why-arent-go-major-versions-used-more)
|
||||
- [Related Items](#related-items)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
# `lotus` Release Flow
|
||||
|
||||
## Purpose
|
||||
|
||||
This document aims to describe how the Lotus team plans to ship releases of the Lotus implementation of Filecoin network. Interested parties can expect new releases to come out as described in this document.
|
||||
|
||||
## High-level Summary
|
||||
|
||||
- **Major releases** (1.0.0, 2.0.0, etc.) are reserved for significant changes to the Filecoin protocol that transform the network and its usecases. Such changes could include the addition of new Virtual Machines to the protocol, major upgrades to the Proofs of Replication used by Filecoin, etc.
|
||||
- Even minor releases (1.2.0, 1.4.0, etc.) of the Lotus software correspond to **mandatory releases** as they ship Filecoin network upgrades. Users **must** upgrade to these releases before a certain time in order to keep in sync with the Filecoin network. We aim to ensure there is at least one week before the upgrade deadline.
|
||||
- Patch versions of even minor releases (1.2.1, 1.4.2, etc.) correspond to **hotfix releases**. Such releases will only be shipped when a critical fix is needed to be applied on top of a mandatory release.
|
||||
- Odd minor releases (1.3.0, 1.5.0, etc.), as well as patch releases in these series (1.3.1, 1.5.2, etc.) correspond to **feature releases** with new development and bugfixes. These releases are not mandatory, but still highly recommended, **as they may contain critical security fixes**.
|
||||
- We aim to ship a new feature release of the Lotus software every 3 weeks, so users can expect a regular cadence of Lotus feature releases. Note that mandatory releases for network upgrades may disrupt this schedule.
|
||||
|
||||
## Motivation and Requirements
|
||||
|
||||
Our primary motivation is for users of the Lotus software (storage providers, storage clients, developers, etc.) to have a clear idea about when they can expect Lotus releases, and what they can expect in a release.
|
||||
|
||||
In order to achieve this, we need the following from our release process and conventions:
|
||||
|
||||
- Lotus version conventions make it immediately obvious whether a new Lotus release is mandatory or not. A release is mandatory if it ships a network upgrade to the Filecoin protocol.
|
||||
- The ability to ship critical fixes on top of mandatory releases, so as to avoid forcing users to consume larger unrelated changes.
|
||||
- A regular cadence of feature releases, so that users can know when a new Lotus release will be available for consumption.
|
||||
- The ability to ship any number of feature releases between two mandatory releases.
|
||||
- A clear description of the various stages of testing that a Lotus Release Candidate (RC) goes through.
|
||||
- Lotus Release issues will present a single source of truth for what may be contained in Lotus releases, including security fixes, and how they will be disclosed.
|
||||
|
||||
## Adopted Conventions
|
||||
|
||||
This section describes the conventions we have adopted. Users of Lotus are encouraged to review this in detail so as to be informed when new Lotus releases are shipped.
|
||||
|
||||
### Major Releases
|
||||
|
||||
Bumps to the Lotus major version number (1.0.0, 2.0.0, etc.) are reserved for significant changes to the Filecoin protocol that dramatically transform the network itself. Such changes could include the addition of new Virtual Machines to the protocol, major upgrades to the Proofs of Replication used by Filecoin, etc. These releases are expected to take lots of time to develop and will be rare. See also "Why aren't Go major versions used more?" below.
|
||||
|
||||
### Mandatory Releases
|
||||
|
||||
Even bumps to the Lotus minor version number (1.2.0, 1.4.0, etc.) are reserved for **mandatory releases** that ship Filecoin network upgrades. Users **must** upgrade to these releases before a certain time in order to keep in sync with the Filecoin network.
|
||||
|
||||
Depending on the scope of the upgrade, these releases may take up to several weeks to fully develop and test. We aim to ensure there are at least 2 weeks between the publication of the final Lotus release and the Filecoin network upgrade deadline.
|
||||
|
||||
These releases do not follow a regular cadence, as they are developed in lockstep with the other implementations of the Filecoin protocol. As of August 2021, the developers aim to ship 3-4 Filecoin network upgrades a year, though smaller security-critical upgrades may occur unpredictably.
|
||||
|
||||
Mandatory releases are somewhat sensitive since all Lotus users are forced to upgrade to them at the same time. As a result, they will be shipped on top of the most recent stable release of Lotus, which will generally be the latest Lotus release that has been in production for more than 2 weeks. (Note: given this rule, the basis of a mandatory release could be a mandatory release or a feature release depending on timing). Mandatory releases will not include any new feature development or bugfixes that haven't already baked in production for 2+ weeks, except for the changes needed for the network upgrade itself. Further, any critical fixes that are needed after the network upgrade will be shipped as patch version bumps to the mandatory release (1.2.1, 1.2.2, etc.) This prevents users from being forced to quickly digest unnecessary changes.
|
||||
|
||||
Users should generally aim to always upgrade to a new even minor version release since they either introduce a mandatory network upgrade or a critical fix.
|
||||
|
||||
### Feature Releases
|
||||
|
||||
All releases under an odd minor version number indicate **feature releases**. These could include releases such as 1.3.0, 1.3.1, 1.5.2, etc.
|
||||
|
||||
Feature releases include new development and bug fixes. They are not mandatory, but still highly recommended, **as they may contain critical security fixes**. Note that some of these releases may be very small patch releases that include critical hotfixes. There is no way to distinguish between a bug fix release and a feature release on the "feature" version. Both cases will use the "patch" version number.
|
||||
|
||||
We aim to ship a new feature release of the Lotus software from our development (master) branch every 3 weeks, so users can expect a regular cadence of Lotus feature releases. Note that mandatory releases for network upgrades may disrupt this schedule. For more, see the Release Cycle section (TODO: Link).
|
||||
|
||||
### Examples Scenarios
|
||||
|
||||
- **Scenario 1**: **Lotus 1.12.0 shipped a network upgrade, and no network upgrades are needed for a long while.**
|
||||
|
||||
In this case, the next feature release will be Lotus 1.13.0. In three-week intervals, we will ship Lotus 1.13.1, 1.13.2, and 1.13.3, all containing new features and bug fixes.
|
||||
|
||||
Let us assume that after the release of 1.13.3, a critical issue is discovered and a hotfix quickly developed. This hotfix will then be shipped in **both** 1.12.1 and 1.13.4. Users who have already upgrade to the 1.13 series can simply upgrade to 1.13.4. Users who have chosen to still be on 1.12.0, however, can use 1.12.1 to patch the critical issue without being forced to consume all the changes in the 1.13 series.
|
||||
|
||||
- **Scenario 2**: **Lotus 1.12.0 shipped a network upgrade, but the need for an unexpected network upgrade soon arises**
|
||||
|
||||
In this case, the Lotus 1.13 series will be dropped entirely, including any RCs that may have been undergoing testing. Instead, the network upgrade will be shipped as Lotus 1.14.0, built on top of Lotus 1.12.0. It will thus include no unnecessary changes, only the work needed to support the new network upgrade.
|
||||
|
||||
Any changes that were being worked on in the 1.13.0 series will then get applied on top of Lotus 1.14.0 and get shipped as Lotus 1.15.0.
|
||||
|
||||
## Release Cycle
|
||||
|
||||
A mandatory release process should take about 3-6 weeks, depending on the amount and the overall complexity of new features being introduced to the network protocol. It may also be shorter if there is a network incident that requires an emergency upgrade. A feature release process should take about 2-3 weeks.
|
||||
|
||||
The start time of the mandatory release process is subject to the network upgrade timeline. We will start a new feature release process every 3 weeks on Tuesdays, regardless of when the previous release landed unless it's still ongoing.
|
||||
|
||||
### Patch Releases
|
||||
|
||||
**Mandatory Release**
|
||||
|
||||
If we encounter a serious bug in a mandatory release post a network upgrade, we will create a patch release based on this release. Strictly only the fix to the bug will be included in the patch, and the bug fix will be backported to the master (dev) branch, and any ongoing feature release branch if applicable.
|
||||
|
||||
Patch release process for the mandatory releases will follow a compressed release cycle from hours to days depending on the severity and the impact to the network of the bug. In a patch release:
|
||||
|
||||
1. Automated and internal testing (stage 0 and 1) will be compressed into a few hours.
|
||||
2. Stage 2-3 will be skipped or shortened case by case.
|
||||
|
||||
Some patch releases, especially ones fixing one or more complex bugs that doesn't require a follow-up mandatory upgrade, may undergo the full release process.
|
||||
|
||||
**Feature Release**
|
||||
|
||||
Patch releases in odd minor releases (1.3.0, 1.5.0, etc.) like 1.3.1, 1.5.2 and etc are corresponding to another **feature releases** with new development and bugfixes. These releases are not mandatory, but still **highly** recommended, **as they may contain critical security fixes**.
|
||||
|
||||
---
|
||||
|
||||
### Performing a Release
|
||||
|
||||
At the beginning of each release cycle, we will generate our "Release tracking issue", which is populated with the content at [https://github.com/filecoin-project/lotus/blob/master/documentation/misc/RELEASE_ISSUE_TEMPLATE.md](https://github.com/filecoin-project/lotus/blob/master/documentation/misc/RELEASE_ISSUE_TEMPLATE.md)
|
||||
|
||||
This template will be used to track major goals we have, a planned shipping date, and a complete release checklist tied to a specific release.
|
||||
|
||||
### Security Fix Policy
|
||||
|
||||
Any release may contain security fixes. Unless the fix addresses a bug being exploited in the wild, the fix will *not* be called out in the release notes. Please make sure to update ASAP.
|
||||
|
||||
By policy, the team will usually wait until about 3 weeks after the final release to announce any fixed security issues. However, depending on the impact and ease of discovery of the issue, the team may wait more or less time.
|
||||
|
||||
It is important to always update to the latest version ASAP and file issues if you're unable to update for some reason.
|
||||
|
||||
Finally, unless a security issue is actively being exploited or a significant number of users are unable to update to the latest version (e.g., due to a difficult migration, breaking changes, etc.), security fixes will *not* be backported to previous releases.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Why aren't Go major versions used more?
|
||||
|
||||
Golang tightly couples source code with versioning (major versions beyond v1 leak into import paths). This poses logistical difficulties to using major versions here. Concretely, if we were to pick a policy that bumped the major version on every network upgrade, we would disrupt every single downstream library/application that consumed the native Lotus API (e.g., libraries depending on the JSON-RPC client, testground tests). They would need to update their code every single time that we released a network breaking change, even if it brought on zero expectation of breakage for the Golang APIs that they depend on. In this scenario, we are signaling breakage on the wrong API surface! We're signaling breakage on the Go level, when what breaks is the network protocol.
|
||||
|
||||
## Related Items
|
||||
|
||||
1. [Release Issue template](https://github.com/filecoin-project/lotus/blob/master/documentation/misc/RELEASE_ISSUE_TEMPLATE.md)
|
||||
2. [Lotus Release Flow Discussion](https://github.com/filecoin-project/lotus/discussions/7053): Leave a comment if you have any questions or feedbacks with regard to the lotus release flow.
|
||||
@@ -5,7 +5,9 @@ all: build
|
||||
|
||||
unexport GOFLAGS
|
||||
|
||||
GOVERSION:=$(shell go version | cut -d' ' -f 3 | sed 's/^go//' | awk -F. '{printf "%d%03d%03d", $$1, $$2, $$3}')
|
||||
GOCC?=go
|
||||
|
||||
GOVERSION:=$(shell $(GOCC) version | tr ' ' '\n' | grep go1 | sed 's/^go//' | awk -F. '{printf "%d%03d%03d", $$1, $$2, $$3}')
|
||||
ifeq ($(shell expr $(GOVERSION) \< 1016000), 1)
|
||||
$(warning Your Golang version is go$(shell expr $(GOVERSION) / 1000000).$(shell expr $(GOVERSION) % 1000000 / 1000).$(shell expr $(GOVERSION) % 1000))
|
||||
$(error Update Golang to version to at least 1.16.0)
|
||||
@@ -85,32 +87,32 @@ interopnet: build-devnets
|
||||
|
||||
lotus: $(BUILD_DEPS)
|
||||
rm -f lotus
|
||||
go build $(GOFLAGS) -o lotus ./cmd/lotus
|
||||
$(GOCC) build $(GOFLAGS) -o lotus ./cmd/lotus
|
||||
|
||||
.PHONY: lotus
|
||||
BINS+=lotus
|
||||
|
||||
lotus-miner: $(BUILD_DEPS)
|
||||
rm -f lotus-miner
|
||||
go build $(GOFLAGS) -o lotus-miner ./cmd/lotus-miner
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-miner ./cmd/lotus-miner
|
||||
.PHONY: lotus-miner
|
||||
BINS+=lotus-miner
|
||||
|
||||
lotus-worker: $(BUILD_DEPS)
|
||||
rm -f lotus-worker
|
||||
go build $(GOFLAGS) -o lotus-worker ./cmd/lotus-seal-worker
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-worker ./cmd/lotus-seal-worker
|
||||
.PHONY: lotus-worker
|
||||
BINS+=lotus-worker
|
||||
|
||||
lotus-shed: $(BUILD_DEPS)
|
||||
rm -f lotus-shed
|
||||
go build $(GOFLAGS) -o lotus-shed ./cmd/lotus-shed
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-shed ./cmd/lotus-shed
|
||||
.PHONY: lotus-shed
|
||||
BINS+=lotus-shed
|
||||
|
||||
lotus-gateway: $(BUILD_DEPS)
|
||||
rm -f lotus-gateway
|
||||
go build $(GOFLAGS) -o lotus-gateway ./cmd/lotus-gateway
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-gateway ./cmd/lotus-gateway
|
||||
.PHONY: lotus-gateway
|
||||
BINS+=lotus-gateway
|
||||
|
||||
@@ -138,19 +140,19 @@ install-app:
|
||||
|
||||
lotus-seed: $(BUILD_DEPS)
|
||||
rm -f lotus-seed
|
||||
go build $(GOFLAGS) -o lotus-seed ./cmd/lotus-seed
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-seed ./cmd/lotus-seed
|
||||
|
||||
.PHONY: lotus-seed
|
||||
BINS+=lotus-seed
|
||||
|
||||
benchmarks:
|
||||
go run github.com/whyrusleeping/bencher ./... > bench.json
|
||||
$(GOCC) run github.com/whyrusleeping/bencher ./... > bench.json
|
||||
@echo Submitting results
|
||||
@curl -X POST 'http://benchmark.kittyhawk.wtf/benchmark' -d '@bench.json' -u "${benchmark_http_cred}"
|
||||
.PHONY: benchmarks
|
||||
|
||||
lotus-pond: 2k
|
||||
go build -o lotus-pond ./lotuspond
|
||||
$(GOCC) build -o lotus-pond ./lotuspond
|
||||
.PHONY: lotus-pond
|
||||
BINS+=lotus-pond
|
||||
|
||||
@@ -161,85 +163,64 @@ lotus-pond-front:
|
||||
lotus-pond-app: lotus-pond-front lotus-pond
|
||||
.PHONY: lotus-pond-app
|
||||
|
||||
lotus-townhall:
|
||||
rm -f lotus-townhall
|
||||
go build -o lotus-townhall ./cmd/lotus-townhall
|
||||
.PHONY: lotus-townhall
|
||||
BINS+=lotus-townhall
|
||||
|
||||
lotus-townhall-front:
|
||||
(cd ./cmd/lotus-townhall/townhall && npm i && npm run build)
|
||||
.PHONY: lotus-townhall-front
|
||||
|
||||
lotus-townhall-app: lotus-touch lotus-townhall-front
|
||||
.PHONY: lotus-townhall-app
|
||||
|
||||
lotus-fountain:
|
||||
rm -f lotus-fountain
|
||||
go build -o lotus-fountain ./cmd/lotus-fountain
|
||||
go build $(GOFLAGS) -o lotus-fountain ./cmd/lotus-fountain
|
||||
go run github.com/GeertJohan/go.rice/rice append --exec lotus-fountain -i ./cmd/lotus-fountain -i ./build
|
||||
.PHONY: lotus-fountain
|
||||
BINS+=lotus-fountain
|
||||
|
||||
lotus-chainwatch:
|
||||
rm -f lotus-chainwatch
|
||||
go build $(GOFLAGS) -o lotus-chainwatch ./cmd/lotus-chainwatch
|
||||
.PHONY: lotus-chainwatch
|
||||
BINS+=lotus-chainwatch
|
||||
|
||||
lotus-bench:
|
||||
rm -f lotus-bench
|
||||
go build -o lotus-bench ./cmd/lotus-bench
|
||||
$(GOCC) build -o lotus-bench ./cmd/lotus-bench
|
||||
.PHONY: lotus-bench
|
||||
BINS+=lotus-bench
|
||||
|
||||
lotus-stats:
|
||||
rm -f lotus-stats
|
||||
go build $(GOFLAGS) -o lotus-stats ./cmd/lotus-stats
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-stats ./cmd/lotus-stats
|
||||
.PHONY: lotus-stats
|
||||
BINS+=lotus-stats
|
||||
|
||||
lotus-pcr:
|
||||
rm -f lotus-pcr
|
||||
go build $(GOFLAGS) -o lotus-pcr ./cmd/lotus-pcr
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-pcr ./cmd/lotus-pcr
|
||||
.PHONY: lotus-pcr
|
||||
BINS+=lotus-pcr
|
||||
|
||||
lotus-health:
|
||||
rm -f lotus-health
|
||||
go build -o lotus-health ./cmd/lotus-health
|
||||
$(GOCC) build -o lotus-health ./cmd/lotus-health
|
||||
.PHONY: lotus-health
|
||||
BINS+=lotus-health
|
||||
|
||||
lotus-wallet:
|
||||
rm -f lotus-wallet
|
||||
go build -o lotus-wallet ./cmd/lotus-wallet
|
||||
$(GOCC) build -o lotus-wallet ./cmd/lotus-wallet
|
||||
.PHONY: lotus-wallet
|
||||
BINS+=lotus-wallet
|
||||
|
||||
lotus-keygen:
|
||||
rm -f lotus-keygen
|
||||
go build -o lotus-keygen ./cmd/lotus-keygen
|
||||
$(GOCC) build -o lotus-keygen ./cmd/lotus-keygen
|
||||
.PHONY: lotus-keygen
|
||||
BINS+=lotus-keygen
|
||||
|
||||
testground:
|
||||
go build -tags testground -o /dev/null ./cmd/lotus
|
||||
$(GOCC) build -tags testground -o /dev/null ./cmd/lotus
|
||||
.PHONY: testground
|
||||
BINS+=testground
|
||||
|
||||
|
||||
tvx:
|
||||
rm -f tvx
|
||||
go build -o tvx ./cmd/tvx
|
||||
$(GOCC) build -o tvx ./cmd/tvx
|
||||
.PHONY: tvx
|
||||
BINS+=tvx
|
||||
|
||||
install-chainwatch: lotus-chainwatch
|
||||
install -C ./lotus-chainwatch /usr/local/bin/lotus-chainwatch
|
||||
|
||||
lotus-sim: $(BUILD_DEPS)
|
||||
rm -f lotus-sim
|
||||
go build $(GOFLAGS) -o lotus-sim ./cmd/lotus-sim
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-sim ./cmd/lotus-sim
|
||||
.PHONY: lotus-sim
|
||||
BINS+=lotus-sim
|
||||
|
||||
@@ -261,21 +242,13 @@ install-miner-service: install-miner install-daemon-service
|
||||
@echo
|
||||
@echo "lotus-miner service installed. Don't forget to run 'sudo systemctl start lotus-miner' to start it and 'sudo systemctl enable lotus-miner' for it to be enabled on startup."
|
||||
|
||||
install-chainwatch-service: install-chainwatch install-daemon-service
|
||||
mkdir -p /etc/systemd/system
|
||||
mkdir -p /var/log/lotus
|
||||
install -C -m 0644 ./scripts/lotus-chainwatch.service /etc/systemd/system/lotus-chainwatch.service
|
||||
systemctl daemon-reload
|
||||
@echo
|
||||
@echo "chainwatch service installed. Don't forget to run 'sudo systemctl start lotus-chainwatch' to start it and 'sudo systemctl enable lotus-chainwatch' for it to be enabled on startup."
|
||||
|
||||
install-main-services: install-miner-service
|
||||
|
||||
install-all-services: install-main-services install-chainwatch-service
|
||||
install-all-services: install-main-services
|
||||
|
||||
install-services: install-main-services
|
||||
|
||||
clean-daemon-service: clean-miner-service clean-chainwatch-service
|
||||
clean-daemon-service: clean-miner-service
|
||||
-systemctl stop lotus-daemon
|
||||
-systemctl disable lotus-daemon
|
||||
rm -f /etc/systemd/system/lotus-daemon.service
|
||||
@@ -287,12 +260,6 @@ clean-miner-service:
|
||||
rm -f /etc/systemd/system/lotus-miner.service
|
||||
systemctl daemon-reload
|
||||
|
||||
clean-chainwatch-service:
|
||||
-systemctl stop lotus-chainwatch
|
||||
-systemctl disable lotus-chainwatch
|
||||
rm -f /etc/systemd/system/lotus-chainwatch.service
|
||||
systemctl daemon-reload
|
||||
|
||||
clean-main-services: clean-daemon-service
|
||||
|
||||
clean-all-services: clean-main-services
|
||||
@@ -319,25 +286,25 @@ dist-clean:
|
||||
.PHONY: dist-clean
|
||||
|
||||
type-gen: api-gen
|
||||
go run ./gen/main.go
|
||||
go generate -x ./...
|
||||
$(GOCC) run ./gen/main.go
|
||||
$(GOCC) generate -x ./...
|
||||
goimports -w api/
|
||||
|
||||
method-gen: api-gen
|
||||
(cd ./lotuspond/front/src/chain && go run ./methodgen.go)
|
||||
(cd ./lotuspond/front/src/chain && $(GOCC) run ./methodgen.go)
|
||||
|
||||
actors-gen:
|
||||
go run ./chain/actors/agen
|
||||
go fmt ./...
|
||||
$(GOCC) run ./chain/actors/agen
|
||||
$(GOCC) fmt ./...
|
||||
|
||||
api-gen:
|
||||
go run ./gen/api
|
||||
$(GOCC) run ./gen/api
|
||||
goimports -w api
|
||||
goimports -w api
|
||||
.PHONY: api-gen
|
||||
|
||||
cfgdoc-gen:
|
||||
go run ./node/config/cfgdocgen > ./node/config/doc_gen.go
|
||||
$(GOCC) run ./node/config/cfgdocgen > ./node/config/doc_gen.go
|
||||
|
||||
appimage: lotus
|
||||
rm -rf appimage-builder-cache || true
|
||||
@@ -351,9 +318,9 @@ appimage: lotus
|
||||
docsgen: docsgen-md docsgen-openrpc
|
||||
|
||||
docsgen-md-bin: api-gen actors-gen
|
||||
go build $(GOFLAGS) -o docgen-md ./api/docgen/cmd
|
||||
$(GOCC) build $(GOFLAGS) -o docgen-md ./api/docgen/cmd
|
||||
docsgen-openrpc-bin: api-gen actors-gen
|
||||
go build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd
|
||||
$(GOCC) build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd
|
||||
|
||||
docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
apitypes "github.com/filecoin-project/lotus/api/types"
|
||||
"github.com/filecoin-project/lotus/journal/alerting"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
@@ -33,6 +34,10 @@ type Common interface {
|
||||
LogList(context.Context) ([]string, error) //perm:write
|
||||
LogSetLevel(context.Context, string, string) error //perm:write
|
||||
|
||||
// LogAlerts returns list of all, active and inactive alerts tracked by the
|
||||
// node
|
||||
LogAlerts(ctx context.Context) ([]alerting.Alert, error) //perm:admin
|
||||
|
||||
// MethodGroup: Common
|
||||
|
||||
// Version provides information about API provider
|
||||
|
||||
+29
-9
@@ -12,14 +12,14 @@ import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
datatransfer "github.com/filecoin-project/go-data-transfer"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/go-multistore"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/filecoin-project/go-state-types/dline"
|
||||
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
|
||||
apitypes "github.com/filecoin-project/lotus/api/types"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
marketevents "github.com/filecoin-project/lotus/markets/loggers"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/repo/imports"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_full.go -package=mocks . FullNode
|
||||
@@ -113,6 +114,11 @@ type FullNode interface {
|
||||
// will be returned.
|
||||
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) //perm:read
|
||||
|
||||
// ChainGetTipSetAfterHeight looks back for a tipset at the specified epoch.
|
||||
// If there are no blocks at the specified epoch, the first non-nil tipset at a later epoch
|
||||
// will be returned.
|
||||
ChainGetTipSetAfterHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) //perm:read
|
||||
|
||||
// ChainReadObj reads ipld nodes referenced by the specified CID from chain
|
||||
// blockstore and returns raw bytes.
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error) //perm:read
|
||||
@@ -331,7 +337,7 @@ type FullNode interface {
|
||||
// ClientImport imports file under the specified path into filestore.
|
||||
ClientImport(ctx context.Context, ref FileRef) (*ImportRes, error) //perm:admin
|
||||
// ClientRemoveImport removes file import
|
||||
ClientRemoveImport(ctx context.Context, importID multistore.StoreID) error //perm:admin
|
||||
ClientRemoveImport(ctx context.Context, importID imports.ID) error //perm:admin
|
||||
// ClientStartDeal proposes a deal with a miner.
|
||||
ClientStartDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error) //perm:admin
|
||||
// ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking.
|
||||
@@ -428,6 +434,8 @@ type FullNode interface {
|
||||
StateListMessages(ctx context.Context, match *MessageMatch, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) //perm:read
|
||||
// StateDecodeParams attempts to decode the provided params, based on the recipient actor address and method number.
|
||||
StateDecodeParams(ctx context.Context, toAddr address.Address, method abi.MethodNum, params []byte, tsk types.TipSetKey) (interface{}, error) //perm:read
|
||||
// StateEncodeParams attempts to encode the provided json params to the binary from
|
||||
StateEncodeParams(ctx context.Context, toActCode cid.Cid, method abi.MethodNum, params json.RawMessage) ([]byte, error) //perm:read
|
||||
|
||||
// StateNetworkName returns the name of the network the node is synced to
|
||||
StateNetworkName(context.Context) (dtypes.NetworkName, error) //perm:read
|
||||
@@ -723,16 +731,28 @@ type MinerSectors struct {
|
||||
|
||||
type ImportRes struct {
|
||||
Root cid.Cid
|
||||
ImportID multistore.StoreID
|
||||
ImportID imports.ID
|
||||
}
|
||||
|
||||
type Import struct {
|
||||
Key multistore.StoreID
|
||||
Key imports.ID
|
||||
Err string
|
||||
|
||||
Root *cid.Cid
|
||||
Source string
|
||||
Root *cid.Cid
|
||||
|
||||
// Source is the provenance of the import, e.g. "import", "unknown", else.
|
||||
// Currently useless but may be used in the future.
|
||||
Source string
|
||||
|
||||
// FilePath is the path of the original file. It is important that the file
|
||||
// is retained at this path, because it will be referenced during
|
||||
// the transfer (when we do the UnixFS chunking, we don't duplicate the
|
||||
// leaves, but rather point to chunks of the original data through
|
||||
// positional references).
|
||||
FilePath string
|
||||
|
||||
// CARPath is the path of the CAR file containing the DAG for this import.
|
||||
CARPath string
|
||||
}
|
||||
|
||||
type DealInfo struct {
|
||||
@@ -915,7 +935,7 @@ type RetrievalOrder struct {
|
||||
Piece *cid.Cid
|
||||
Size uint64
|
||||
|
||||
LocalStore *multistore.StoreID // if specified, get data from local store
|
||||
FromLocalCAR string // if specified, get data from a local CARv2 file.
|
||||
// TODO: support offset
|
||||
Total types.BigInt
|
||||
UnsealPrice types.BigInt
|
||||
|
||||
@@ -33,8 +33,10 @@ type Gateway interface {
|
||||
ChainHead(ctx context.Context) (*types.TipSet, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
|
||||
ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error)
|
||||
ChainGetPath(ctx context.Context, from, to types.TipSetKey) ([]*HeadChange, error)
|
||||
ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetAfterHeight(ctx context.Context, h abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error)
|
||||
ChainNotify(context.Context) (<-chan []*HeadChange, error)
|
||||
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
|
||||
GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *MessageSendSpec, tsk types.TipSetKey) (*types.Message, error)
|
||||
|
||||
+83
-3
@@ -13,13 +13,14 @@ import (
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
datatransfer "github.com/filecoin-project/go-data-transfer"
|
||||
"github.com/filecoin-project/go-fil-markets/piecestore"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/specs-actors/v2/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
|
||||
"github.com/filecoin-project/go-fil-markets/piecestore"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
|
||||
@@ -166,6 +167,48 @@ type StorageMiner interface {
|
||||
MarketPendingDeals(ctx context.Context) (PendingDealInfo, error) //perm:write
|
||||
MarketPublishPendingDeals(ctx context.Context) error //perm:admin
|
||||
|
||||
// DagstoreListShards returns information about all shards known to the
|
||||
// DAG store. Only available on nodes running the markets subsystem.
|
||||
DagstoreListShards(ctx context.Context) ([]DagstoreShardInfo, error) //perm:read
|
||||
|
||||
// DagstoreInitializeShard initializes an uninitialized shard.
|
||||
//
|
||||
// Initialization consists of fetching the shard's data (deal payload) from
|
||||
// the storage subsystem, generating an index, and persisting the index
|
||||
// to facilitate later retrievals, and/or to publish to external sources.
|
||||
//
|
||||
// This operation is intended to complement the initial migration. The
|
||||
// migration registers a shard for every unique piece CID, with lazy
|
||||
// initialization. Thus, shards are not initialized immediately to avoid
|
||||
// IO activity competing with proving. Instead, shard are initialized
|
||||
// when first accessed. This method forces the initialization of a shard by
|
||||
// accessing it and immediately releasing it. This is useful to warm up the
|
||||
// cache to facilitate subsequent retrievals, and to generate the indexes
|
||||
// to publish them externally.
|
||||
//
|
||||
// This operation fails if the shard is not in ShardStateNew state.
|
||||
// It blocks until initialization finishes.
|
||||
DagstoreInitializeShard(ctx context.Context, key string) error //perm:write
|
||||
|
||||
// DagstoreRecoverShard attempts to recover a failed shard.
|
||||
//
|
||||
// This operation fails if the shard is not in ShardStateErrored state.
|
||||
// It blocks until recovery finishes. If recovery failed, it returns the
|
||||
// error.
|
||||
DagstoreRecoverShard(ctx context.Context, key string) error //perm:write
|
||||
|
||||
// DagstoreInitializeAll initializes all uninitialized shards in bulk,
|
||||
// according to the policy passed in the parameters.
|
||||
//
|
||||
// It is recommended to set a maximum concurrency to avoid extreme
|
||||
// IO pressure if the storage subsystem has a large amount of deals.
|
||||
//
|
||||
// It returns a stream of events to report progress.
|
||||
DagstoreInitializeAll(ctx context.Context, params DagstoreInitializeAllParams) (<-chan DagstoreInitializeAllEvent, error) //perm:write
|
||||
|
||||
// DagstoreGC runs garbage collection on the DAG store.
|
||||
DagstoreGC(ctx context.Context) ([]DagstoreShardResult, error) //perm:admin
|
||||
|
||||
// RuntimeSubsystems returns the subsystems that are enabled
|
||||
// in this instance.
|
||||
RuntimeSubsystems(ctx context.Context) (MinerSubsystems, error) //perm:read
|
||||
@@ -224,6 +267,11 @@ type SectorLog struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
type SectorPiece struct {
|
||||
Piece abi.PieceInfo
|
||||
DealInfo *PieceDealInfo // nil for pieces which do not appear in deals (e.g. filler pieces)
|
||||
}
|
||||
|
||||
type SectorInfo struct {
|
||||
SectorID abi.SectorNumber
|
||||
State SectorState
|
||||
@@ -231,6 +279,7 @@ type SectorInfo struct {
|
||||
CommR *cid.Cid
|
||||
Proof []byte
|
||||
Deals []abi.DealID
|
||||
Pieces []SectorPiece
|
||||
Ticket SealTicket
|
||||
Seed SealSeed
|
||||
PreCommitMsg *cid.Cid
|
||||
@@ -336,3 +385,34 @@ type DealSchedule struct {
|
||||
StartEpoch abi.ChainEpoch
|
||||
EndEpoch abi.ChainEpoch
|
||||
}
|
||||
|
||||
// DagstoreShardInfo is the serialized form of dagstore.DagstoreShardInfo that
|
||||
// we expose through JSON-RPC to avoid clients having to depend on the
|
||||
// dagstore lib.
|
||||
type DagstoreShardInfo struct {
|
||||
Key string
|
||||
State string
|
||||
Error string
|
||||
}
|
||||
|
||||
// DagstoreShardResult enumerates results per shard.
|
||||
type DagstoreShardResult struct {
|
||||
Key string
|
||||
Success bool
|
||||
Error string
|
||||
}
|
||||
|
||||
type DagstoreInitializeAllParams struct {
|
||||
MaxConcurrency int
|
||||
IncludeSealed bool
|
||||
}
|
||||
|
||||
// DagstoreInitializeAllEvent represents an initialization event.
|
||||
type DagstoreInitializeAllEvent struct {
|
||||
Key string
|
||||
Event string // "start", "end"
|
||||
Success bool
|
||||
Error string
|
||||
Total int
|
||||
Current int
|
||||
}
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ func TestReturnTypes(t *testing.T) {
|
||||
seen[typ] = struct{}{}
|
||||
|
||||
if typ.Kind() == reflect.Interface && typ != bareIface && !typ.Implements(jmarsh) {
|
||||
t.Error("methods can't return interfaces", m.Name)
|
||||
t.Error("methods can't return interfaces or struct types not implementing json.Marshaller", m.Name)
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
|
||||
@@ -5,6 +5,7 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
abi "github.com/filecoin-project/go-state-types/abi"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
var _ = cid.Undef
|
||||
var _ = math.E
|
||||
var _ = sort.Sort
|
||||
|
||||
func (t *PaymentInfo) MarshalCBOR(w io.Writer) error {
|
||||
|
||||
+14
-5
@@ -27,7 +27,6 @@ import (
|
||||
filestore2 "github.com/filecoin-project/go-fil-markets/filestore"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
"github.com/filecoin-project/go-multistore"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
@@ -43,6 +42,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/repo/imports"
|
||||
)
|
||||
|
||||
var ExampleValues = map[reflect.Type]interface{}{
|
||||
@@ -89,7 +89,7 @@ func init() {
|
||||
addExample(pid)
|
||||
addExample(&pid)
|
||||
|
||||
multistoreIDExample := multistore.StoreID(50)
|
||||
storeIDExample := imports.ID(50)
|
||||
|
||||
addExample(bitfield.NewFromSet([]uint64{5}))
|
||||
addExample(abi.RegisteredSealProof_StackedDrg32GiBV1_1)
|
||||
@@ -120,8 +120,8 @@ func init() {
|
||||
addExample(time.Minute)
|
||||
addExample(datatransfer.TransferID(3))
|
||||
addExample(datatransfer.Ongoing)
|
||||
addExample(multistoreIDExample)
|
||||
addExample(&multistoreIDExample)
|
||||
addExample(storeIDExample)
|
||||
addExample(&storeIDExample)
|
||||
addExample(retrievalmarket.ClientEventDealAccepted)
|
||||
addExample(retrievalmarket.DealStatusNew)
|
||||
addExample(network.ReachabilityPublic)
|
||||
@@ -176,7 +176,7 @@ func init() {
|
||||
|
||||
// miner specific
|
||||
addExample(filestore2.Path(".lotusminer/fstmp123"))
|
||||
si := multistore.StoreID(12)
|
||||
si := uint64(12)
|
||||
addExample(&si)
|
||||
addExample(retrievalmarket.DealID(5))
|
||||
addExample(abi.ActorID(1000))
|
||||
@@ -271,6 +271,15 @@ func init() {
|
||||
api.SubsystemSectorStorage,
|
||||
api.SubsystemMarkets,
|
||||
})
|
||||
addExample(api.DagstoreShardResult{
|
||||
Key: "baga6ea4seaqecmtz7iak33dsfshi627abz4i4665dfuzr3qfs4bmad6dx3iigdq",
|
||||
Error: "<error>",
|
||||
})
|
||||
addExample(api.DagstoreShardInfo{
|
||||
Key: "baga6ea4seaqecmtz7iak33dsfshi627abz4i4665dfuzr3qfs4bmad6dx3iigdq",
|
||||
State: "ShardStateAvailable",
|
||||
Error: "<error>",
|
||||
})
|
||||
}
|
||||
|
||||
func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []reflect.Type) {
|
||||
|
||||
+49
-2
@@ -6,6 +6,7 @@ package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
json "encoding/json"
|
||||
reflect "reflect"
|
||||
|
||||
address "github.com/filecoin-project/go-address"
|
||||
@@ -14,7 +15,6 @@ import (
|
||||
retrievalmarket "github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
storagemarket "github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
auth "github.com/filecoin-project/go-jsonrpc/auth"
|
||||
multistore "github.com/filecoin-project/go-multistore"
|
||||
abi "github.com/filecoin-project/go-state-types/abi"
|
||||
big "github.com/filecoin-project/go-state-types/big"
|
||||
crypto "github.com/filecoin-project/go-state-types/crypto"
|
||||
@@ -24,8 +24,10 @@ import (
|
||||
apitypes "github.com/filecoin-project/lotus/api/types"
|
||||
miner "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
alerting "github.com/filecoin-project/lotus/journal/alerting"
|
||||
marketevents "github.com/filecoin-project/lotus/markets/loggers"
|
||||
dtypes "github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
imports "github.com/filecoin-project/lotus/node/repo/imports"
|
||||
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
paych "github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
@@ -343,6 +345,21 @@ func (mr *MockFullNodeMockRecorder) ChainGetTipSet(arg0, arg1 interface{}) *gomo
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetTipSet", reflect.TypeOf((*MockFullNode)(nil).ChainGetTipSet), arg0, arg1)
|
||||
}
|
||||
|
||||
// ChainGetTipSetAfterHeight mocks base method.
|
||||
func (m *MockFullNode) ChainGetTipSetAfterHeight(arg0 context.Context, arg1 abi.ChainEpoch, arg2 types.TipSetKey) (*types.TipSet, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ChainGetTipSetAfterHeight", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*types.TipSet)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ChainGetTipSetAfterHeight indicates an expected call of ChainGetTipSetAfterHeight.
|
||||
func (mr *MockFullNodeMockRecorder) ChainGetTipSetAfterHeight(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetTipSetAfterHeight", reflect.TypeOf((*MockFullNode)(nil).ChainGetTipSetAfterHeight), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// ChainGetTipSetByHeight mocks base method.
|
||||
func (m *MockFullNode) ChainGetTipSetByHeight(arg0 context.Context, arg1 abi.ChainEpoch, arg2 types.TipSetKey) (*types.TipSet, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -760,7 +777,7 @@ func (mr *MockFullNodeMockRecorder) ClientQueryAsk(arg0, arg1, arg2 interface{})
|
||||
}
|
||||
|
||||
// ClientRemoveImport mocks base method.
|
||||
func (m *MockFullNode) ClientRemoveImport(arg0 context.Context, arg1 multistore.StoreID) error {
|
||||
func (m *MockFullNode) ClientRemoveImport(arg0 context.Context, arg1 imports.ID) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ClientRemoveImport", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
@@ -979,6 +996,21 @@ func (mr *MockFullNodeMockRecorder) ID(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockFullNode)(nil).ID), arg0)
|
||||
}
|
||||
|
||||
// LogAlerts mocks base method.
|
||||
func (m *MockFullNode) LogAlerts(arg0 context.Context) ([]alerting.Alert, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LogAlerts", arg0)
|
||||
ret0, _ := ret[0].([]alerting.Alert)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LogAlerts indicates an expected call of LogAlerts.
|
||||
func (mr *MockFullNodeMockRecorder) LogAlerts(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogAlerts", reflect.TypeOf((*MockFullNode)(nil).LogAlerts), arg0)
|
||||
}
|
||||
|
||||
// LogList mocks base method.
|
||||
func (m *MockFullNode) LogList(arg0 context.Context) ([]string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -2229,6 +2261,21 @@ func (mr *MockFullNodeMockRecorder) StateDecodeParams(arg0, arg1, arg2, arg3, ar
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateDecodeParams", reflect.TypeOf((*MockFullNode)(nil).StateDecodeParams), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// StateEncodeParams mocks base method.
|
||||
func (m *MockFullNode) StateEncodeParams(arg0 context.Context, arg1 cid.Cid, arg2 abi.MethodNum, arg3 json.RawMessage) ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "StateEncodeParams", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// StateEncodeParams indicates an expected call of StateEncodeParams.
|
||||
func (mr *MockFullNodeMockRecorder) StateEncodeParams(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateEncodeParams", reflect.TypeOf((*MockFullNode)(nil).StateEncodeParams), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// StateGetActor mocks base method.
|
||||
func (m *MockFullNode) StateGetActor(arg0 context.Context, arg1 address.Address, arg2 types.TipSetKey) (*types.Actor, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
+136
-4
@@ -4,6 +4,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
@@ -13,7 +14,6 @@ import (
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||
"github.com/filecoin-project/go-multistore"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/filecoin-project/go-state-types/dline"
|
||||
@@ -27,8 +27,10 @@ import (
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||
"github.com/filecoin-project/lotus/extern/storage-sealing/sealiface"
|
||||
"github.com/filecoin-project/lotus/journal/alerting"
|
||||
marketevents "github.com/filecoin-project/lotus/markets/loggers"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/repo/imports"
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
"github.com/google/uuid"
|
||||
"github.com/ipfs/go-cid"
|
||||
@@ -62,6 +64,8 @@ type CommonStruct struct {
|
||||
|
||||
Discover func(p0 context.Context) (apitypes.OpenRPCDocument, error) `perm:"read"`
|
||||
|
||||
LogAlerts func(p0 context.Context) ([]alerting.Alert, error) `perm:"admin"`
|
||||
|
||||
LogList func(p0 context.Context) ([]string, error) `perm:"write"`
|
||||
|
||||
LogSetLevel func(p0 context.Context, p1 string, p2 string) error `perm:"write"`
|
||||
@@ -132,6 +136,8 @@ type FullNodeStruct struct {
|
||||
|
||||
ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
|
||||
ChainGetTipSetAfterHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
|
||||
ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) `perm:"read"`
|
||||
|
||||
ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `perm:"read"`
|
||||
@@ -188,7 +194,7 @@ type FullNodeStruct struct {
|
||||
|
||||
ClientQueryAsk func(p0 context.Context, p1 peer.ID, p2 address.Address) (*storagemarket.StorageAsk, error) `perm:"read"`
|
||||
|
||||
ClientRemoveImport func(p0 context.Context, p1 multistore.StoreID) error `perm:"admin"`
|
||||
ClientRemoveImport func(p0 context.Context, p1 imports.ID) error `perm:"admin"`
|
||||
|
||||
ClientRestartDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"`
|
||||
|
||||
@@ -340,6 +346,8 @@ type FullNodeStruct struct {
|
||||
|
||||
StateDecodeParams func(p0 context.Context, p1 address.Address, p2 abi.MethodNum, p3 []byte, p4 types.TipSetKey) (interface{}, error) `perm:"read"`
|
||||
|
||||
StateEncodeParams func(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) `perm:"read"`
|
||||
|
||||
StateGetActor func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) `perm:"read"`
|
||||
|
||||
StateListActors func(p0 context.Context, p1 types.TipSetKey) ([]address.Address, error) `perm:"read"`
|
||||
@@ -472,8 +480,12 @@ type GatewayStruct struct {
|
||||
|
||||
ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) ``
|
||||
|
||||
ChainGetPath func(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) ``
|
||||
|
||||
ChainGetTipSet func(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) ``
|
||||
|
||||
ChainGetTipSetAfterHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) ``
|
||||
|
||||
ChainGetTipSetByHeight func(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) ``
|
||||
|
||||
ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) ``
|
||||
@@ -603,6 +615,16 @@ type StorageMinerStruct struct {
|
||||
|
||||
CreateBackup func(p0 context.Context, p1 string) error `perm:"admin"`
|
||||
|
||||
DagstoreGC func(p0 context.Context) ([]DagstoreShardResult, error) `perm:"admin"`
|
||||
|
||||
DagstoreInitializeAll func(p0 context.Context, p1 DagstoreInitializeAllParams) (<-chan DagstoreInitializeAllEvent, error) `perm:"write"`
|
||||
|
||||
DagstoreInitializeShard func(p0 context.Context, p1 string) error `perm:"write"`
|
||||
|
||||
DagstoreListShards func(p0 context.Context) ([]DagstoreShardInfo, error) `perm:"read"`
|
||||
|
||||
DagstoreRecoverShard func(p0 context.Context, p1 string) error `perm:"write"`
|
||||
|
||||
DealsConsiderOfflineRetrievalDeals func(p0 context.Context) (bool, error) `perm:"admin"`
|
||||
|
||||
DealsConsiderOfflineStorageDeals func(p0 context.Context) (bool, error) `perm:"admin"`
|
||||
@@ -929,6 +951,17 @@ func (s *CommonStub) Discover(p0 context.Context) (apitypes.OpenRPCDocument, err
|
||||
return *new(apitypes.OpenRPCDocument), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *CommonStruct) LogAlerts(p0 context.Context) ([]alerting.Alert, error) {
|
||||
if s.Internal.LogAlerts == nil {
|
||||
return *new([]alerting.Alert), ErrNotSupported
|
||||
}
|
||||
return s.Internal.LogAlerts(p0)
|
||||
}
|
||||
|
||||
func (s *CommonStub) LogAlerts(p0 context.Context) ([]alerting.Alert, error) {
|
||||
return *new([]alerting.Alert), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *CommonStruct) LogList(p0 context.Context) ([]string, error) {
|
||||
if s.Internal.LogList == nil {
|
||||
return *new([]string), ErrNotSupported
|
||||
@@ -1171,6 +1204,17 @@ func (s *FullNodeStub) ChainGetTipSet(p0 context.Context, p1 types.TipSetKey) (*
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) ChainGetTipSetAfterHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {
|
||||
if s.Internal.ChainGetTipSetAfterHeight == nil {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
return s.Internal.ChainGetTipSetAfterHeight(p0, p1, p2)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) ChainGetTipSetAfterHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) ChainGetTipSetByHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {
|
||||
if s.Internal.ChainGetTipSetByHeight == nil {
|
||||
return nil, ErrNotSupported
|
||||
@@ -1479,14 +1523,14 @@ func (s *FullNodeStub) ClientQueryAsk(p0 context.Context, p1 peer.ID, p2 address
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) ClientRemoveImport(p0 context.Context, p1 multistore.StoreID) error {
|
||||
func (s *FullNodeStruct) ClientRemoveImport(p0 context.Context, p1 imports.ID) error {
|
||||
if s.Internal.ClientRemoveImport == nil {
|
||||
return ErrNotSupported
|
||||
}
|
||||
return s.Internal.ClientRemoveImport(p0, p1)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) ClientRemoveImport(p0 context.Context, p1 multistore.StoreID) error {
|
||||
func (s *FullNodeStub) ClientRemoveImport(p0 context.Context, p1 imports.ID) error {
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
@@ -2315,6 +2359,17 @@ func (s *FullNodeStub) StateDecodeParams(p0 context.Context, p1 address.Address,
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) StateEncodeParams(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) {
|
||||
if s.Internal.StateEncodeParams == nil {
|
||||
return *new([]byte), ErrNotSupported
|
||||
}
|
||||
return s.Internal.StateEncodeParams(p0, p1, p2, p3)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) StateEncodeParams(p0 context.Context, p1 cid.Cid, p2 abi.MethodNum, p3 json.RawMessage) ([]byte, error) {
|
||||
return *new([]byte), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) StateGetActor(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (*types.Actor, error) {
|
||||
if s.Internal.StateGetActor == nil {
|
||||
return nil, ErrNotSupported
|
||||
@@ -2986,6 +3041,17 @@ func (s *GatewayStub) ChainGetMessage(p0 context.Context, p1 cid.Cid) (*types.Me
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *GatewayStruct) ChainGetPath(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) {
|
||||
if s.Internal.ChainGetPath == nil {
|
||||
return *new([]*HeadChange), ErrNotSupported
|
||||
}
|
||||
return s.Internal.ChainGetPath(p0, p1, p2)
|
||||
}
|
||||
|
||||
func (s *GatewayStub) ChainGetPath(p0 context.Context, p1 types.TipSetKey, p2 types.TipSetKey) ([]*HeadChange, error) {
|
||||
return *new([]*HeadChange), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *GatewayStruct) ChainGetTipSet(p0 context.Context, p1 types.TipSetKey) (*types.TipSet, error) {
|
||||
if s.Internal.ChainGetTipSet == nil {
|
||||
return nil, ErrNotSupported
|
||||
@@ -2997,6 +3063,17 @@ func (s *GatewayStub) ChainGetTipSet(p0 context.Context, p1 types.TipSetKey) (*t
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *GatewayStruct) ChainGetTipSetAfterHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {
|
||||
if s.Internal.ChainGetTipSetAfterHeight == nil {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
return s.Internal.ChainGetTipSetAfterHeight(p0, p1, p2)
|
||||
}
|
||||
|
||||
func (s *GatewayStub) ChainGetTipSetAfterHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *GatewayStruct) ChainGetTipSetByHeight(p0 context.Context, p1 abi.ChainEpoch, p2 types.TipSetKey) (*types.TipSet, error) {
|
||||
if s.Internal.ChainGetTipSetByHeight == nil {
|
||||
return nil, ErrNotSupported
|
||||
@@ -3569,6 +3646,61 @@ func (s *StorageMinerStub) CreateBackup(p0 context.Context, p1 string) error {
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *StorageMinerStruct) DagstoreGC(p0 context.Context) ([]DagstoreShardResult, error) {
|
||||
if s.Internal.DagstoreGC == nil {
|
||||
return *new([]DagstoreShardResult), ErrNotSupported
|
||||
}
|
||||
return s.Internal.DagstoreGC(p0)
|
||||
}
|
||||
|
||||
func (s *StorageMinerStub) DagstoreGC(p0 context.Context) ([]DagstoreShardResult, error) {
|
||||
return *new([]DagstoreShardResult), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *StorageMinerStruct) DagstoreInitializeAll(p0 context.Context, p1 DagstoreInitializeAllParams) (<-chan DagstoreInitializeAllEvent, error) {
|
||||
if s.Internal.DagstoreInitializeAll == nil {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
return s.Internal.DagstoreInitializeAll(p0, p1)
|
||||
}
|
||||
|
||||
func (s *StorageMinerStub) DagstoreInitializeAll(p0 context.Context, p1 DagstoreInitializeAllParams) (<-chan DagstoreInitializeAllEvent, error) {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *StorageMinerStruct) DagstoreInitializeShard(p0 context.Context, p1 string) error {
|
||||
if s.Internal.DagstoreInitializeShard == nil {
|
||||
return ErrNotSupported
|
||||
}
|
||||
return s.Internal.DagstoreInitializeShard(p0, p1)
|
||||
}
|
||||
|
||||
func (s *StorageMinerStub) DagstoreInitializeShard(p0 context.Context, p1 string) error {
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *StorageMinerStruct) DagstoreListShards(p0 context.Context) ([]DagstoreShardInfo, error) {
|
||||
if s.Internal.DagstoreListShards == nil {
|
||||
return *new([]DagstoreShardInfo), ErrNotSupported
|
||||
}
|
||||
return s.Internal.DagstoreListShards(p0)
|
||||
}
|
||||
|
||||
func (s *StorageMinerStub) DagstoreListShards(p0 context.Context) ([]DagstoreShardInfo, error) {
|
||||
return *new([]DagstoreShardInfo), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *StorageMinerStruct) DagstoreRecoverShard(p0 context.Context, p1 string) error {
|
||||
if s.Internal.DagstoreRecoverShard == nil {
|
||||
return ErrNotSupported
|
||||
}
|
||||
return s.Internal.DagstoreRecoverShard(p0, p1)
|
||||
}
|
||||
|
||||
func (s *StorageMinerStub) DagstoreRecoverShard(p0 context.Context, p1 string) error {
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *StorageMinerStruct) DealsConsiderOfflineRetrievalDeals(p0 context.Context) (bool, error) {
|
||||
if s.Internal.DealsConsiderOfflineRetrievalDeals == nil {
|
||||
return false, ErrNotSupported
|
||||
|
||||
+2
-2
@@ -8,7 +8,6 @@ import (
|
||||
datatransfer "github.com/filecoin-project/go-data-transfer"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/go-multistore"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/filecoin-project/go-state-types/dline"
|
||||
@@ -22,6 +21,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
marketevents "github.com/filecoin-project/lotus/markets/loggers"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/repo/imports"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/golang/mock/mockgen -destination=v0mocks/mock_full.go -package=v0mocks . FullNode
|
||||
@@ -305,7 +305,7 @@ type FullNode interface {
|
||||
// ClientImport imports file under the specified path into filestore.
|
||||
ClientImport(ctx context.Context, ref api.FileRef) (*api.ImportRes, error) //perm:admin
|
||||
// ClientRemoveImport removes file import
|
||||
ClientRemoveImport(ctx context.Context, importID multistore.StoreID) error //perm:admin
|
||||
ClientRemoveImport(ctx context.Context, importID imports.ID) error //perm:admin
|
||||
// ClientStartDeal proposes a deal with a miner.
|
||||
ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) //perm:admin
|
||||
// ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking.
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
datatransfer "github.com/filecoin-project/go-data-transfer"
|
||||
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
"github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
"github.com/filecoin-project/go-multistore"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/filecoin-project/go-state-types/dline"
|
||||
@@ -22,6 +21,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
marketevents "github.com/filecoin-project/lotus/markets/loggers"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/repo/imports"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -121,7 +121,7 @@ type FullNodeStruct struct {
|
||||
|
||||
ClientQueryAsk func(p0 context.Context, p1 peer.ID, p2 address.Address) (*storagemarket.StorageAsk, error) `perm:"read"`
|
||||
|
||||
ClientRemoveImport func(p0 context.Context, p1 multistore.StoreID) error `perm:"admin"`
|
||||
ClientRemoveImport func(p0 context.Context, p1 imports.ID) error `perm:"admin"`
|
||||
|
||||
ClientRestartDataTransfer func(p0 context.Context, p1 datatransfer.TransferID, p2 peer.ID, p3 bool) error `perm:"write"`
|
||||
|
||||
@@ -939,14 +939,14 @@ func (s *FullNodeStub) ClientQueryAsk(p0 context.Context, p1 peer.ID, p2 address
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) ClientRemoveImport(p0 context.Context, p1 multistore.StoreID) error {
|
||||
func (s *FullNodeStruct) ClientRemoveImport(p0 context.Context, p1 imports.ID) error {
|
||||
if s.Internal.ClientRemoveImport == nil {
|
||||
return ErrNotSupported
|
||||
}
|
||||
return s.Internal.ClientRemoveImport(p0, p1)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) ClientRemoveImport(p0 context.Context, p1 multistore.StoreID) error {
|
||||
func (s *FullNodeStub) ClientRemoveImport(p0 context.Context, p1 imports.ID) error {
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
retrievalmarket "github.com/filecoin-project/go-fil-markets/retrievalmarket"
|
||||
storagemarket "github.com/filecoin-project/go-fil-markets/storagemarket"
|
||||
auth "github.com/filecoin-project/go-jsonrpc/auth"
|
||||
multistore "github.com/filecoin-project/go-multistore"
|
||||
abi "github.com/filecoin-project/go-state-types/abi"
|
||||
big "github.com/filecoin-project/go-state-types/big"
|
||||
crypto "github.com/filecoin-project/go-state-types/crypto"
|
||||
@@ -24,8 +23,10 @@ import (
|
||||
apitypes "github.com/filecoin-project/lotus/api/types"
|
||||
miner "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
alerting "github.com/filecoin-project/lotus/journal/alerting"
|
||||
marketevents "github.com/filecoin-project/lotus/markets/loggers"
|
||||
dtypes "github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
imports "github.com/filecoin-project/lotus/node/repo/imports"
|
||||
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
paych "github.com/filecoin-project/specs-actors/actors/builtin/paych"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
@@ -731,7 +732,7 @@ func (mr *MockFullNodeMockRecorder) ClientQueryAsk(arg0, arg1, arg2 interface{})
|
||||
}
|
||||
|
||||
// ClientRemoveImport mocks base method.
|
||||
func (m *MockFullNode) ClientRemoveImport(arg0 context.Context, arg1 multistore.StoreID) error {
|
||||
func (m *MockFullNode) ClientRemoveImport(arg0 context.Context, arg1 imports.ID) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ClientRemoveImport", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
@@ -950,6 +951,21 @@ func (mr *MockFullNodeMockRecorder) ID(arg0 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockFullNode)(nil).ID), arg0)
|
||||
}
|
||||
|
||||
// LogAlerts mocks base method.
|
||||
func (m *MockFullNode) LogAlerts(arg0 context.Context) ([]alerting.Alert, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "LogAlerts", arg0)
|
||||
ret0, _ := ret[0].([]alerting.Alert)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// LogAlerts indicates an expected call of LogAlerts.
|
||||
func (mr *MockFullNodeMockRecorder) LogAlerts(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogAlerts", reflect.TypeOf((*MockFullNode)(nil).LogAlerts), arg0)
|
||||
}
|
||||
|
||||
// LogList mocks base method.
|
||||
func (m *MockFullNode) LogList(arg0 context.Context) ([]string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
+2
-2
@@ -54,8 +54,8 @@ func VersionForType(nodeType NodeType) (Version, error) {
|
||||
|
||||
// semver versions of the rpc api exposed
|
||||
var (
|
||||
FullAPIVersion0 = newVer(1, 3, 1)
|
||||
FullAPIVersion1 = newVer(2, 1, 1)
|
||||
FullAPIVersion0 = newVer(1, 3, 0)
|
||||
FullAPIVersion1 = newVer(2, 1, 0)
|
||||
|
||||
MinerAPIVersion0 = newVer(1, 2, 0)
|
||||
WorkerAPIVersion0 = newVer(1, 1, 0)
|
||||
|
||||
@@ -119,6 +119,7 @@ TBD -- see [#6577](https://github.com/filecoin-project/lotus/issues/6577)
|
||||
It can also optionally compact/gc the coldstore after the copy (with the `--gc-coldstore` flag)
|
||||
and automatically rewrite the lotus config to disable splitstore (with the `--rewrite-config` flag).
|
||||
Note: the node *must be stopped* before running this command.
|
||||
- `clear` -- clears a splitstore installation for restart from snapshot.
|
||||
- `check` -- asynchronously runs a basic healthcheck on the splitstore.
|
||||
The results are appended to `<lotus-repo>/datastore/splitstore/check.txt`.
|
||||
- `info` -- prints some basic information about the splitstore.
|
||||
|
||||
@@ -21,15 +21,26 @@ type MarkSet interface {
|
||||
SetConcurrent()
|
||||
}
|
||||
|
||||
type MarkSetVisitor interface {
|
||||
MarkSet
|
||||
ObjectVisitor
|
||||
}
|
||||
|
||||
type MarkSetEnv interface {
|
||||
// Create creates a new markset within the environment.
|
||||
// name is a unique name for this markset, mapped to the filesystem in disk-backed environments
|
||||
// sizeHint is a hint about the expected size of the markset
|
||||
Create(name string, sizeHint int64) (MarkSet, error)
|
||||
// CreateVisitor is like Create, but returns a wider interface that supports atomic visits.
|
||||
// It may not be supported by some markset types (e.g. bloom).
|
||||
CreateVisitor(name string, sizeHint int64) (MarkSetVisitor, error)
|
||||
// SupportsVisitor returns true if the marksets created by this environment support the visitor interface.
|
||||
SupportsVisitor() bool
|
||||
Close() error
|
||||
}
|
||||
|
||||
func OpenMarkSetEnv(path string, mtype string) (MarkSetEnv, error) {
|
||||
switch mtype {
|
||||
case "bloom":
|
||||
return NewBloomMarkSetEnv()
|
||||
case "map":
|
||||
return NewMapMarkSetEnv()
|
||||
case "badger":
|
||||
|
||||
@@ -27,12 +27,14 @@ type BadgerMarkSet struct {
|
||||
writing map[int]map[string]struct{}
|
||||
writers int
|
||||
seqno int
|
||||
version int
|
||||
|
||||
db *badger.DB
|
||||
path string
|
||||
}
|
||||
|
||||
var _ MarkSet = (*BadgerMarkSet)(nil)
|
||||
var _ MarkSetVisitor = (*BadgerMarkSet)(nil)
|
||||
|
||||
var badgerMarkSetBatchSize = 16384
|
||||
|
||||
@@ -46,9 +48,241 @@ func NewBadgerMarkSetEnv(path string) (MarkSetEnv, error) {
|
||||
return &BadgerMarkSetEnv{path: msPath}, nil
|
||||
}
|
||||
|
||||
func (e *BadgerMarkSetEnv) Create(name string, sizeHint int64) (MarkSet, error) {
|
||||
func (e *BadgerMarkSetEnv) create(name string, sizeHint int64) (*BadgerMarkSet, error) {
|
||||
name += ".tmp"
|
||||
path := filepath.Join(e.path, name)
|
||||
|
||||
db, err := openTransientBadgerDB(path)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error creating badger db: %w", err)
|
||||
}
|
||||
|
||||
ms := &BadgerMarkSet{
|
||||
pend: make(map[string]struct{}),
|
||||
writing: make(map[int]map[string]struct{}),
|
||||
db: db,
|
||||
path: path,
|
||||
}
|
||||
ms.cond.L = &ms.mx
|
||||
|
||||
return ms, nil
|
||||
}
|
||||
|
||||
func (e *BadgerMarkSetEnv) Create(name string, sizeHint int64) (MarkSet, error) {
|
||||
return e.create(name, sizeHint)
|
||||
}
|
||||
|
||||
func (e *BadgerMarkSetEnv) CreateVisitor(name string, sizeHint int64) (MarkSetVisitor, error) {
|
||||
return e.create(name, sizeHint)
|
||||
}
|
||||
|
||||
func (e *BadgerMarkSetEnv) SupportsVisitor() bool { return true }
|
||||
|
||||
func (e *BadgerMarkSetEnv) Close() error {
|
||||
return os.RemoveAll(e.path)
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) Mark(c cid.Cid) error {
|
||||
s.mx.Lock()
|
||||
if s.pend == nil {
|
||||
s.mx.Unlock()
|
||||
return errMarkSetClosed
|
||||
}
|
||||
|
||||
write, seqno := s.put(string(c.Hash()))
|
||||
s.mx.Unlock()
|
||||
|
||||
if write {
|
||||
return s.write(seqno)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) Has(c cid.Cid) (bool, error) {
|
||||
s.mx.RLock()
|
||||
defer s.mx.RUnlock()
|
||||
|
||||
key := c.Hash()
|
||||
pendKey := string(key)
|
||||
|
||||
has, err := s.tryPending(pendKey)
|
||||
if has || err != nil {
|
||||
return has, err
|
||||
}
|
||||
|
||||
return s.tryDB(key)
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) Visit(c cid.Cid) (bool, error) {
|
||||
key := c.Hash()
|
||||
pendKey := string(key)
|
||||
|
||||
s.mx.RLock()
|
||||
|
||||
has, err := s.tryPending(pendKey)
|
||||
if has || err != nil {
|
||||
s.mx.RUnlock()
|
||||
return false, err
|
||||
}
|
||||
|
||||
has, err = s.tryDB(key)
|
||||
if has || err != nil {
|
||||
s.mx.RUnlock()
|
||||
return false, err
|
||||
}
|
||||
|
||||
// we need to upgrade the lock to exclusive in order to write; take the version count to see
|
||||
// if there was another write while we were upgrading
|
||||
version := s.version
|
||||
s.mx.RUnlock()
|
||||
|
||||
s.mx.Lock()
|
||||
// we have to do the check dance again
|
||||
has, err = s.tryPending(pendKey)
|
||||
if has || err != nil {
|
||||
s.mx.Unlock()
|
||||
return false, err
|
||||
}
|
||||
|
||||
if version != s.version {
|
||||
// something was written to the db, we need to check it
|
||||
has, err = s.tryDB(key)
|
||||
if has || err != nil {
|
||||
s.mx.Unlock()
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
write, seqno := s.put(pendKey)
|
||||
s.mx.Unlock()
|
||||
|
||||
if write {
|
||||
err = s.write(seqno)
|
||||
}
|
||||
|
||||
return true, err
|
||||
}
|
||||
|
||||
// reader holds the (r)lock
|
||||
func (s *BadgerMarkSet) tryPending(key string) (has bool, err error) {
|
||||
if s.pend == nil {
|
||||
return false, errMarkSetClosed
|
||||
}
|
||||
|
||||
if _, ok := s.pend[key]; ok {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, wr := range s.writing {
|
||||
if _, ok := wr[key]; ok {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) tryDB(key []byte) (has bool, err error) {
|
||||
err = s.db.View(func(txn *badger.Txn) error {
|
||||
_, err := txn.Get(key)
|
||||
return err
|
||||
})
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
return true, nil
|
||||
|
||||
case badger.ErrKeyNotFound:
|
||||
return false, nil
|
||||
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// writer holds the exclusive lock
|
||||
func (s *BadgerMarkSet) put(key string) (write bool, seqno int) {
|
||||
s.pend[key] = struct{}{}
|
||||
if len(s.pend) < badgerMarkSetBatchSize {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
seqno = s.seqno
|
||||
s.seqno++
|
||||
s.writing[seqno] = s.pend
|
||||
s.pend = make(map[string]struct{})
|
||||
|
||||
return true, seqno
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) write(seqno int) (err error) {
|
||||
s.mx.Lock()
|
||||
if s.pend == nil {
|
||||
s.mx.Unlock()
|
||||
return errMarkSetClosed
|
||||
}
|
||||
|
||||
pend := s.writing[seqno]
|
||||
s.writers++
|
||||
s.mx.Unlock()
|
||||
|
||||
defer func() {
|
||||
s.mx.Lock()
|
||||
defer s.mx.Unlock()
|
||||
|
||||
if err == nil {
|
||||
delete(s.writing, seqno)
|
||||
s.version++
|
||||
}
|
||||
|
||||
s.writers--
|
||||
if s.writers == 0 {
|
||||
s.cond.Broadcast()
|
||||
}
|
||||
}()
|
||||
|
||||
empty := []byte{} // not nil
|
||||
|
||||
batch := s.db.NewWriteBatch()
|
||||
defer batch.Cancel()
|
||||
|
||||
for k := range pend {
|
||||
if err = batch.Set([]byte(k), empty); err != nil {
|
||||
return xerrors.Errorf("error setting batch: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = batch.Flush()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error flushing batch to badger markset: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) Close() error {
|
||||
s.mx.Lock()
|
||||
defer s.mx.Unlock()
|
||||
|
||||
if s.pend == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for s.writers > 0 {
|
||||
s.cond.Wait()
|
||||
}
|
||||
|
||||
s.pend = nil
|
||||
db := s.db
|
||||
s.db = nil
|
||||
|
||||
return closeTransientBadgerDB(db, s.path)
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) SetConcurrent() {}
|
||||
|
||||
func openTransientBadgerDB(path string) (*badger.DB, error) {
|
||||
// clean up first
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
@@ -76,140 +310,16 @@ func (e *BadgerMarkSetEnv) Create(name string, sizeHint int64) (MarkSet, error)
|
||||
skip2: log.Desugar().WithOptions(zap.AddCallerSkip(2)).Sugar(),
|
||||
}
|
||||
|
||||
db, err := badger.Open(opts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error creating badger markset: %w", err)
|
||||
}
|
||||
|
||||
ms := &BadgerMarkSet{
|
||||
pend: make(map[string]struct{}),
|
||||
writing: make(map[int]map[string]struct{}),
|
||||
db: db,
|
||||
path: path,
|
||||
}
|
||||
ms.cond.L = &ms.mx
|
||||
|
||||
return ms, nil
|
||||
return badger.Open(opts)
|
||||
}
|
||||
|
||||
func (e *BadgerMarkSetEnv) Close() error {
|
||||
return os.RemoveAll(e.path)
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) Mark(c cid.Cid) error {
|
||||
s.mx.Lock()
|
||||
|
||||
if s.pend == nil {
|
||||
s.mx.Unlock()
|
||||
return errMarkSetClosed
|
||||
}
|
||||
|
||||
s.pend[string(c.Hash())] = struct{}{}
|
||||
|
||||
if len(s.pend) < badgerMarkSetBatchSize {
|
||||
s.mx.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
pend := s.pend
|
||||
seqno := s.seqno
|
||||
s.seqno++
|
||||
s.writing[seqno] = pend
|
||||
s.pend = make(map[string]struct{})
|
||||
s.writers++
|
||||
s.mx.Unlock()
|
||||
|
||||
defer func() {
|
||||
s.mx.Lock()
|
||||
defer s.mx.Unlock()
|
||||
|
||||
delete(s.writing, seqno)
|
||||
s.writers--
|
||||
if s.writers == 0 {
|
||||
s.cond.Broadcast()
|
||||
}
|
||||
}()
|
||||
|
||||
empty := []byte{} // not nil
|
||||
|
||||
batch := s.db.NewWriteBatch()
|
||||
defer batch.Cancel()
|
||||
|
||||
for k := range pend {
|
||||
if err := batch.Set([]byte(k), empty); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := batch.Flush()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error flushing batch to badger markset: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) Has(c cid.Cid) (bool, error) {
|
||||
s.mx.RLock()
|
||||
defer s.mx.RUnlock()
|
||||
|
||||
if s.pend == nil {
|
||||
return false, errMarkSetClosed
|
||||
}
|
||||
|
||||
key := c.Hash()
|
||||
pendKey := string(key)
|
||||
_, ok := s.pend[pendKey]
|
||||
if ok {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, wr := range s.writing {
|
||||
_, ok := wr[pendKey]
|
||||
if ok {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
err := s.db.View(func(txn *badger.Txn) error {
|
||||
_, err := txn.Get(key)
|
||||
return err
|
||||
})
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
return true, nil
|
||||
|
||||
case badger.ErrKeyNotFound:
|
||||
return false, nil
|
||||
|
||||
default:
|
||||
return false, xerrors.Errorf("error checking badger markset: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) Close() error {
|
||||
s.mx.Lock()
|
||||
defer s.mx.Unlock()
|
||||
|
||||
if s.pend == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for s.writers > 0 {
|
||||
s.cond.Wait()
|
||||
}
|
||||
|
||||
s.pend = nil
|
||||
db := s.db
|
||||
s.db = nil
|
||||
|
||||
func closeTransientBadgerDB(db *badger.DB, path string) error {
|
||||
err := db.Close()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error closing badger markset: %w", err)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(s.path)
|
||||
err = os.RemoveAll(path)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error deleting badger markset: %w", err)
|
||||
}
|
||||
@@ -217,8 +327,6 @@ func (s *BadgerMarkSet) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BadgerMarkSet) SetConcurrent() {}
|
||||
|
||||
// badger logging through go-log
|
||||
type badgerLogger struct {
|
||||
*zap.SugaredLogger
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
package splitstore
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
bbloom "github.com/ipfs/bbloom"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
const (
|
||||
BloomFilterMinSize = 10_000_000
|
||||
BloomFilterProbability = 0.01
|
||||
)
|
||||
|
||||
type BloomMarkSetEnv struct{}
|
||||
|
||||
var _ MarkSetEnv = (*BloomMarkSetEnv)(nil)
|
||||
|
||||
type BloomMarkSet struct {
|
||||
salt []byte
|
||||
mx sync.RWMutex
|
||||
bf *bbloom.Bloom
|
||||
ts bool
|
||||
}
|
||||
|
||||
var _ MarkSet = (*BloomMarkSet)(nil)
|
||||
|
||||
func NewBloomMarkSetEnv() (*BloomMarkSetEnv, error) {
|
||||
return &BloomMarkSetEnv{}, nil
|
||||
}
|
||||
|
||||
func (e *BloomMarkSetEnv) Create(name string, sizeHint int64) (MarkSet, error) {
|
||||
size := int64(BloomFilterMinSize)
|
||||
for size < sizeHint {
|
||||
size += BloomFilterMinSize
|
||||
}
|
||||
|
||||
salt := make([]byte, 4)
|
||||
_, err := rand.Read(salt)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error reading salt: %w", err)
|
||||
}
|
||||
|
||||
bf, err := bbloom.New(float64(size), BloomFilterProbability)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error creating bloom filter: %w", err)
|
||||
}
|
||||
|
||||
return &BloomMarkSet{salt: salt, bf: bf}, nil
|
||||
}
|
||||
|
||||
func (e *BloomMarkSetEnv) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BloomMarkSet) saltedKey(cid cid.Cid) []byte {
|
||||
hash := cid.Hash()
|
||||
key := make([]byte, len(s.salt)+len(hash))
|
||||
n := copy(key, s.salt)
|
||||
copy(key[n:], hash)
|
||||
rehash := sha256.Sum256(key)
|
||||
return rehash[:]
|
||||
}
|
||||
|
||||
func (s *BloomMarkSet) Mark(cid cid.Cid) error {
|
||||
if s.ts {
|
||||
s.mx.Lock()
|
||||
defer s.mx.Unlock()
|
||||
}
|
||||
|
||||
if s.bf == nil {
|
||||
return errMarkSetClosed
|
||||
}
|
||||
|
||||
s.bf.Add(s.saltedKey(cid))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BloomMarkSet) Has(cid cid.Cid) (bool, error) {
|
||||
if s.ts {
|
||||
s.mx.RLock()
|
||||
defer s.mx.RUnlock()
|
||||
}
|
||||
|
||||
if s.bf == nil {
|
||||
return false, errMarkSetClosed
|
||||
}
|
||||
|
||||
return s.bf.Has(s.saltedKey(cid)), nil
|
||||
}
|
||||
|
||||
func (s *BloomMarkSet) Close() error {
|
||||
if s.ts {
|
||||
s.mx.Lock()
|
||||
defer s.mx.Unlock()
|
||||
}
|
||||
s.bf = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BloomMarkSet) SetConcurrent() {
|
||||
s.ts = true
|
||||
}
|
||||
@@ -18,17 +18,28 @@ type MapMarkSet struct {
|
||||
}
|
||||
|
||||
var _ MarkSet = (*MapMarkSet)(nil)
|
||||
var _ MarkSetVisitor = (*MapMarkSet)(nil)
|
||||
|
||||
func NewMapMarkSetEnv() (*MapMarkSetEnv, error) {
|
||||
return &MapMarkSetEnv{}, nil
|
||||
}
|
||||
|
||||
func (e *MapMarkSetEnv) Create(name string, sizeHint int64) (MarkSet, error) {
|
||||
func (e *MapMarkSetEnv) create(name string, sizeHint int64) (*MapMarkSet, error) {
|
||||
return &MapMarkSet{
|
||||
set: make(map[string]struct{}, sizeHint),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *MapMarkSetEnv) Create(name string, sizeHint int64) (MarkSet, error) {
|
||||
return e.create(name, sizeHint)
|
||||
}
|
||||
|
||||
func (e *MapMarkSetEnv) CreateVisitor(name string, sizeHint int64) (MarkSetVisitor, error) {
|
||||
return e.create(name, sizeHint)
|
||||
}
|
||||
|
||||
func (e *MapMarkSetEnv) SupportsVisitor() bool { return true }
|
||||
|
||||
func (e *MapMarkSetEnv) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -61,6 +72,25 @@ func (s *MapMarkSet) Has(cid cid.Cid) (bool, error) {
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func (s *MapMarkSet) Visit(c cid.Cid) (bool, error) {
|
||||
if s.ts {
|
||||
s.mx.Lock()
|
||||
defer s.mx.Unlock()
|
||||
}
|
||||
|
||||
if s.set == nil {
|
||||
return false, errMarkSetClosed
|
||||
}
|
||||
|
||||
key := string(c.Hash())
|
||||
if _, ok := s.set[key]; ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
s.set[key] = struct{}{}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *MapMarkSet) Close() error {
|
||||
if s.ts {
|
||||
s.mx.Lock()
|
||||
|
||||
@@ -2,6 +2,7 @@ package splitstore
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
@@ -10,10 +11,7 @@ import (
|
||||
|
||||
func TestMapMarkSet(t *testing.T) {
|
||||
testMarkSet(t, "map")
|
||||
}
|
||||
|
||||
func TestBloomMarkSet(t *testing.T) {
|
||||
testMarkSet(t, "bloom")
|
||||
testMarkSetVisitor(t, "map")
|
||||
}
|
||||
|
||||
func TestBadgerMarkSet(t *testing.T) {
|
||||
@@ -23,16 +21,21 @@ func TestBadgerMarkSet(t *testing.T) {
|
||||
badgerMarkSetBatchSize = bs
|
||||
})
|
||||
testMarkSet(t, "badger")
|
||||
testMarkSetVisitor(t, "badger")
|
||||
}
|
||||
|
||||
func testMarkSet(t *testing.T, lsType string) {
|
||||
t.Helper()
|
||||
|
||||
path, err := ioutil.TempDir("", "sweep-test.*")
|
||||
path, err := ioutil.TempDir("", "markset.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(path)
|
||||
})
|
||||
|
||||
env, err := OpenMarkSetEnv(path, lsType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -145,3 +148,74 @@ func testMarkSet(t *testing.T, lsType string) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testMarkSetVisitor(t *testing.T, lsType string) {
|
||||
t.Helper()
|
||||
|
||||
path, err := ioutil.TempDir("", "markset.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(path)
|
||||
})
|
||||
|
||||
env, err := OpenMarkSetEnv(path, lsType)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer env.Close() //nolint:errcheck
|
||||
|
||||
visitor, err := env.CreateVisitor("test", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer visitor.Close() //nolint:errcheck
|
||||
|
||||
makeCid := func(key string) cid.Cid {
|
||||
h, err := multihash.Sum([]byte(key), multihash.SHA2_256, -1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return cid.NewCidV1(cid.Raw, h)
|
||||
}
|
||||
|
||||
mustVisit := func(v ObjectVisitor, cid cid.Cid) {
|
||||
visit, err := v.Visit(cid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !visit {
|
||||
t.Fatal("object should be visited")
|
||||
}
|
||||
}
|
||||
|
||||
mustNotVisit := func(v ObjectVisitor, cid cid.Cid) {
|
||||
visit, err := v.Visit(cid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if visit {
|
||||
t.Fatal("unexpected visit")
|
||||
}
|
||||
}
|
||||
|
||||
k1 := makeCid("a")
|
||||
k2 := makeCid("b")
|
||||
k3 := makeCid("c")
|
||||
k4 := makeCid("d")
|
||||
|
||||
mustVisit(visitor, k1)
|
||||
mustVisit(visitor, k2)
|
||||
mustVisit(visitor, k3)
|
||||
mustVisit(visitor, k4)
|
||||
|
||||
mustNotVisit(visitor, k1)
|
||||
mustNotVisit(visitor, k2)
|
||||
mustNotVisit(visitor, k3)
|
||||
mustNotVisit(visitor, k4)
|
||||
}
|
||||
|
||||
@@ -142,7 +142,6 @@ type SplitStore struct {
|
||||
txnViews int
|
||||
txnViewsWaiting bool
|
||||
txnActive bool
|
||||
txnProtect MarkSet
|
||||
txnRefsMx sync.Mutex
|
||||
txnRefs map[cid.Cid]struct{}
|
||||
txnMissing map[cid.Cid]struct{}
|
||||
@@ -174,6 +173,10 @@ func Open(path string, ds dstore.Datastore, hot, cold bstore.Blockstore, cfg *Co
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !markSetEnv.SupportsVisitor() {
|
||||
return nil, xerrors.Errorf("markset type does not support atomic visitors")
|
||||
}
|
||||
|
||||
// and now we can make a SplitStore
|
||||
ss := &SplitStore{
|
||||
cfg: cfg,
|
||||
|
||||
@@ -83,7 +83,14 @@ func (s *SplitStore) doCheck(curTs *types.TipSet) error {
|
||||
write("--")
|
||||
|
||||
var coldCnt, missingCnt int64
|
||||
err = s.walkChain(curTs, boundaryEpoch, boundaryEpoch,
|
||||
|
||||
visitor, err := s.markSetEnv.CreateVisitor("check", 0)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error creating visitor: %w", err)
|
||||
}
|
||||
defer visitor.Close() //nolint
|
||||
|
||||
err = s.walkChain(curTs, boundaryEpoch, boundaryEpoch, visitor,
|
||||
func(c cid.Cid) error {
|
||||
if isUnitaryObject(c) {
|
||||
return errStopWalk
|
||||
|
||||
@@ -211,7 +211,7 @@ func (s *SplitStore) trackTxnRefMany(cids []cid.Cid) {
|
||||
}
|
||||
|
||||
// protect all pending transactional references
|
||||
func (s *SplitStore) protectTxnRefs(markSet MarkSet) error {
|
||||
func (s *SplitStore) protectTxnRefs(markSet MarkSetVisitor) error {
|
||||
for {
|
||||
var txnRefs map[cid.Cid]struct{}
|
||||
|
||||
@@ -283,30 +283,29 @@ func (s *SplitStore) protectTxnRefs(markSet MarkSet) error {
|
||||
|
||||
// transactionally protect a reference by walking the object and marking.
|
||||
// concurrent markings are short circuited by checking the markset.
|
||||
func (s *SplitStore) doTxnProtect(root cid.Cid, markSet MarkSet) error {
|
||||
func (s *SplitStore) doTxnProtect(root cid.Cid, markSet MarkSetVisitor) error {
|
||||
if err := s.checkClosing(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Note: cold objects are deleted heaviest first, so the consituents of an object
|
||||
// cannot be deleted before the object itself.
|
||||
return s.walkObjectIncomplete(root, cid.NewSet(),
|
||||
return s.walkObjectIncomplete(root, tmpVisitor(),
|
||||
func(c cid.Cid) error {
|
||||
if isUnitaryObject(c) {
|
||||
return errStopWalk
|
||||
}
|
||||
|
||||
mark, err := markSet.Has(c)
|
||||
visit, err := markSet.Visit(c)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error checking markset: %w", err)
|
||||
return xerrors.Errorf("error visiting object: %w", err)
|
||||
}
|
||||
|
||||
// it's marked, nothing to do
|
||||
if mark {
|
||||
if !visit {
|
||||
return errStopWalk
|
||||
}
|
||||
|
||||
return markSet.Mark(c)
|
||||
return nil
|
||||
},
|
||||
func(c cid.Cid) error {
|
||||
if s.txnMissing != nil {
|
||||
@@ -382,7 +381,7 @@ func (s *SplitStore) doCompact(curTs *types.TipSet) error {
|
||||
|
||||
log.Infow("running compaction", "currentEpoch", currentEpoch, "baseEpoch", s.baseEpoch, "boundaryEpoch", boundaryEpoch, "inclMsgsEpoch", inclMsgsEpoch, "compactionIndex", s.compactionIndex)
|
||||
|
||||
markSet, err := s.markSetEnv.Create("live", s.markSetSize)
|
||||
markSet, err := s.markSetEnv.CreateVisitor("live", s.markSetSize)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error creating mark set: %w", err)
|
||||
}
|
||||
@@ -410,14 +409,23 @@ func (s *SplitStore) doCompact(curTs *types.TipSet) error {
|
||||
startMark := time.Now()
|
||||
|
||||
var count int64
|
||||
err = s.walkChain(curTs, boundaryEpoch, inclMsgsEpoch,
|
||||
err = s.walkChain(curTs, boundaryEpoch, inclMsgsEpoch, &noopVisitor{},
|
||||
func(c cid.Cid) error {
|
||||
if isUnitaryObject(c) {
|
||||
return errStopWalk
|
||||
}
|
||||
|
||||
visit, err := markSet.Visit(c)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error visiting object: %w", err)
|
||||
}
|
||||
|
||||
if !visit {
|
||||
return errStopWalk
|
||||
}
|
||||
|
||||
count++
|
||||
return markSet.Mark(c)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -578,12 +586,8 @@ func (s *SplitStore) beginTxnProtect() {
|
||||
s.txnMissing = make(map[cid.Cid]struct{})
|
||||
}
|
||||
|
||||
func (s *SplitStore) beginTxnMarking(markSet MarkSet) {
|
||||
func (s *SplitStore) beginTxnMarking(markSet MarkSetVisitor) {
|
||||
markSet.SetConcurrent()
|
||||
|
||||
s.txnLk.Lock()
|
||||
s.txnProtect = markSet
|
||||
s.txnLk.Unlock()
|
||||
}
|
||||
|
||||
func (s *SplitStore) endTxnProtect() {
|
||||
@@ -594,21 +598,14 @@ func (s *SplitStore) endTxnProtect() {
|
||||
return
|
||||
}
|
||||
|
||||
// release markset memory
|
||||
if s.txnProtect != nil {
|
||||
_ = s.txnProtect.Close()
|
||||
}
|
||||
|
||||
s.txnActive = false
|
||||
s.txnProtect = nil
|
||||
s.txnRefs = nil
|
||||
s.txnMissing = nil
|
||||
}
|
||||
|
||||
func (s *SplitStore) walkChain(ts *types.TipSet, inclState, inclMsgs abi.ChainEpoch,
|
||||
f func(cid.Cid) error) error {
|
||||
visited := cid.NewSet()
|
||||
walked := cid.NewSet()
|
||||
visitor ObjectVisitor, f func(cid.Cid) error) error {
|
||||
var walked *cid.Set
|
||||
toWalk := ts.Cids()
|
||||
walkCnt := 0
|
||||
scanCnt := 0
|
||||
@@ -616,7 +613,7 @@ func (s *SplitStore) walkChain(ts *types.TipSet, inclState, inclMsgs abi.ChainEp
|
||||
stopWalk := func(_ cid.Cid) error { return errStopWalk }
|
||||
|
||||
walkBlock := func(c cid.Cid) error {
|
||||
if !visited.Visit(c) {
|
||||
if !walked.Visit(c) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -640,19 +637,19 @@ func (s *SplitStore) walkChain(ts *types.TipSet, inclState, inclMsgs abi.ChainEp
|
||||
if inclMsgs < inclState {
|
||||
// we need to use walkObjectIncomplete here, as messages/receipts may be missing early on if we
|
||||
// synced from snapshot and have a long HotStoreMessageRetentionPolicy.
|
||||
if err := s.walkObjectIncomplete(hdr.Messages, walked, f, stopWalk); err != nil {
|
||||
if err := s.walkObjectIncomplete(hdr.Messages, visitor, f, stopWalk); err != nil {
|
||||
return xerrors.Errorf("error walking messages (cid: %s): %w", hdr.Messages, err)
|
||||
}
|
||||
|
||||
if err := s.walkObjectIncomplete(hdr.ParentMessageReceipts, walked, f, stopWalk); err != nil {
|
||||
if err := s.walkObjectIncomplete(hdr.ParentMessageReceipts, visitor, f, stopWalk); err != nil {
|
||||
return xerrors.Errorf("error walking messages receipts (cid: %s): %w", hdr.ParentMessageReceipts, err)
|
||||
}
|
||||
} else {
|
||||
if err := s.walkObject(hdr.Messages, walked, f); err != nil {
|
||||
if err := s.walkObject(hdr.Messages, visitor, f); err != nil {
|
||||
return xerrors.Errorf("error walking messages (cid: %s): %w", hdr.Messages, err)
|
||||
}
|
||||
|
||||
if err := s.walkObject(hdr.ParentMessageReceipts, walked, f); err != nil {
|
||||
if err := s.walkObject(hdr.ParentMessageReceipts, visitor, f); err != nil {
|
||||
return xerrors.Errorf("error walking message receipts (cid: %s): %w", hdr.ParentMessageReceipts, err)
|
||||
}
|
||||
}
|
||||
@@ -660,7 +657,7 @@ func (s *SplitStore) walkChain(ts *types.TipSet, inclState, inclMsgs abi.ChainEp
|
||||
|
||||
// state is only retained if within the inclState boundary, with the exception of genesis
|
||||
if hdr.Height >= inclState || hdr.Height == 0 {
|
||||
if err := s.walkObject(hdr.ParentStateRoot, walked, f); err != nil {
|
||||
if err := s.walkObject(hdr.ParentStateRoot, visitor, f); err != nil {
|
||||
return xerrors.Errorf("error walking state root (cid: %s): %w", hdr.ParentStateRoot, err)
|
||||
}
|
||||
scanCnt++
|
||||
@@ -679,6 +676,10 @@ func (s *SplitStore) walkChain(ts *types.TipSet, inclState, inclMsgs abi.ChainEp
|
||||
return err
|
||||
}
|
||||
|
||||
// the walk is BFS, so we can reset the walked set in every iteration and avoid building up
|
||||
// a set that contains all blocks (1M epochs -> 5M blocks -> 200MB worth of memory and growing
|
||||
// over time)
|
||||
walked = cid.NewSet()
|
||||
walking := toWalk
|
||||
toWalk = nil
|
||||
for _, c := range walking {
|
||||
@@ -693,8 +694,13 @@ func (s *SplitStore) walkChain(ts *types.TipSet, inclState, inclMsgs abi.ChainEp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SplitStore) walkObject(c cid.Cid, walked *cid.Set, f func(cid.Cid) error) error {
|
||||
if !walked.Visit(c) {
|
||||
func (s *SplitStore) walkObject(c cid.Cid, visitor ObjectVisitor, f func(cid.Cid) error) error {
|
||||
visit, err := visitor.Visit(c)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error visiting object: %w", err)
|
||||
}
|
||||
|
||||
if !visit {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -716,7 +722,7 @@ func (s *SplitStore) walkObject(c cid.Cid, walked *cid.Set, f func(cid.Cid) erro
|
||||
}
|
||||
|
||||
var links []cid.Cid
|
||||
err := s.view(c, func(data []byte) error {
|
||||
err = s.view(c, func(data []byte) error {
|
||||
return cbg.ScanForLinks(bytes.NewReader(data), func(c cid.Cid) {
|
||||
links = append(links, c)
|
||||
})
|
||||
@@ -727,7 +733,7 @@ func (s *SplitStore) walkObject(c cid.Cid, walked *cid.Set, f func(cid.Cid) erro
|
||||
}
|
||||
|
||||
for _, c := range links {
|
||||
err := s.walkObject(c, walked, f)
|
||||
err := s.walkObject(c, visitor, f)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error walking link (cid: %s): %w", c, err)
|
||||
}
|
||||
@@ -737,8 +743,13 @@ func (s *SplitStore) walkObject(c cid.Cid, walked *cid.Set, f func(cid.Cid) erro
|
||||
}
|
||||
|
||||
// like walkObject, but the object may be potentially incomplete (references missing)
|
||||
func (s *SplitStore) walkObjectIncomplete(c cid.Cid, walked *cid.Set, f, missing func(cid.Cid) error) error {
|
||||
if !walked.Visit(c) {
|
||||
func (s *SplitStore) walkObjectIncomplete(c cid.Cid, visitor ObjectVisitor, f, missing func(cid.Cid) error) error {
|
||||
visit, err := visitor.Visit(c)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error visiting object: %w", err)
|
||||
}
|
||||
|
||||
if !visit {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -777,7 +788,7 @@ func (s *SplitStore) walkObjectIncomplete(c cid.Cid, walked *cid.Set, f, missing
|
||||
}
|
||||
|
||||
var links []cid.Cid
|
||||
err := s.view(c, func(data []byte) error {
|
||||
err = s.view(c, func(data []byte) error {
|
||||
return cbg.ScanForLinks(bytes.NewReader(data), func(c cid.Cid) {
|
||||
links = append(links, c)
|
||||
})
|
||||
@@ -788,7 +799,7 @@ func (s *SplitStore) walkObjectIncomplete(c cid.Cid, walked *cid.Set, f, missing
|
||||
}
|
||||
|
||||
for _, c := range links {
|
||||
err := s.walkObjectIncomplete(c, walked, f, missing)
|
||||
err := s.walkObjectIncomplete(c, visitor, f, missing)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error walking link (cid: %s): %w", c, err)
|
||||
}
|
||||
@@ -984,7 +995,7 @@ func (s *SplitStore) purgeBatch(cids []cid.Cid, deleteBatch func([]cid.Cid) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SplitStore) purge(cids []cid.Cid, markSet MarkSet) error {
|
||||
func (s *SplitStore) purge(cids []cid.Cid, markSet MarkSetVisitor) error {
|
||||
deadCids := make([]cid.Cid, 0, batchSize)
|
||||
var purgeCnt, liveCnt int
|
||||
defer func() {
|
||||
@@ -1050,7 +1061,7 @@ func (s *SplitStore) purge(cids []cid.Cid, markSet MarkSet) error {
|
||||
// have this gem[TM].
|
||||
// My best guess is that they are parent message receipts or yet to be computed state roots; magik
|
||||
// thinks the cause may be block validation.
|
||||
func (s *SplitStore) waitForMissingRefs(markSet MarkSet) {
|
||||
func (s *SplitStore) waitForMissingRefs(markSet MarkSetVisitor) {
|
||||
s.txnLk.Lock()
|
||||
missing := s.txnMissing
|
||||
s.txnMissing = nil
|
||||
@@ -1079,27 +1090,27 @@ func (s *SplitStore) waitForMissingRefs(markSet MarkSet) {
|
||||
}
|
||||
|
||||
towalk := missing
|
||||
walked := cid.NewSet()
|
||||
visitor := tmpVisitor()
|
||||
missing = make(map[cid.Cid]struct{})
|
||||
|
||||
for c := range towalk {
|
||||
err := s.walkObjectIncomplete(c, walked,
|
||||
err := s.walkObjectIncomplete(c, visitor,
|
||||
func(c cid.Cid) error {
|
||||
if isUnitaryObject(c) {
|
||||
return errStopWalk
|
||||
}
|
||||
|
||||
mark, err := markSet.Has(c)
|
||||
visit, err := markSet.Visit(c)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error checking markset for %s: %w", c, err)
|
||||
return xerrors.Errorf("error visiting object: %w", err)
|
||||
}
|
||||
|
||||
if mark {
|
||||
if !visit {
|
||||
return errStopWalk
|
||||
}
|
||||
|
||||
count++
|
||||
return markSet.Mark(c)
|
||||
return nil
|
||||
},
|
||||
func(c cid.Cid) error {
|
||||
missing[c] = struct{}{}
|
||||
|
||||
@@ -59,7 +59,15 @@ func (s *SplitStore) doWarmup(curTs *types.TipSet) error {
|
||||
count := int64(0)
|
||||
xcount := int64(0)
|
||||
missing := int64(0)
|
||||
err := s.walkChain(curTs, boundaryEpoch, epoch+1, // we don't load messages/receipts in warmup
|
||||
|
||||
visitor, err := s.markSetEnv.CreateVisitor("warmup", 0)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("error creating visitor: %w", err)
|
||||
}
|
||||
defer visitor.Close() //nolint
|
||||
|
||||
err = s.walkChain(curTs, boundaryEpoch, epoch+1, // we don't load messages/receipts in warmup
|
||||
visitor,
|
||||
func(c cid.Cid) error {
|
||||
if isUnitaryObject(c) {
|
||||
return errStopWalk
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package splitstore
|
||||
|
||||
import (
|
||||
cid "github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
// ObjectVisitor is an interface for deduplicating objects during walks
|
||||
type ObjectVisitor interface {
|
||||
Visit(cid.Cid) (bool, error)
|
||||
}
|
||||
|
||||
type noopVisitor struct{}
|
||||
|
||||
var _ ObjectVisitor = (*noopVisitor)(nil)
|
||||
|
||||
func (v *noopVisitor) Visit(_ cid.Cid) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type cidSetVisitor struct {
|
||||
set *cid.Set
|
||||
}
|
||||
|
||||
var _ ObjectVisitor = (*cidSetVisitor)(nil)
|
||||
|
||||
func (v *cidSetVisitor) Visit(c cid.Cid) (bool, error) {
|
||||
return v.set.Visit(c), nil
|
||||
}
|
||||
|
||||
func tmpVisitor() ObjectVisitor {
|
||||
return &cidSetVisitor{set: cid.NewSet()}
|
||||
}
|
||||
+5
-1
@@ -47,8 +47,12 @@ func (t *TimedCacheBlockstore) Start(_ context.Context) error {
|
||||
return fmt.Errorf("already started")
|
||||
}
|
||||
t.closeCh = make(chan struct{})
|
||||
|
||||
// Create this timer before starting the goroutine. Otherwise, creating the timer will race
|
||||
// with adding time to the mock clock, and we could add time _first_, then stall waiting for
|
||||
// a timer that'll never fire.
|
||||
ticker := t.clock.Ticker(t.interval)
|
||||
go func() {
|
||||
ticker := t.clock.Ticker(t.interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package build
|
||||
|
||||
var (
|
||||
DefaultFDLimit uint64 = 16 << 10
|
||||
MinerFDLimit uint64 = 100_000
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
-1
@@ -1,3 +1,4 @@
|
||||
//go:build debug || 2k
|
||||
// +build debug 2k
|
||||
|
||||
package build
|
||||
@@ -28,7 +29,6 @@ var UpgradeAssemblyHeight = abi.ChainEpoch(-5)
|
||||
var UpgradeLiftoffHeight = abi.ChainEpoch(-6)
|
||||
|
||||
var UpgradeKumquatHeight = abi.ChainEpoch(-7)
|
||||
var UpgradePricelistOopsHeight = abi.ChainEpoch(-8)
|
||||
var UpgradeCalicoHeight = abi.ChainEpoch(-9)
|
||||
var UpgradePersianHeight = abi.ChainEpoch(-10)
|
||||
var UpgradeOrangeHeight = abi.ChainEpoch(-11)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build butterflynet
|
||||
// +build butterflynet
|
||||
|
||||
package build
|
||||
@@ -28,7 +29,6 @@ var UpgradeAssemblyHeight = abi.ChainEpoch(30)
|
||||
const UpgradeTapeHeight = 60
|
||||
const UpgradeLiftoffHeight = -5
|
||||
const UpgradeKumquatHeight = 90
|
||||
const UpgradePricelistOopsHeight = 119
|
||||
const UpgradeCalicoHeight = 120
|
||||
const UpgradePersianHeight = 150
|
||||
const UpgradeClausHeight = 180
|
||||
@@ -47,6 +47,8 @@ func init() {
|
||||
SetAddressNetwork(address.Testnet)
|
||||
|
||||
Devnet = true
|
||||
|
||||
BuildType = BuildButterflynet
|
||||
}
|
||||
|
||||
const BlockDelaySecs = uint64(builtin2.EpochDurationSeconds)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build calibnet
|
||||
// +build calibnet
|
||||
|
||||
package build
|
||||
@@ -33,8 +34,6 @@ const UpgradeLiftoffHeight = -5
|
||||
|
||||
const UpgradeKumquatHeight = 90
|
||||
|
||||
const UpgradePricelistOopsHeight = 119
|
||||
|
||||
const UpgradeCalicoHeight = 120
|
||||
const UpgradePersianHeight = UpgradeCalicoHeight + (builtin2.EpochsInHour * 1)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package build
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build interopnet
|
||||
// +build interopnet
|
||||
|
||||
package build
|
||||
@@ -31,7 +32,6 @@ var UpgradeAssemblyHeight = abi.ChainEpoch(-5)
|
||||
var UpgradeLiftoffHeight = abi.ChainEpoch(-6)
|
||||
|
||||
var UpgradeKumquatHeight = abi.ChainEpoch(-7)
|
||||
var UpgradePricelistOopsHeight = abi.ChainEpoch(-8)
|
||||
var UpgradeCalicoHeight = abi.ChainEpoch(-9)
|
||||
var UpgradePersianHeight = abi.ChainEpoch(-10)
|
||||
var UpgradeOrangeHeight = abi.ChainEpoch(-11)
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
// +build !debug
|
||||
// +build !2k
|
||||
// +build !testground
|
||||
// +build !calibnet
|
||||
// +build !nerpanet
|
||||
// +build !butterflynet
|
||||
// +build !interopnet
|
||||
//go:build !debug && !2k && !testground && !calibnet && !nerpanet && !butterflynet && !interopnet
|
||||
// +build !debug,!2k,!testground,!calibnet,!nerpanet,!butterflynet,!interopnet
|
||||
|
||||
package build
|
||||
|
||||
@@ -45,7 +40,6 @@ const UpgradeLiftoffHeight = 148888
|
||||
|
||||
const UpgradeKumquatHeight = 170000
|
||||
|
||||
const UpgradePricelistOopsHeight = 265199
|
||||
const UpgradeCalicoHeight = 265200
|
||||
const UpgradePersianHeight = UpgradeCalicoHeight + (builtin2.EpochsInHour * 60)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build nerpanet
|
||||
// +build nerpanet
|
||||
|
||||
package build
|
||||
@@ -32,7 +33,6 @@ const UpgradeTapeHeight = 60
|
||||
|
||||
const UpgradeKumquatHeight = 90
|
||||
|
||||
const UpgradePricelistOopsHeight = 99
|
||||
const UpgradeCalicoHeight = 100
|
||||
const UpgradePersianHeight = UpgradeCalicoHeight + (builtin2.EpochsInHour * 1)
|
||||
|
||||
@@ -67,6 +67,8 @@ func init() {
|
||||
//miner.WPoStChallengeLookback = abi.ChainEpoch(2)
|
||||
|
||||
Devnet = false
|
||||
|
||||
BuildType = BuildNerpanet
|
||||
}
|
||||
|
||||
const BlockDelaySecs = uint64(builtin2.EpochDurationSeconds)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !testground
|
||||
// +build !testground
|
||||
|
||||
package build
|
||||
@@ -26,7 +27,6 @@ const UnixfsLinksPerLevel = 1024
|
||||
|
||||
const AllowableClockDriftSecs = uint64(1)
|
||||
const NewestNetworkVersion = network.Version13
|
||||
const ActorUpgradeNetworkVersion = network.Version4
|
||||
|
||||
// Epochs
|
||||
const ForkLengthThreshold = Finality
|
||||
|
||||
+16
-16
@@ -1,3 +1,4 @@
|
||||
//go:build testground
|
||||
// +build testground
|
||||
|
||||
// This file makes hardcoded parameters (const) configurable as vars.
|
||||
@@ -82,22 +83,21 @@ var (
|
||||
UpgradeBreezeHeight abi.ChainEpoch = -1
|
||||
BreezeGasTampingDuration abi.ChainEpoch = 0
|
||||
|
||||
UpgradeSmokeHeight abi.ChainEpoch = -1
|
||||
UpgradeIgnitionHeight abi.ChainEpoch = -2
|
||||
UpgradeRefuelHeight abi.ChainEpoch = -3
|
||||
UpgradeTapeHeight abi.ChainEpoch = -4
|
||||
UpgradeAssemblyHeight abi.ChainEpoch = 10
|
||||
UpgradeLiftoffHeight abi.ChainEpoch = -5
|
||||
UpgradeKumquatHeight abi.ChainEpoch = -6
|
||||
UpgradePricelistOopsHeight abi.ChainEpoch = -7
|
||||
UpgradeCalicoHeight abi.ChainEpoch = -8
|
||||
UpgradePersianHeight abi.ChainEpoch = -9
|
||||
UpgradeOrangeHeight abi.ChainEpoch = -10
|
||||
UpgradeClausHeight abi.ChainEpoch = -11
|
||||
UpgradeTrustHeight abi.ChainEpoch = -12
|
||||
UpgradeNorwegianHeight abi.ChainEpoch = -13
|
||||
UpgradeTurboHeight abi.ChainEpoch = -14
|
||||
UpgradeHyperdriveHeight abi.ChainEpoch = -15
|
||||
UpgradeSmokeHeight abi.ChainEpoch = -1
|
||||
UpgradeIgnitionHeight abi.ChainEpoch = -2
|
||||
UpgradeRefuelHeight abi.ChainEpoch = -3
|
||||
UpgradeTapeHeight abi.ChainEpoch = -4
|
||||
UpgradeAssemblyHeight abi.ChainEpoch = 10
|
||||
UpgradeLiftoffHeight abi.ChainEpoch = -5
|
||||
UpgradeKumquatHeight abi.ChainEpoch = -6
|
||||
UpgradeCalicoHeight abi.ChainEpoch = -8
|
||||
UpgradePersianHeight abi.ChainEpoch = -9
|
||||
UpgradeOrangeHeight abi.ChainEpoch = -10
|
||||
UpgradeClausHeight abi.ChainEpoch = -11
|
||||
UpgradeTrustHeight abi.ChainEpoch = -12
|
||||
UpgradeNorwegianHeight abi.ChainEpoch = -13
|
||||
UpgradeTurboHeight abi.ChainEpoch = -14
|
||||
UpgradeHyperdriveHeight abi.ChainEpoch = -15
|
||||
|
||||
DrandSchedule = map[abi.ChainEpoch]DrandEnum{
|
||||
0: DrandMainnet,
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
//+build tools
|
||||
//go:build tools
|
||||
// +build tools
|
||||
|
||||
package build
|
||||
|
||||
|
||||
+14
-8
@@ -6,12 +6,14 @@ var CurrentCommit string
|
||||
var BuildType int
|
||||
|
||||
const (
|
||||
BuildDefault = 0
|
||||
BuildMainnet = 0x1
|
||||
Build2k = 0x2
|
||||
BuildDebug = 0x3
|
||||
BuildCalibnet = 0x4
|
||||
BuildInteropnet = 0x5
|
||||
BuildDefault = 0
|
||||
BuildMainnet = 0x1
|
||||
Build2k = 0x2
|
||||
BuildDebug = 0x3
|
||||
BuildCalibnet = 0x4
|
||||
BuildInteropnet = 0x5
|
||||
BuildNerpanet = 0x6
|
||||
BuildButterflynet = 0x7
|
||||
)
|
||||
|
||||
func buildType() string {
|
||||
@@ -28,13 +30,17 @@ func buildType() string {
|
||||
return "+calibnet"
|
||||
case BuildInteropnet:
|
||||
return "+interopnet"
|
||||
case BuildNerpanet:
|
||||
return "+nerpanet"
|
||||
case BuildButterflynet:
|
||||
return "+butterflynet"
|
||||
default:
|
||||
return "+huh?"
|
||||
}
|
||||
}
|
||||
|
||||
// BuildVersion is the local build version, set by build system
|
||||
const BuildVersion = "1.11.1-rc2"
|
||||
// BuildVersion is the local build version
|
||||
const BuildVersion = "1.11.3"
|
||||
|
||||
func UserVersion() string {
|
||||
if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" {
|
||||
|
||||
@@ -38,7 +38,7 @@ func init() {
|
||||
|
||||
var Methods = builtin{{.latestVersion}}.MethodsMiner
|
||||
|
||||
// Unchanged between v0, v2, v3, and v4 actors
|
||||
// Unchanged between v0, v2, v3, v4, and v5 actors
|
||||
var WPoStProvingPeriod = miner0.WPoStProvingPeriod
|
||||
var WPoStPeriodDeadlines = miner0.WPoStPeriodDeadlines
|
||||
var WPoStChallengeWindow = miner0.WPoStChallengeWindow
|
||||
@@ -141,11 +141,28 @@ type Deadline interface {
|
||||
}
|
||||
|
||||
type Partition interface {
|
||||
// AllSectors returns all sector numbers in this partition, including faulty, unproven, and terminated sectors
|
||||
AllSectors() (bitfield.BitField, error)
|
||||
|
||||
// Subset of sectors detected/declared faulty and not yet recovered (excl. from PoSt).
|
||||
// Faults ∩ Terminated = ∅
|
||||
FaultySectors() (bitfield.BitField, error)
|
||||
|
||||
// Subset of faulty sectors expected to recover on next PoSt
|
||||
// Recoveries ∩ Terminated = ∅
|
||||
RecoveringSectors() (bitfield.BitField, error)
|
||||
|
||||
// Live sectors are those that are not terminated (but may be faulty).
|
||||
LiveSectors() (bitfield.BitField, error)
|
||||
|
||||
// Active sectors are those that are neither terminated nor faulty nor unproven, i.e. actively contributing power.
|
||||
ActiveSectors() (bitfield.BitField, error)
|
||||
|
||||
// Unproven sectors in this partition. This bitfield will be cleared on
|
||||
// a successful window post (or at the end of the partition's next
|
||||
// deadline). At that time, any still unproven sectors will be added to
|
||||
// the faulty sector bitfield.
|
||||
UnprovenSectors() (bitfield.BitField, error)
|
||||
}
|
||||
|
||||
type SectorOnChainInfo struct {
|
||||
|
||||
@@ -61,7 +61,7 @@ func init() {
|
||||
|
||||
var Methods = builtin5.MethodsMiner
|
||||
|
||||
// Unchanged between v0, v2, v3, and v4 actors
|
||||
// Unchanged between v0, v2, v3, v4, and v5 actors
|
||||
var WPoStProvingPeriod = miner0.WPoStProvingPeriod
|
||||
var WPoStPeriodDeadlines = miner0.WPoStPeriodDeadlines
|
||||
var WPoStChallengeWindow = miner0.WPoStChallengeWindow
|
||||
@@ -200,11 +200,28 @@ type Deadline interface {
|
||||
}
|
||||
|
||||
type Partition interface {
|
||||
// AllSectors returns all sector numbers in this partition, including faulty, unproven, and terminated sectors
|
||||
AllSectors() (bitfield.BitField, error)
|
||||
|
||||
// Subset of sectors detected/declared faulty and not yet recovered (excl. from PoSt).
|
||||
// Faults ∩ Terminated = ∅
|
||||
FaultySectors() (bitfield.BitField, error)
|
||||
|
||||
// Subset of faulty sectors expected to recover on next PoSt
|
||||
// Recoveries ∩ Terminated = ∅
|
||||
RecoveringSectors() (bitfield.BitField, error)
|
||||
|
||||
// Live sectors are those that are not terminated (but may be faulty).
|
||||
LiveSectors() (bitfield.BitField, error)
|
||||
|
||||
// Active sectors are those that are neither terminated nor faulty nor unproven, i.e. actively contributing power.
|
||||
ActiveSectors() (bitfield.BitField, error)
|
||||
|
||||
// Unproven sectors in this partition. This bitfield will be cleared on
|
||||
// a successful window post (or at the end of the partition's next
|
||||
// deadline). At that time, any still unproven sectors will be added to
|
||||
// the faulty sector bitfield.
|
||||
UnprovenSectors() (bitfield.BitField, error)
|
||||
}
|
||||
|
||||
type SectorOnChainInfo struct {
|
||||
|
||||
@@ -479,12 +479,15 @@ func (s *state{{.v}}) EraseAllUnproven() error {
|
||||
|
||||
return dls.UpdateDeadline(s.store, dindx, dl)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.State.SaveDeadlines(s.store, dls)
|
||||
{{else}}
|
||||
// field doesn't exist until v2
|
||||
return nil
|
||||
{{end}}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *deadline{{.v}}) LoadPartition(idx uint64) (Partition, error) {
|
||||
@@ -546,6 +549,10 @@ func (p *partition{{.v}}) RecoveringSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Recoveries, nil
|
||||
}
|
||||
|
||||
func (p *partition{{.v}}) UnprovenSectors() (bitfield.BitField, error) {
|
||||
return {{if (ge .v 2)}}p.Partition.Unproven{{else}}bitfield.New(){{end}}, nil
|
||||
}
|
||||
|
||||
func fromV{{.v}}SectorOnChainInfo(v{{.v}} miner{{.v}}.SectorOnChainInfo) SectorOnChainInfo {
|
||||
{{if (ge .v 2)}}
|
||||
return SectorOnChainInfo{
|
||||
|
||||
@@ -444,8 +444,8 @@ func (s *state0) decodeSectorPreCommitOnChainInfo(val *cbg.Deferred) (SectorPreC
|
||||
func (s *state0) EraseAllUnproven() error {
|
||||
|
||||
// field doesn't exist until v2
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (d *deadline0) LoadPartition(idx uint64) (Partition, error) {
|
||||
@@ -500,6 +500,10 @@ func (p *partition0) RecoveringSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Recoveries, nil
|
||||
}
|
||||
|
||||
func (p *partition0) UnprovenSectors() (bitfield.BitField, error) {
|
||||
return bitfield.New(), nil
|
||||
}
|
||||
|
||||
func fromV0SectorOnChainInfo(v0 miner0.SectorOnChainInfo) SectorOnChainInfo {
|
||||
|
||||
return (SectorOnChainInfo)(v0)
|
||||
|
||||
@@ -470,10 +470,12 @@ func (s *state2) EraseAllUnproven() error {
|
||||
|
||||
return dls.UpdateDeadline(s.store, dindx, dl)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.State.SaveDeadlines(s.store, dls)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *deadline2) LoadPartition(idx uint64) (Partition, error) {
|
||||
@@ -528,6 +530,10 @@ func (p *partition2) RecoveringSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Recoveries, nil
|
||||
}
|
||||
|
||||
func (p *partition2) UnprovenSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Unproven, nil
|
||||
}
|
||||
|
||||
func fromV2SectorOnChainInfo(v2 miner2.SectorOnChainInfo) SectorOnChainInfo {
|
||||
|
||||
return SectorOnChainInfo{
|
||||
|
||||
@@ -467,10 +467,12 @@ func (s *state3) EraseAllUnproven() error {
|
||||
|
||||
return dls.UpdateDeadline(s.store, dindx, dl)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.State.SaveDeadlines(s.store, dls)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *deadline3) LoadPartition(idx uint64) (Partition, error) {
|
||||
@@ -529,6 +531,10 @@ func (p *partition3) RecoveringSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Recoveries, nil
|
||||
}
|
||||
|
||||
func (p *partition3) UnprovenSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Unproven, nil
|
||||
}
|
||||
|
||||
func fromV3SectorOnChainInfo(v3 miner3.SectorOnChainInfo) SectorOnChainInfo {
|
||||
|
||||
return SectorOnChainInfo{
|
||||
|
||||
@@ -467,10 +467,12 @@ func (s *state4) EraseAllUnproven() error {
|
||||
|
||||
return dls.UpdateDeadline(s.store, dindx, dl)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.State.SaveDeadlines(s.store, dls)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *deadline4) LoadPartition(idx uint64) (Partition, error) {
|
||||
@@ -529,6 +531,10 @@ func (p *partition4) RecoveringSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Recoveries, nil
|
||||
}
|
||||
|
||||
func (p *partition4) UnprovenSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Unproven, nil
|
||||
}
|
||||
|
||||
func fromV4SectorOnChainInfo(v4 miner4.SectorOnChainInfo) SectorOnChainInfo {
|
||||
|
||||
return SectorOnChainInfo{
|
||||
|
||||
@@ -467,10 +467,12 @@ func (s *state5) EraseAllUnproven() error {
|
||||
|
||||
return dls.UpdateDeadline(s.store, dindx, dl)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.State.SaveDeadlines(s.store, dls)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *deadline5) LoadPartition(idx uint64) (Partition, error) {
|
||||
@@ -529,6 +531,10 @@ func (p *partition5) RecoveringSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Recoveries, nil
|
||||
}
|
||||
|
||||
func (p *partition5) UnprovenSectors() (bitfield.BitField, error) {
|
||||
return p.Partition.Unproven, nil
|
||||
}
|
||||
|
||||
func fromV5SectorOnChainInfo(v5 miner5.SectorOnChainInfo) SectorOnChainInfo {
|
||||
|
||||
return SectorOnChainInfo{
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/network"
|
||||
@@ -168,31 +169,31 @@ func SetMinVerifiedDealSize(size abi.StoragePower) {
|
||||
|
||||
}
|
||||
|
||||
func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) abi.ChainEpoch {
|
||||
func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) (abi.ChainEpoch, error) {
|
||||
switch ver {
|
||||
|
||||
case actors.Version0:
|
||||
|
||||
return miner0.MaxSealDuration[t]
|
||||
return miner0.MaxSealDuration[t], nil
|
||||
|
||||
case actors.Version2:
|
||||
|
||||
return miner2.MaxProveCommitDuration[t]
|
||||
return miner2.MaxProveCommitDuration[t], nil
|
||||
|
||||
case actors.Version3:
|
||||
|
||||
return miner3.MaxProveCommitDuration[t]
|
||||
return miner3.MaxProveCommitDuration[t], nil
|
||||
|
||||
case actors.Version4:
|
||||
|
||||
return miner4.MaxProveCommitDuration[t]
|
||||
return miner4.MaxProveCommitDuration[t], nil
|
||||
|
||||
case actors.Version5:
|
||||
|
||||
return miner5.MaxProveCommitDuration[t]
|
||||
return miner5.MaxProveCommitDuration[t], nil
|
||||
|
||||
default:
|
||||
panic("unsupported actors version")
|
||||
return 0, xerrors.Errorf("unsupported actors version")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,31 +228,40 @@ func DealProviderCollateralBounds(
|
||||
size abi.PaddedPieceSize, verified bool,
|
||||
rawBytePower, qaPower, baselinePower abi.StoragePower,
|
||||
circulatingFil abi.TokenAmount, nwVer network.Version,
|
||||
) (min, max abi.TokenAmount) {
|
||||
switch actors.VersionForNetwork(nwVer) {
|
||||
) (min, max abi.TokenAmount, err error) {
|
||||
v, err := actors.VersionForNetwork(nwVer)
|
||||
if err != nil {
|
||||
return big.Zero(), big.Zero(), err
|
||||
}
|
||||
switch v {
|
||||
|
||||
case actors.Version0:
|
||||
|
||||
return market0.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil, nwVer)
|
||||
min, max := market0.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil, nwVer)
|
||||
return min, max, nil
|
||||
|
||||
case actors.Version2:
|
||||
|
||||
return market2.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
min, max := market2.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
return min, max, nil
|
||||
|
||||
case actors.Version3:
|
||||
|
||||
return market3.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
min, max := market3.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
return min, max, nil
|
||||
|
||||
case actors.Version4:
|
||||
|
||||
return market4.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
min, max := market4.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
return min, max, nil
|
||||
|
||||
case actors.Version5:
|
||||
|
||||
return market5.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
min, max := market5.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
return min, max, nil
|
||||
|
||||
default:
|
||||
panic("unsupported actors version")
|
||||
return big.Zero(), big.Zero(), xerrors.Errorf("unsupported actors version")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,13 +315,20 @@ func GetMaxSectorExpirationExtension() abi.ChainEpoch {
|
||||
return miner5.MaxSectorExpirationExtension
|
||||
}
|
||||
|
||||
func GetMinSectorExpiration() abi.ChainEpoch {
|
||||
return miner5.MinSectorExpiration
|
||||
}
|
||||
|
||||
func GetMaxPoStPartitions(nv network.Version, p abi.RegisteredPoStProof) (int, error) {
|
||||
sectorsPerPart, err := builtin5.PoStProofWindowPoStPartitionSectors(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
maxSectors := uint64(GetAddressedSectorsMax(nv))
|
||||
return int(maxSectors / sectorsPerPart), nil
|
||||
maxSectors, err := GetAddressedSectorsMax(nv)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(uint64(maxSectors) / sectorsPerPart), nil
|
||||
}
|
||||
|
||||
func GetDefaultSectorSize() abi.SectorSize {
|
||||
@@ -345,82 +362,94 @@ func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version)
|
||||
return builtin5.SealProofPoliciesV11[proof].SectorMaxLifetime
|
||||
}
|
||||
|
||||
func GetAddressedSectorsMax(nwVer network.Version) int {
|
||||
switch actors.VersionForNetwork(nwVer) {
|
||||
func GetAddressedSectorsMax(nwVer network.Version) (int, error) {
|
||||
v, err := actors.VersionForNetwork(nwVer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch v {
|
||||
|
||||
case actors.Version0:
|
||||
return miner0.AddressedSectorsMax
|
||||
return miner0.AddressedSectorsMax, nil
|
||||
|
||||
case actors.Version2:
|
||||
return miner2.AddressedSectorsMax
|
||||
return miner2.AddressedSectorsMax, nil
|
||||
|
||||
case actors.Version3:
|
||||
return miner3.AddressedSectorsMax
|
||||
return miner3.AddressedSectorsMax, nil
|
||||
|
||||
case actors.Version4:
|
||||
return miner4.AddressedSectorsMax
|
||||
return miner4.AddressedSectorsMax, nil
|
||||
|
||||
case actors.Version5:
|
||||
return miner5.AddressedSectorsMax
|
||||
return miner5.AddressedSectorsMax, nil
|
||||
|
||||
default:
|
||||
panic("unsupported network version")
|
||||
return 0, xerrors.Errorf("unsupported network version")
|
||||
}
|
||||
}
|
||||
|
||||
func GetDeclarationsMax(nwVer network.Version) int {
|
||||
switch actors.VersionForNetwork(nwVer) {
|
||||
func GetDeclarationsMax(nwVer network.Version) (int, error) {
|
||||
v, err := actors.VersionForNetwork(nwVer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch v {
|
||||
|
||||
case actors.Version0:
|
||||
|
||||
// TODO: Should we instead panic here since the concept doesn't exist yet?
|
||||
return miner0.AddressedPartitionsMax
|
||||
// TODO: Should we instead error here since the concept doesn't exist yet?
|
||||
return miner0.AddressedPartitionsMax, nil
|
||||
|
||||
case actors.Version2:
|
||||
|
||||
return miner2.DeclarationsMax
|
||||
return miner2.DeclarationsMax, nil
|
||||
|
||||
case actors.Version3:
|
||||
|
||||
return miner3.DeclarationsMax
|
||||
return miner3.DeclarationsMax, nil
|
||||
|
||||
case actors.Version4:
|
||||
|
||||
return miner4.DeclarationsMax
|
||||
return miner4.DeclarationsMax, nil
|
||||
|
||||
case actors.Version5:
|
||||
|
||||
return miner5.DeclarationsMax
|
||||
return miner5.DeclarationsMax, nil
|
||||
|
||||
default:
|
||||
panic("unsupported network version")
|
||||
return 0, xerrors.Errorf("unsupported network version")
|
||||
}
|
||||
}
|
||||
|
||||
func AggregateNetworkFee(nwVer network.Version, aggregateSize int, baseFee abi.TokenAmount) abi.TokenAmount {
|
||||
switch actors.VersionForNetwork(nwVer) {
|
||||
func AggregateNetworkFee(nwVer network.Version, aggregateSize int, baseFee abi.TokenAmount) (abi.TokenAmount, error) {
|
||||
v, err := actors.VersionForNetwork(nwVer)
|
||||
if err != nil {
|
||||
return big.Zero(), err
|
||||
}
|
||||
switch v {
|
||||
|
||||
case actors.Version0:
|
||||
|
||||
return big.Zero()
|
||||
return big.Zero(), nil
|
||||
|
||||
case actors.Version2:
|
||||
|
||||
return big.Zero()
|
||||
return big.Zero(), nil
|
||||
|
||||
case actors.Version3:
|
||||
|
||||
return big.Zero()
|
||||
return big.Zero(), nil
|
||||
|
||||
case actors.Version4:
|
||||
|
||||
return big.Zero()
|
||||
return big.Zero(), nil
|
||||
|
||||
case actors.Version5:
|
||||
|
||||
return miner5.AggregateNetworkFee(aggregateSize, baseFee)
|
||||
return miner5.AggregateNetworkFee(aggregateSize, baseFee), nil
|
||||
|
||||
default:
|
||||
panic("unsupported network version")
|
||||
return big.Zero(), xerrors.Errorf("unsupported network version")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/network"
|
||||
@@ -117,18 +118,18 @@ func SetMinVerifiedDealSize(size abi.StoragePower) {
|
||||
{{end}}
|
||||
}
|
||||
|
||||
func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) abi.ChainEpoch {
|
||||
func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) (abi.ChainEpoch, error) {
|
||||
switch ver {
|
||||
{{range .versions}}
|
||||
case actors.Version{{.}}:
|
||||
{{if (eq . 0)}}
|
||||
return miner{{.}}.MaxSealDuration[t]
|
||||
return miner{{.}}.MaxSealDuration[t], nil
|
||||
{{else}}
|
||||
return miner{{.}}.MaxProveCommitDuration[t]
|
||||
return miner{{.}}.MaxProveCommitDuration[t], nil
|
||||
{{end}}
|
||||
{{end}}
|
||||
default:
|
||||
panic("unsupported actors version")
|
||||
return 0, xerrors.Errorf("unsupported actors version")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,18 +151,24 @@ func DealProviderCollateralBounds(
|
||||
size abi.PaddedPieceSize, verified bool,
|
||||
rawBytePower, qaPower, baselinePower abi.StoragePower,
|
||||
circulatingFil abi.TokenAmount, nwVer network.Version,
|
||||
) (min, max abi.TokenAmount) {
|
||||
switch actors.VersionForNetwork(nwVer) {
|
||||
) (min, max abi.TokenAmount, err error) {
|
||||
v, err := actors.VersionForNetwork(nwVer)
|
||||
if err != nil {
|
||||
return big.Zero(), big.Zero(), err
|
||||
}
|
||||
switch v {
|
||||
{{range .versions}}
|
||||
case actors.Version{{.}}:
|
||||
{{if (eq . 0)}}
|
||||
return market{{.}}.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil, nwVer)
|
||||
min, max := market{{.}}.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil, nwVer)
|
||||
return min, max, nil
|
||||
{{else}}
|
||||
return market{{.}}.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
min, max := market{{.}}.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil)
|
||||
return min, max, nil
|
||||
{{end}}
|
||||
{{end}}
|
||||
default:
|
||||
panic("unsupported actors version")
|
||||
return big.Zero(), big.Zero(), xerrors.Errorf("unsupported actors version")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,13 +203,20 @@ func GetMaxSectorExpirationExtension() abi.ChainEpoch {
|
||||
return miner{{.latestVersion}}.MaxSectorExpirationExtension
|
||||
}
|
||||
|
||||
func GetMinSectorExpiration() abi.ChainEpoch {
|
||||
return miner{{.latestVersion}}.MinSectorExpiration
|
||||
}
|
||||
|
||||
func GetMaxPoStPartitions(nv network.Version, p abi.RegisteredPoStProof) (int, error) {
|
||||
sectorsPerPart, err := builtin{{.latestVersion}}.PoStProofWindowPoStPartitionSectors(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
maxSectors := uint64(GetAddressedSectorsMax(nv))
|
||||
return int(maxSectors / sectorsPerPart), nil
|
||||
maxSectors, err := GetAddressedSectorsMax(nv)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(uint64(maxSectors) / sectorsPerPart), nil
|
||||
}
|
||||
|
||||
func GetDefaultSectorSize() abi.SectorSize {
|
||||
@@ -236,44 +250,56 @@ func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version)
|
||||
return builtin{{.latestVersion}}.SealProofPoliciesV11[proof].SectorMaxLifetime
|
||||
}
|
||||
|
||||
func GetAddressedSectorsMax(nwVer network.Version) int {
|
||||
switch actors.VersionForNetwork(nwVer) {
|
||||
func GetAddressedSectorsMax(nwVer network.Version) (int, error) {
|
||||
v, err := actors.VersionForNetwork(nwVer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch v {
|
||||
{{range .versions}}
|
||||
case actors.Version{{.}}:
|
||||
return miner{{.}}.AddressedSectorsMax
|
||||
return miner{{.}}.AddressedSectorsMax, nil
|
||||
{{end}}
|
||||
default:
|
||||
panic("unsupported network version")
|
||||
return 0, xerrors.Errorf("unsupported network version")
|
||||
}
|
||||
}
|
||||
|
||||
func GetDeclarationsMax(nwVer network.Version) int {
|
||||
switch actors.VersionForNetwork(nwVer) {
|
||||
func GetDeclarationsMax(nwVer network.Version) (int, error) {
|
||||
v, err := actors.VersionForNetwork(nwVer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch v {
|
||||
{{range .versions}}
|
||||
case actors.Version{{.}}:
|
||||
{{if (eq . 0)}}
|
||||
// TODO: Should we instead panic here since the concept doesn't exist yet?
|
||||
return miner{{.}}.AddressedPartitionsMax
|
||||
// TODO: Should we instead error here since the concept doesn't exist yet?
|
||||
return miner{{.}}.AddressedPartitionsMax, nil
|
||||
{{else}}
|
||||
return miner{{.}}.DeclarationsMax
|
||||
return miner{{.}}.DeclarationsMax, nil
|
||||
{{end}}
|
||||
{{end}}
|
||||
default:
|
||||
panic("unsupported network version")
|
||||
return 0, xerrors.Errorf("unsupported network version")
|
||||
}
|
||||
}
|
||||
|
||||
func AggregateNetworkFee(nwVer network.Version, aggregateSize int, baseFee abi.TokenAmount) abi.TokenAmount {
|
||||
switch actors.VersionForNetwork(nwVer) {
|
||||
func AggregateNetworkFee(nwVer network.Version, aggregateSize int, baseFee abi.TokenAmount) (abi.TokenAmount, error) {
|
||||
v, err := actors.VersionForNetwork(nwVer)
|
||||
if err != nil {
|
||||
return big.Zero(), err
|
||||
}
|
||||
switch v {
|
||||
{{range .versions}}
|
||||
case actors.Version{{.}}:
|
||||
{{if (le . 4)}}
|
||||
return big.Zero()
|
||||
return big.Zero(), nil
|
||||
{{else}}
|
||||
return miner{{.}}.AggregateNetworkFee(aggregateSize, baseFee)
|
||||
return miner{{.}}.AggregateNetworkFee(aggregateSize, baseFee), nil
|
||||
{{end}}
|
||||
{{end}}
|
||||
default:
|
||||
panic("unsupported network version")
|
||||
return big.Zero(), xerrors.Errorf("unsupported network version")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,19 +21,19 @@ const (
|
||||
)
|
||||
|
||||
// Converts a network version into an actors adt version.
|
||||
func VersionForNetwork(version network.Version) Version {
|
||||
func VersionForNetwork(version network.Version) (Version, error) {
|
||||
switch version {
|
||||
case network.Version0, network.Version1, network.Version2, network.Version3:
|
||||
return Version0
|
||||
case network.Version4, network.Version5, network.Version6, network.Version6AndAHalf, network.Version7, network.Version8, network.Version9:
|
||||
return Version2
|
||||
return Version0, nil
|
||||
case network.Version4, network.Version5, network.Version6, network.Version7, network.Version8, network.Version9:
|
||||
return Version2, nil
|
||||
case network.Version10, network.Version11:
|
||||
return Version3
|
||||
return Version3, nil
|
||||
case network.Version12:
|
||||
return Version4
|
||||
return Version4, nil
|
||||
case network.Version13:
|
||||
return Version5
|
||||
return Version5, nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported network version %d", version))
|
||||
return -1, fmt.Errorf("unsupported network version %d", version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,11 @@ func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntr
|
||||
// TODO handle genesis better
|
||||
return nil
|
||||
}
|
||||
|
||||
if curr.Round != prev.Round+1 {
|
||||
return xerrors.Errorf("invalid beacon entry: cur (%d) != prev (%d) + 1", curr.Round, prev.Round)
|
||||
}
|
||||
|
||||
if be := db.getCachedValue(curr.Round); be != nil {
|
||||
if !bytes.Equal(curr.Data, be.Data) {
|
||||
return xerrors.New("invalid beacon value, does not match cached good value")
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
package filcns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
exported0 "github.com/filecoin-project/specs-actors/actors/builtin/exported"
|
||||
exported2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/exported"
|
||||
exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported"
|
||||
exported4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported"
|
||||
exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/cron"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/reward"
|
||||
"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/metrics"
|
||||
)
|
||||
|
||||
func NewActorRegistry() *vm.ActorRegistry {
|
||||
inv := vm.NewActorRegistry()
|
||||
|
||||
// TODO: define all these properties on the actors themselves, in specs-actors.
|
||||
|
||||
inv.Register(vm.ActorsVersionPredicate(actors.Version0), exported0.BuiltinActors()...)
|
||||
inv.Register(vm.ActorsVersionPredicate(actors.Version2), exported2.BuiltinActors()...)
|
||||
inv.Register(vm.ActorsVersionPredicate(actors.Version3), exported3.BuiltinActors()...)
|
||||
inv.Register(vm.ActorsVersionPredicate(actors.Version4), exported4.BuiltinActors()...)
|
||||
inv.Register(vm.ActorsVersionPredicate(actors.Version5), exported5.BuiltinActors()...)
|
||||
|
||||
return inv
|
||||
}
|
||||
|
||||
type TipSetExecutor struct{}
|
||||
|
||||
func NewTipSetExecutor() *TipSetExecutor {
|
||||
return &TipSetExecutor{}
|
||||
}
|
||||
|
||||
func (t *TipSetExecutor) NewActorRegistry() *vm.ActorRegistry {
|
||||
return NewActorRegistry()
|
||||
}
|
||||
|
||||
type FilecoinBlockMessages struct {
|
||||
store.BlockMessages
|
||||
|
||||
WinCount int64
|
||||
}
|
||||
|
||||
func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, sm *stmgr.StateManager, parentEpoch abi.ChainEpoch, pstate cid.Cid, bms []FilecoinBlockMessages, epoch abi.ChainEpoch, r vm.Rand, em stmgr.ExecMonitor, baseFee abi.TokenAmount, ts *types.TipSet) (cid.Cid, cid.Cid, error) {
|
||||
done := metrics.Timer(ctx, metrics.VMApplyBlocksTotal)
|
||||
defer done()
|
||||
|
||||
partDone := metrics.Timer(ctx, metrics.VMApplyEarly)
|
||||
defer func() {
|
||||
partDone()
|
||||
}()
|
||||
|
||||
makeVmWithBaseState := func(base cid.Cid) (*vm.VM, error) {
|
||||
vmopt := &vm.VMOpts{
|
||||
StateBase: base,
|
||||
Epoch: epoch,
|
||||
Rand: r,
|
||||
Bstore: sm.ChainStore().StateBlockstore(),
|
||||
Actors: NewActorRegistry(),
|
||||
Syscalls: sm.Syscalls,
|
||||
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
||||
NtwkVersion: sm.GetNtwkVersion,
|
||||
BaseFee: baseFee,
|
||||
LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts),
|
||||
}
|
||||
|
||||
return sm.VMConstructor()(ctx, vmopt)
|
||||
}
|
||||
|
||||
vmi, err := makeVmWithBaseState(pstate)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err)
|
||||
}
|
||||
|
||||
runCron := func(epoch abi.ChainEpoch) error {
|
||||
cronMsg := &types.Message{
|
||||
To: cron.Address,
|
||||
From: builtin.SystemActorAddr,
|
||||
Nonce: uint64(epoch),
|
||||
Value: types.NewInt(0),
|
||||
GasFeeCap: types.NewInt(0),
|
||||
GasPremium: types.NewInt(0),
|
||||
GasLimit: build.BlockGasLimit * 10000, // Make super sure this is never too little
|
||||
Method: cron.Methods.EpochTick,
|
||||
Params: nil,
|
||||
}
|
||||
ret, err := vmi.ApplyImplicitMessage(ctx, cronMsg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if em != nil {
|
||||
if err := em.MessageApplied(ctx, ts, cronMsg.Cid(), cronMsg, ret, true); err != nil {
|
||||
return xerrors.Errorf("callback failed on cron message: %w", err)
|
||||
}
|
||||
}
|
||||
if ret.ExitCode != 0 {
|
||||
return xerrors.Errorf("CheckProofSubmissions exit was non-zero: %d", ret.ExitCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := parentEpoch; i < epoch; i++ {
|
||||
if i > parentEpoch {
|
||||
// run cron for null rounds if any
|
||||
if err := runCron(i); err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
pstate, err = vmi.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("flushing vm: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// handle state forks
|
||||
// XXX: The state tree
|
||||
newState, err := sm.HandleStateForks(ctx, pstate, i, em, ts)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("error handling state forks: %w", err)
|
||||
}
|
||||
|
||||
if pstate != newState {
|
||||
vmi, err = makeVmWithBaseState(newState)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
vmi.SetBlockHeight(i + 1)
|
||||
pstate = newState
|
||||
}
|
||||
|
||||
partDone()
|
||||
partDone = metrics.Timer(ctx, metrics.VMApplyMessages)
|
||||
|
||||
var receipts []cbg.CBORMarshaler
|
||||
processedMsgs := make(map[cid.Cid]struct{})
|
||||
for _, b := range bms {
|
||||
penalty := types.NewInt(0)
|
||||
gasReward := big.Zero()
|
||||
|
||||
for _, cm := range append(b.BlsMessages, b.SecpkMessages...) {
|
||||
m := cm.VMMessage()
|
||||
if _, found := processedMsgs[m.Cid()]; found {
|
||||
continue
|
||||
}
|
||||
r, err := vmi.ApplyMessage(ctx, cm)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
receipts = append(receipts, &r.MessageReceipt)
|
||||
gasReward = big.Add(gasReward, r.GasCosts.MinerTip)
|
||||
penalty = big.Add(penalty, r.GasCosts.MinerPenalty)
|
||||
|
||||
if em != nil {
|
||||
if err := em.MessageApplied(ctx, ts, cm.Cid(), m, r, false); err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
}
|
||||
processedMsgs[m.Cid()] = struct{}{}
|
||||
}
|
||||
|
||||
params, err := actors.SerializeParams(&reward.AwardBlockRewardParams{
|
||||
Miner: b.Miner,
|
||||
Penalty: penalty,
|
||||
GasReward: gasReward,
|
||||
WinCount: b.WinCount,
|
||||
})
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to serialize award params: %w", err)
|
||||
}
|
||||
|
||||
rwMsg := &types.Message{
|
||||
From: builtin.SystemActorAddr,
|
||||
To: reward.Address,
|
||||
Nonce: uint64(epoch),
|
||||
Value: types.NewInt(0),
|
||||
GasFeeCap: types.NewInt(0),
|
||||
GasPremium: types.NewInt(0),
|
||||
GasLimit: 1 << 30,
|
||||
Method: reward.Methods.AwardBlockReward,
|
||||
Params: params,
|
||||
}
|
||||
ret, actErr := vmi.ApplyImplicitMessage(ctx, rwMsg)
|
||||
if actErr != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to apply reward message for miner %s: %w", b.Miner, actErr)
|
||||
}
|
||||
if em != nil {
|
||||
if err := em.MessageApplied(ctx, ts, rwMsg.Cid(), rwMsg, ret, true); err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on reward message: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ret.ExitCode != 0 {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("reward application message failed (exit %d): %s", ret.ExitCode, ret.ActorErr)
|
||||
}
|
||||
}
|
||||
|
||||
partDone()
|
||||
partDone = metrics.Timer(ctx, metrics.VMApplyCron)
|
||||
|
||||
if err := runCron(epoch); err != nil {
|
||||
return cid.Cid{}, cid.Cid{}, err
|
||||
}
|
||||
|
||||
partDone()
|
||||
partDone = metrics.Timer(ctx, metrics.VMApplyFlush)
|
||||
|
||||
rectarr := blockadt.MakeEmptyArray(sm.ChainStore().ActorStore(ctx))
|
||||
for i, receipt := range receipts {
|
||||
if err := rectarr.Set(uint64(i), receipt); err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err)
|
||||
}
|
||||
}
|
||||
rectroot, err := rectarr.Root()
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err)
|
||||
}
|
||||
|
||||
st, err := vmi.Flush(ctx)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err)
|
||||
}
|
||||
|
||||
stats.Record(ctx, metrics.VMSends.M(int64(atomic.LoadUint64(&vm.StatSends))),
|
||||
metrics.VMApplied.M(int64(atomic.LoadUint64(&vm.StatApplied))))
|
||||
|
||||
return st, rectroot, nil
|
||||
}
|
||||
|
||||
func (t *TipSetExecutor) ExecuteTipSet(ctx context.Context, sm *stmgr.StateManager, ts *types.TipSet, em stmgr.ExecMonitor) (stateroot cid.Cid, rectsroot cid.Cid, err error) {
|
||||
ctx, span := trace.StartSpan(ctx, "computeTipSetState")
|
||||
defer span.End()
|
||||
|
||||
blks := ts.Blocks()
|
||||
|
||||
for i := 0; i < len(blks); i++ {
|
||||
for j := i + 1; j < len(blks); j++ {
|
||||
if blks[i].Miner == blks[j].Miner {
|
||||
return cid.Undef, cid.Undef,
|
||||
xerrors.Errorf("duplicate miner in a tipset (%s %s)",
|
||||
blks[i].Miner, blks[j].Miner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parentEpoch abi.ChainEpoch
|
||||
pstate := blks[0].ParentStateRoot
|
||||
if blks[0].Height > 0 {
|
||||
parent, err := sm.ChainStore().GetBlock(blks[0].Parents[0])
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("getting parent block: %w", err)
|
||||
}
|
||||
|
||||
parentEpoch = parent.Height
|
||||
}
|
||||
|
||||
r := store.NewChainRand(sm.ChainStore(), ts.Cids())
|
||||
|
||||
blkmsgs, err := sm.ChainStore().BlockMsgsForTipset(ts)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, xerrors.Errorf("getting block messages for tipset: %w", err)
|
||||
}
|
||||
fbmsgs := make([]FilecoinBlockMessages, len(blkmsgs))
|
||||
for i := range fbmsgs {
|
||||
fbmsgs[i].BlockMessages = blkmsgs[i]
|
||||
fbmsgs[i].WinCount = ts.Blocks()[i].ElectionProof.WinCount
|
||||
}
|
||||
baseFee := blks[0].ParentBaseFee
|
||||
|
||||
return t.ApplyBlocks(ctx, sm, parentEpoch, pstate, fbmsgs, blks[0].Height, r, em, baseFee, ts)
|
||||
}
|
||||
|
||||
var _ stmgr.Executor = &TipSetExecutor{}
|
||||
@@ -0,0 +1,847 @@
|
||||
package filcns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/filecoin-project/go-state-types/network"
|
||||
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof"
|
||||
|
||||
bstore "github.com/filecoin-project/lotus/blockstore"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
"github.com/filecoin-project/lotus/chain/consensus"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"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/extern/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/lotus/lib/async"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
"github.com/filecoin-project/lotus/metrics"
|
||||
)
|
||||
|
||||
var log = logging.Logger("fil-consensus")
|
||||
|
||||
type FilecoinEC struct {
|
||||
// The interface for accessing and putting tipsets into local storage
|
||||
store *store.ChainStore
|
||||
|
||||
// handle to the random beacon for verification
|
||||
beacon beacon.Schedule
|
||||
|
||||
// the state manager handles making state queries
|
||||
sm *stmgr.StateManager
|
||||
|
||||
verifier ffiwrapper.Verifier
|
||||
|
||||
genesis *types.TipSet
|
||||
}
|
||||
|
||||
// Blocks that are more than MaxHeightDrift epochs above
|
||||
// the theoretical max height based on systime are quickly rejected
|
||||
const MaxHeightDrift = 5
|
||||
|
||||
func NewFilecoinExpectedConsensus(sm *stmgr.StateManager, beacon beacon.Schedule, verifier ffiwrapper.Verifier, genesis chain.Genesis) consensus.Consensus {
|
||||
if build.InsecurePoStValidation {
|
||||
log.Warn("*********************************************************************************************")
|
||||
log.Warn(" [INSECURE-POST-VALIDATION] Insecure test validation is enabled. If you see this outside of a test, it is a severe bug! ")
|
||||
log.Warn("*********************************************************************************************")
|
||||
}
|
||||
|
||||
return &FilecoinEC{
|
||||
store: sm.ChainStore(),
|
||||
beacon: beacon,
|
||||
sm: sm,
|
||||
verifier: verifier,
|
||||
genesis: genesis,
|
||||
}
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) ValidateBlock(ctx context.Context, b *types.FullBlock) (err error) {
|
||||
if err := blockSanityChecks(b.Header); err != nil {
|
||||
return xerrors.Errorf("incoming header failed basic sanity checks: %w", err)
|
||||
}
|
||||
|
||||
h := b.Header
|
||||
|
||||
baseTs, err := filec.store.LoadTipSet(types.NewTipSetKey(h.Parents...))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err)
|
||||
}
|
||||
|
||||
winPoStNv := filec.sm.GetNtwkVersion(ctx, baseTs.Height())
|
||||
|
||||
lbts, lbst, err := stmgr.GetLookbackTipSetForRound(ctx, filec.sm, baseTs, h.Height)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get lookback tipset for block: %w", err)
|
||||
}
|
||||
|
||||
prevBeacon, err := filec.store.GetLatestBeaconEntry(baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get latest beacon entry: %w", err)
|
||||
}
|
||||
|
||||
// fast checks first
|
||||
if h.Height <= baseTs.Height() {
|
||||
return xerrors.Errorf("block height not greater than parent height: %d != %d", h.Height, baseTs.Height())
|
||||
}
|
||||
|
||||
nulls := h.Height - (baseTs.Height() + 1)
|
||||
if tgtTs := baseTs.MinTimestamp() + build.BlockDelaySecs*uint64(nulls+1); h.Timestamp != tgtTs {
|
||||
return xerrors.Errorf("block has wrong timestamp: %d != %d", h.Timestamp, tgtTs)
|
||||
}
|
||||
|
||||
now := uint64(build.Clock.Now().Unix())
|
||||
if h.Timestamp > now+build.AllowableClockDriftSecs {
|
||||
return xerrors.Errorf("block was from the future (now=%d, blk=%d): %w", now, h.Timestamp, consensus.ErrTemporal)
|
||||
}
|
||||
if h.Timestamp > now {
|
||||
log.Warn("Got block from the future, but within threshold", h.Timestamp, build.Clock.Now().Unix())
|
||||
}
|
||||
|
||||
msgsCheck := async.Err(func() error {
|
||||
if b.Cid() == build.WhitelistedBlock {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := filec.checkBlockMessages(ctx, b, baseTs); err != nil {
|
||||
return xerrors.Errorf("block had invalid messages: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
minerCheck := async.Err(func() error {
|
||||
if err := filec.minerIsValid(ctx, h.Miner, baseTs); err != nil {
|
||||
return xerrors.Errorf("minerIsValid failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
baseFeeCheck := async.Err(func() error {
|
||||
baseFee, err := filec.store.ComputeBaseFee(ctx, baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("computing base fee: %w", err)
|
||||
}
|
||||
if types.BigCmp(baseFee, b.Header.ParentBaseFee) != 0 {
|
||||
return xerrors.Errorf("base fee doesn't match: %s (header) != %s (computed)",
|
||||
b.Header.ParentBaseFee, baseFee)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
pweight, err := filec.store.Weight(ctx, baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting parent weight: %w", err)
|
||||
}
|
||||
|
||||
if types.BigCmp(pweight, b.Header.ParentWeight) != 0 {
|
||||
return xerrors.Errorf("parrent weight different: %s (header) != %s (computed)",
|
||||
b.Header.ParentWeight, pweight)
|
||||
}
|
||||
|
||||
stateRootCheck := async.Err(func() error {
|
||||
stateroot, precp, err := filec.sm.TipSetState(ctx, baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err)
|
||||
}
|
||||
|
||||
if stateroot != h.ParentStateRoot {
|
||||
msgs, err := filec.store.MessagesForTipset(baseTs)
|
||||
if err != nil {
|
||||
log.Error("failed to load messages for tipset during tipset state mismatch error: ", err)
|
||||
} else {
|
||||
log.Warn("Messages for tipset with mismatching state:")
|
||||
for i, m := range msgs {
|
||||
mm := m.VMMessage()
|
||||
log.Warnf("Message[%d]: from=%s to=%s method=%d params=%x", i, mm.From, mm.To, mm.Method, mm.Params)
|
||||
}
|
||||
}
|
||||
|
||||
return xerrors.Errorf("parent state root did not match computed state (%s != %s)", stateroot, h.ParentStateRoot)
|
||||
}
|
||||
|
||||
if precp != h.ParentMessageReceipts {
|
||||
return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
// Stuff that needs worker address
|
||||
waddr, err := stmgr.GetMinerWorkerRaw(ctx, filec.sm, lbst, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("GetMinerWorkerRaw failed: %w", err)
|
||||
}
|
||||
|
||||
winnerCheck := async.Err(func() error {
|
||||
if h.ElectionProof.WinCount < 1 {
|
||||
return xerrors.Errorf("block is not claiming to be a winner")
|
||||
}
|
||||
|
||||
eligible, err := stmgr.MinerEligibleToMine(ctx, filec.sm, h.Miner, baseTs, lbts)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("determining if miner has min power failed: %w", err)
|
||||
}
|
||||
|
||||
if !eligible {
|
||||
return xerrors.New("block's miner is ineligible to mine")
|
||||
}
|
||||
|
||||
rBeacon := *prevBeacon
|
||||
if len(h.BeaconEntries) != 0 {
|
||||
rBeacon = h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if err := h.Miner.MarshalCBOR(buf); err != nil {
|
||||
return xerrors.Errorf("failed to marshal miner address to cbor: %w", err)
|
||||
}
|
||||
|
||||
vrfBase, err := store.DrawRandomness(rBeacon.Data, crypto.DomainSeparationTag_ElectionProofProduction, h.Height, buf.Bytes())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("could not draw randomness: %w", err)
|
||||
}
|
||||
|
||||
if err := VerifyElectionPoStVRF(ctx, waddr, vrfBase, h.ElectionProof.VRFProof); err != nil {
|
||||
return xerrors.Errorf("validating block election proof failed: %w", err)
|
||||
}
|
||||
|
||||
slashed, err := stmgr.GetMinerSlashed(ctx, filec.sm, baseTs, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to check if block miner was slashed: %w", err)
|
||||
}
|
||||
|
||||
if slashed {
|
||||
return xerrors.Errorf("received block was from slashed or invalid miner")
|
||||
}
|
||||
|
||||
mpow, tpow, _, err := stmgr.GetPowerRaw(ctx, filec.sm, lbst, h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed getting power: %w", err)
|
||||
}
|
||||
|
||||
j := h.ElectionProof.ComputeWinCount(mpow.QualityAdjPower, tpow.QualityAdjPower)
|
||||
if h.ElectionProof.WinCount != j {
|
||||
return xerrors.Errorf("miner claims wrong number of wins: miner: %d, computed: %d", h.ElectionProof.WinCount, j)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
blockSigCheck := async.Err(func() error {
|
||||
if err := sigs.CheckBlockSignature(ctx, h, waddr); err != nil {
|
||||
return xerrors.Errorf("check block signature failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
beaconValuesCheck := async.Err(func() error {
|
||||
if os.Getenv("LOTUS_IGNORE_DRAND") == "_yes_" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := beacon.ValidateBlockValues(filec.beacon, h, baseTs.Height(), *prevBeacon); err != nil {
|
||||
return xerrors.Errorf("failed to validate blocks random beacon values: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
tktsCheck := async.Err(func() error {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := h.Miner.MarshalCBOR(buf); err != nil {
|
||||
return xerrors.Errorf("failed to marshal miner address to cbor: %w", err)
|
||||
}
|
||||
|
||||
if h.Height > build.UpgradeSmokeHeight {
|
||||
buf.Write(baseTs.MinTicket().VRFProof)
|
||||
}
|
||||
|
||||
beaconBase := *prevBeacon
|
||||
if len(h.BeaconEntries) != 0 {
|
||||
beaconBase = h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
}
|
||||
|
||||
vrfBase, err := store.DrawRandomness(beaconBase.Data, crypto.DomainSeparationTag_TicketProduction, h.Height-build.TicketRandomnessLookback, buf.Bytes())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to compute vrf base for ticket: %w", err)
|
||||
}
|
||||
|
||||
err = VerifyElectionPoStVRF(ctx, waddr, vrfBase, h.Ticket.VRFProof)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("validating block tickets failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
wproofCheck := async.Err(func() error {
|
||||
if err := filec.VerifyWinningPoStProof(ctx, winPoStNv, h, *prevBeacon, lbst, waddr); err != nil {
|
||||
return xerrors.Errorf("invalid election post: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
await := []async.ErrorFuture{
|
||||
minerCheck,
|
||||
tktsCheck,
|
||||
blockSigCheck,
|
||||
beaconValuesCheck,
|
||||
wproofCheck,
|
||||
winnerCheck,
|
||||
msgsCheck,
|
||||
baseFeeCheck,
|
||||
stateRootCheck,
|
||||
}
|
||||
|
||||
var merr error
|
||||
for _, fut := range await {
|
||||
if err := fut.AwaitContext(ctx); err != nil {
|
||||
merr = multierror.Append(merr, err)
|
||||
}
|
||||
}
|
||||
if merr != nil {
|
||||
mulErr := merr.(*multierror.Error)
|
||||
mulErr.ErrorFormat = func(es []error) string {
|
||||
if len(es) == 1 {
|
||||
return fmt.Sprintf("1 error occurred:\n\t* %+v\n\n", es[0])
|
||||
}
|
||||
|
||||
points := make([]string, len(es))
|
||||
for i, err := range es {
|
||||
points[i] = fmt.Sprintf("* %+v", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%d errors occurred:\n\t%s\n\n",
|
||||
len(es), strings.Join(points, "\n\t"))
|
||||
}
|
||||
return mulErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func blockSanityChecks(h *types.BlockHeader) error {
|
||||
if h.ElectionProof == nil {
|
||||
return xerrors.Errorf("block cannot have nil election proof")
|
||||
}
|
||||
|
||||
if h.Ticket == nil {
|
||||
return xerrors.Errorf("block cannot have nil ticket")
|
||||
}
|
||||
|
||||
if h.BlockSig == nil {
|
||||
return xerrors.Errorf("block had nil signature")
|
||||
}
|
||||
|
||||
if h.BLSAggregate == nil {
|
||||
return xerrors.Errorf("block had nil bls aggregate signature")
|
||||
}
|
||||
|
||||
if h.Miner.Protocol() != address.ID {
|
||||
return xerrors.Errorf("block had non-ID miner address")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) VerifyWinningPoStProof(ctx context.Context, nv network.Version, h *types.BlockHeader, prevBeacon types.BeaconEntry, lbst cid.Cid, waddr address.Address) error {
|
||||
if build.InsecurePoStValidation {
|
||||
if len(h.WinPoStProof) == 0 {
|
||||
return xerrors.Errorf("[INSECURE-POST-VALIDATION] No winning post proof given")
|
||||
}
|
||||
|
||||
if string(h.WinPoStProof[0].ProofBytes) == "valid proof" {
|
||||
return nil
|
||||
}
|
||||
return xerrors.Errorf("[INSECURE-POST-VALIDATION] winning post was invalid")
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := h.Miner.MarshalCBOR(buf); err != nil {
|
||||
return xerrors.Errorf("failed to marshal miner address: %w", err)
|
||||
}
|
||||
|
||||
rbase := prevBeacon
|
||||
if len(h.BeaconEntries) > 0 {
|
||||
rbase = h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
}
|
||||
|
||||
rand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, h.Height, buf.Bytes())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get randomness for verifying winning post proof: %w", err)
|
||||
}
|
||||
|
||||
mid, err := address.IDFromAddress(h.Miner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get ID from miner address %s: %w", h.Miner, err)
|
||||
}
|
||||
|
||||
sectors, err := stmgr.GetSectorsForWinningPoSt(ctx, nv, filec.verifier, filec.sm, lbst, h.Miner, rand)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting winning post sector set: %w", err)
|
||||
}
|
||||
|
||||
ok, err := ffiwrapper.ProofVerifier.VerifyWinningPoSt(ctx, proof2.WinningPoStVerifyInfo{
|
||||
Randomness: rand,
|
||||
Proofs: h.WinPoStProof,
|
||||
ChallengedSectors: sectors,
|
||||
Prover: abi.ActorID(mid),
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to verify election post: %w", err)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
log.Errorf("invalid winning post (block: %s, %x; %v)", h.Cid(), rand, sectors)
|
||||
return xerrors.Errorf("winning post was invalid")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: We should extract this somewhere else and make the message pool and miner use the same logic
|
||||
func (filec *FilecoinEC) checkBlockMessages(ctx context.Context, b *types.FullBlock, baseTs *types.TipSet) error {
|
||||
{
|
||||
var sigCids []cid.Cid // this is what we get for people not wanting the marshalcbor method on the cid type
|
||||
var pubks [][]byte
|
||||
|
||||
for _, m := range b.BlsMessages {
|
||||
sigCids = append(sigCids, m.Cid())
|
||||
|
||||
pubk, err := filec.sm.GetBlsPublicKey(ctx, m.From, baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load bls public to validate block: %w", err)
|
||||
}
|
||||
|
||||
pubks = append(pubks, pubk)
|
||||
}
|
||||
|
||||
if err := consensus.VerifyBlsAggregate(ctx, b.Header.BLSAggregate, sigCids, pubks); err != nil {
|
||||
return xerrors.Errorf("bls aggregate signature was invalid: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
nonces := make(map[address.Address]uint64)
|
||||
|
||||
stateroot, _, err := filec.sm.TipSetState(ctx, baseTs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
st, err := state.LoadStateTree(filec.store.ActorStore(ctx), stateroot)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load base state tree: %w", err)
|
||||
}
|
||||
|
||||
nv := filec.sm.GetNtwkVersion(ctx, b.Header.Height)
|
||||
pl := vm.PricelistByEpoch(baseTs.Height())
|
||||
var sumGasLimit int64
|
||||
checkMsg := func(msg types.ChainMsg) error {
|
||||
m := msg.VMMessage()
|
||||
|
||||
// Phase 1: syntactic validation, as defined in the spec
|
||||
minGas := pl.OnChainMessage(msg.ChainLength())
|
||||
if err := m.ValidForBlockInclusion(minGas.Total(), nv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ValidForBlockInclusion checks if any single message does not exceed BlockGasLimit
|
||||
// So below is overflow safe
|
||||
sumGasLimit += m.GasLimit
|
||||
if sumGasLimit > build.BlockGasLimit {
|
||||
return xerrors.Errorf("block gas limit exceeded")
|
||||
}
|
||||
|
||||
// Phase 2: (Partial) semantic validation:
|
||||
// the sender exists and is an account actor, and the nonces make sense
|
||||
var sender address.Address
|
||||
if filec.sm.GetNtwkVersion(ctx, b.Header.Height) >= network.Version13 {
|
||||
sender, err = st.LookupID(m.From)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
sender = m.From
|
||||
}
|
||||
|
||||
if _, ok := nonces[sender]; !ok {
|
||||
// `GetActor` does not validate that this is an account actor.
|
||||
act, err := st.GetActor(sender)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get actor: %w", err)
|
||||
}
|
||||
|
||||
if !builtin.IsAccountActor(act.Code) {
|
||||
return xerrors.New("Sender must be an account actor")
|
||||
}
|
||||
nonces[sender] = act.Nonce
|
||||
}
|
||||
|
||||
if nonces[sender] != m.Nonce {
|
||||
return xerrors.Errorf("wrong nonce (exp: %d, got: %d)", nonces[sender], m.Nonce)
|
||||
}
|
||||
nonces[sender]++
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate message arrays in a temporary blockstore.
|
||||
tmpbs := bstore.NewMemory()
|
||||
tmpstore := blockadt.WrapStore(ctx, cbor.NewCborStore(tmpbs))
|
||||
|
||||
bmArr := blockadt.MakeEmptyArray(tmpstore)
|
||||
for i, m := range b.BlsMessages {
|
||||
if err := checkMsg(m); err != nil {
|
||||
return xerrors.Errorf("block had invalid bls message at index %d: %w", i, err)
|
||||
}
|
||||
|
||||
c, err := store.PutMessage(tmpbs, m)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to store message %s: %w", m.Cid(), err)
|
||||
}
|
||||
|
||||
k := cbg.CborCid(c)
|
||||
if err := bmArr.Set(uint64(i), &k); err != nil {
|
||||
return xerrors.Errorf("failed to put bls message at index %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
smArr := blockadt.MakeEmptyArray(tmpstore)
|
||||
for i, m := range b.SecpkMessages {
|
||||
if err := checkMsg(m); err != nil {
|
||||
return xerrors.Errorf("block had invalid secpk message at index %d: %w", i, err)
|
||||
}
|
||||
|
||||
// `From` being an account actor is only validated inside the `vm.ResolveToKeyAddr` call
|
||||
// in `StateManager.ResolveToKeyAddress` here (and not in `checkMsg`).
|
||||
kaddr, err := filec.sm.ResolveToKeyAddress(ctx, m.Message.From, baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to resolve key addr: %w", err)
|
||||
}
|
||||
|
||||
if err := sigs.Verify(&m.Signature, kaddr, m.Message.Cid().Bytes()); err != nil {
|
||||
return xerrors.Errorf("secpk message %s has invalid signature: %w", m.Cid(), err)
|
||||
}
|
||||
|
||||
c, err := store.PutMessage(tmpbs, m)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to store message %s: %w", m.Cid(), err)
|
||||
}
|
||||
k := cbg.CborCid(c)
|
||||
if err := smArr.Set(uint64(i), &k); err != nil {
|
||||
return xerrors.Errorf("failed to put secpk message at index %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
bmroot, err := bmArr.Root()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
smroot, err := smArr.Root()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mrcid, err := tmpstore.Put(ctx, &types.MsgMeta{
|
||||
BlsMessages: bmroot,
|
||||
SecpkMessages: smroot,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Header.Messages != mrcid {
|
||||
return fmt.Errorf("messages didnt match message root in header")
|
||||
}
|
||||
|
||||
// Finally, flush.
|
||||
return vm.Copy(ctx, tmpbs, filec.store.ChainBlockstore(), mrcid)
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool {
|
||||
if filec.genesis == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
now := uint64(build.Clock.Now().Unix())
|
||||
return epoch > (abi.ChainEpoch((now-filec.genesis.MinTimestamp())/build.BlockDelaySecs) + MaxHeightDrift)
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) minerIsValid(ctx context.Context, maddr address.Address, baseTs *types.TipSet) error {
|
||||
act, err := filec.sm.LoadActor(ctx, power.Address, baseTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load power actor: %w", err)
|
||||
}
|
||||
|
||||
powState, err := power.Load(filec.store.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load power actor state: %w", err)
|
||||
}
|
||||
|
||||
_, exist, err := powState.MinerPower(maddr)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to look up miner's claim: %w", err)
|
||||
}
|
||||
|
||||
if !exist {
|
||||
return xerrors.New("miner isn't valid")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyElectionPoStVRF(ctx context.Context, worker address.Address, rand []byte, evrf []byte) error {
|
||||
return VerifyVRF(ctx, worker, rand, evrf)
|
||||
}
|
||||
|
||||
func VerifyVRF(ctx context.Context, worker address.Address, vrfBase, vrfproof []byte) error {
|
||||
_, span := trace.StartSpan(ctx, "VerifyVRF")
|
||||
defer span.End()
|
||||
|
||||
sig := &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: vrfproof,
|
||||
}
|
||||
|
||||
if err := sigs.Verify(sig, worker, vrfBase); err != nil {
|
||||
return xerrors.Errorf("vrf was invalid: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var ErrSoftFailure = errors.New("soft validation failure")
|
||||
var ErrInsufficientPower = errors.New("incoming block's miner does not have minimum power")
|
||||
|
||||
func (filec *FilecoinEC) ValidateBlockPubsub(ctx context.Context, self bool, msg *pubsub.Message) (pubsub.ValidationResult, string) {
|
||||
if self {
|
||||
return filec.validateLocalBlock(ctx, msg)
|
||||
}
|
||||
|
||||
// track validation time
|
||||
begin := build.Clock.Now()
|
||||
defer func() {
|
||||
log.Debugf("block validation time: %s", build.Clock.Since(begin))
|
||||
}()
|
||||
|
||||
stats.Record(ctx, metrics.BlockReceived.M(1))
|
||||
|
||||
recordFailureFlagPeer := func(what string) {
|
||||
// bv.Validate will flag the peer in that case
|
||||
panic(what)
|
||||
}
|
||||
|
||||
blk, what, err := filec.decodeAndCheckBlock(msg)
|
||||
if err != nil {
|
||||
log.Error("got invalid block over pubsub: ", err)
|
||||
recordFailureFlagPeer(what)
|
||||
return pubsub.ValidationReject, what
|
||||
}
|
||||
|
||||
// validate the block meta: the Message CID in the header must match the included messages
|
||||
err = filec.validateMsgMeta(ctx, blk)
|
||||
if err != nil {
|
||||
log.Warnf("error validating message metadata: %s", err)
|
||||
recordFailureFlagPeer("invalid_block_meta")
|
||||
return pubsub.ValidationReject, "invalid_block_meta"
|
||||
}
|
||||
|
||||
reject, err := filec.validateBlockHeader(ctx, blk.Header)
|
||||
if err != nil {
|
||||
if reject == "" {
|
||||
log.Warn("ignoring block msg: ", err)
|
||||
return pubsub.ValidationIgnore, reject
|
||||
}
|
||||
recordFailureFlagPeer(reject)
|
||||
return pubsub.ValidationReject, reject
|
||||
}
|
||||
|
||||
// all good, accept the block
|
||||
msg.ValidatorData = blk
|
||||
stats.Record(ctx, metrics.BlockValidationSuccess.M(1))
|
||||
return pubsub.ValidationAccept, ""
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) validateLocalBlock(ctx context.Context, msg *pubsub.Message) (pubsub.ValidationResult, string) {
|
||||
stats.Record(ctx, metrics.BlockPublished.M(1))
|
||||
|
||||
if size := msg.Size(); size > 1<<20-1<<15 {
|
||||
log.Errorf("ignoring oversize block (%dB)", size)
|
||||
return pubsub.ValidationIgnore, "oversize_block"
|
||||
}
|
||||
|
||||
blk, what, err := filec.decodeAndCheckBlock(msg)
|
||||
if err != nil {
|
||||
log.Errorf("got invalid local block: %s", err)
|
||||
return pubsub.ValidationIgnore, what
|
||||
}
|
||||
|
||||
msg.ValidatorData = blk
|
||||
stats.Record(ctx, metrics.BlockValidationSuccess.M(1))
|
||||
return pubsub.ValidationAccept, ""
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) decodeAndCheckBlock(msg *pubsub.Message) (*types.BlockMsg, string, error) {
|
||||
blk, err := types.DecodeBlockMsg(msg.GetData())
|
||||
if err != nil {
|
||||
return nil, "invalid", xerrors.Errorf("error decoding block: %w", err)
|
||||
}
|
||||
|
||||
if count := len(blk.BlsMessages) + len(blk.SecpkMessages); count > build.BlockMessageLimit {
|
||||
return nil, "too_many_messages", fmt.Errorf("block contains too many messages (%d)", count)
|
||||
}
|
||||
|
||||
// make sure we have a signature
|
||||
if blk.Header.BlockSig == nil {
|
||||
return nil, "missing_signature", fmt.Errorf("block without a signature")
|
||||
}
|
||||
|
||||
return blk, "", nil
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) validateMsgMeta(ctx context.Context, msg *types.BlockMsg) error {
|
||||
// TODO there has to be a simpler way to do this without the blockstore dance
|
||||
// block headers use adt0
|
||||
store := blockadt.WrapStore(ctx, cbor.NewCborStore(bstore.NewMemory()))
|
||||
bmArr := blockadt.MakeEmptyArray(store)
|
||||
smArr := blockadt.MakeEmptyArray(store)
|
||||
|
||||
for i, m := range msg.BlsMessages {
|
||||
c := cbg.CborCid(m)
|
||||
if err := bmArr.Set(uint64(i), &c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for i, m := range msg.SecpkMessages {
|
||||
c := cbg.CborCid(m)
|
||||
if err := smArr.Set(uint64(i), &c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bmroot, err := bmArr.Root()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
smroot, err := smArr.Root()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mrcid, err := store.Put(store.Context(), &types.MsgMeta{
|
||||
BlsMessages: bmroot,
|
||||
SecpkMessages: smroot,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.Header.Messages != mrcid {
|
||||
return fmt.Errorf("messages didn't match root cid in header")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) validateBlockHeader(ctx context.Context, b *types.BlockHeader) (rejectReason string, err error) {
|
||||
|
||||
// we want to ensure that it is a block from a known miner; we reject blocks from unknown miners
|
||||
// to prevent spam attacks.
|
||||
// the logic works as follows: we lookup the miner in the chain for its key.
|
||||
// if we can find it then it's a known miner and we can validate the signature.
|
||||
// if we can't find it, we check whether we are (near) synced in the chain.
|
||||
// if we are not synced we cannot validate the block and we must ignore it.
|
||||
// if we are synced and the miner is unknown, then the block is rejcected.
|
||||
key, err := filec.checkPowerAndGetWorkerKey(ctx, b)
|
||||
if err != nil {
|
||||
if err != ErrSoftFailure && filec.isChainNearSynced() {
|
||||
log.Warnf("received block from unknown miner or miner that doesn't meet min power over pubsub; rejecting message")
|
||||
return "unknown_miner", err
|
||||
}
|
||||
|
||||
log.Warnf("cannot validate block message; unknown miner or miner that doesn't meet min power in unsynced chain: %s", b.Cid())
|
||||
return "", err // ignore
|
||||
}
|
||||
|
||||
if b.ElectionProof.WinCount < 1 {
|
||||
log.Errorf("block is not claiming to be winning")
|
||||
return "not_winning", xerrors.Errorf("block not winning")
|
||||
}
|
||||
|
||||
err = sigs.CheckBlockSignature(ctx, b, key)
|
||||
if err != nil {
|
||||
log.Errorf("block signature verification failed: %s", err)
|
||||
return "signature_verification_failed", err
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) checkPowerAndGetWorkerKey(ctx context.Context, bh *types.BlockHeader) (address.Address, error) {
|
||||
// we check that the miner met the minimum power at the lookback tipset
|
||||
|
||||
baseTs := filec.store.GetHeaviestTipSet()
|
||||
lbts, lbst, err := stmgr.GetLookbackTipSetForRound(ctx, filec.sm, baseTs, bh.Height)
|
||||
if err != nil {
|
||||
log.Warnf("failed to load lookback tipset for incoming block: %s", err)
|
||||
return address.Undef, ErrSoftFailure
|
||||
}
|
||||
|
||||
key, err := stmgr.GetMinerWorkerRaw(ctx, filec.sm, lbst, bh.Miner)
|
||||
if err != nil {
|
||||
log.Warnf("failed to resolve worker key for miner %s: %s", bh.Miner, err)
|
||||
return address.Undef, ErrSoftFailure
|
||||
}
|
||||
|
||||
// NOTE: we check to see if the miner was eligible in the lookback
|
||||
// tipset - 1 for historical reasons. DO NOT use the lookback state
|
||||
// returned by GetLookbackTipSetForRound.
|
||||
|
||||
eligible, err := stmgr.MinerEligibleToMine(ctx, filec.sm, bh.Miner, baseTs, lbts)
|
||||
if err != nil {
|
||||
log.Warnf("failed to determine if incoming block's miner has minimum power: %s", err)
|
||||
return address.Undef, ErrSoftFailure
|
||||
}
|
||||
|
||||
if !eligible {
|
||||
log.Warnf("incoming block's miner is ineligible")
|
||||
return address.Undef, ErrInsufficientPower
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (filec *FilecoinEC) isChainNearSynced() bool {
|
||||
ts := filec.store.GetHeaviestTipSet()
|
||||
timestamp := ts.MinTimestamp()
|
||||
timestampTime := time.Unix(int64(timestamp), 0)
|
||||
return build.Clock.Since(timestampTime) < 6*time.Hour
|
||||
}
|
||||
|
||||
var _ consensus.Consensus = &FilecoinEC{}
|
||||
@@ -1,38 +1,36 @@
|
||||
package gen
|
||||
package filcns
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"github.com/ipfs/go-cid"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/consensus"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w api.Wallet, bt *api.BlockTemplate) (*types.FullBlock, error) {
|
||||
|
||||
pts, err := sm.ChainStore().LoadTipSet(bt.Parents)
|
||||
func (filec *FilecoinEC) CreateBlock(ctx context.Context, w api.Wallet, bt *api.BlockTemplate) (*types.FullBlock, error) {
|
||||
pts, err := filec.sm.ChainStore().LoadTipSet(bt.Parents)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load parent tipset: %w", err)
|
||||
}
|
||||
|
||||
st, recpts, err := sm.TipSetState(ctx, pts)
|
||||
st, recpts, err := filec.sm.TipSetState(ctx, pts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load tipset state: %w", err)
|
||||
}
|
||||
|
||||
_, lbst, err := stmgr.GetLookbackTipSetForRound(ctx, sm, pts, bt.Epoch)
|
||||
_, lbst, err := stmgr.GetLookbackTipSetForRound(ctx, filec.sm, pts, bt.Epoch)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting lookback miner actor state: %w", err)
|
||||
}
|
||||
|
||||
worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, lbst, bt.Miner)
|
||||
worker, err := stmgr.GetMinerWorkerRaw(ctx, filec.sm, lbst, bt.Miner)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get miner worker: %w", err)
|
||||
}
|
||||
@@ -61,14 +59,14 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w api.Wallet,
|
||||
blsSigs = append(blsSigs, msg.Signature)
|
||||
blsMessages = append(blsMessages, &msg.Message)
|
||||
|
||||
c, err := sm.ChainStore().PutMessage(&msg.Message)
|
||||
c, err := filec.sm.ChainStore().PutMessage(&msg.Message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blsMsgCids = append(blsMsgCids, c)
|
||||
} else {
|
||||
c, err := sm.ChainStore().PutMessage(msg)
|
||||
c, err := filec.sm.ChainStore().PutMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -79,12 +77,12 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w api.Wallet,
|
||||
}
|
||||
}
|
||||
|
||||
store := sm.ChainStore().ActorStore(ctx)
|
||||
blsmsgroot, err := toArray(store, blsMsgCids)
|
||||
store := filec.sm.ChainStore().ActorStore(ctx)
|
||||
blsmsgroot, err := consensus.ToMessagesArray(store, blsMsgCids)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("building bls amt: %w", err)
|
||||
}
|
||||
secpkmsgroot, err := toArray(store, secpkMsgCids)
|
||||
secpkmsgroot, err := consensus.ToMessagesArray(store, secpkMsgCids)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("building secpk amt: %w", err)
|
||||
}
|
||||
@@ -98,19 +96,19 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w api.Wallet,
|
||||
}
|
||||
next.Messages = mmcid
|
||||
|
||||
aggSig, err := aggregateSignatures(blsSigs)
|
||||
aggSig, err := consensus.AggregateSignatures(blsSigs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
next.BLSAggregate = aggSig
|
||||
pweight, err := sm.ChainStore().Weight(ctx, pts)
|
||||
pweight, err := filec.sm.ChainStore().Weight(ctx, pts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
next.ParentWeight = pweight
|
||||
|
||||
baseFee, err := sm.ChainStore().ComputeBaseFee(ctx, pts)
|
||||
baseFee, err := filec.sm.ChainStore().ComputeBaseFee(ctx, pts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("computing base fee: %w", err)
|
||||
}
|
||||
@@ -138,41 +136,3 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w api.Wallet,
|
||||
|
||||
return fullBlock, nil
|
||||
}
|
||||
|
||||
func aggregateSignatures(sigs []crypto.Signature) (*crypto.Signature, error) {
|
||||
sigsS := make([]ffi.Signature, len(sigs))
|
||||
for i := 0; i < len(sigs); i++ {
|
||||
copy(sigsS[i][:], sigs[i].Data[:ffi.SignatureBytes])
|
||||
}
|
||||
|
||||
aggSig := ffi.Aggregate(sigsS)
|
||||
if aggSig == nil {
|
||||
if len(sigs) > 0 {
|
||||
return nil, xerrors.Errorf("bls.Aggregate returned nil with %d signatures", len(sigs))
|
||||
}
|
||||
|
||||
zeroSig := ffi.CreateZeroSignature()
|
||||
|
||||
// Note: for blst this condition should not happen - nil should not
|
||||
// be returned
|
||||
return &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: zeroSig[:],
|
||||
}, nil
|
||||
}
|
||||
return &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: aggSig[:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toArray(store blockadt.Store, cids []cid.Cid) (cid.Cid, error) {
|
||||
arr := blockadt.MakeEmptyArray(store)
|
||||
for i, c := range cids {
|
||||
oc := cbg.CborCid(c)
|
||||
if err := arr.Set(uint64(i), &oc); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
}
|
||||
return arr.Root()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,25 @@
|
||||
package store
|
||||
package filcns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||
|
||||
big2 "github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
big2 "github.com/filecoin-project/go-state-types/big"
|
||||
|
||||
bstore "github.com/filecoin-project/lotus/blockstore"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
var zero = types.NewInt(0)
|
||||
|
||||
func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigInt, error) {
|
||||
func Weight(ctx context.Context, stateBs bstore.Blockstore, ts *types.TipSet) (types.BigInt, error) {
|
||||
if ts == nil {
|
||||
return types.NewInt(0), nil
|
||||
}
|
||||
@@ -28,7 +31,7 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn
|
||||
|
||||
tpow := big2.Zero()
|
||||
{
|
||||
cst := cbor.NewCborStore(cs.StateBlockstore())
|
||||
cst := cbor.NewCborStore(stateBs)
|
||||
state, err := state.LoadStateTree(cst, ts.ParentState())
|
||||
if err != nil {
|
||||
return types.NewInt(0), xerrors.Errorf("load state tree: %w", err)
|
||||
@@ -39,7 +42,7 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn
|
||||
return types.NewInt(0), xerrors.Errorf("get power actor: %w", err)
|
||||
}
|
||||
|
||||
powState, err := power.Load(cs.ActorStore(ctx), act)
|
||||
powState, err := power.Load(store.ActorStore(ctx, stateBs), act)
|
||||
if err != nil {
|
||||
return types.NewInt(0), xerrors.Errorf("failed to load power actor state: %w", err)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type Consensus interface {
|
||||
ValidateBlock(ctx context.Context, b *types.FullBlock) (err error)
|
||||
ValidateBlockPubsub(ctx context.Context, self bool, msg *pubsub.Message) (pubsub.ValidationResult, string)
|
||||
IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool
|
||||
|
||||
CreateBlock(ctx context.Context, w api.Wallet, bt *api.BlockTemplate) (*types.FullBlock, error)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
"github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
ffi "github.com/filecoin-project/filecoin-ffi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
)
|
||||
|
||||
var ErrTemporal = errors.New("temporal error")
|
||||
|
||||
func VerifyBlsAggregate(ctx context.Context, sig *crypto.Signature, msgs []cid.Cid, pubks [][]byte) error {
|
||||
_, span := trace.StartSpan(ctx, "syncer.VerifyBlsAggregate")
|
||||
defer span.End()
|
||||
span.AddAttributes(
|
||||
trace.Int64Attribute("msgCount", int64(len(msgs))),
|
||||
)
|
||||
|
||||
msgsS := make([]ffi.Message, len(msgs))
|
||||
pubksS := make([]ffi.PublicKey, len(msgs))
|
||||
for i := 0; i < len(msgs); i++ {
|
||||
msgsS[i] = msgs[i].Bytes()
|
||||
copy(pubksS[i][:], pubks[i][:ffi.PublicKeyBytes])
|
||||
}
|
||||
|
||||
sigS := new(ffi.Signature)
|
||||
copy(sigS[:], sig.Data[:ffi.SignatureBytes])
|
||||
|
||||
if len(msgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
valid := ffi.HashVerify(sigS, msgsS, pubksS)
|
||||
if !valid {
|
||||
return xerrors.New("bls aggregate signature failed to verify")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AggregateSignatures(sigs []crypto.Signature) (*crypto.Signature, error) {
|
||||
sigsS := make([]ffi.Signature, len(sigs))
|
||||
for i := 0; i < len(sigs); i++ {
|
||||
copy(sigsS[i][:], sigs[i].Data[:ffi.SignatureBytes])
|
||||
}
|
||||
|
||||
aggSig := ffi.Aggregate(sigsS)
|
||||
if aggSig == nil {
|
||||
if len(sigs) > 0 {
|
||||
return nil, xerrors.Errorf("bls.Aggregate returned nil with %d signatures", len(sigs))
|
||||
}
|
||||
|
||||
zeroSig := ffi.CreateZeroSignature()
|
||||
|
||||
// Note: for blst this condition should not happen - nil should not
|
||||
// be returned
|
||||
return &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: zeroSig[:],
|
||||
}, nil
|
||||
}
|
||||
return &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: aggSig[:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ToMessagesArray(store blockadt.Store, cids []cid.Cid) (cid.Cid, error) {
|
||||
arr := blockadt.MakeEmptyArray(store)
|
||||
for i, c := range cids {
|
||||
oc := cbg.CborCid(c)
|
||||
if err := arr.Set(uint64(i), &oc); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
}
|
||||
return arr.Root()
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
type uncachedAPI interface {
|
||||
ChainNotify(context.Context) (<-chan []*api.HeadChange, error)
|
||||
ChainGetPath(ctx context.Context, from, to types.TipSetKey) ([]*api.HeadChange, error)
|
||||
StateSearchMsg(ctx context.Context, from types.TipSetKey, msg cid.Cid, limit abi.ChainEpoch, allowReplaced bool) (*api.MsgLookup, error)
|
||||
|
||||
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) // optional / for CalledMsg
|
||||
}
|
||||
|
||||
type cache struct {
|
||||
*tipSetCache
|
||||
*messageCache
|
||||
uncachedAPI
|
||||
}
|
||||
|
||||
func newCache(api EventAPI, gcConfidence abi.ChainEpoch) *cache {
|
||||
return &cache{
|
||||
newTSCache(api, gcConfidence),
|
||||
newMessageCache(api),
|
||||
api,
|
||||
}
|
||||
}
|
||||
+17
-184
@@ -2,18 +2,14 @@ package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
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/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@@ -25,209 +21,46 @@ type (
|
||||
RevertHandler func(ctx context.Context, ts *types.TipSet) error
|
||||
)
|
||||
|
||||
type heightHandler struct {
|
||||
confidence int
|
||||
called bool
|
||||
|
||||
handle HeightHandler
|
||||
revert RevertHandler
|
||||
// A TipSetObserver receives notifications of tipsets
|
||||
type TipSetObserver interface {
|
||||
Apply(ctx context.Context, from, to *types.TipSet) error
|
||||
Revert(ctx context.Context, from, to *types.TipSet) error
|
||||
}
|
||||
|
||||
type EventAPI interface {
|
||||
ChainNotify(context.Context) (<-chan []*api.HeadChange, error)
|
||||
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
|
||||
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetAfterHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainHead(context.Context) (*types.TipSet, error)
|
||||
StateSearchMsg(ctx context.Context, from types.TipSetKey, msg cid.Cid, limit abi.ChainEpoch, allowReplaced bool) (*api.MsgLookup, error)
|
||||
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetPath(ctx context.Context, from, to types.TipSetKey) ([]*api.HeadChange, error)
|
||||
|
||||
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) // optional / for CalledMsg
|
||||
}
|
||||
|
||||
type Events struct {
|
||||
api EventAPI
|
||||
|
||||
tsc *tipSetCache
|
||||
lk sync.Mutex
|
||||
|
||||
ready chan struct{}
|
||||
readyOnce sync.Once
|
||||
|
||||
heightEvents
|
||||
*observer
|
||||
*heightEvents
|
||||
*hcEvents
|
||||
|
||||
observers []TipSetObserver
|
||||
}
|
||||
|
||||
func NewEventsWithConfidence(ctx context.Context, api EventAPI, gcConfidence abi.ChainEpoch) *Events {
|
||||
tsc := newTSCache(gcConfidence, api)
|
||||
func NewEventsWithConfidence(ctx context.Context, api EventAPI, gcConfidence abi.ChainEpoch) (*Events, error) {
|
||||
cache := newCache(api, gcConfidence)
|
||||
|
||||
e := &Events{
|
||||
api: api,
|
||||
|
||||
tsc: tsc,
|
||||
|
||||
heightEvents: heightEvents{
|
||||
tsc: tsc,
|
||||
ctx: ctx,
|
||||
gcConfidence: gcConfidence,
|
||||
|
||||
heightTriggers: map[uint64]*heightHandler{},
|
||||
htTriggerHeights: map[abi.ChainEpoch][]uint64{},
|
||||
htHeights: map[abi.ChainEpoch][]uint64{},
|
||||
},
|
||||
|
||||
hcEvents: newHCEvents(ctx, api, tsc, uint64(gcConfidence)),
|
||||
ready: make(chan struct{}),
|
||||
observers: []TipSetObserver{},
|
||||
ob := newObserver(cache, gcConfidence)
|
||||
if err := ob.start(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go e.listenHeadChanges(ctx)
|
||||
he := newHeightEvents(cache, ob, gcConfidence)
|
||||
headChange := newHCEvents(cache, ob)
|
||||
|
||||
// Wait for the first tipset to be seen or bail if shutting down
|
||||
select {
|
||||
case <-e.ready:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
return e
|
||||
return &Events{ob, he, headChange}, nil
|
||||
}
|
||||
|
||||
func NewEvents(ctx context.Context, api EventAPI) *Events {
|
||||
func NewEvents(ctx context.Context, api EventAPI) (*Events, error) {
|
||||
gcConfidence := 2 * build.ForkLengthThreshold
|
||||
return NewEventsWithConfidence(ctx, api, gcConfidence)
|
||||
}
|
||||
|
||||
func (e *Events) listenHeadChanges(ctx context.Context) {
|
||||
for {
|
||||
if err := e.listenHeadChangesOnce(ctx); err != nil {
|
||||
log.Errorf("listen head changes errored: %s", err)
|
||||
} else {
|
||||
log.Warn("listenHeadChanges quit")
|
||||
}
|
||||
select {
|
||||
case <-build.Clock.After(time.Second):
|
||||
case <-ctx.Done():
|
||||
log.Warnf("not restarting listenHeadChanges: context error: %s", ctx.Err())
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("restarting listenHeadChanges")
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Events) listenHeadChangesOnce(ctx context.Context) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
notifs, err := e.api.ChainNotify(ctx)
|
||||
if err != nil {
|
||||
// Retry is handled by caller
|
||||
return xerrors.Errorf("listenHeadChanges ChainNotify call failed: %w", err)
|
||||
}
|
||||
|
||||
var cur []*api.HeadChange
|
||||
var ok bool
|
||||
|
||||
// Wait for first tipset or bail
|
||||
select {
|
||||
case cur, ok = <-notifs:
|
||||
if !ok {
|
||||
return xerrors.Errorf("notification channel closed")
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
if len(cur) != 1 {
|
||||
return xerrors.Errorf("unexpected initial head notification length: %d", len(cur))
|
||||
}
|
||||
|
||||
if cur[0].Type != store.HCCurrent {
|
||||
return xerrors.Errorf("expected first head notification type to be 'current', was '%s'", cur[0].Type)
|
||||
}
|
||||
|
||||
if err := e.tsc.add(cur[0].Val); err != nil {
|
||||
log.Warnf("tsc.add: adding current tipset failed: %v", err)
|
||||
}
|
||||
|
||||
e.readyOnce.Do(func() {
|
||||
e.lastTs = cur[0].Val
|
||||
// Signal that we have seen first tipset
|
||||
close(e.ready)
|
||||
})
|
||||
|
||||
for notif := range notifs {
|
||||
var rev, app []*types.TipSet
|
||||
for _, notif := range notif {
|
||||
switch notif.Type {
|
||||
case store.HCRevert:
|
||||
rev = append(rev, notif.Val)
|
||||
case store.HCApply:
|
||||
app = append(app, notif.Val)
|
||||
default:
|
||||
log.Warnf("unexpected head change notification type: '%s'", notif.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if err := e.headChange(ctx, 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
|
||||
}
|
||||
|
||||
func (e *Events) headChange(ctx context.Context, rev, app []*types.TipSet) error {
|
||||
if len(app) == 0 {
|
||||
return xerrors.New("events.headChange expected at least one applied tipset")
|
||||
}
|
||||
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
|
||||
if err := e.headChangeAt(rev, app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := e.observeChanges(ctx, rev, app); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.processHeadChangeEvent(rev, app)
|
||||
}
|
||||
|
||||
// A TipSetObserver receives notifications of tipsets
|
||||
type TipSetObserver interface {
|
||||
Apply(ctx context.Context, ts *types.TipSet) error
|
||||
Revert(ctx context.Context, ts *types.TipSet) error
|
||||
}
|
||||
|
||||
// TODO: add a confidence level so we can have observers with difference levels of confidence
|
||||
func (e *Events) Observe(obs TipSetObserver) error {
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
e.observers = append(e.observers, obs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// observeChanges expects caller to hold e.lk
|
||||
func (e *Events) observeChanges(ctx context.Context, rev, app []*types.TipSet) error {
|
||||
for _, ts := range rev {
|
||||
for _, o := range e.observers {
|
||||
_ = o.Revert(ctx, ts)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ts := range app {
|
||||
for _, o := range e.observers {
|
||||
_ = o.Apply(ctx, ts)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+107
-173
@@ -5,9 +5,6 @@ import (
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
@@ -35,7 +32,7 @@ type eventData interface{}
|
||||
// `prevTs` is the previous tipset, eg the "from" tipset for a state change.
|
||||
// `ts` is the event tipset, eg the tipset in which the `msg` is included.
|
||||
// `curH`-`ts.Height` = `confidence`
|
||||
type EventHandler func(data eventData, prevTs, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error)
|
||||
type EventHandler func(ctx context.Context, data eventData, prevTs, 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`
|
||||
@@ -43,7 +40,7 @@ type EventHandler func(data eventData, prevTs, ts *types.TipSet, curH abi.ChainE
|
||||
// If `done` is true, timeout won't be triggered
|
||||
// If `more` is false, no messages will be sent to EventHandler (RevertHandler
|
||||
// may still be called)
|
||||
type CheckFunc func(ts *types.TipSet) (done bool, more bool, err error)
|
||||
type CheckFunc func(ctx context.Context, ts *types.TipSet) (done bool, more bool, err error)
|
||||
|
||||
// Keep track of information for an event handler
|
||||
type handlerInfo struct {
|
||||
@@ -60,10 +57,9 @@ type handlerInfo struct {
|
||||
// until the required confidence is reached
|
||||
type queuedEvent struct {
|
||||
trigger triggerID
|
||||
data eventData
|
||||
|
||||
prevH abi.ChainEpoch
|
||||
h abi.ChainEpoch
|
||||
data eventData
|
||||
prevTipset, tipset *types.TipSet
|
||||
|
||||
called bool
|
||||
}
|
||||
@@ -71,19 +67,17 @@ type queuedEvent struct {
|
||||
// Manages chain head change events, which may be forward (new tipset added to
|
||||
// chain) or backward (chain branch discarded in favour of heavier branch)
|
||||
type hcEvents struct {
|
||||
cs EventAPI
|
||||
tsc *tipSetCache
|
||||
ctx context.Context
|
||||
gcConfidence uint64
|
||||
cs EventAPI
|
||||
|
||||
lk sync.Mutex
|
||||
lastTs *types.TipSet
|
||||
|
||||
lk sync.Mutex
|
||||
|
||||
ctr triggerID
|
||||
|
||||
// TODO: get rid of trigger IDs and just use pointers as keys.
|
||||
triggers map[triggerID]*handlerInfo
|
||||
|
||||
// TODO: instead of scheduling events in the future, look at the chain in the past. We can sip the "confidence" queue entirely.
|
||||
// maps block heights to events
|
||||
// [triggerH][msgH][event]
|
||||
confQueue map[triggerH]map[msgH][]*queuedEvent
|
||||
@@ -98,83 +92,77 @@ type hcEvents struct {
|
||||
watcherEvents
|
||||
}
|
||||
|
||||
func newHCEvents(ctx context.Context, cs EventAPI, tsc *tipSetCache, gcConfidence uint64) *hcEvents {
|
||||
e := hcEvents{
|
||||
ctx: ctx,
|
||||
cs: cs,
|
||||
tsc: tsc,
|
||||
gcConfidence: gcConfidence,
|
||||
|
||||
func newHCEvents(api EventAPI, obs *observer) *hcEvents {
|
||||
e := &hcEvents{
|
||||
cs: api,
|
||||
confQueue: map[triggerH]map[msgH][]*queuedEvent{},
|
||||
revertQueue: map[msgH][]triggerH{},
|
||||
triggers: map[triggerID]*handlerInfo{},
|
||||
timeouts: map[abi.ChainEpoch]map[triggerID]int{},
|
||||
}
|
||||
|
||||
e.messageEvents = newMessageEvents(ctx, &e, cs)
|
||||
e.watcherEvents = newWatcherEvents(ctx, &e, cs)
|
||||
e.messageEvents = newMessageEvents(e, api)
|
||||
e.watcherEvents = newWatcherEvents(e, api)
|
||||
|
||||
return &e
|
||||
// We need to take the lock as the observer could immediately try calling us.
|
||||
e.lk.Lock()
|
||||
e.lastTs = obs.Observe((*hcEventsObserver)(e))
|
||||
e.lk.Unlock()
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// Called when there is a change to the head with tipsets to be
|
||||
// reverted / applied
|
||||
func (e *hcEvents) processHeadChangeEvent(rev, app []*types.TipSet) error {
|
||||
type hcEventsObserver hcEvents
|
||||
|
||||
func (e *hcEventsObserver) Apply(ctx context.Context, from, to *types.TipSet) error {
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
|
||||
for _, ts := range rev {
|
||||
e.handleReverts(ts)
|
||||
e.lastTs = ts
|
||||
defer func() { e.lastTs = to }()
|
||||
|
||||
// Check if the head change caused any state changes that we were
|
||||
// waiting for
|
||||
stateChanges := e.checkStateChanges(from, to)
|
||||
|
||||
// Queue up calls until there have been enough blocks to reach
|
||||
// confidence on the state changes
|
||||
for tid, data := range stateChanges {
|
||||
e.queueForConfidence(tid, data, from, to)
|
||||
}
|
||||
|
||||
for _, ts := range app {
|
||||
// Check if the head change caused any state changes that we were
|
||||
// waiting for
|
||||
stateChanges := e.watcherEvents.checkStateChanges(e.lastTs, ts)
|
||||
// Check if the head change included any new message calls
|
||||
newCalls := e.checkNewCalls(ctx, from, to)
|
||||
|
||||
// Queue up calls until there have been enough blocks to reach
|
||||
// confidence on the state changes
|
||||
for tid, data := range stateChanges {
|
||||
e.queueForConfidence(tid, data, e.lastTs, ts)
|
||||
// Queue up calls until there have been enough blocks to reach
|
||||
// confidence on the message calls
|
||||
for tid, calls := range newCalls {
|
||||
for _, data := range calls {
|
||||
e.queueForConfidence(tid, data, nil, to)
|
||||
}
|
||||
|
||||
// Check if the head change included any new message calls
|
||||
newCalls, err := e.messageEvents.checkNewCalls(ts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Queue up calls until there have been enough blocks to reach
|
||||
// confidence on the message calls
|
||||
for tid, calls := range newCalls {
|
||||
for _, data := range calls {
|
||||
e.queueForConfidence(tid, data, nil, ts)
|
||||
}
|
||||
}
|
||||
|
||||
for at := e.lastTs.Height(); at <= ts.Height(); at++ {
|
||||
// Apply any queued events and timeouts that were targeted at the
|
||||
// current chain height
|
||||
e.applyWithConfidence(ts, at)
|
||||
e.applyTimeouts(ts)
|
||||
}
|
||||
|
||||
// Update the latest known tipset
|
||||
e.lastTs = ts
|
||||
}
|
||||
|
||||
for at := from.Height() + 1; at <= to.Height(); at++ {
|
||||
// Apply any queued events and timeouts that were targeted at the
|
||||
// current chain height
|
||||
e.applyWithConfidence(ctx, at)
|
||||
e.applyTimeouts(ctx, at, to)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *hcEvents) handleReverts(ts *types.TipSet) {
|
||||
reverts, ok := e.revertQueue[ts.Height()]
|
||||
func (e *hcEventsObserver) Revert(ctx context.Context, from, to *types.TipSet) error {
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
|
||||
defer func() { e.lastTs = to }()
|
||||
|
||||
reverts, ok := e.revertQueue[from.Height()]
|
||||
if !ok {
|
||||
return // nothing to do
|
||||
return nil // nothing to do
|
||||
}
|
||||
|
||||
for _, triggerH := range reverts {
|
||||
toRevert := e.confQueue[triggerH][ts.Height()]
|
||||
toRevert := e.confQueue[triggerH][from.Height()]
|
||||
for _, event := range toRevert {
|
||||
if !event.called {
|
||||
continue // event wasn't apply()-ied yet
|
||||
@@ -182,24 +170,21 @@ func (e *hcEvents) handleReverts(ts *types.TipSet) {
|
||||
|
||||
trigger := e.triggers[event.trigger]
|
||||
|
||||
if err := trigger.revert(e.ctx, ts); err != nil {
|
||||
log.Errorf("reverting chain trigger (@H %d, triggered @ %d) failed: %s", ts.Height(), triggerH, err)
|
||||
if err := trigger.revert(ctx, from); err != nil {
|
||||
log.Errorf("reverting chain trigger (@H %d, triggered @ %d) failed: %s", from.Height(), triggerH, err)
|
||||
}
|
||||
}
|
||||
delete(e.confQueue[triggerH], ts.Height())
|
||||
delete(e.confQueue[triggerH], from.Height())
|
||||
}
|
||||
delete(e.revertQueue, ts.Height())
|
||||
delete(e.revertQueue, from.Height())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Queue up events until the chain has reached a height that reflects the
|
||||
// desired confidence
|
||||
func (e *hcEvents) queueForConfidence(trigID uint64, data eventData, prevTs, ts *types.TipSet) {
|
||||
func (e *hcEventsObserver) queueForConfidence(trigID uint64, data eventData, prevTs, ts *types.TipSet) {
|
||||
trigger := e.triggers[trigID]
|
||||
|
||||
prevH := NoHeight
|
||||
if prevTs != nil {
|
||||
prevH = prevTs.Height()
|
||||
}
|
||||
appliedH := ts.Height()
|
||||
|
||||
triggerH := appliedH + abi.ChainEpoch(trigger.confidence)
|
||||
@@ -211,28 +196,23 @@ func (e *hcEvents) queueForConfidence(trigID uint64, data eventData, prevTs, ts
|
||||
}
|
||||
|
||||
byOrigH[appliedH] = append(byOrigH[appliedH], &queuedEvent{
|
||||
trigger: trigID,
|
||||
prevH: prevH,
|
||||
h: appliedH,
|
||||
data: data,
|
||||
trigger: trigID,
|
||||
data: data,
|
||||
tipset: ts,
|
||||
prevTipset: prevTs,
|
||||
})
|
||||
|
||||
e.revertQueue[appliedH] = append(e.revertQueue[appliedH], triggerH)
|
||||
}
|
||||
|
||||
// Apply any events that were waiting for this chain height for confidence
|
||||
func (e *hcEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch) {
|
||||
func (e *hcEventsObserver) applyWithConfidence(ctx context.Context, height abi.ChainEpoch) {
|
||||
byOrigH, ok := e.confQueue[height]
|
||||
if !ok {
|
||||
return // no triggers at this height
|
||||
}
|
||||
|
||||
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, height)
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
if event.called {
|
||||
continue
|
||||
@@ -243,18 +223,7 @@ func (e *hcEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch)
|
||||
continue
|
||||
}
|
||||
|
||||
// Previous tipset - this is relevant for example in a state change
|
||||
// from one tipset to another
|
||||
var prevTs *types.TipSet
|
||||
if event.prevH != NoHeight {
|
||||
prevTs, err = e.tsc.get(event.prevH)
|
||||
if err != nil {
|
||||
log.Errorf("events: applyWithConfidence didn't find tipset for previous event; wanted %d; current %d", event.prevH, height)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
more, err := trigger.handle(event.data, prevTs, triggerTs, height)
|
||||
more, err := trigger.handle(ctx, event.data, event.prevTipset, event.tipset, height)
|
||||
if err != nil {
|
||||
log.Errorf("chain trigger (@H %d, triggered @ %d) failed: %s", origH, height, err)
|
||||
continue // don't revert failed calls
|
||||
@@ -273,8 +242,8 @@ func (e *hcEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch)
|
||||
}
|
||||
|
||||
// Apply any timeouts that expire at this height
|
||||
func (e *hcEvents) applyTimeouts(ts *types.TipSet) {
|
||||
triggers, ok := e.timeouts[ts.Height()]
|
||||
func (e *hcEventsObserver) applyTimeouts(ctx context.Context, at abi.ChainEpoch, ts *types.TipSet) {
|
||||
triggers, ok := e.timeouts[at]
|
||||
if !ok {
|
||||
return // nothing to do
|
||||
}
|
||||
@@ -288,14 +257,15 @@ func (e *hcEvents) applyTimeouts(ts *types.TipSet) {
|
||||
continue
|
||||
}
|
||||
|
||||
timeoutTs, err := e.tsc.get(ts.Height() - abi.ChainEpoch(trigger.confidence))
|
||||
// This should be cached.
|
||||
timeoutTs, err := e.cs.ChainGetTipSetAfterHeight(ctx, at-abi.ChainEpoch(trigger.confidence), ts.Key())
|
||||
if err != nil {
|
||||
log.Errorf("events: applyTimeouts didn't find tipset for event; wanted %d; current %d", ts.Height()-abi.ChainEpoch(trigger.confidence), ts.Height())
|
||||
log.Errorf("events: applyTimeouts didn't find tipset for event; wanted %d; current %d", at-abi.ChainEpoch(trigger.confidence), at)
|
||||
}
|
||||
|
||||
more, err := trigger.handle(nil, nil, timeoutTs, ts.Height())
|
||||
more, err := trigger.handle(ctx, nil, nil, timeoutTs, at)
|
||||
if err != nil {
|
||||
log.Errorf("chain trigger (call @H %d, called @ %d) failed: %s", timeoutTs.Height(), ts.Height(), err)
|
||||
log.Errorf("chain trigger (call @H %d, called @ %d) failed: %s", timeoutTs.Height(), at, err)
|
||||
continue // don't revert failed calls
|
||||
}
|
||||
|
||||
@@ -309,24 +279,19 @@ func (e *hcEvents) applyTimeouts(ts *types.TipSet) {
|
||||
// - RevertHandler: called if the chain head changes causing the event to revert
|
||||
// - confidence: wait this many tipsets before calling EventHandler
|
||||
// - timeout: at this chain height, timeout on waiting for this event
|
||||
func (e *hcEvents) onHeadChanged(check CheckFunc, hnd EventHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch) (triggerID, error) {
|
||||
func (e *hcEvents) onHeadChanged(ctx context.Context, check CheckFunc, hnd EventHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch) (triggerID, error) {
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
|
||||
// Check if the event has already occurred
|
||||
ts, err := e.tsc.best()
|
||||
done, more, err := check(ctx, e.lastTs)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("error getting best tipset: %w", err)
|
||||
}
|
||||
done, more, err := check(ts)
|
||||
if err != nil {
|
||||
return 0, xerrors.Errorf("called check error (h: %d): %w", ts.Height(), err)
|
||||
return 0, xerrors.Errorf("called check error (h: %d): %w", e.lastTs.Height(), err)
|
||||
}
|
||||
if done {
|
||||
timeout = NoTimeout
|
||||
}
|
||||
|
||||
// Create a trigger for the event
|
||||
id := e.ctr
|
||||
e.ctr++
|
||||
|
||||
@@ -354,12 +319,11 @@ func (e *hcEvents) onHeadChanged(check CheckFunc, hnd EventHandler, rev RevertHa
|
||||
// headChangeAPI is used to allow the composed event APIs to call back to hcEvents
|
||||
// to listen for changes
|
||||
type headChangeAPI interface {
|
||||
onHeadChanged(check CheckFunc, hnd EventHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch) (triggerID, error)
|
||||
onHeadChanged(ctx context.Context, check CheckFunc, hnd EventHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch) (triggerID, error)
|
||||
}
|
||||
|
||||
// watcherEvents watches for a state change
|
||||
type watcherEvents struct {
|
||||
ctx context.Context
|
||||
cs EventAPI
|
||||
hcAPI headChangeAPI
|
||||
|
||||
@@ -367,9 +331,8 @@ type watcherEvents struct {
|
||||
matchers map[triggerID]StateMatchFunc
|
||||
}
|
||||
|
||||
func newWatcherEvents(ctx context.Context, hcAPI headChangeAPI, cs EventAPI) watcherEvents {
|
||||
func newWatcherEvents(hcAPI headChangeAPI, cs EventAPI) watcherEvents {
|
||||
return watcherEvents{
|
||||
ctx: ctx,
|
||||
cs: cs,
|
||||
hcAPI: hcAPI,
|
||||
matchers: make(map[triggerID]StateMatchFunc),
|
||||
@@ -425,7 +388,7 @@ type StateMatchFunc func(oldTs, newTs *types.TipSet) (bool, StateChange, error)
|
||||
// * `StateChangeHandler` is called when the specified state change was observed
|
||||
// on-chain, and a confidence threshold was reached, or the specified `timeout`
|
||||
// height was reached with no state change observed. When this callback is
|
||||
// invoked on a timeout, `oldState` and `newState` are set to nil.
|
||||
// invoked on a timeout, `oldTs` and `states are set to nil.
|
||||
// This callback returns a boolean specifying whether further notifications
|
||||
// should be sent, like `more` return param from `CheckFunc` above.
|
||||
//
|
||||
@@ -438,7 +401,7 @@ type StateMatchFunc func(oldTs, newTs *types.TipSet) (bool, StateChange, error)
|
||||
// the state change is queued up until the confidence interval has elapsed (and
|
||||
// `StateChangeHandler` is called)
|
||||
func (we *watcherEvents) StateChanged(check CheckFunc, scHnd StateChangeHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf StateMatchFunc) error {
|
||||
hnd := func(data eventData, prevTs, ts *types.TipSet, height abi.ChainEpoch) (bool, error) {
|
||||
hnd := func(ctx context.Context, data eventData, prevTs, ts *types.TipSet, height abi.ChainEpoch) (bool, error) {
|
||||
states, ok := data.(StateChange)
|
||||
if data != nil && !ok {
|
||||
panic("expected StateChange")
|
||||
@@ -447,7 +410,7 @@ func (we *watcherEvents) StateChanged(check CheckFunc, scHnd StateChangeHandler,
|
||||
return scHnd(prevTs, ts, states, height)
|
||||
}
|
||||
|
||||
id, err := we.hcAPI.onHeadChanged(check, hnd, rev, confidence, timeout)
|
||||
id, err := we.hcAPI.onHeadChanged(context.TODO(), check, hnd, rev, confidence, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -461,43 +424,29 @@ func (we *watcherEvents) StateChanged(check CheckFunc, scHnd StateChangeHandler,
|
||||
|
||||
// messageEvents watches for message calls to actors
|
||||
type messageEvents struct {
|
||||
ctx context.Context
|
||||
cs EventAPI
|
||||
hcAPI headChangeAPI
|
||||
|
||||
lk sync.RWMutex
|
||||
matchers map[triggerID]MsgMatchFunc
|
||||
|
||||
blockMsgLk sync.Mutex
|
||||
blockMsgCache *lru.ARCCache
|
||||
}
|
||||
|
||||
func newMessageEvents(ctx context.Context, hcAPI headChangeAPI, cs EventAPI) messageEvents {
|
||||
blsMsgCache, _ := lru.NewARC(500)
|
||||
func newMessageEvents(hcAPI headChangeAPI, cs EventAPI) messageEvents {
|
||||
return messageEvents{
|
||||
ctx: ctx,
|
||||
cs: cs,
|
||||
hcAPI: hcAPI,
|
||||
matchers: make(map[triggerID]MsgMatchFunc),
|
||||
blockMsgLk: sync.Mutex{},
|
||||
blockMsgCache: blsMsgCache,
|
||||
cs: cs,
|
||||
hcAPI: hcAPI,
|
||||
matchers: make(map[triggerID]MsgMatchFunc),
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are any new actor calls
|
||||
func (me *messageEvents) checkNewCalls(ts *types.TipSet) (map[triggerID][]eventData, error) {
|
||||
pts, err := me.cs.ChainGetTipSet(me.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 nil, err
|
||||
}
|
||||
|
||||
func (me *messageEvents) checkNewCalls(ctx context.Context, from, to *types.TipSet) map[triggerID][]eventData {
|
||||
me.lk.RLock()
|
||||
defer me.lk.RUnlock()
|
||||
|
||||
// For each message in the tipset
|
||||
res := make(map[triggerID][]eventData)
|
||||
me.messagesForTs(pts, func(msg *types.Message) {
|
||||
me.messagesForTs(from, func(msg *types.Message) {
|
||||
// TODO: provide receipts
|
||||
|
||||
// Run each trigger's matcher against the message
|
||||
@@ -516,47 +465,32 @@ func (me *messageEvents) checkNewCalls(ts *types.TipSet) (map[triggerID][]eventD
|
||||
}
|
||||
})
|
||||
|
||||
return res, nil
|
||||
return res
|
||||
}
|
||||
|
||||
// Get the messages in a tipset
|
||||
func (me *messageEvents) messagesForTs(ts *types.TipSet, consume func(*types.Message)) {
|
||||
seen := map[cid.Cid]struct{}{}
|
||||
|
||||
for _, tsb := range ts.Blocks() {
|
||||
me.blockMsgLk.Lock()
|
||||
msgsI, ok := me.blockMsgCache.Get(tsb.Cid())
|
||||
var err error
|
||||
if !ok {
|
||||
msgsI, err = me.cs.ChainGetBlockMessages(context.TODO(), tsb.Cid())
|
||||
if err != nil {
|
||||
log.Errorf("messagesForTs MessagesForBlock failed (ts.H=%d, Bcid:%s, B.Mcid:%s): %s", ts.Height(), tsb.Cid(), tsb.Messages, err)
|
||||
// this is quite bad, but probably better than missing all the other updates
|
||||
me.blockMsgLk.Unlock()
|
||||
continue
|
||||
}
|
||||
me.blockMsgCache.Add(tsb.Cid(), msgsI)
|
||||
for i, tsb := range ts.Cids() {
|
||||
msgs, err := me.cs.ChainGetBlockMessages(context.TODO(), tsb)
|
||||
if err != nil {
|
||||
log.Errorf("messagesForTs MessagesForBlock failed (ts.H=%d, Bcid:%s, B.Mcid:%s): %s",
|
||||
ts.Height(), tsb, ts.Blocks()[i].Messages, err)
|
||||
continue
|
||||
}
|
||||
me.blockMsgLk.Unlock()
|
||||
msgs := msgsI.(*api.BlockMessages)
|
||||
for _, m := range msgs.BlsMessages {
|
||||
_, ok := seen[m.Cid()]
|
||||
for i, c := range msgs.Cids {
|
||||
// We iterate over the CIDs to avoid having to recompute them.
|
||||
_, ok := seen[c]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
seen[m.Cid()] = struct{}{}
|
||||
|
||||
consume(m)
|
||||
}
|
||||
|
||||
for _, m := range msgs.SecpkMessages {
|
||||
_, ok := seen[m.Message.Cid()]
|
||||
if ok {
|
||||
continue
|
||||
seen[c] = struct{}{}
|
||||
if i < len(msgs.BlsMessages) {
|
||||
consume(msgs.BlsMessages[i])
|
||||
} else {
|
||||
consume(&msgs.SecpkMessages[i-len(msgs.BlsMessages)].Message)
|
||||
}
|
||||
seen[m.Message.Cid()] = struct{}{}
|
||||
|
||||
consume(&m.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -596,14 +530,14 @@ type MsgMatchFunc func(msg *types.Message) (matched bool, err error)
|
||||
// * `MsgMatchFunc` is called against each message. If there is a match, the
|
||||
// message is queued up until the confidence interval has elapsed (and
|
||||
// `MsgHandler` is called)
|
||||
func (me *messageEvents) Called(check CheckFunc, msgHnd MsgHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf MsgMatchFunc) error {
|
||||
hnd := func(data eventData, prevTs, ts *types.TipSet, height abi.ChainEpoch) (bool, error) {
|
||||
func (me *messageEvents) Called(ctx context.Context, check CheckFunc, msgHnd MsgHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf MsgMatchFunc) error {
|
||||
hnd := func(ctx context.Context, data eventData, prevTs, ts *types.TipSet, height abi.ChainEpoch) (bool, error) {
|
||||
msg, ok := data.(*types.Message)
|
||||
if data != nil && !ok {
|
||||
panic("expected msg")
|
||||
}
|
||||
|
||||
ml, err := me.cs.StateSearchMsg(me.ctx, ts.Key(), msg.Cid(), stmgr.LookbackNoLimit, true)
|
||||
ml, err := me.cs.StateSearchMsg(ctx, ts.Key(), msg.Cid(), stmgr.LookbackNoLimit, true)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -615,7 +549,7 @@ func (me *messageEvents) Called(check CheckFunc, msgHnd MsgHandler, rev RevertHa
|
||||
return msgHnd(msg, &ml.Receipt, ts, height)
|
||||
}
|
||||
|
||||
id, err := me.hcAPI.onHeadChanged(check, hnd, rev, confidence, timeout)
|
||||
id, err := me.hcAPI.onHeadChanged(ctx, check, hnd, rev, confidence, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -629,5 +563,5 @@ func (me *messageEvents) Called(check CheckFunc, msgHnd MsgHandler, rev RevertHa
|
||||
|
||||
// Convenience function for checking and matching messages
|
||||
func (me *messageEvents) CalledMsg(ctx context.Context, hnd MsgHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, msg types.ChainMsg) error {
|
||||
return me.Called(me.CheckMsg(ctx, msg, hnd), hnd, rev, confidence, timeout, me.MatchMsg(msg.VMMessage()))
|
||||
return me.Called(ctx, me.CheckMsg(msg, hnd), hnd, rev, confidence, timeout, me.MatchMsg(msg.VMMessage()))
|
||||
}
|
||||
|
||||
+212
-176
@@ -11,199 +11,235 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type heightEvents struct {
|
||||
lk sync.Mutex
|
||||
tsc *tipSetCache
|
||||
gcConfidence abi.ChainEpoch
|
||||
type heightHandler struct {
|
||||
ts *types.TipSet
|
||||
height abi.ChainEpoch
|
||||
called bool
|
||||
|
||||
ctr triggerID
|
||||
|
||||
heightTriggers map[triggerID]*heightHandler
|
||||
|
||||
htTriggerHeights map[triggerH][]triggerID
|
||||
htHeights map[msgH][]triggerID
|
||||
|
||||
ctx context.Context
|
||||
handle HeightHandler
|
||||
revert RevertHandler
|
||||
}
|
||||
|
||||
func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
|
||||
ctx, span := trace.StartSpan(e.ctx, "events.HeightHeadChange")
|
||||
defer span.End()
|
||||
span.AddAttributes(trace.Int64Attribute("endHeight", int64(app[0].Height())))
|
||||
span.AddAttributes(trace.Int64Attribute("reverts", int64(len(rev))))
|
||||
span.AddAttributes(trace.Int64Attribute("applies", int64(len(app))))
|
||||
type heightEvents struct {
|
||||
api EventAPI
|
||||
gcConfidence abi.ChainEpoch
|
||||
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
for _, ts := range rev {
|
||||
// TODO: log error if h below gcconfidence
|
||||
// revert height-based triggers
|
||||
lk sync.Mutex
|
||||
head *types.TipSet
|
||||
tsHeights, triggerHeights map[abi.ChainEpoch][]*heightHandler
|
||||
lastGc abi.ChainEpoch //nolint:structcheck
|
||||
}
|
||||
|
||||
revert := func(h abi.ChainEpoch, ts *types.TipSet) {
|
||||
for _, tid := range e.htHeights[h] {
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightRevert")
|
||||
|
||||
rev := e.heightTriggers[tid].revert
|
||||
e.lk.Unlock()
|
||||
err := rev(ctx, ts)
|
||||
e.lk.Lock()
|
||||
e.heightTriggers[tid].called = false
|
||||
|
||||
span.End()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("reverting chain trigger (@H %d): %s", h, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
revert(ts.Height(), ts)
|
||||
|
||||
subh := ts.Height() - 1
|
||||
for {
|
||||
cts, err := e.tsc.get(subh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cts != nil {
|
||||
break
|
||||
}
|
||||
|
||||
revert(subh, ts)
|
||||
subh--
|
||||
}
|
||||
|
||||
if err := e.tsc.revert(ts); err != nil {
|
||||
return err
|
||||
}
|
||||
func newHeightEvents(api EventAPI, obs *observer, gcConfidence abi.ChainEpoch) *heightEvents {
|
||||
he := &heightEvents{
|
||||
api: api,
|
||||
gcConfidence: gcConfidence,
|
||||
tsHeights: map[abi.ChainEpoch][]*heightHandler{},
|
||||
triggerHeights: map[abi.ChainEpoch][]*heightHandler{},
|
||||
}
|
||||
|
||||
for i := range app {
|
||||
ts := app[i]
|
||||
|
||||
if err := e.tsc.add(ts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// height triggers
|
||||
|
||||
apply := func(h abi.ChainEpoch, ts *types.TipSet) error {
|
||||
for _, tid := range e.htTriggerHeights[h] {
|
||||
hnd := e.heightTriggers[tid]
|
||||
if hnd.called {
|
||||
return nil
|
||||
}
|
||||
|
||||
triggerH := h - abi.ChainEpoch(hnd.confidence)
|
||||
|
||||
incTs, err := e.tsc.getNonNull(triggerH)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightApply")
|
||||
span.AddAttributes(trace.BoolAttribute("immediate", false))
|
||||
handle := hnd.handle
|
||||
e.lk.Unlock()
|
||||
err = handle(ctx, incTs, h)
|
||||
e.lk.Lock()
|
||||
hnd.called = true
|
||||
span.End()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("chain trigger (@H %d, called @ %d) failed: %+v", triggerH, ts.Height(), err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := apply(ts.Height(), ts); err != nil {
|
||||
return err
|
||||
}
|
||||
subh := ts.Height() - 1
|
||||
for {
|
||||
cts, err := e.tsc.get(subh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cts != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if err := apply(subh, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subh--
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
he.lk.Lock()
|
||||
he.head = obs.Observe((*heightEventsObserver)(he))
|
||||
he.lk.Unlock()
|
||||
return he
|
||||
}
|
||||
|
||||
// ChainAt invokes the specified `HeightHandler` when the chain reaches the
|
||||
// specified height+confidence threshold. If the chain is rolled-back under the
|
||||
// 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 abi.ChainEpoch) error {
|
||||
e.lk.Lock() // Tricky locking, check your locks if you modify this function!
|
||||
|
||||
best, err := e.tsc.best()
|
||||
if err != nil {
|
||||
e.lk.Unlock()
|
||||
return xerrors.Errorf("error getting best tipset: %w", err)
|
||||
// ts passed to handlers is the tipset at the specified epoch, or above if lower tipsets were null.
|
||||
//
|
||||
// The context governs cancellations of this call, it won't cancel the event handler.
|
||||
func (e *heightEvents) ChainAt(ctx context.Context, hnd HeightHandler, rev RevertHandler, confidence int, h abi.ChainEpoch) error {
|
||||
if abi.ChainEpoch(confidence) > e.gcConfidence {
|
||||
// Need this to be able to GC effectively.
|
||||
return xerrors.Errorf("confidence cannot be greater than gcConfidence: %d > %d", confidence, e.gcConfidence)
|
||||
}
|
||||
|
||||
bestH := best.Height()
|
||||
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)
|
||||
}
|
||||
|
||||
e.lk.Unlock()
|
||||
ctx, span := trace.StartSpan(e.ctx, "events.HeightApply")
|
||||
span.AddAttributes(trace.BoolAttribute("immediate", true))
|
||||
|
||||
err = hnd(ctx, ts, bestH)
|
||||
span.End()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.lk.Lock()
|
||||
best, err = e.tsc.best()
|
||||
if err != nil {
|
||||
e.lk.Unlock()
|
||||
return xerrors.Errorf("error getting best tipset: %w", err)
|
||||
}
|
||||
bestH = best.Height()
|
||||
}
|
||||
|
||||
defer e.lk.Unlock()
|
||||
|
||||
if bestH >= h+abi.ChainEpoch(confidence)+e.gcConfidence {
|
||||
return nil
|
||||
}
|
||||
|
||||
triggerAt := h + abi.ChainEpoch(confidence)
|
||||
|
||||
id := e.ctr
|
||||
e.ctr++
|
||||
|
||||
e.heightTriggers[id] = &heightHandler{
|
||||
confidence: confidence,
|
||||
|
||||
handler := &heightHandler{
|
||||
height: h,
|
||||
handle: hnd,
|
||||
revert: rev,
|
||||
}
|
||||
triggerAt := h + abi.ChainEpoch(confidence)
|
||||
|
||||
e.htHeights[h] = append(e.htHeights[h], id)
|
||||
e.htTriggerHeights[triggerAt] = append(e.htTriggerHeights[triggerAt], id)
|
||||
// Here we try to jump onto a moving train. To avoid stopping the train, we release the lock
|
||||
// while calling the API and/or the trigger functions. Unfortunately, it's entirely possible
|
||||
// (although unlikely) to go back and forth across the trigger heights, so we need to keep
|
||||
// going back and forth here till we're synced.
|
||||
//
|
||||
// TODO: Consider using a worker goroutine so we can just drop the handler in a channel? The
|
||||
// downside is that we'd either need a tipset cache, or we'd need to potentially fetch
|
||||
// tipsets in-line inside the event loop.
|
||||
e.lk.Lock()
|
||||
for {
|
||||
head := e.head
|
||||
if head.Height() >= h {
|
||||
// Head is past the handler height. We at least need to stash the tipset to
|
||||
// avoid doing this from the main event loop.
|
||||
e.lk.Unlock()
|
||||
|
||||
var ts *types.TipSet
|
||||
if head.Height() == h {
|
||||
ts = head
|
||||
} else {
|
||||
var err error
|
||||
ts, err = e.api.ChainGetTipSetAfterHeight(ctx, handler.height, head.Key())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("events.ChainAt: failed to get tipset: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// If we've applied the handler on the wrong tipset, revert.
|
||||
if handler.called && !ts.Equals(handler.ts) {
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightRevert")
|
||||
span.AddAttributes(trace.BoolAttribute("immediate", true))
|
||||
err := handler.revert(ctx, handler.ts)
|
||||
span.End()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handler.called = false
|
||||
}
|
||||
|
||||
// Save the tipset.
|
||||
handler.ts = ts
|
||||
|
||||
// If we've reached confidence and haven't called, call.
|
||||
if !handler.called && head.Height() >= triggerAt {
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightApply")
|
||||
span.AddAttributes(trace.BoolAttribute("immediate", true))
|
||||
err := handler.handle(ctx, handler.ts, head.Height())
|
||||
span.End()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
handler.called = true
|
||||
|
||||
// If we've reached gcConfidence, return without saving anything.
|
||||
if head.Height() >= h+e.gcConfidence {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
e.lk.Lock()
|
||||
} else if handler.called {
|
||||
// We're not passed the head (anymore) but have applied the handler. Revert, try again.
|
||||
e.lk.Unlock()
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightRevert")
|
||||
span.AddAttributes(trace.BoolAttribute("immediate", true))
|
||||
err := handler.revert(ctx, handler.ts)
|
||||
span.End()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handler.called = false
|
||||
e.lk.Lock()
|
||||
} // otherwise, we changed heads but the change didn't matter.
|
||||
|
||||
// If we managed to get through this without the head changing, we're finally done.
|
||||
if head.Equals(e.head) {
|
||||
e.triggerHeights[triggerAt] = append(e.triggerHeights[triggerAt], handler)
|
||||
e.tsHeights[h] = append(e.tsHeights[h], handler)
|
||||
e.lk.Unlock()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the head and garbage collects if we're 2x over our garbage collection confidence period.
|
||||
func (e *heightEventsObserver) updateHead(h *types.TipSet) {
|
||||
e.lk.Lock()
|
||||
defer e.lk.Unlock()
|
||||
e.head = h
|
||||
|
||||
if e.head.Height() < e.lastGc+e.gcConfidence*2 {
|
||||
return
|
||||
}
|
||||
e.lastGc = h.Height()
|
||||
|
||||
targetGcHeight := e.head.Height() - e.gcConfidence
|
||||
for h := range e.tsHeights {
|
||||
if h >= targetGcHeight {
|
||||
continue
|
||||
}
|
||||
delete(e.tsHeights, h)
|
||||
}
|
||||
for h := range e.triggerHeights {
|
||||
if h >= targetGcHeight {
|
||||
continue
|
||||
}
|
||||
delete(e.triggerHeights, h)
|
||||
}
|
||||
}
|
||||
|
||||
type heightEventsObserver heightEvents
|
||||
|
||||
func (e *heightEventsObserver) Revert(ctx context.Context, from, to *types.TipSet) error {
|
||||
// Update the head first so we don't accidental skip reverting a concurrent call to ChainAt.
|
||||
e.updateHead(to)
|
||||
|
||||
// Call revert on all hights between the two tipsets, handling empty tipsets.
|
||||
for h := from.Height(); h > to.Height(); h-- {
|
||||
e.lk.Lock()
|
||||
triggers := e.tsHeights[h]
|
||||
e.lk.Unlock()
|
||||
|
||||
// 1. Triggers are only invoked from the global event loop, we don't need to hold the lock while calling.
|
||||
// 2. We only ever append to or replace the trigger slice, so it's safe to iterate over it without the lock.
|
||||
for _, handler := range triggers {
|
||||
handler.ts = nil // invalidate
|
||||
if !handler.called {
|
||||
// We haven't triggered this yet, or there has been a concurrent call to ChainAt.
|
||||
continue
|
||||
}
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightRevert")
|
||||
err := handler.revert(ctx, from)
|
||||
span.End()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("reverting chain trigger (@H %d): %s", h, err)
|
||||
}
|
||||
handler.called = false
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *heightEventsObserver) Apply(ctx context.Context, from, to *types.TipSet) error {
|
||||
// Update the head first so we don't accidental skip applying a concurrent call to ChainAt.
|
||||
e.updateHead(to)
|
||||
|
||||
for h := from.Height() + 1; h <= to.Height(); h++ {
|
||||
e.lk.Lock()
|
||||
triggers := e.triggerHeights[h]
|
||||
tipsets := e.tsHeights[h]
|
||||
e.lk.Unlock()
|
||||
|
||||
// Stash the tipset for future triggers.
|
||||
for _, handler := range tipsets {
|
||||
handler.ts = to
|
||||
}
|
||||
|
||||
// Trigger the ready triggers.
|
||||
for _, handler := range triggers {
|
||||
if handler.called {
|
||||
// We may have reverted past the trigger point, but not past the call point.
|
||||
// Or there has been a concurrent call to ChainAt.
|
||||
continue
|
||||
}
|
||||
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeightApply")
|
||||
span.AddAttributes(trace.BoolAttribute("immediate", false))
|
||||
err := handler.handle(ctx, handler.ts, h)
|
||||
span.End()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("chain trigger (@H %d, called @ %d) failed: %+v", h, to.Height(), err)
|
||||
}
|
||||
|
||||
handler.called = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+444
-445
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
type messageCache struct {
|
||||
api EventAPI
|
||||
|
||||
blockMsgLk sync.Mutex
|
||||
blockMsgCache *lru.ARCCache
|
||||
}
|
||||
|
||||
func newMessageCache(api EventAPI) *messageCache {
|
||||
blsMsgCache, _ := lru.NewARC(500)
|
||||
|
||||
return &messageCache{
|
||||
api: api,
|
||||
blockMsgCache: blsMsgCache,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *messageCache) ChainGetBlockMessages(ctx context.Context, blkCid cid.Cid) (*api.BlockMessages, error) {
|
||||
c.blockMsgLk.Lock()
|
||||
defer c.blockMsgLk.Unlock()
|
||||
|
||||
msgsI, ok := c.blockMsgCache.Get(blkCid)
|
||||
var err error
|
||||
if !ok {
|
||||
msgsI, err = c.api.ChainGetBlockMessages(ctx, blkCid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.blockMsgCache.Add(blkCid, msgsI)
|
||||
}
|
||||
return msgsI.(*api.BlockMessages), nil
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"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/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type observer struct {
|
||||
api EventAPI
|
||||
|
||||
gcConfidence abi.ChainEpoch
|
||||
|
||||
ready chan struct{}
|
||||
|
||||
lk sync.Mutex
|
||||
head *types.TipSet
|
||||
maxHeight abi.ChainEpoch
|
||||
observers []TipSetObserver
|
||||
}
|
||||
|
||||
func newObserver(api *cache, gcConfidence abi.ChainEpoch) *observer {
|
||||
obs := &observer{
|
||||
api: api,
|
||||
gcConfidence: gcConfidence,
|
||||
|
||||
ready: make(chan struct{}),
|
||||
observers: []TipSetObserver{},
|
||||
}
|
||||
obs.Observe(api.observer())
|
||||
return obs
|
||||
}
|
||||
|
||||
func (o *observer) start(ctx context.Context) error {
|
||||
go o.listenHeadChanges(ctx)
|
||||
|
||||
// Wait for the first tipset to be seen or bail if shutting down
|
||||
select {
|
||||
case <-o.ready:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (o *observer) listenHeadChanges(ctx context.Context) {
|
||||
for {
|
||||
if err := o.listenHeadChangesOnce(ctx); err != nil {
|
||||
log.Errorf("listen head changes errored: %s", err)
|
||||
} else {
|
||||
log.Warn("listenHeadChanges quit")
|
||||
}
|
||||
select {
|
||||
case <-build.Clock.After(time.Second):
|
||||
case <-ctx.Done():
|
||||
log.Warnf("not restarting listenHeadChanges: context error: %s", ctx.Err())
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("restarting listenHeadChanges")
|
||||
}
|
||||
}
|
||||
|
||||
func (o *observer) listenHeadChangesOnce(ctx context.Context) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
notifs, err := o.api.ChainNotify(ctx)
|
||||
if err != nil {
|
||||
// Retry is handled by caller
|
||||
return xerrors.Errorf("listenHeadChanges ChainNotify call failed: %w", err)
|
||||
}
|
||||
|
||||
var cur []*api.HeadChange
|
||||
var ok bool
|
||||
|
||||
// Wait for first tipset or bail
|
||||
select {
|
||||
case cur, ok = <-notifs:
|
||||
if !ok {
|
||||
return xerrors.Errorf("notification channel closed")
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
if len(cur) != 1 {
|
||||
return xerrors.Errorf("unexpected initial head notification length: %d", len(cur))
|
||||
}
|
||||
|
||||
if cur[0].Type != store.HCCurrent {
|
||||
return xerrors.Errorf("expected first head notification type to be 'current', was '%s'", cur[0].Type)
|
||||
}
|
||||
|
||||
curHead := cur[0].Val
|
||||
|
||||
o.lk.Lock()
|
||||
if o.head == nil {
|
||||
o.head = curHead
|
||||
close(o.ready)
|
||||
}
|
||||
startHead := o.head
|
||||
o.lk.Unlock()
|
||||
|
||||
if !startHead.Equals(curHead) {
|
||||
changes, err := o.api.ChainGetPath(ctx, startHead.Key(), curHead.Key())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get path from last applied tipset to head: %w", err)
|
||||
}
|
||||
|
||||
if err := o.applyChanges(ctx, changes); err != nil {
|
||||
return xerrors.Errorf("failed catch-up head changes: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for changes := range notifs {
|
||||
if err := o.applyChanges(ctx, changes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *observer) applyChanges(ctx context.Context, changes []*api.HeadChange) error {
|
||||
// Used to wait for a prior notification round to finish (by tests)
|
||||
if len(changes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var rev, app []*types.TipSet
|
||||
for _, changes := range changes {
|
||||
switch changes.Type {
|
||||
case store.HCRevert:
|
||||
rev = append(rev, changes.Val)
|
||||
case store.HCApply:
|
||||
app = append(app, changes.Val)
|
||||
default:
|
||||
log.Errorf("unexpected head change notification type: '%s'", changes.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if err := o.headChange(ctx, rev, app); err != nil {
|
||||
return xerrors.Errorf("failed to apply head changes: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *observer) headChange(ctx context.Context, rev, app []*types.TipSet) error {
|
||||
ctx, span := trace.StartSpan(ctx, "events.HeadChange")
|
||||
span.AddAttributes(trace.Int64Attribute("reverts", int64(len(rev))))
|
||||
span.AddAttributes(trace.Int64Attribute("applies", int64(len(app))))
|
||||
|
||||
o.lk.Lock()
|
||||
head := o.head
|
||||
o.lk.Unlock()
|
||||
|
||||
defer func() {
|
||||
span.AddAttributes(trace.Int64Attribute("endHeight", int64(head.Height())))
|
||||
span.End()
|
||||
}()
|
||||
|
||||
// NOTE: bailing out here if the head isn't what we expected is fine. We'll re-start the
|
||||
// entire process and handle any strange reorgs.
|
||||
for i, from := range rev {
|
||||
if !from.Equals(head) {
|
||||
return xerrors.Errorf(
|
||||
"expected to revert %s (%d), reverting %s (%d)",
|
||||
head.Key(), head.Height(), from.Key(), from.Height(),
|
||||
)
|
||||
}
|
||||
var to *types.TipSet
|
||||
if i+1 < len(rev) {
|
||||
// If we have more reverts, the next revert is the next head.
|
||||
to = rev[i+1]
|
||||
} else {
|
||||
// At the end of the revert sequenece, we need to lookup the joint tipset
|
||||
// between the revert sequence and the apply sequence.
|
||||
var err error
|
||||
to, err = o.api.ChainGetTipSet(ctx, from.Parents())
|
||||
if err != nil {
|
||||
// Well, this sucks. We'll bail and restart.
|
||||
return xerrors.Errorf("failed to get tipset when reverting due to a SetHeead: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current observers and atomically set the head.
|
||||
//
|
||||
// 1. We need to get the observers every time in case some registered/deregistered.
|
||||
// 2. We need to atomically set the head so new observers don't see events twice or
|
||||
// skip them.
|
||||
o.lk.Lock()
|
||||
observers := o.observers
|
||||
o.head = to
|
||||
o.lk.Unlock()
|
||||
|
||||
for _, obs := range observers {
|
||||
if err := obs.Revert(ctx, from, to); err != nil {
|
||||
log.Errorf("observer %T failed to apply tipset %s (%d) with: %s", obs, from.Key(), from.Height(), err)
|
||||
}
|
||||
}
|
||||
|
||||
if to.Height() < o.maxHeight-o.gcConfidence {
|
||||
log.Errorf("reverted past finality, from %d to %d", o.maxHeight, to.Height())
|
||||
}
|
||||
|
||||
head = to
|
||||
}
|
||||
|
||||
for _, to := range app {
|
||||
if to.Parents() != head.Key() {
|
||||
return xerrors.Errorf(
|
||||
"cannot apply %s (%d) with parents %s on top of %s (%d)",
|
||||
to.Key(), to.Height(), to.Parents(), head.Key(), head.Height(),
|
||||
)
|
||||
}
|
||||
|
||||
o.lk.Lock()
|
||||
observers := o.observers
|
||||
o.head = to
|
||||
o.lk.Unlock()
|
||||
|
||||
for _, obs := range observers {
|
||||
if err := obs.Apply(ctx, head, to); err != nil {
|
||||
log.Errorf("observer %T failed to revert tipset %s (%d) with: %s", obs, to.Key(), to.Height(), err)
|
||||
}
|
||||
}
|
||||
if to.Height() > o.maxHeight {
|
||||
o.maxHeight = to.Height()
|
||||
}
|
||||
|
||||
head = to
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Observe registers the observer, and returns the current tipset. The observer is guaranteed to
|
||||
// observe events starting at this tipset.
|
||||
//
|
||||
// Returns nil if the observer hasn't started yet (but still registers).
|
||||
func (o *observer) Observe(obs TipSetObserver) *types.TipSet {
|
||||
o.lk.Lock()
|
||||
defer o.lk.Unlock()
|
||||
o.observers = append(o.observers, obs)
|
||||
return o.head
|
||||
}
|
||||
+133
-75
@@ -11,7 +11,9 @@ import (
|
||||
)
|
||||
|
||||
type tsCacheAPI interface {
|
||||
ChainGetTipSetAfterHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
|
||||
ChainHead(context.Context) (*types.TipSet, error)
|
||||
}
|
||||
|
||||
@@ -20,61 +22,157 @@ type tsCacheAPI interface {
|
||||
type tipSetCache struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
cache []*types.TipSet
|
||||
start int
|
||||
len int
|
||||
byKey map[types.TipSetKey]*types.TipSet
|
||||
byHeight []*types.TipSet
|
||||
start int // chain head (end)
|
||||
len int
|
||||
|
||||
storage tsCacheAPI
|
||||
}
|
||||
|
||||
func newTSCache(cap abi.ChainEpoch, storage tsCacheAPI) *tipSetCache {
|
||||
func newTSCache(storage tsCacheAPI, cap abi.ChainEpoch) *tipSetCache {
|
||||
return &tipSetCache{
|
||||
cache: make([]*types.TipSet, cap),
|
||||
start: 0,
|
||||
len: 0,
|
||||
byKey: make(map[types.TipSetKey]*types.TipSet, cap),
|
||||
byHeight: make([]*types.TipSet, cap),
|
||||
start: 0,
|
||||
len: 0,
|
||||
|
||||
storage: storage,
|
||||
}
|
||||
}
|
||||
func (tsc *tipSetCache) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
if ts, ok := tsc.byKey[tsk]; ok {
|
||||
return ts, nil
|
||||
}
|
||||
return tsc.storage.ChainGetTipSet(ctx, tsk)
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) add(ts *types.TipSet) error {
|
||||
func (tsc *tipSetCache) ChainGetTipSetByHeight(ctx context.Context, height abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
return tsc.get(ctx, height, tsk, true)
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) ChainGetTipSetAfterHeight(ctx context.Context, height abi.ChainEpoch, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
return tsc.get(ctx, height, tsk, false)
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) get(ctx context.Context, height abi.ChainEpoch, tsk types.TipSetKey, prev bool) (*types.TipSet, error) {
|
||||
fallback := tsc.storage.ChainGetTipSetAfterHeight
|
||||
if prev {
|
||||
fallback = tsc.storage.ChainGetTipSetByHeight
|
||||
}
|
||||
tsc.mu.RLock()
|
||||
|
||||
// Nothing in the cache?
|
||||
if tsc.len == 0 {
|
||||
tsc.mu.RUnlock()
|
||||
log.Warnf("tipSetCache.get: cache is empty, requesting from storage (h=%d)", height)
|
||||
return fallback(ctx, height, tsk)
|
||||
}
|
||||
|
||||
// Resolve the head.
|
||||
head := tsc.byHeight[tsc.start]
|
||||
if !tsk.IsEmpty() {
|
||||
// Not on this chain?
|
||||
var ok bool
|
||||
head, ok = tsc.byKey[tsk]
|
||||
if !ok {
|
||||
tsc.mu.RUnlock()
|
||||
return fallback(ctx, height, tsk)
|
||||
}
|
||||
}
|
||||
|
||||
headH := head.Height()
|
||||
tailH := headH - abi.ChainEpoch(tsc.len)
|
||||
|
||||
if headH == height {
|
||||
tsc.mu.RUnlock()
|
||||
return head, nil
|
||||
} else if headH < height {
|
||||
tsc.mu.RUnlock()
|
||||
// If the user doesn't pass a tsk, we assume "head" is the last tipset we processed.
|
||||
return nil, xerrors.Errorf("requested epoch is in the future")
|
||||
} else if height < tailH {
|
||||
log.Warnf("tipSetCache.get: requested tipset not in cache, requesting from storage (h=%d; tail=%d)", height, tailH)
|
||||
tsc.mu.RUnlock()
|
||||
return fallback(ctx, height, head.Key())
|
||||
}
|
||||
|
||||
direction := 1
|
||||
if prev {
|
||||
direction = -1
|
||||
}
|
||||
var ts *types.TipSet
|
||||
for i := 0; i < tsc.len && ts == nil; i += direction {
|
||||
ts = tsc.byHeight[normalModulo(tsc.start-int(headH-height)+i, len(tsc.byHeight))]
|
||||
}
|
||||
tsc.mu.RUnlock()
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) ChainHead(ctx context.Context) (*types.TipSet, error) {
|
||||
tsc.mu.RLock()
|
||||
best := tsc.byHeight[tsc.start]
|
||||
tsc.mu.RUnlock()
|
||||
if best == nil {
|
||||
return tsc.storage.ChainHead(ctx)
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) add(to *types.TipSet) error {
|
||||
tsc.mu.Lock()
|
||||
defer tsc.mu.Unlock()
|
||||
|
||||
if tsc.len > 0 {
|
||||
if tsc.cache[tsc.start].Height() >= ts.Height() {
|
||||
return xerrors.Errorf("tipSetCache.add: expected new tipset height to be at least %d, was %d", tsc.cache[tsc.start].Height()+1, ts.Height())
|
||||
best := tsc.byHeight[tsc.start]
|
||||
if best.Height() >= to.Height() {
|
||||
return xerrors.Errorf("tipSetCache.add: expected new tipset height to be at least %d, was %d", tsc.byHeight[tsc.start].Height()+1, to.Height())
|
||||
}
|
||||
if best.Key() != to.Parents() {
|
||||
return xerrors.Errorf(
|
||||
"tipSetCache.add: expected new tipset %s (%d) to follow %s (%d), its parents are %s",
|
||||
to.Key(), to.Height(), best.Key(), best.Height(), best.Parents(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
nextH := ts.Height()
|
||||
nextH := to.Height()
|
||||
if tsc.len > 0 {
|
||||
nextH = tsc.cache[tsc.start].Height() + 1
|
||||
nextH = tsc.byHeight[tsc.start].Height() + 1
|
||||
}
|
||||
|
||||
// fill null blocks
|
||||
for nextH != ts.Height() {
|
||||
tsc.start = normalModulo(tsc.start+1, len(tsc.cache))
|
||||
tsc.cache[tsc.start] = nil
|
||||
if tsc.len < len(tsc.cache) {
|
||||
for nextH != to.Height() {
|
||||
tsc.start = normalModulo(tsc.start+1, len(tsc.byHeight))
|
||||
was := tsc.byHeight[tsc.start]
|
||||
if was != nil {
|
||||
tsc.byHeight[tsc.start] = nil
|
||||
delete(tsc.byKey, was.Key())
|
||||
}
|
||||
if tsc.len < len(tsc.byHeight) {
|
||||
tsc.len++
|
||||
}
|
||||
nextH++
|
||||
}
|
||||
|
||||
tsc.start = normalModulo(tsc.start+1, len(tsc.cache))
|
||||
tsc.cache[tsc.start] = ts
|
||||
if tsc.len < len(tsc.cache) {
|
||||
tsc.start = normalModulo(tsc.start+1, len(tsc.byHeight))
|
||||
was := tsc.byHeight[tsc.start]
|
||||
if was != nil {
|
||||
delete(tsc.byKey, was.Key())
|
||||
}
|
||||
tsc.byHeight[tsc.start] = to
|
||||
if tsc.len < len(tsc.byHeight) {
|
||||
tsc.len++
|
||||
}
|
||||
tsc.byKey[to.Key()] = to
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) revert(ts *types.TipSet) error {
|
||||
func (tsc *tipSetCache) revert(from *types.TipSet) error {
|
||||
tsc.mu.Lock()
|
||||
defer tsc.mu.Unlock()
|
||||
|
||||
return tsc.revertUnlocked(ts)
|
||||
return tsc.revertUnlocked(from)
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) revertUnlocked(ts *types.TipSet) error {
|
||||
@@ -82,75 +180,35 @@ func (tsc *tipSetCache) revertUnlocked(ts *types.TipSet) error {
|
||||
return nil // this can happen, and it's fine
|
||||
}
|
||||
|
||||
if !tsc.cache[tsc.start].Equals(ts) {
|
||||
was := tsc.byHeight[tsc.start]
|
||||
|
||||
if !was.Equals(ts) {
|
||||
return xerrors.New("tipSetCache.revert: revert tipset didn't match cache head")
|
||||
}
|
||||
delete(tsc.byKey, was.Key())
|
||||
|
||||
tsc.cache[tsc.start] = nil
|
||||
tsc.start = normalModulo(tsc.start-1, len(tsc.cache))
|
||||
tsc.byHeight[tsc.start] = nil
|
||||
tsc.start = normalModulo(tsc.start-1, len(tsc.byHeight))
|
||||
tsc.len--
|
||||
|
||||
_ = tsc.revertUnlocked(nil) // revert null block gap
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) getNonNull(height abi.ChainEpoch) (*types.TipSet, error) {
|
||||
for {
|
||||
ts, err := tsc.get(height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ts != nil {
|
||||
return ts, nil
|
||||
}
|
||||
height++
|
||||
}
|
||||
func (tsc *tipSetCache) observer() TipSetObserver {
|
||||
return (*tipSetCacheObserver)(tsc)
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) get(height abi.ChainEpoch) (*types.TipSet, error) {
|
||||
tsc.mu.RLock()
|
||||
type tipSetCacheObserver tipSetCache
|
||||
|
||||
if tsc.len == 0 {
|
||||
tsc.mu.RUnlock()
|
||||
log.Warnf("tipSetCache.get: cache is empty, requesting from storage (h=%d)", height)
|
||||
return tsc.storage.ChainGetTipSetByHeight(context.TODO(), height, types.EmptyTSK)
|
||||
}
|
||||
var _ TipSetObserver = new(tipSetCacheObserver)
|
||||
|
||||
headH := tsc.cache[tsc.start].Height()
|
||||
|
||||
if height > headH {
|
||||
tsc.mu.RUnlock()
|
||||
return nil, xerrors.Errorf("tipSetCache.get: requested tipset not in cache (req: %d, cache head: %d)", height, headH)
|
||||
}
|
||||
|
||||
clen := len(tsc.cache)
|
||||
var tail *types.TipSet
|
||||
for i := 1; i <= tsc.len; i++ {
|
||||
tail = tsc.cache[normalModulo(tsc.start-tsc.len+i, clen)]
|
||||
if tail != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if height < tail.Height() {
|
||||
tsc.mu.RUnlock()
|
||||
log.Warnf("tipSetCache.get: requested tipset not in cache, requesting from storage (h=%d; tail=%d)", height, tail.Height())
|
||||
return tsc.storage.ChainGetTipSetByHeight(context.TODO(), height, tail.Key())
|
||||
}
|
||||
|
||||
ts := tsc.cache[normalModulo(tsc.start-int(headH-height), clen)]
|
||||
tsc.mu.RUnlock()
|
||||
return ts, nil
|
||||
func (tsc *tipSetCacheObserver) Apply(_ context.Context, _, to *types.TipSet) error {
|
||||
return (*tipSetCache)(tsc).add(to)
|
||||
}
|
||||
|
||||
func (tsc *tipSetCache) best() (*types.TipSet, error) {
|
||||
tsc.mu.RLock()
|
||||
best := tsc.cache[tsc.start]
|
||||
tsc.mu.RUnlock()
|
||||
if best == nil {
|
||||
return tsc.storage.ChainHead(context.TODO())
|
||||
}
|
||||
return best, nil
|
||||
func (tsc *tipSetCacheObserver) Revert(ctx context.Context, from, _ *types.TipSet) error {
|
||||
return (*tipSetCache)(tsc).revert(from)
|
||||
}
|
||||
|
||||
func normalModulo(n, m int) int {
|
||||
|
||||
+141
-99
@@ -6,61 +6,22 @@ import (
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func TestTsCache(t *testing.T) {
|
||||
tsc := newTSCache(50, &tsCacheAPIFailOnStorageCall{t: t})
|
||||
|
||||
h := abi.ChainEpoch(75)
|
||||
|
||||
a, _ := address.NewFromString("t00")
|
||||
|
||||
add := func() {
|
||||
ts, err := types.NewTipSet([]*types.BlockHeader{{
|
||||
Miner: a,
|
||||
Height: h,
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: dummyCid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := tsc.add(ts); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h++
|
||||
}
|
||||
|
||||
for i := 0; i < 9000; i++ {
|
||||
if i%90 > 60 {
|
||||
best, err := tsc.best()
|
||||
if err != nil {
|
||||
t.Fatal(err, "; i:", i)
|
||||
return
|
||||
}
|
||||
if err := tsc.revert(best); err != nil {
|
||||
t.Fatal(err, "; i:", i)
|
||||
return
|
||||
}
|
||||
h--
|
||||
} else {
|
||||
add()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type tsCacheAPIFailOnStorageCall struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (tc *tsCacheAPIFailOnStorageCall) ChainGetTipSetAfterHeight(ctx context.Context, epoch abi.ChainEpoch, key types.TipSetKey) (*types.TipSet, error) {
|
||||
tc.t.Fatal("storage call")
|
||||
return &types.TipSet{}, nil
|
||||
}
|
||||
|
||||
func (tc *tsCacheAPIFailOnStorageCall) ChainGetTipSetByHeight(ctx context.Context, epoch abi.ChainEpoch, key types.TipSetKey) (*types.TipSet, error) {
|
||||
tc.t.Fatal("storage call")
|
||||
return &types.TipSet{}, nil
|
||||
@@ -69,100 +30,181 @@ func (tc *tsCacheAPIFailOnStorageCall) ChainHead(ctx context.Context) (*types.Ti
|
||||
tc.t.Fatal("storage call")
|
||||
return &types.TipSet{}, nil
|
||||
}
|
||||
func (tc *tsCacheAPIFailOnStorageCall) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
tc.t.Fatal("storage call")
|
||||
return &types.TipSet{}, nil
|
||||
}
|
||||
|
||||
type cacheHarness struct {
|
||||
t *testing.T
|
||||
|
||||
miner address.Address
|
||||
tsc *tipSetCache
|
||||
height abi.ChainEpoch
|
||||
}
|
||||
|
||||
func newCacheharness(t *testing.T) *cacheHarness {
|
||||
a, err := address.NewFromString("t00")
|
||||
require.NoError(t, err)
|
||||
|
||||
h := &cacheHarness{
|
||||
t: t,
|
||||
tsc: newTSCache(&tsCacheAPIFailOnStorageCall{t: t}, 50),
|
||||
height: 75,
|
||||
miner: a,
|
||||
}
|
||||
h.addWithParents(nil)
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *cacheHarness) addWithParents(parents []cid.Cid) {
|
||||
ts, err := types.NewTipSet([]*types.BlockHeader{{
|
||||
Miner: h.miner,
|
||||
Height: h.height,
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: dummyCid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
Parents: parents,
|
||||
}})
|
||||
require.NoError(h.t, err)
|
||||
require.NoError(h.t, h.tsc.add(ts))
|
||||
h.height++
|
||||
}
|
||||
|
||||
func (h *cacheHarness) add() {
|
||||
last, err := h.tsc.ChainHead(context.Background())
|
||||
require.NoError(h.t, err)
|
||||
h.addWithParents(last.Cids())
|
||||
}
|
||||
|
||||
func (h *cacheHarness) revert() {
|
||||
best, err := h.tsc.ChainHead(context.Background())
|
||||
require.NoError(h.t, err)
|
||||
err = h.tsc.revert(best)
|
||||
require.NoError(h.t, err)
|
||||
h.height--
|
||||
}
|
||||
|
||||
func (h *cacheHarness) skip(n abi.ChainEpoch) {
|
||||
h.height += n
|
||||
}
|
||||
|
||||
func TestTsCache(t *testing.T) {
|
||||
h := newCacheharness(t)
|
||||
|
||||
for i := 0; i < 9000; i++ {
|
||||
if i%90 > 60 {
|
||||
h.revert()
|
||||
} else {
|
||||
h.add()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTsCacheNulls(t *testing.T) {
|
||||
tsc := newTSCache(50, &tsCacheAPIFailOnStorageCall{t: t})
|
||||
ctx := context.Background()
|
||||
h := newCacheharness(t)
|
||||
|
||||
h := abi.ChainEpoch(75)
|
||||
h.add()
|
||||
h.add()
|
||||
h.add()
|
||||
h.skip(5)
|
||||
|
||||
a, _ := address.NewFromString("t00")
|
||||
add := func() {
|
||||
ts, err := types.NewTipSet([]*types.BlockHeader{{
|
||||
Miner: a,
|
||||
Height: h,
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: dummyCid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := tsc.add(ts); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h++
|
||||
}
|
||||
h.add()
|
||||
h.add()
|
||||
|
||||
add()
|
||||
add()
|
||||
add()
|
||||
h += 5
|
||||
|
||||
add()
|
||||
add()
|
||||
|
||||
best, err := tsc.best()
|
||||
best, err := h.tsc.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, h-1, best.Height())
|
||||
require.Equal(t, h.height-1, best.Height())
|
||||
|
||||
ts, err := tsc.get(h - 1)
|
||||
ts, err := h.tsc.ChainGetTipSetByHeight(ctx, h.height-1, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, h-1, ts.Height())
|
||||
require.Equal(t, h.height-1, ts.Height())
|
||||
|
||||
ts, err = tsc.get(h - 2)
|
||||
ts, err = h.tsc.ChainGetTipSetByHeight(ctx, h.height-2, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, h-2, ts.Height())
|
||||
require.Equal(t, h.height-2, ts.Height())
|
||||
|
||||
ts, err = tsc.get(h - 3)
|
||||
// Should skip the nulls and walk back to the last tipset.
|
||||
ts, err = h.tsc.ChainGetTipSetByHeight(ctx, h.height-3, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, ts)
|
||||
require.Equal(t, h.height-8, ts.Height())
|
||||
|
||||
ts, err = tsc.get(h - 8)
|
||||
ts, err = h.tsc.ChainGetTipSetByHeight(ctx, h.height-8, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, h-8, ts.Height())
|
||||
require.Equal(t, h.height-8, ts.Height())
|
||||
|
||||
best, err = tsc.best()
|
||||
best, err = h.tsc.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, tsc.revert(best))
|
||||
require.NoError(t, h.tsc.revert(best))
|
||||
|
||||
best, err = tsc.best()
|
||||
best, err = h.tsc.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, tsc.revert(best))
|
||||
require.NoError(t, h.tsc.revert(best))
|
||||
|
||||
best, err = tsc.best()
|
||||
best, err = h.tsc.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, h-8, best.Height())
|
||||
require.Equal(t, h.height-8, best.Height())
|
||||
|
||||
h += 50
|
||||
add()
|
||||
h.skip(50)
|
||||
h.add()
|
||||
|
||||
ts, err = tsc.get(h - 1)
|
||||
ts, err = h.tsc.ChainGetTipSetByHeight(ctx, h.height-1, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, h-1, ts.Height())
|
||||
require.Equal(t, h.height-1, ts.Height())
|
||||
}
|
||||
|
||||
type tsCacheAPIStorageCallCounter struct {
|
||||
t *testing.T
|
||||
chainGetTipSetByHeight int
|
||||
chainHead int
|
||||
t *testing.T
|
||||
chainGetTipSetByHeight int
|
||||
chainGetTipSetAfterHeight int
|
||||
chainGetTipSet int
|
||||
chainHead int
|
||||
}
|
||||
|
||||
func (tc *tsCacheAPIStorageCallCounter) ChainGetTipSetByHeight(ctx context.Context, epoch abi.ChainEpoch, key types.TipSetKey) (*types.TipSet, error) {
|
||||
tc.chainGetTipSetByHeight++
|
||||
return &types.TipSet{}, nil
|
||||
}
|
||||
func (tc *tsCacheAPIStorageCallCounter) ChainGetTipSetAfterHeight(ctx context.Context, epoch abi.ChainEpoch, key types.TipSetKey) (*types.TipSet, error) {
|
||||
tc.chainGetTipSetAfterHeight++
|
||||
return &types.TipSet{}, nil
|
||||
}
|
||||
func (tc *tsCacheAPIStorageCallCounter) ChainHead(ctx context.Context) (*types.TipSet, error) {
|
||||
tc.chainHead++
|
||||
return &types.TipSet{}, nil
|
||||
}
|
||||
func (tc *tsCacheAPIStorageCallCounter) ChainGetTipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) {
|
||||
tc.chainGetTipSet++
|
||||
return &types.TipSet{}, nil
|
||||
}
|
||||
|
||||
func TestTsCacheEmpty(t *testing.T) {
|
||||
// Calling best on an empty cache should just call out to the chain API
|
||||
callCounter := &tsCacheAPIStorageCallCounter{t: t}
|
||||
tsc := newTSCache(50, callCounter)
|
||||
_, err := tsc.best()
|
||||
tsc := newTSCache(callCounter, 50)
|
||||
_, err := tsc.ChainHead(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, callCounter.chainHead)
|
||||
}
|
||||
|
||||
func TestTsCacheSkip(t *testing.T) {
|
||||
h := newCacheharness(t)
|
||||
|
||||
ts, err := types.NewTipSet([]*types.BlockHeader{{
|
||||
Miner: h.miner,
|
||||
Height: h.height,
|
||||
ParentStateRoot: dummyCid,
|
||||
Messages: dummyCid,
|
||||
ParentMessageReceipts: dummyCid,
|
||||
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
|
||||
// With parents that don't match the last block.
|
||||
Parents: nil,
|
||||
}})
|
||||
require.NoError(h.t, err)
|
||||
err = h.tsc.add(ts)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func (me *messageEvents) CheckMsg(ctx context.Context, smsg types.ChainMsg, hnd MsgHandler) CheckFunc {
|
||||
func (me *messageEvents) CheckMsg(smsg types.ChainMsg, hnd MsgHandler) CheckFunc {
|
||||
msg := smsg.VMMessage()
|
||||
|
||||
return func(ts *types.TipSet) (done bool, more bool, err error) {
|
||||
return func(ctx context.Context, ts *types.TipSet) (done bool, more bool, err error) {
|
||||
fa, err := me.cs.StateGetActor(ctx, msg.From, ts.Key())
|
||||
if err != nil {
|
||||
return false, true, err
|
||||
@@ -24,7 +24,7 @@ func (me *messageEvents) CheckMsg(ctx context.Context, smsg types.ChainMsg, hnd
|
||||
return false, true, nil
|
||||
}
|
||||
|
||||
ml, err := me.cs.StateSearchMsg(me.ctx, ts.Key(), msg.Cid(), stmgr.LookbackNoLimit, true)
|
||||
ml, err := me.cs.StateSearchMsg(ctx, ts.Key(), msg.Cid(), stmgr.LookbackNoLimit, true)
|
||||
if err != nil {
|
||||
return false, true, xerrors.Errorf("getting receipt in CheckMsg: %w", err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package exchange
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
var _ = cid.Undef
|
||||
var _ = math.E
|
||||
var _ = sort.Sort
|
||||
|
||||
var lengthBufRequest = []byte{131}
|
||||
|
||||
@@ -151,12 +151,20 @@ func (c *client) doRequest(
|
||||
// errors. Peer penalization should happen here then, before returning, so
|
||||
// we can apply the correct penalties depending on the cause of the error.
|
||||
// FIXME: Add the `peer` as argument once we implement penalties.
|
||||
func (c *client) processResponse(req *Request, res *Response, tipsets []*types.TipSet) (*validatedResponse, error) {
|
||||
err := res.statusToError()
|
||||
func (c *client) processResponse(req *Request, res *Response, tipsets []*types.TipSet) (r *validatedResponse, err error) {
|
||||
err = res.statusToError()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("status error: %s", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if rerr := recover(); rerr != nil {
|
||||
log.Errorf("process response error: %s", rerr)
|
||||
err = xerrors.Errorf("process response error: %s", rerr)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
options := parseOptions(req.Options)
|
||||
if options.noOptionsSet() {
|
||||
// Safety check: this shouldn't have been sent, and even if it did
|
||||
|
||||
+5
-22
@@ -23,7 +23,6 @@ import (
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/ipfs/go-merkledag"
|
||||
"github.com/ipld/go-car"
|
||||
"go.opencensus.io/trace"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof"
|
||||
@@ -33,6 +32,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
@@ -43,7 +43,6 @@ import (
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
"github.com/filecoin-project/lotus/journal"
|
||||
"github.com/filecoin-project/lotus/lib/sigs"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
|
||||
@@ -233,7 +232,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS
|
||||
return nil, xerrors.Errorf("make genesis block failed: %w", err)
|
||||
}
|
||||
|
||||
cs := store.NewChainStore(bs, bs, ds, sys, j)
|
||||
cs := store.NewChainStore(bs, bs, ds, filcns.Weight, j)
|
||||
|
||||
genfb := &types.FullBlock{Header: genb.Genesis}
|
||||
gents := store.NewFullTipSet([]*types.FullBlock{genfb})
|
||||
@@ -247,7 +246,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS
|
||||
mgen[genesis2.MinerAddress(uint64(i))] = &wppProvider{}
|
||||
}
|
||||
|
||||
sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, us)
|
||||
sm, err := stmgr.NewStateManager(cs, filcns.NewTipSetExecutor(), sys, us)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("initing stmgr: %w", err)
|
||||
}
|
||||
@@ -289,7 +288,7 @@ func NewGenerator() (*ChainGen, error) {
|
||||
}
|
||||
|
||||
func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
|
||||
return NewGeneratorWithSectorsAndUpgradeSchedule(numSectors, stmgr.DefaultUpgradeSchedule())
|
||||
return NewGeneratorWithSectorsAndUpgradeSchedule(numSectors, filcns.DefaultUpgradeSchedule())
|
||||
}
|
||||
|
||||
func NewGeneratorWithUpgradeSchedule(us stmgr.UpgradeSchedule) (*ChainGen, error) {
|
||||
@@ -487,7 +486,7 @@ func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, vrfticke
|
||||
ts = parents.MinTimestamp() + uint64(height-parents.Height())*build.BlockDelaySecs
|
||||
}
|
||||
|
||||
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, &api.BlockTemplate{
|
||||
fblk, err := filcns.NewFilecoinExpectedConsensus(cg.sm, nil, nil, nil).CreateBlock(context.TODO(), cg.w, &api.BlockTemplate{
|
||||
Miner: m,
|
||||
Parents: parents.Key(),
|
||||
Ticket: vrfticket,
|
||||
@@ -667,22 +666,6 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch,
|
||||
|
||||
type SignFunc func(context.Context, address.Address, []byte) (*crypto.Signature, error)
|
||||
|
||||
func VerifyVRF(ctx context.Context, worker address.Address, vrfBase, vrfproof []byte) error {
|
||||
_, span := trace.StartSpan(ctx, "VerifyVRF")
|
||||
defer span.End()
|
||||
|
||||
sig := &crypto.Signature{
|
||||
Type: crypto.SigTypeBLS,
|
||||
Data: vrfproof,
|
||||
}
|
||||
|
||||
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 address.Address, sigInput []byte) ([]byte, error) {
|
||||
sig, err := sign(ctx, worker, sigInput)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
|
||||
adt0 "github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
@@ -149,7 +150,10 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return nil, nil, xerrors.Errorf("making new state tree: %w", err)
|
||||
}
|
||||
|
||||
av := actors.VersionForNetwork(template.NetworkVersion)
|
||||
av, err := actors.VersionForNetwork(template.NetworkVersion)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("getting network version: %w", err)
|
||||
}
|
||||
|
||||
// Create system actor
|
||||
|
||||
@@ -219,7 +223,7 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return nil, nil, xerrors.Errorf("set verified registry actor: %w", err)
|
||||
}
|
||||
|
||||
bact, err := makeAccountActor(ctx, cst, av, builtin.BurntFundsActorAddr, big.Zero())
|
||||
bact, err := MakeAccountActor(ctx, cst, av, builtin.BurntFundsActorAddr, big.Zero())
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("setup burnt funds actor state: %w", err)
|
||||
}
|
||||
@@ -232,7 +236,7 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
|
||||
switch info.Type {
|
||||
case genesis.TAccount:
|
||||
if err := createAccountActor(ctx, cst, state, info, keyIDs, av); err != nil {
|
||||
if err := CreateAccountActor(ctx, cst, state, info, keyIDs, av); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to create account actor: %w", err)
|
||||
}
|
||||
|
||||
@@ -244,7 +248,7 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
}
|
||||
idStart++
|
||||
|
||||
if err := createMultisigAccount(ctx, cst, state, ida, info, keyIDs, av); err != nil {
|
||||
if err := CreateMultisigAccount(ctx, cst, state, ida, info, keyIDs, av); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
@@ -265,7 +269,7 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return nil, nil, fmt.Errorf("rootkey account has already been declared, cannot be assigned 80: %s", ainfo.Owner)
|
||||
}
|
||||
|
||||
vact, err := makeAccountActor(ctx, cst, av, ainfo.Owner, template.VerifregRootKey.Balance)
|
||||
vact, err := MakeAccountActor(ctx, cst, av, ainfo.Owner, template.VerifregRootKey.Balance)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("setup verifreg rootkey account state: %w", err)
|
||||
}
|
||||
@@ -273,7 +277,7 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return nil, nil, xerrors.Errorf("set verifreg rootkey account actor: %w", err)
|
||||
}
|
||||
case genesis.TMultisig:
|
||||
if err = createMultisigAccount(ctx, cst, state, builtin.RootVerifierAddress, template.VerifregRootKey, keyIDs, av); err != nil {
|
||||
if err = CreateMultisigAccount(ctx, cst, state, builtin.RootVerifierAddress, template.VerifregRootKey, keyIDs, av); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to set up verified registry signer: %w", err)
|
||||
}
|
||||
default:
|
||||
@@ -302,7 +306,7 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
verifierAct, err := makeAccountActor(ctx, cst, av, verifierAd, big.Zero())
|
||||
verifierAct, err := MakeAccountActor(ctx, cst, av, verifierAd, big.Zero())
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("setup first verifier state: %w", err)
|
||||
}
|
||||
@@ -345,13 +349,13 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
}
|
||||
|
||||
keyIDs[ainfo.Owner] = builtin.ReserveAddress
|
||||
err = createAccountActor(ctx, cst, state, template.RemainderAccount, keyIDs, av)
|
||||
err = CreateAccountActor(ctx, cst, state, template.RemainderAccount, keyIDs, av)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("creating remainder acct: %w", err)
|
||||
}
|
||||
|
||||
case genesis.TMultisig:
|
||||
if err = createMultisigAccount(ctx, cst, state, builtin.ReserveAddress, template.RemainderAccount, keyIDs, av); err != nil {
|
||||
if err = CreateMultisigAccount(ctx, cst, state, builtin.ReserveAddress, template.RemainderAccount, keyIDs, av); err != nil {
|
||||
return nil, nil, xerrors.Errorf("failed to set up remainder: %w", err)
|
||||
}
|
||||
default:
|
||||
@@ -361,7 +365,7 @@ func MakeInitialStateTree(ctx context.Context, bs bstore.Blockstore, template ge
|
||||
return state, keyIDs, nil
|
||||
}
|
||||
|
||||
func makeAccountActor(ctx context.Context, cst cbor.IpldStore, av actors.Version, addr address.Address, bal types.BigInt) (*types.Actor, error) {
|
||||
func MakeAccountActor(ctx context.Context, cst cbor.IpldStore, av actors.Version, addr address.Address, bal types.BigInt) (*types.Actor, error) {
|
||||
ast, err := account.MakeState(adt.WrapStore(ctx, cst), av, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -386,13 +390,13 @@ func makeAccountActor(ctx context.Context, cst cbor.IpldStore, av actors.Version
|
||||
return act, nil
|
||||
}
|
||||
|
||||
func createAccountActor(ctx context.Context, cst cbor.IpldStore, state *state.StateTree, info genesis.Actor, keyIDs map[address.Address]address.Address, av actors.Version) error {
|
||||
func CreateAccountActor(ctx context.Context, cst cbor.IpldStore, state *state.StateTree, info genesis.Actor, keyIDs map[address.Address]address.Address, av actors.Version) error {
|
||||
var ainfo genesis.AccountMeta
|
||||
if err := json.Unmarshal(info.Meta, &ainfo); err != nil {
|
||||
return xerrors.Errorf("unmarshaling account meta: %w", err)
|
||||
}
|
||||
|
||||
aa, err := makeAccountActor(ctx, cst, av, ainfo.Owner, info.Balance)
|
||||
aa, err := MakeAccountActor(ctx, cst, av, ainfo.Owner, info.Balance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -409,9 +413,9 @@ func createAccountActor(ctx context.Context, cst cbor.IpldStore, state *state.St
|
||||
return nil
|
||||
}
|
||||
|
||||
func createMultisigAccount(ctx context.Context, cst cbor.IpldStore, state *state.StateTree, ida address.Address, info genesis.Actor, keyIDs map[address.Address]address.Address, av actors.Version) error {
|
||||
func CreateMultisigAccount(ctx context.Context, cst cbor.IpldStore, state *state.StateTree, ida address.Address, info genesis.Actor, keyIDs map[address.Address]address.Address, av actors.Version) error {
|
||||
if info.Type != genesis.TMultisig {
|
||||
return fmt.Errorf("can only call createMultisigAccount with multisig Actor info")
|
||||
return fmt.Errorf("can only call CreateMultisigAccount with multisig Actor info")
|
||||
}
|
||||
var ainfo genesis.MultisigMeta
|
||||
if err := json.Unmarshal(info.Meta, &ainfo); err != nil {
|
||||
@@ -433,7 +437,7 @@ func createMultisigAccount(ctx context.Context, cst cbor.IpldStore, state *state
|
||||
continue
|
||||
}
|
||||
|
||||
aa, err := makeAccountActor(ctx, cst, av, e, big.Zero())
|
||||
aa, err := MakeAccountActor(ctx, cst, av, e, big.Zero())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -471,7 +475,7 @@ func createMultisigAccount(ctx context.Context, cst cbor.IpldStore, state *state
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot cid.Cid, template genesis.Template, keyIDs map[address.Address]address.Address, nv network.Version) (cid.Cid, error) {
|
||||
func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, sys vm.SyscallBuilder, stateroot cid.Cid, template genesis.Template, keyIDs map[address.Address]address.Address, nv network.Version) (cid.Cid, error) {
|
||||
verifNeeds := make(map[address.Address]abi.PaddedPieceSize)
|
||||
var sum abi.PaddedPieceSize
|
||||
|
||||
@@ -480,7 +484,8 @@ func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot ci
|
||||
Epoch: 0,
|
||||
Rand: &fakeRand{},
|
||||
Bstore: cs.StateBlockstore(),
|
||||
Syscalls: mkFakedSigSyscalls(cs.VMSys()),
|
||||
Actors: filcns.NewActorRegistry(),
|
||||
Syscalls: mkFakedSigSyscalls(sys),
|
||||
CircSupplyCalc: nil,
|
||||
NtwkVersion: func(_ context.Context, _ abi.ChainEpoch) network.Version {
|
||||
return nv
|
||||
@@ -559,15 +564,15 @@ func MakeGenesisBlock(ctx context.Context, j journal.Journal, bs bstore.Blocksto
|
||||
}
|
||||
|
||||
// temp chainstore
|
||||
cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), sys, j)
|
||||
cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), nil, j)
|
||||
|
||||
// Verify PreSealed Data
|
||||
stateroot, err = VerifyPreSealedData(ctx, cs, stateroot, template, keyIDs, template.NetworkVersion)
|
||||
stateroot, err = VerifyPreSealedData(ctx, cs, sys, stateroot, template, keyIDs, template.NetworkVersion)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to verify presealed data: %w", err)
|
||||
}
|
||||
|
||||
stateroot, err = SetupStorageMiners(ctx, cs, stateroot, template.Miners, template.NetworkVersion)
|
||||
stateroot, err = SetupStorageMiners(ctx, cs, sys, stateroot, template.Miners, template.NetworkVersion)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("setup miners failed: %w", err)
|
||||
}
|
||||
|
||||
+51
-34
@@ -6,30 +6,6 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
power4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/power"
|
||||
|
||||
reward4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/reward"
|
||||
|
||||
market4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/market"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/network"
|
||||
|
||||
market0 "github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/reward"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
@@ -39,12 +15,29 @@ import (
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/filecoin-project/go-state-types/network"
|
||||
|
||||
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
market0 "github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
reward0 "github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
||||
market2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/market"
|
||||
reward2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/reward"
|
||||
market4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/market"
|
||||
power4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/power"
|
||||
reward4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/reward"
|
||||
runtime5 "github.com/filecoin-project/specs-actors/v5/actors/runtime"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/reward"
|
||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@@ -78,10 +71,13 @@ func mkFakedSigSyscalls(base vm.SyscallBuilder) vm.SyscallBuilder {
|
||||
}
|
||||
|
||||
// Note: Much of this is brittle, if the methodNum / param / return changes, it will break things
|
||||
func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, miners []genesis.Miner, nv network.Version) (cid.Cid, error) {
|
||||
func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.SyscallBuilder, sroot cid.Cid, miners []genesis.Miner, nv network.Version) (cid.Cid, error) {
|
||||
|
||||
cst := cbor.NewCborStore(cs.StateBlockstore())
|
||||
av := actors.VersionForNetwork(nv)
|
||||
av, err := actors.VersionForNetwork(nv)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("failed to get network version: %w", err)
|
||||
}
|
||||
|
||||
csc := func(context.Context, abi.ChainEpoch, *state.StateTree) (abi.TokenAmount, error) {
|
||||
return big.Zero(), nil
|
||||
@@ -92,7 +88,8 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
||||
Epoch: 0,
|
||||
Rand: &fakeRand{},
|
||||
Bstore: cs.StateBlockstore(),
|
||||
Syscalls: mkFakedSigSyscalls(cs.VMSys()),
|
||||
Actors: filcns.NewActorRegistry(),
|
||||
Syscalls: mkFakedSigSyscalls(sys),
|
||||
CircSupplyCalc: csc,
|
||||
NtwkVersion: func(_ context.Context, _ abi.ChainEpoch) network.Version {
|
||||
return nv
|
||||
@@ -291,7 +288,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
||||
return cid.Undef, xerrors.Errorf("setting power state: %w", err)
|
||||
}
|
||||
|
||||
rewact, err := SetupRewardActor(ctx, cs.StateBlockstore(), big.Zero(), actors.VersionForNetwork(nv))
|
||||
rewact, err := SetupRewardActor(ctx, cs.StateBlockstore(), big.Zero(), av)
|
||||
if err != nil {
|
||||
return cid.Undef, xerrors.Errorf("setup reward actor: %w", err)
|
||||
}
|
||||
@@ -401,8 +398,8 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
||||
return cid.Undef, xerrors.Errorf("failed to confirm presealed sectors: %w", err)
|
||||
}
|
||||
|
||||
if av > actors.Version2 {
|
||||
// post v2, we need to explicitly Claim this power since ConfirmSectorProofsValid doesn't do it anymore
|
||||
if av >= actors.Version2 {
|
||||
// post v0, we need to explicitly Claim this power since ConfirmSectorProofsValid doesn't do it anymore
|
||||
claimParams := &power4.UpdateClaimedPowerParams{
|
||||
RawByteDelta: types.NewInt(uint64(m.SectorSize)),
|
||||
QualityAdjustedDelta: sectorWeight,
|
||||
@@ -534,7 +531,6 @@ func dealWeight(ctx context.Context, vm *vm.VM, maddr address.Address, dealIDs [
|
||||
SectorExpiry: sectorExpiry,
|
||||
}
|
||||
|
||||
var dealWeights market0.VerifyDealsForActivationReturn
|
||||
ret, err := doExecValue(ctx, vm,
|
||||
market.Address,
|
||||
maddr,
|
||||
@@ -545,11 +541,23 @@ func dealWeight(ctx context.Context, vm *vm.VM, maddr address.Address, dealIDs [
|
||||
if err != nil {
|
||||
return big.Zero(), big.Zero(), err
|
||||
}
|
||||
if err := dealWeights.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
|
||||
var weight, verifiedWeight abi.DealWeight
|
||||
if av < actors.Version2 {
|
||||
var dealWeights market0.VerifyDealsForActivationReturn
|
||||
err = dealWeights.UnmarshalCBOR(bytes.NewReader(ret))
|
||||
weight = dealWeights.DealWeight
|
||||
verifiedWeight = dealWeights.VerifiedDealWeight
|
||||
} else {
|
||||
var dealWeights market2.VerifyDealsForActivationReturn
|
||||
err = dealWeights.UnmarshalCBOR(bytes.NewReader(ret))
|
||||
weight = dealWeights.DealWeight
|
||||
verifiedWeight = dealWeights.VerifiedDealWeight
|
||||
}
|
||||
if err != nil {
|
||||
return big.Zero(), big.Zero(), err
|
||||
}
|
||||
|
||||
return dealWeights.DealWeight, dealWeights.VerifiedDealWeight, nil
|
||||
return weight, verifiedWeight, nil
|
||||
}
|
||||
params := &market4.VerifyDealsForActivationParams{Sectors: []market4.SectorDeals{{
|
||||
SectorExpiry: sectorExpiry,
|
||||
@@ -581,7 +589,8 @@ func currentEpochBlockReward(ctx context.Context, vm *vm.VM, maddr address.Addre
|
||||
}
|
||||
|
||||
// TODO: This hack should move to reward actor wrapper
|
||||
if av <= actors.Version2 {
|
||||
switch av {
|
||||
case actors.Version0:
|
||||
var epochReward reward0.ThisEpochRewardReturn
|
||||
|
||||
if err := epochReward.UnmarshalCBOR(bytes.NewReader(rwret)); err != nil {
|
||||
@@ -589,6 +598,14 @@ func currentEpochBlockReward(ctx context.Context, vm *vm.VM, maddr address.Addre
|
||||
}
|
||||
|
||||
return epochReward.ThisEpochBaselinePower, *epochReward.ThisEpochRewardSmoothed, nil
|
||||
case actors.Version2:
|
||||
var epochReward reward2.ThisEpochRewardReturn
|
||||
|
||||
if err := epochReward.UnmarshalCBOR(bytes.NewReader(rwret)); err != nil {
|
||||
return big.Zero(), builtin.FilterEstimate{}, err
|
||||
}
|
||||
|
||||
return epochReward.ThisEpochBaselinePower, builtin.FilterEstimate(epochReward.ThisEpochRewardSmoothed), nil
|
||||
}
|
||||
|
||||
var epochReward reward4.ThisEpochRewardReturn
|
||||
|
||||
@@ -5,6 +5,7 @@ package market
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
var _ = cid.Undef
|
||||
var _ = math.E
|
||||
var _ = sort.Sort
|
||||
|
||||
var lengthBufFundedAddressState = []byte{131}
|
||||
|
||||
@@ -281,7 +281,7 @@ func (mp *MessagePool) checkMessages(ctx context.Context, msgs []*types.Message,
|
||||
// gas checks
|
||||
|
||||
// 4. Min Gas
|
||||
minGas := vm.PricelistByVersion(nv).OnChainMessage(m.ChainLength())
|
||||
minGas := vm.PricelistByEpoch(epoch).OnChainMessage(m.ChainLength())
|
||||
|
||||
check = api.MessageCheckStatus{
|
||||
Cid: m.Cid(),
|
||||
|
||||
@@ -354,7 +354,7 @@ func (ms *msgSet) toSlice() []*types.SignedMessage {
|
||||
return set
|
||||
}
|
||||
|
||||
func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) {
|
||||
func New(api Provider, ds dtypes.MetadataDS, us stmgr.UpgradeSchedule, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) {
|
||||
cache, _ := lru.New2Q(build.BlsSignatureCacheSize)
|
||||
verifcache, _ := lru.New2Q(build.VerifSigCacheSize)
|
||||
|
||||
@@ -366,7 +366,6 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journ
|
||||
if j == nil {
|
||||
j = journal.NilJournal()
|
||||
}
|
||||
us := stmgr.DefaultUpgradeSchedule()
|
||||
|
||||
mp := &MessagePool{
|
||||
ds: ds,
|
||||
@@ -616,7 +615,8 @@ func (mp *MessagePool) addLocal(ctx context.Context, m *types.SignedMessage) err
|
||||
// For non local messages, if the message cannot be included in the next 20 blocks it returns
|
||||
// a (soft) validation error.
|
||||
func (mp *MessagePool) verifyMsgBeforeAdd(m *types.SignedMessage, curTs *types.TipSet, local bool) (bool, error) {
|
||||
minGas := vm.PricelistByVersion(build.NewestNetworkVersion).OnChainMessage(m.ChainLength())
|
||||
epoch := curTs.Height()
|
||||
minGas := vm.PricelistByEpoch(epoch).OnChainMessage(m.ChainLength())
|
||||
|
||||
if err := m.VMMessage().ValidForBlockInclusion(minGas.Total(), build.NewestNetworkVersion); err != nil {
|
||||
return false, xerrors.Errorf("message will not be included in a block: %w", err)
|
||||
|
||||
@@ -11,17 +11,18 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-datastore"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
"github.com/filecoin-project/lotus/chain/messagepool/gasguess"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/types/mock"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
|
||||
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -232,7 +233,7 @@ func TestMessagePool(t *testing.T) {
|
||||
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -276,7 +277,7 @@ func TestCheckMessageBig(t *testing.T) {
|
||||
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
to := mock.Address(1001)
|
||||
@@ -339,7 +340,7 @@ func TestMessagePoolMessagesInEachBlock(t *testing.T) {
|
||||
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -388,7 +389,7 @@ func TestRevertMessages(t *testing.T) {
|
||||
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -451,7 +452,7 @@ func TestPruningSimple(t *testing.T) {
|
||||
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -495,7 +496,7 @@ func TestLoadLocal(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -538,7 +539,7 @@ func TestLoadLocal(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err = New(tma, ds, "mptest", nil)
|
||||
mp, err = New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -567,7 +568,7 @@ func TestClearAll(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -621,7 +622,7 @@ func TestClearNonLocal(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -682,7 +683,7 @@ func TestUpdates(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
"github.com/filecoin-project/lotus/chain/messagepool/gasguess"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
@@ -24,7 +25,7 @@ func TestRepubMessages(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "mptest", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ import (
|
||||
|
||||
var bigBlockGasLimit = big.NewInt(build.BlockGasLimit)
|
||||
|
||||
var MaxBlockMessages = 16000
|
||||
|
||||
const MaxBlocks = 15
|
||||
|
||||
type msgChain struct {
|
||||
@@ -58,8 +56,8 @@ func (mp *MessagePool) SelectMessages(ctx context.Context, ts *types.TipSet, tq
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(msgs) > MaxBlockMessages {
|
||||
msgs = msgs[:MaxBlockMessages]
|
||||
if len(msgs) > build.BlockMessageLimit {
|
||||
msgs = msgs[:build.BlockMessageLimit]
|
||||
}
|
||||
|
||||
return msgs, nil
|
||||
@@ -749,7 +747,7 @@ func (mp *MessagePool) createMessageChains(actor address.Address, mset map[uint6
|
||||
}
|
||||
curNonce++
|
||||
|
||||
minGas := vm.PricelistByVersion(build.NewestNetworkVersion).OnChainMessage(m.ChainLength()).Total()
|
||||
minGas := vm.PricelistByEpoch(ts.Height()).OnChainMessage(m.ChainLength()).Total()
|
||||
if m.Message.GasLimit < minGas {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
"github.com/filecoin-project/lotus/chain/messagepool/gasguess"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/types/mock"
|
||||
@@ -60,7 +61,7 @@ func makeTestMessage(w *wallet.LocalWallet, from, to address.Address, nonce uint
|
||||
func makeTestMpool() (*MessagePool, *testMpoolAPI) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
mp, err := New(tma, ds, "test", nil)
|
||||
mp, err := New(tma, ds, filcns.DefaultUpgradeSchedule(), "test", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"go.uber.org/fx"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/node/impl/full"
|
||||
"github.com/filecoin-project/lotus/node/modules/helpers"
|
||||
)
|
||||
|
||||
var log = logging.Logger("metrics")
|
||||
|
||||
const baseTopic = "/fil/headnotifs/"
|
||||
|
||||
type Update struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
func SendHeadNotifs(nickname string) func(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, chain full.ChainAPI) error {
|
||||
return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, chain full.ChainAPI) error {
|
||||
ctx := helpers.LifecycleCtx(mctx, lc)
|
||||
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(_ context.Context) error {
|
||||
gen, err := chain.Chain.GetGenesis()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
topic := baseTopic + gen.Cid().String()
|
||||
|
||||
go func() {
|
||||
if err := sendHeadNotifs(ctx, ps, topic, chain, nickname); err != nil {
|
||||
log.Error("consensus metrics error", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
sub, err := ps.Subscribe(topic) //nolint
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer sub.Cancel()
|
||||
|
||||
for {
|
||||
if _, err := sub.Next(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type message struct {
|
||||
// TipSet
|
||||
Cids []cid.Cid
|
||||
Blocks []*types.BlockHeader
|
||||
Height abi.ChainEpoch
|
||||
Weight types.BigInt
|
||||
Time uint64
|
||||
Nonce uint64
|
||||
|
||||
// Meta
|
||||
|
||||
NodeName string
|
||||
}
|
||||
|
||||
func sendHeadNotifs(ctx context.Context, ps *pubsub.PubSub, topic string, chain full.ChainAPI, nickname string) error {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
notifs, err := chain.ChainNotify(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// using unix nano time makes very sure we pick a nonce higher than previous restart
|
||||
nonce := uint64(build.Clock.Now().UnixNano())
|
||||
|
||||
for {
|
||||
select {
|
||||
case notif := <-notifs:
|
||||
n := notif[len(notif)-1]
|
||||
|
||||
w, err := chain.ChainTipSetWeight(ctx, n.Val.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := message{
|
||||
Cids: n.Val.Cids(),
|
||||
Blocks: n.Val.Blocks(),
|
||||
Height: n.Val.Height(),
|
||||
Weight: w,
|
||||
NodeName: nickname,
|
||||
Time: uint64(build.Clock.Now().UnixNano() / 1000_000),
|
||||
Nonce: nonce,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint
|
||||
if err := ps.Publish(topic, b); err != nil {
|
||||
return err
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
|
||||
nonce++
|
||||
}
|
||||
}
|
||||
@@ -273,7 +273,7 @@ func LoadStateTree(cst cbor.IpldStore, c cid.Cid) (*StateTree, error) {
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("failed to load state tree: %s", err)
|
||||
return nil, xerrors.Errorf("failed to load state tree: %w", err)
|
||||
return nil, xerrors.Errorf("failed to load state tree %s: %w", c, err)
|
||||
}
|
||||
|
||||
s := &StateTree{
|
||||
|
||||
@@ -0,0 +1,551 @@
|
||||
package stmgr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/filecoin-project/go-state-types/network"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
|
||||
"github.com/filecoin-project/lotus/chain/beacon"
|
||||
"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/extern/sector-storage/ffiwrapper"
|
||||
)
|
||||
|
||||
func GetMinerWorkerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) {
|
||||
state, err := sm.StateTree(st)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("(get sset) failed to load state tree: %w", err)
|
||||
}
|
||||
act, err := state.GetActor(maddr)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("(get sset) failed to load miner actor: %w", err)
|
||||
}
|
||||
mas, err := miner.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
info, err := mas.Info()
|
||||
if err != nil {
|
||||
return address.Undef, xerrors.Errorf("failed to load actor info: %w", err)
|
||||
}
|
||||
|
||||
return vm.ResolveToKeyAddr(state, sm.cs.ActorStore(ctx), info.Worker)
|
||||
}
|
||||
|
||||
func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (power.Claim, power.Claim, bool, error) {
|
||||
return GetPowerRaw(ctx, sm, ts.ParentState(), maddr)
|
||||
}
|
||||
|
||||
func GetPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (power.Claim, power.Claim, bool, error) {
|
||||
act, err := sm.LoadActorRaw(ctx, power.Address, st)
|
||||
if err != nil {
|
||||
return power.Claim{}, power.Claim{}, false, xerrors.Errorf("(get sset) failed to load power actor state: %w", err)
|
||||
}
|
||||
|
||||
pas, err := power.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return power.Claim{}, power.Claim{}, false, err
|
||||
}
|
||||
|
||||
tpow, err := pas.TotalPower()
|
||||
if err != nil {
|
||||
return power.Claim{}, power.Claim{}, false, err
|
||||
}
|
||||
|
||||
var mpow power.Claim
|
||||
var minpow bool
|
||||
if maddr != address.Undef {
|
||||
var found bool
|
||||
mpow, found, err = pas.MinerPower(maddr)
|
||||
if err != nil || !found {
|
||||
return power.Claim{}, tpow, false, err
|
||||
}
|
||||
|
||||
minpow, err = pas.MinerNominalPowerMeetsConsensusMinimum(maddr)
|
||||
if err != nil {
|
||||
return power.Claim{}, power.Claim{}, false, err
|
||||
}
|
||||
}
|
||||
|
||||
return mpow, tpow, minpow, nil
|
||||
}
|
||||
|
||||
func PreCommitInfo(ctx context.Context, sm *StateManager, maddr address.Address, sid abi.SectorNumber, ts *types.TipSet) (*miner.SectorPreCommitOnChainInfo, error) {
|
||||
act, err := sm.LoadActor(ctx, maddr, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err)
|
||||
}
|
||||
|
||||
mas, err := miner.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
return mas.GetPrecommittedSector(sid)
|
||||
}
|
||||
|
||||
func MinerSectorInfo(ctx context.Context, sm *StateManager, maddr address.Address, sid abi.SectorNumber, ts *types.TipSet) (*miner.SectorOnChainInfo, error) {
|
||||
act, err := sm.LoadActor(ctx, maddr, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err)
|
||||
}
|
||||
|
||||
mas, err := miner.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
return mas.GetSector(sid)
|
||||
}
|
||||
|
||||
func GetSectorsForWinningPoSt(ctx context.Context, nv network.Version, pv ffiwrapper.Verifier, sm *StateManager, st cid.Cid, maddr address.Address, rand abi.PoStRandomness) ([]builtin.SectorInfo, error) {
|
||||
act, err := sm.LoadActorRaw(ctx, maddr, st)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load miner actor: %w", err)
|
||||
}
|
||||
|
||||
mas, err := miner.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
var provingSectors bitfield.BitField
|
||||
if nv < network.Version7 {
|
||||
allSectors, err := miner.AllPartSectors(mas, miner.Partition.AllSectors)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get all sectors: %w", err)
|
||||
}
|
||||
|
||||
faultySectors, err := miner.AllPartSectors(mas, miner.Partition.FaultySectors)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get faulty sectors: %w", err)
|
||||
}
|
||||
|
||||
provingSectors, err = bitfield.SubtractBitField(allSectors, faultySectors)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("calc proving sectors: %w", err)
|
||||
}
|
||||
} else {
|
||||
provingSectors, err = miner.AllPartSectors(mas, miner.Partition.ActiveSectors)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get active sectors sectors: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
numProvSect, err := provingSectors.Count()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to count bits: %w", err)
|
||||
}
|
||||
|
||||
// TODO(review): is this right? feels fishy to me
|
||||
if numProvSect == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
info, err := mas.Info()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting miner info: %w", err)
|
||||
}
|
||||
|
||||
mid, err := address.IDFromAddress(maddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting miner ID: %w", err)
|
||||
}
|
||||
|
||||
proofType, err := miner.WinningPoStProofTypeFromWindowPoStProofType(nv, info.WindowPoStProofType)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("determining winning post proof type: %w", err)
|
||||
}
|
||||
|
||||
ids, err := pv.GenerateWinningPoStSectorChallenge(ctx, proofType, abi.ActorID(mid), rand, numProvSect)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("generating winning post challenges: %w", err)
|
||||
}
|
||||
|
||||
iter, err := provingSectors.BitIterator()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("iterating over proving sectors: %w", err)
|
||||
}
|
||||
|
||||
// Select winning sectors by _index_ in the all-sectors bitfield.
|
||||
selectedSectors := bitfield.New()
|
||||
prev := uint64(0)
|
||||
for _, n := range ids {
|
||||
sno, err := iter.Nth(n - prev)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("iterating over proving sectors: %w", err)
|
||||
}
|
||||
selectedSectors.Set(sno)
|
||||
prev = n
|
||||
}
|
||||
|
||||
sectors, err := mas.LoadSectors(&selectedSectors)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading proving sectors: %w", err)
|
||||
}
|
||||
|
||||
out := make([]builtin.SectorInfo, len(sectors))
|
||||
for i, sinfo := range sectors {
|
||||
out[i] = builtin.SectorInfo{
|
||||
SealProof: sinfo.SealProof,
|
||||
SectorNumber: sinfo.SectorNumber,
|
||||
SealedCID: sinfo.SealedCID,
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func GetMinerSlashed(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (bool, error) {
|
||||
act, err := sm.LoadActor(ctx, power.Address, ts)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("failed to load power actor: %w", err)
|
||||
}
|
||||
|
||||
spas, err := power.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("failed to load power actor state: %w", err)
|
||||
}
|
||||
|
||||
_, ok, err := spas.MinerPower(maddr)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("getting miner power: %w", err)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func GetStorageDeal(ctx context.Context, sm *StateManager, dealID abi.DealID, ts *types.TipSet) (*api.MarketDeal, error) {
|
||||
act, err := sm.LoadActor(ctx, market.Address, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load market actor: %w", err)
|
||||
}
|
||||
|
||||
state, err := market.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load market actor state: %w", err)
|
||||
}
|
||||
|
||||
proposals, err := state.Proposals()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proposal, found, err := proposals.Get(dealID)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !found {
|
||||
return nil, xerrors.Errorf(
|
||||
"deal %d not found "+
|
||||
"- deal may not have completed sealing before deal proposal "+
|
||||
"start epoch, or deal may have been slashed",
|
||||
dealID)
|
||||
}
|
||||
|
||||
states, err := state.States()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st, found, err := states.Get(dealID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !found {
|
||||
st = market.EmptyDealState()
|
||||
}
|
||||
|
||||
return &api.MarketDeal{
|
||||
Proposal: *proposal,
|
||||
State: *st,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ListMinerActors(ctx context.Context, sm *StateManager, ts *types.TipSet) ([]address.Address, error) {
|
||||
act, err := sm.LoadActor(ctx, power.Address, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load power actor: %w", err)
|
||||
}
|
||||
|
||||
powState, err := power.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load power actor state: %w", err)
|
||||
}
|
||||
|
||||
return powState.ListAllMiners()
|
||||
}
|
||||
|
||||
func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule, tsk types.TipSetKey, round abi.ChainEpoch, maddr address.Address, pv ffiwrapper.Verifier) (*api.MiningBaseInfo, error) {
|
||||
ts, err := sm.ChainStore().LoadTipSet(tsk)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load tipset for mining base: %w", err)
|
||||
}
|
||||
|
||||
prev, err := sm.ChainStore().GetLatestBeaconEntry(ts)
|
||||
if err != nil {
|
||||
if os.Getenv("LOTUS_IGNORE_DRAND") != "_yes_" {
|
||||
return nil, xerrors.Errorf("failed to get latest beacon entry: %w", err)
|
||||
}
|
||||
|
||||
prev = &types.BeaconEntry{}
|
||||
}
|
||||
|
||||
entries, err := beacon.BeaconEntriesForBlock(ctx, bcs, round, ts.Height(), *prev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rbase := *prev
|
||||
if len(entries) > 0 {
|
||||
rbase = entries[len(entries)-1]
|
||||
}
|
||||
|
||||
lbts, lbst, err := GetLookbackTipSetForRound(ctx, sm, ts, round)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting lookback miner actor state: %w", err)
|
||||
}
|
||||
|
||||
act, err := sm.LoadActorRaw(ctx, maddr, lbst)
|
||||
if xerrors.Is(err, types.ErrActorNotFound) {
|
||||
_, err := sm.LoadActor(ctx, maddr, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("loading miner in current state: %w", err)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load miner actor: %w", err)
|
||||
}
|
||||
|
||||
mas, err := miner.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load miner actor state: %w", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := maddr.MarshalCBOR(buf); err != nil {
|
||||
return nil, xerrors.Errorf("failed to marshal miner address: %w", err)
|
||||
}
|
||||
|
||||
prand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get randomness for winning post: %w", err)
|
||||
}
|
||||
|
||||
nv := sm.GetNtwkVersion(ctx, ts.Height())
|
||||
|
||||
sectors, err := GetSectorsForWinningPoSt(ctx, nv, pv, sm, lbst, maddr, prand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting winning post proving set: %w", err)
|
||||
}
|
||||
|
||||
if len(sectors) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mpow, tpow, _, err := GetPowerRaw(ctx, sm, lbst, maddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get power: %w", err)
|
||||
}
|
||||
|
||||
info, err := mas.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
worker, err := sm.ResolveToKeyAddress(ctx, info.Worker, ts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("resolving worker address: %w", err)
|
||||
}
|
||||
|
||||
// TODO: Not ideal performance...This method reloads miner and power state (already looked up here and in GetPowerRaw)
|
||||
eligible, err := MinerEligibleToMine(ctx, sm, maddr, ts, lbts)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("determining miner eligibility: %w", err)
|
||||
}
|
||||
|
||||
return &api.MiningBaseInfo{
|
||||
MinerPower: mpow.QualityAdjPower,
|
||||
NetworkPower: tpow.QualityAdjPower,
|
||||
Sectors: sectors,
|
||||
WorkerKey: worker,
|
||||
SectorSize: info.SectorSize,
|
||||
PrevBeaconEntry: *prev,
|
||||
BeaconEntries: entries,
|
||||
EligibleForMining: eligible,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func minerHasMinPower(ctx context.Context, sm *StateManager, addr address.Address, ts *types.TipSet) (bool, error) {
|
||||
pact, err := sm.LoadActor(ctx, power.Address, ts)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("loading power actor state: %w", err)
|
||||
}
|
||||
|
||||
ps, err := power.Load(sm.cs.ActorStore(ctx), pact)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ps.MinerNominalPowerMeetsConsensusMinimum(addr)
|
||||
}
|
||||
|
||||
func MinerEligibleToMine(ctx context.Context, sm *StateManager, addr address.Address, baseTs *types.TipSet, lookbackTs *types.TipSet) (bool, error) {
|
||||
hmp, err := minerHasMinPower(ctx, sm, addr, lookbackTs)
|
||||
|
||||
// TODO: We're blurring the lines between a "runtime network version" and a "Lotus upgrade epoch", is that unavoidable?
|
||||
if sm.GetNtwkVersion(ctx, baseTs.Height()) <= network.Version3 {
|
||||
return hmp, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !hmp {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Post actors v2, also check MinerEligibleForElection with base ts
|
||||
|
||||
pact, err := sm.LoadActor(ctx, power.Address, baseTs)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("loading power actor state: %w", err)
|
||||
}
|
||||
|
||||
pstate, err := power.Load(sm.cs.ActorStore(ctx), pact)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
mact, err := sm.LoadActor(ctx, addr, baseTs)
|
||||
if err != nil {
|
||||
return false, xerrors.Errorf("loading miner actor state: %w", err)
|
||||
}
|
||||
|
||||
mstate, err := miner.Load(sm.cs.ActorStore(ctx), mact)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Non-empty power claim.
|
||||
if claim, found, err := pstate.MinerPower(addr); err != nil {
|
||||
return false, err
|
||||
} else if !found {
|
||||
return false, err
|
||||
} else if claim.QualityAdjPower.LessThanEqual(big.Zero()) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// No fee debt.
|
||||
if debt, err := mstate.FeeDebt(); err != nil {
|
||||
return false, err
|
||||
} else if !debt.IsZero() {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// No active consensus faults.
|
||||
if mInfo, err := mstate.Info(); err != nil {
|
||||
return false, err
|
||||
} else if baseTs.Height() <= mInfo.ConsensusFaultElapsed {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) {
|
||||
st, err := sm.ParentState(ts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
act, err := st.GetActor(addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
actState, err := paych.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return act, actState, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) GetMarketState(ctx context.Context, ts *types.TipSet) (market.State, error) {
|
||||
st, err := sm.ParentState(ts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
act, err := st.GetActor(market.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
actState, err := market.Load(sm.cs.ActorStore(ctx), act)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return actState, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MarketBalance, error) {
|
||||
mstate, err := sm.GetMarketState(ctx, ts)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
|
||||
addr, err = sm.LookupID(ctx, addr, ts)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
|
||||
var out api.MarketBalance
|
||||
|
||||
et, err := mstate.EscrowTable()
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
out.Escrow, err = et.Get(addr)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, xerrors.Errorf("getting escrow balance: %w", err)
|
||||
}
|
||||
|
||||
lt, err := mstate.LockedTable()
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, err
|
||||
}
|
||||
out.Locked, err = lt.Get(addr)
|
||||
if err != nil {
|
||||
return api.MarketBalance{}, xerrors.Errorf("getting locked balance: %w", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
var _ StateManagerAPI = (*StateManager)(nil)
|
||||
+52
-30
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/crypto"
|
||||
"github.com/ipfs/go-cid"
|
||||
"go.opencensus.io/trace"
|
||||
@@ -20,41 +21,52 @@ import (
|
||||
|
||||
var ErrExpensiveFork = errors.New("refusing explicit call due to state fork at epoch")
|
||||
|
||||
// Call applies the given message to the given tipset's parent state, at the epoch following the
|
||||
// tipset's parent. In the presence of null blocks, the height at which the message is invoked may
|
||||
// be less than the specified tipset.
|
||||
//
|
||||
// - If no tipset is specified, the first tipset without an expensive migration is used.
|
||||
// - If executing a message at a given tipset would trigger an expensive migration, the call will
|
||||
// fail with ErrExpensiveFork.
|
||||
func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "statemanager.Call")
|
||||
defer span.End()
|
||||
|
||||
var pheight abi.ChainEpoch = -1
|
||||
|
||||
// If no tipset is provided, try to find one without a fork.
|
||||
if ts == nil {
|
||||
ts = sm.cs.GetHeaviestTipSet()
|
||||
|
||||
// Search back till we find a height with no fork, or we reach the beginning.
|
||||
for ts.Height() > 0 && sm.hasExpensiveFork(ctx, ts.Height()-1) {
|
||||
var err error
|
||||
ts, err = sm.cs.GetTipSetFromKey(ts.Parents())
|
||||
for ts.Height() > 0 {
|
||||
pts, err := sm.cs.GetTipSetFromKey(ts.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to find a non-forking epoch: %w", err)
|
||||
}
|
||||
if !sm.hasExpensiveFork(pts.Height()) {
|
||||
pheight = pts.Height()
|
||||
break
|
||||
}
|
||||
ts = pts
|
||||
}
|
||||
} else if ts.Height() > 0 {
|
||||
pts, err := sm.cs.LoadTipSet(ts.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load parent tipset: %w", err)
|
||||
}
|
||||
pheight = pts.Height()
|
||||
if sm.hasExpensiveFork(pheight) {
|
||||
return nil, ErrExpensiveFork
|
||||
}
|
||||
} else {
|
||||
// We can't get the parent tipset in this case.
|
||||
pheight = ts.Height() - 1
|
||||
}
|
||||
|
||||
bstate := ts.ParentState()
|
||||
pts, err := sm.cs.LoadTipSet(ts.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to load parent tipset: %w", err)
|
||||
}
|
||||
pheight := pts.Height()
|
||||
|
||||
// If we have to run an expensive migration, and we're not at genesis,
|
||||
// return an error because the migration will take too long.
|
||||
//
|
||||
// We allow this at height 0 for at-genesis migrations (for testing).
|
||||
if pheight > 0 && sm.hasExpensiveFork(ctx, pheight) {
|
||||
return nil, ErrExpensiveFork
|
||||
}
|
||||
|
||||
// Run the (not expensive) migration.
|
||||
bstate, err = sm.handleStateForks(ctx, bstate, pheight, nil, ts)
|
||||
bstate, err := sm.HandleStateForks(ctx, bstate, pheight, nil, ts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to handle fork: %w", err)
|
||||
}
|
||||
@@ -64,7 +76,8 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
|
||||
Epoch: pheight + 1,
|
||||
Rand: store.NewChainRand(sm.cs, ts.Cids()),
|
||||
Bstore: sm.cs.StateBlockstore(),
|
||||
Syscalls: sm.cs.VMSys(),
|
||||
Actors: sm.tsExec.NewActorRegistry(),
|
||||
Syscalls: sm.Syscalls,
|
||||
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
||||
NtwkVersion: sm.GetNtwkVersion,
|
||||
BaseFee: types.NewInt(0),
|
||||
@@ -140,18 +153,25 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
|
||||
// run the fork logic in `sm.TipSetState`. We need the _current_
|
||||
// height to have no fork, because we'll run it inside this
|
||||
// function before executing the given message.
|
||||
for ts.Height() > 0 && (sm.hasExpensiveFork(ctx, ts.Height()) || sm.hasExpensiveFork(ctx, ts.Height()-1)) {
|
||||
var err error
|
||||
ts, err = sm.cs.GetTipSetFromKey(ts.Parents())
|
||||
for ts.Height() > 0 {
|
||||
pts, err := sm.cs.GetTipSetFromKey(ts.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to find a non-forking epoch: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !sm.hasExpensiveForkBetween(pts.Height(), ts.Height()+1) {
|
||||
break
|
||||
}
|
||||
|
||||
// When we're not at the genesis block, make sure we don't have an expensive migration.
|
||||
if ts.Height() > 0 && (sm.hasExpensiveFork(ctx, ts.Height()) || sm.hasExpensiveFork(ctx, ts.Height()-1)) {
|
||||
return nil, ErrExpensiveFork
|
||||
ts = pts
|
||||
}
|
||||
} else if ts.Height() > 0 {
|
||||
pts, err := sm.cs.GetTipSetFromKey(ts.Parents())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to find a non-forking epoch: %w", err)
|
||||
}
|
||||
if sm.hasExpensiveForkBetween(pts.Height(), ts.Height()+1) {
|
||||
return nil, ErrExpensiveFork
|
||||
}
|
||||
}
|
||||
|
||||
state, _, err := sm.TipSetState(ctx, ts)
|
||||
@@ -159,7 +179,8 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
|
||||
return nil, xerrors.Errorf("computing tipset state: %w", err)
|
||||
}
|
||||
|
||||
state, err = sm.handleStateForks(ctx, state, ts.Height(), nil, ts)
|
||||
// Technically, the tipset we're passing in here should be ts+1, but that may not exist.
|
||||
state, err = sm.HandleStateForks(ctx, state, ts.Height(), nil, ts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to handle fork: %w", err)
|
||||
}
|
||||
@@ -179,7 +200,8 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
|
||||
Epoch: ts.Height() + 1,
|
||||
Rand: r,
|
||||
Bstore: sm.cs.StateBlockstore(),
|
||||
Syscalls: sm.cs.VMSys(),
|
||||
Actors: sm.tsExec.NewActorRegistry(),
|
||||
Syscalls: sm.Syscalls,
|
||||
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
||||
NtwkVersion: sm.GetNtwkVersion,
|
||||
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
||||
@@ -252,7 +274,7 @@ func (sm *StateManager) Replay(ctx context.Context, ts *types.TipSet, mcid cid.C
|
||||
// message to find
|
||||
finder.mcid = mcid
|
||||
|
||||
_, _, err := sm.computeTipSetState(ctx, ts, &finder)
|
||||
_, _, err := sm.tsExec.ExecuteTipSet(ctx, sm, ts, &finder)
|
||||
if err != nil && !xerrors.Is(err, errHaltExecution) {
|
||||
return nil, nil, xerrors.Errorf("unexpected error during execution: %w", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package stmgr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) {
|
||||
ctx, span := trace.StartSpan(ctx, "tipSetState")
|
||||
defer span.End()
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(trace.StringAttribute("tipset", fmt.Sprint(ts.Cids())))
|
||||
}
|
||||
|
||||
ck := cidsToKey(ts.Cids())
|
||||
sm.stlk.Lock()
|
||||
cw, cwok := sm.compWait[ck]
|
||||
if cwok {
|
||||
sm.stlk.Unlock()
|
||||
span.AddAttributes(trace.BoolAttribute("waited", true))
|
||||
select {
|
||||
case <-cw:
|
||||
sm.stlk.Lock()
|
||||
case <-ctx.Done():
|
||||
return cid.Undef, cid.Undef, ctx.Err()
|
||||
}
|
||||
}
|
||||
cached, ok := sm.stCache[ck]
|
||||
if ok {
|
||||
sm.stlk.Unlock()
|
||||
span.AddAttributes(trace.BoolAttribute("cache", true))
|
||||
return cached[0], cached[1], nil
|
||||
}
|
||||
ch := make(chan struct{})
|
||||
sm.compWait[ck] = ch
|
||||
|
||||
defer func() {
|
||||
sm.stlk.Lock()
|
||||
delete(sm.compWait, ck)
|
||||
if st != cid.Undef {
|
||||
sm.stCache[ck] = []cid.Cid{st, rec}
|
||||
}
|
||||
sm.stlk.Unlock()
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
sm.stlk.Unlock()
|
||||
|
||||
if ts.Height() == 0 {
|
||||
// NB: This is here because the process that executes blocks requires that the
|
||||
// block miner reference a valid miner in the state tree. Unless we create some
|
||||
// magical genesis miner, this won't work properly, so we short circuit here
|
||||
// This avoids the question of 'who gets paid the genesis block reward'
|
||||
return ts.Blocks()[0].ParentStateRoot, ts.Blocks()[0].ParentMessageReceipts, nil
|
||||
}
|
||||
|
||||
st, rec, err = sm.tsExec.ExecuteTipSet(ctx, sm, ts, sm.tsExecMonitor)
|
||||
if err != nil {
|
||||
return cid.Undef, cid.Undef, err
|
||||
}
|
||||
|
||||
return st, rec, nil
|
||||
}
|
||||
|
||||
func (sm *StateManager) ExecutionTraceWithMonitor(ctx context.Context, ts *types.TipSet, em ExecMonitor) (cid.Cid, error) {
|
||||
st, _, err := sm.tsExec.ExecuteTipSet(ctx, sm, ts, em)
|
||||
return st, err
|
||||
}
|
||||
|
||||
func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) {
|
||||
var invocTrace []*api.InvocResult
|
||||
st, err := sm.ExecutionTraceWithMonitor(ctx, ts, &InvocationTracer{trace: &invocTrace})
|
||||
if err != nil {
|
||||
return cid.Undef, nil, err
|
||||
}
|
||||
return st, invocTrace, nil
|
||||
}
|
||||
+36
-1110
File diff suppressed because it is too large
Load Diff
+48
-13
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/actors/aerrors"
|
||||
_init "github.com/filecoin-project/lotus/chain/actors/builtin/init"
|
||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
. "github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
@@ -120,8 +121,8 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sm, err := NewStateManagerWithUpgradeSchedule(
|
||||
cg.ChainStore(), UpgradeSchedule{{
|
||||
sm, err := NewStateManager(
|
||||
cg.ChainStore(), filcns.NewTipSetExecutor(), cg.StateManager().VMSys(), UpgradeSchedule{{
|
||||
Network: network.Version1,
|
||||
Height: testForkHeight,
|
||||
Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor,
|
||||
@@ -162,7 +163,7 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
inv := vm.NewActorRegistry()
|
||||
inv := filcns.NewActorRegistry()
|
||||
inv.Register(nil, testActor{})
|
||||
|
||||
sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (*vm.VM, error) {
|
||||
@@ -242,6 +243,19 @@ func TestForkHeightTriggers(t *testing.T) {
|
||||
func TestForkRefuseCall(t *testing.T) {
|
||||
logging.SetAllLoggers(logging.LevelInfo)
|
||||
|
||||
for after := 0; after < 3; after++ {
|
||||
for before := 0; before < 3; before++ {
|
||||
// Makes the lints happy...
|
||||
after := after
|
||||
before := before
|
||||
t.Run(fmt.Sprintf("after:%d,before:%d", after, before), func(t *testing.T) {
|
||||
testForkRefuseCall(t, before, after)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
func testForkRefuseCall(t *testing.T, nullsBefore, nullsAfter int) {
|
||||
ctx := context.TODO()
|
||||
|
||||
cg, err := gen.NewGenerator()
|
||||
@@ -249,20 +263,22 @@ func TestForkRefuseCall(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sm, err := NewStateManagerWithUpgradeSchedule(
|
||||
cg.ChainStore(), UpgradeSchedule{{
|
||||
var migrationCount int
|
||||
sm, err := NewStateManager(
|
||||
cg.ChainStore(), filcns.NewTipSetExecutor(), cg.StateManager().VMSys(), UpgradeSchedule{{
|
||||
Network: network.Version1,
|
||||
Expensive: true,
|
||||
Height: testForkHeight,
|
||||
Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor,
|
||||
root cid.Cid, height abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) {
|
||||
migrationCount++
|
||||
return root, nil
|
||||
}}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
inv := vm.NewActorRegistry()
|
||||
inv := filcns.NewActorRegistry()
|
||||
inv.Register(nil, testActor{})
|
||||
|
||||
sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (*vm.VM, error) {
|
||||
@@ -292,14 +308,20 @@ func TestForkRefuseCall(t *testing.T) {
|
||||
GasFeeCap: types.NewInt(0),
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
ts, err := cg.NextTipSet()
|
||||
nullStart := abi.ChainEpoch(testForkHeight - nullsBefore)
|
||||
nullLength := abi.ChainEpoch(nullsBefore + nullsAfter)
|
||||
|
||||
for i := 0; i < testForkHeight*2; i++ {
|
||||
pts := cg.CurTipset.TipSet()
|
||||
skip := abi.ChainEpoch(0)
|
||||
if pts.Height() == nullStart {
|
||||
skip = nullLength
|
||||
}
|
||||
ts, err := cg.NextTipSetFromMiners(pts, cg.Miners, skip)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pts, err := cg.ChainStore().LoadTipSet(ts.TipSet.TipSet().Parents())
|
||||
require.NoError(t, err)
|
||||
parentHeight := pts.Height()
|
||||
currentHeight := ts.TipSet.TipSet().Height()
|
||||
|
||||
@@ -321,7 +343,20 @@ func TestForkRefuseCall(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.True(t, ret.MsgRct.ExitCode.IsSuccess())
|
||||
}
|
||||
|
||||
// Calls without a tipset should walk back to the last non-fork tipset.
|
||||
// We _verify_ that the migration wasn't run multiple times at the end of the
|
||||
// test.
|
||||
ret, err = sm.CallWithGas(ctx, m, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ret.MsgRct.ExitCode.IsSuccess())
|
||||
|
||||
ret, err = sm.Call(ctx, m, nil)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ret.MsgRct.ExitCode.IsSuccess())
|
||||
}
|
||||
// Make sure we didn't execute the migration multiple times.
|
||||
require.Equal(t, migrationCount, 1)
|
||||
}
|
||||
|
||||
func TestForkPreMigration(t *testing.T) {
|
||||
@@ -364,8 +399,8 @@ func TestForkPreMigration(t *testing.T) {
|
||||
|
||||
counter := make(chan struct{}, 10)
|
||||
|
||||
sm, err := NewStateManagerWithUpgradeSchedule(
|
||||
cg.ChainStore(), UpgradeSchedule{{
|
||||
sm, err := NewStateManager(
|
||||
cg.ChainStore(), filcns.NewTipSetExecutor(), cg.StateManager().VMSys(), UpgradeSchedule{{
|
||||
Network: network.Version1,
|
||||
Height: testForkHeight,
|
||||
Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor,
|
||||
@@ -462,7 +497,7 @@ func TestForkPreMigration(t *testing.T) {
|
||||
require.NoError(t, sm.Stop(context.Background()))
|
||||
}()
|
||||
|
||||
inv := vm.NewActorRegistry()
|
||||
inv := filcns.NewActorRegistry()
|
||||
inv.Register(nil, testActor{})
|
||||
|
||||
sm.SetVMConstructor(func(ctx context.Context, vmopt *vm.VMOpts) (*vm.VM, error) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user