Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49d64f7f7e | ||
|
|
f09f38085e | ||
|
|
04a9822573 | ||
|
|
93814cc85a | ||
|
|
4f30311971 | ||
|
|
cfe5134375 | ||
|
|
219d3c9f18 | ||
|
|
9110776a01 | ||
|
|
625c6951bb | ||
|
|
56235aee90 | ||
|
|
e14c80360d | ||
|
|
98d51d3d80 | ||
|
|
4f45c623a5 | ||
|
|
c96613a499 | ||
|
|
e1e01524e7 | ||
|
|
0926f95979 | ||
|
|
a4decaa188 | ||
|
|
27f7d3a658 | ||
|
|
0e6ff668eb | ||
|
|
fba3ed7b4d | ||
|
|
648130eec5 | ||
|
|
606a58bc6b | ||
|
|
09a645e455 | ||
|
|
d972a47c84 | ||
|
|
494c774303 | ||
|
|
28ac2fce61 | ||
|
|
5095e6f07b | ||
|
|
9a3b2abbab | ||
|
|
4e5b11b1d5 | ||
|
|
7eb2710e67 | ||
|
|
0a36db06ed | ||
|
|
e86f90fe8d | ||
|
|
953effcc43 | ||
|
|
a62e44c4e5 | ||
|
|
84a632787c | ||
|
|
16f0daab90 | ||
|
|
3149390b3b | ||
|
|
0185090cbb | ||
|
|
806f7dd15f | ||
|
|
013ebc617e | ||
|
|
63a60cdd75 | ||
|
|
4bcc71c68f | ||
|
|
7f1374e058 | ||
|
|
3935fd77ac | ||
|
|
9d0c8ae3dd | ||
|
|
4e7fd5b6d7 | ||
|
|
a4ded8c2b9 | ||
|
|
4a75e1e4b4 | ||
|
|
11b11e416b | ||
|
|
1f660c9e04 | ||
|
|
20ffce5c0d | ||
|
|
a7ef61279c | ||
|
|
5ee85dc263 | ||
|
|
93dc33687a | ||
|
|
932ab61c2d | ||
|
|
1d9b1f4aab | ||
|
|
7aed16ce4c | ||
|
|
9f4506bda5 | ||
|
|
fa6f9f4ae0 | ||
|
|
78ff5e270c | ||
|
|
6d1682a27e | ||
|
|
64768f093d | ||
|
|
a24db6c584 | ||
|
|
050a0ec5e4 | ||
|
|
0254e4b365 | ||
|
|
3ebe4ebb3d | ||
|
|
094baf8aa9 | ||
|
|
ff5ac51c54 | ||
|
|
9e130af9e7 | ||
|
|
bed7158212 | ||
|
|
398cbd7f2c | ||
|
|
5aa90b0478 | ||
|
|
86c936759b | ||
|
|
22bdc43023 | ||
|
|
ec2bc0baf6 | ||
|
|
5b860261a0 | ||
|
|
010e32d778 | ||
|
|
1097d29213 | ||
|
|
a6492b1ed6 | ||
|
|
59d2034cbb | ||
|
|
39ef3a701b | ||
|
|
7fdffc0340 | ||
|
|
f2bd680cc5 | ||
|
|
59f554b658 | ||
|
|
0ad38fe38f | ||
|
|
5c4db23da4 | ||
|
|
708a8b6543 | ||
|
|
d8e58e67c6 | ||
|
|
6c7874c883 | ||
|
|
15204a9b90 | ||
|
|
489d5239a5 | ||
|
|
f934ebd429 | ||
|
|
fccdd70524 | ||
|
|
5e40dda3bc | ||
|
|
fb8340acb2 | ||
|
|
20fbd66c90 | ||
|
|
b27ee8241a | ||
|
|
7806a9885a | ||
|
|
6e4889d340 | ||
|
|
679a8e0c24 | ||
|
|
6c3d8ec1de | ||
|
|
29078aaae9 | ||
|
|
8a45148070 | ||
|
|
df635579c4 | ||
|
|
788c7dbf48 | ||
|
|
99ecef89b8 | ||
|
|
51ca460f18 | ||
|
|
e236173417 | ||
|
|
15191ff80f | ||
|
|
efd2129187 | ||
|
|
73da4b9eed | ||
|
|
36ff37ba47 | ||
|
|
a33784c434 | ||
|
|
deb013cecb | ||
|
|
cb411637cc | ||
|
|
be89466367 | ||
|
|
111c7cf688 | ||
|
|
75e0387be2 | ||
|
|
c4e32fac1f | ||
|
|
038e83b647 | ||
|
|
82ef0529d8 | ||
|
|
6abfbbddd5 | ||
|
|
ad889a7240 | ||
|
|
e22ae9f826 | ||
|
|
d3e95d6b77 | ||
|
|
8db262c941 | ||
|
|
793eda986b | ||
|
|
1cd49cec39 | ||
|
|
10c1399474 | ||
|
|
4709e231b8 | ||
|
|
f8a14184c7 | ||
|
|
0155a31d1f | ||
|
|
8848c549a0 | ||
|
|
4919a005d3 | ||
|
|
dabe14889d | ||
|
|
6d1e711b2a | ||
|
|
e19087722f | ||
|
|
d3ac243cea | ||
|
|
348dadf7ae | ||
|
|
cba5ae81ed | ||
|
|
6d328c0a71 | ||
|
|
7a7066896e | ||
|
|
a2c0c10d2a | ||
|
|
c473d3c682 | ||
|
|
bedbdcaf13 | ||
|
|
87e6c0973a | ||
|
|
78876947a4 | ||
|
|
b59f58435c | ||
|
|
4adb83eaae | ||
|
|
4ac9828b0c | ||
|
|
77f6e0d4f8 | ||
|
|
835dbfe001 | ||
|
|
f5eb3fd086 | ||
|
|
da9630c561 | ||
|
|
a89faa72ea | ||
|
|
e042ce21f6 | ||
|
|
d4fef1b5f8 | ||
|
|
d5c4b66ec7 | ||
|
|
0009f7c8dc | ||
|
|
cd2e794d3f | ||
|
|
9bb862d025 | ||
|
|
91bb559bdf | ||
|
|
7fb4851eb9 | ||
|
|
6c2fa9086e | ||
|
|
0d61e0d717 | ||
|
|
d0585ca7bb | ||
|
|
14cda27e9f | ||
|
|
cf177bfea5 | ||
|
|
f84bbd4520 | ||
|
|
2bf8fb8c84 | ||
|
|
a553db4b82 | ||
|
|
121be5c6e8 | ||
|
|
e23e3db5b5 | ||
|
|
babaab20ea | ||
|
|
9d67c16ab9 | ||
|
|
9908031b0b | ||
|
|
6b3282150b | ||
|
|
58cfac9f67 | ||
|
|
340d11be38 | ||
|
|
62b15ecc12 | ||
|
|
6b9d392d0e | ||
|
|
9c68123ede | ||
|
|
7049b6478c | ||
|
|
0e6d34c0c9 | ||
|
|
a01fa11a26 | ||
|
|
4c71182c6b | ||
|
|
7e81081899 | ||
|
|
fb033a3719 | ||
|
|
a03931daab | ||
|
|
6ed1c9e905 | ||
|
|
e38f4219c5 | ||
|
|
30b6f38704 | ||
|
|
04e4bab313 | ||
|
|
dddf7951ef | ||
|
|
b13681df32 | ||
|
|
a21234cd54 | ||
|
|
4f4cebadc7 | ||
|
|
f1c452754f | ||
|
|
b9d67296ef | ||
|
|
483e82e14b | ||
|
|
a0c0d9c98a | ||
|
|
322b33197c |
+67
-1
@@ -113,7 +113,7 @@ jobs:
|
||||
test: &test
|
||||
description: |
|
||||
Run tests with gotestsum.
|
||||
parameters:
|
||||
parameters: &test-params
|
||||
executor:
|
||||
type: executor
|
||||
default: golang
|
||||
@@ -161,6 +161,7 @@ jobs:
|
||||
name: go test
|
||||
environment:
|
||||
LOTUS_TEST_WINDOW_POST: << parameters.winpost-test >>
|
||||
SKIP_CONFORMANCE: "1"
|
||||
command: |
|
||||
mkdir -p /tmp/test-reports/<< parameters.test-suite-name >>
|
||||
mkdir -p /tmp/test-artifacts
|
||||
@@ -191,6 +192,63 @@ jobs:
|
||||
<<: *test
|
||||
test-window-post:
|
||||
<<: *test
|
||||
test-conformance:
|
||||
description: |
|
||||
Run tests using a corpus of interoperable test vectors for Filecoin
|
||||
implementations to test their correctness and compliance with the Filecoin
|
||||
specifications.
|
||||
parameters:
|
||||
<<: *test-params
|
||||
vectors-branch:
|
||||
type: string
|
||||
default: ""
|
||||
description: |
|
||||
Branch on github.com/filecoin-project/test-vectors to checkout and
|
||||
test with. If empty (the default) the commit defined by the git
|
||||
submodule is used.
|
||||
executor: << parameters.executor >>
|
||||
steps:
|
||||
- install-deps
|
||||
- prepare
|
||||
- run:
|
||||
command: make deps lotus
|
||||
no_output_timeout: 30m
|
||||
- download-params
|
||||
- when:
|
||||
condition:
|
||||
not:
|
||||
equal: [ "", << parameters.vectors-branch >> ]
|
||||
steps:
|
||||
- run:
|
||||
name: checkout vectors branch
|
||||
command: |
|
||||
cd extern/test-vectors
|
||||
git fetch
|
||||
git checkout origin/<< parameters.vectors-branch >>
|
||||
- run:
|
||||
name: go get vectors branch
|
||||
command: go get github.com/filecoin-project/test-vectors@<< parameters.vectors-branch >>
|
||||
- go/install-gotestsum:
|
||||
gobin: $HOME/.local/bin
|
||||
version: 0.5.2
|
||||
- run:
|
||||
name: go test
|
||||
environment:
|
||||
SKIP_CONFORMANCE: "0"
|
||||
command: |
|
||||
mkdir -p /tmp/test-reports
|
||||
mkdir -p /tmp/test-artifacts
|
||||
gotestsum \
|
||||
--format pkgname-and-test-fails \
|
||||
--junitfile /tmp/test-reports/junit.xml \
|
||||
-- \
|
||||
-v -coverpkg ./chain/vm/,github.com/filecoin-project/specs-actors/... -coverprofile=/tmp/conformance.out ./conformance/
|
||||
go tool cover -html=/tmp/conformance.out -o /tmp/test-artifacts/conformance-coverage.html
|
||||
no_output_timeout: 30m
|
||||
- store_test_results:
|
||||
path: /tmp/test-reports
|
||||
- store_artifacts:
|
||||
path: /tmp/test-artifacts/conformance-coverage.html
|
||||
|
||||
build-macos:
|
||||
description: build darwin lotus binary
|
||||
@@ -356,6 +414,14 @@ workflows:
|
||||
tags:
|
||||
only:
|
||||
- /^v\d+\.\d+\.\d+$/
|
||||
- test-conformance:
|
||||
test-suite-name: conformance
|
||||
packages: "./conformance"
|
||||
- test-conformance:
|
||||
name: test-conformance-bleeding-edge
|
||||
test-suite-name: conformance-bleeding-edge
|
||||
packages: "./conformance"
|
||||
vectors-branch: master
|
||||
- build-debug
|
||||
- build-all:
|
||||
requires:
|
||||
|
||||
+5
-2
@@ -26,11 +26,14 @@
|
||||
color: 00A4E2
|
||||
description: "Area: Chain/VM"
|
||||
- name: area/chain/sync
|
||||
color: 00A4E2
|
||||
color: 00A4E4
|
||||
description: "Area: Chain/Sync"
|
||||
- name: area/chain/misc
|
||||
color: 00A4E2
|
||||
color: 00A4E6
|
||||
description: "Area: Chain/Misc"
|
||||
- name: area/markets
|
||||
color: 00A4E8
|
||||
description: "Area: Markets"
|
||||
- name: area/sealing/fsm
|
||||
color: 0bb1ed
|
||||
description: "Area: Sealing/FSM"
|
||||
|
||||
@@ -5,3 +5,6 @@
|
||||
[submodule "extern/serialization-vectors"]
|
||||
path = extern/serialization-vectors
|
||||
url = https://github.com/filecoin-project/serialization-vectors
|
||||
[submodule "extern/test-vectors"]
|
||||
path = extern/test-vectors
|
||||
url = https://github.com/filecoin-project/test-vectors.git
|
||||
|
||||
@@ -1,5 +1,82 @@
|
||||
# Lotus changelog
|
||||
|
||||
# 0.5.7 / 2020-08-31
|
||||
|
||||
This patch release includes some bugfixes and enhancements to the sector lifecycle and message pool logic.
|
||||
|
||||
## Changes
|
||||
|
||||
- Rebuild unsealed infos on miner restart (https://github.com/filecoin-project/lotus/pull/3401)
|
||||
- CLI to attach storage paths to workers (https://github.com/filecoin-project/lotus/pull/3405)
|
||||
- Do not select negative performing message chains for inclusion (https://github.com/filecoin-project/lotus/pull/3392)
|
||||
- Remove a redundant error-check (https://github.com/filecoin-project/lotus/pull/3421)
|
||||
- Correctly move unsealed sectors in `FinalizeSectors` (https://github.com/filecoin-project/lotus/pull/3424)
|
||||
- Improve worker selection logic (https://github.com/filecoin-project/lotus/pull/3425)
|
||||
- Don't use context to close bitswap (https://github.com/filecoin-project/lotus/pull/3430)
|
||||
- Correctly estimate gas premium when there is only one message on chain (https://github.com/filecoin-project/lotus/pull/3428)
|
||||
|
||||
# 0.5.6 / 2020-08-29
|
||||
|
||||
Hotfix release that fixes a panic in the sealing scheduler (https://github.com/filecoin-project/lotus/pull/3389).
|
||||
|
||||
# 0.5.5
|
||||
|
||||
This patch release introduces a large number of improvements to the sealing process.
|
||||
It also updates go-fil-markets to
|
||||
[version 0.5.8](https://github.com/filecoin-project/go-fil-markets/releases/tag/v0.5.8),
|
||||
and go-libp2p-pubsub to [v0.3.5](https://github.com/libp2p/go-libp2p-pubsub/releases/tag/v0.3.5).
|
||||
|
||||
#### Downstream upgrades
|
||||
|
||||
- Upgrades markets to v0.5.8 (https://github.com/filecoin-project/lotus/pull/3384)
|
||||
- Upgrades go-libp2p-pubsub to v0.3.5 (https://github.com/filecoin-project/lotus/pull/3305)
|
||||
|
||||
#### Sector sealing
|
||||
|
||||
- The following improvements were introduced in https://github.com/filecoin-project/lotus/pull/3350.
|
||||
|
||||
- Allow `lotus-miner sectors remove` to remove a sector in any state.
|
||||
- Create a separate state in the storage FSM dedicated to submitting the Commit message.
|
||||
- Recovery for when the Deal IDs of deals in a sector get changed in a reorg.
|
||||
- Auto-retry sending Precommit and Commit messages if they run out of gas
|
||||
- Auto-retry sector remove tasks when they fail
|
||||
- Compact worker windows, and allow their tasks to be executed in any order
|
||||
|
||||
- Don't simply skip PoSt for bad sectors (https://github.com/filecoin-project/lotus/pull/3323)
|
||||
|
||||
#### Message Pool
|
||||
|
||||
- Spam Protection: Track required funds for pending messages (https://github.com/filecoin-project/lotus/pull/3313)
|
||||
|
||||
#### Chainwatch
|
||||
|
||||
- Add more power and reward metrics (https://github.com/filecoin-project/lotus/pull/3367)
|
||||
- Fix raciness in sector deal table (https://github.com/filecoin-project/lotus/pull/3275)
|
||||
- Parallelize miner processing (https://github.com/filecoin-project/lotus/pull/3380)
|
||||
- Accept Lotus API and token (https://github.com/filecoin-project/lotus/pull/3337)
|
||||
|
||||
# 0.5.4
|
||||
|
||||
A patch release, containing a few nice bugfixes and improvements:
|
||||
|
||||
- Fix parsing of peer ID in `lotus-miner actor set-peer-id` (@whyrusleeping)
|
||||
- Update dependencies, fixing several bugs (@Stebalien)
|
||||
- Fix remaining linter warnings (@Stebalien)
|
||||
- Use safe string truncation (@Ingar)
|
||||
- Allow tweaking of blocksync message window size (@whyrusleeping)
|
||||
- Add some additional gas stats to metrics (@Kubuxu)
|
||||
- Fix an edge case bug in message selection, add many tests (@vyzo)
|
||||
|
||||
# 0.5.3
|
||||
|
||||
Yet another hotfix release.
|
||||
A lesson for readers, having people who have been awake for 12+ hours review
|
||||
your hotfix PR is not a good idea. Find someone who has enough slept recently
|
||||
enough to give you good code review, otherwise you'll end up quickly bumping
|
||||
versions again.
|
||||
|
||||
- Fixed a bug in the mempool that was introduced in v0.5.2
|
||||
|
||||
# 0.5.2 / 2020-08-24
|
||||
|
||||
This is a hotfix release.
|
||||
|
||||
@@ -40,9 +40,6 @@ All work is tracked via issues. An attempt at keeping an up-to-date view on rema
|
||||
The lotus Filecoin implementation unfolds into the following packages:
|
||||
|
||||
- [This repo](https://github.com/filecoin-project/lotus)
|
||||
- [storage-fsm](https://github.com/filecoin-project/storage-fsm)
|
||||
- [sector-storage](https://github.com/filecoin-project/sector-storage)
|
||||
- [specs-storage](https://github.com/filecoin-project/specs-storage)
|
||||
- [go-fil-markets](https://github.com/filecoin-project/go-fil-markets) which has its own [kanban work tracker available here](https://app.zenhub.com/workspaces/markets-shared-components-5daa144a7046a60001c6e253/board)
|
||||
- [spec-actors](https://github.com/filecoin-project/specs-actors) which has its own [kanban work tracker available here](https://app.zenhub.com/workspaces/actors-5ee6f3aa87591f0016c05685/board)
|
||||
|
||||
|
||||
+4
-2
@@ -243,6 +243,8 @@ type FullNode interface {
|
||||
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error)
|
||||
// ClientListDeals returns information about the deals made by the local client.
|
||||
ClientListDeals(ctx context.Context) ([]DealInfo, error)
|
||||
// ClientGetDealUpdates returns the status of updated deals
|
||||
ClientGetDealUpdates(ctx context.Context) (<-chan DealInfo, error)
|
||||
// ClientHasLocal indicates whether a certain CID is locally stored.
|
||||
ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error)
|
||||
// ClientFindData identifies peers that have a certain file, and returns QueryOffers (one per peer).
|
||||
@@ -323,7 +325,7 @@ type FullNode interface {
|
||||
StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
|
||||
// StateSectorPreCommitInfo returns the PreCommit info for the specified miner's sector
|
||||
StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error)
|
||||
// StateSectorGetInfo returns the on-chain info for the specified miner's sector
|
||||
// StateSectorGetInfo returns the on-chain info for the specified miner's sector. Returns null in case the sector info isn't found
|
||||
// NOTE: returned info.Expiration may not be accurate in some cases, use StateSectorExpiration to get accurate
|
||||
// expiration epoch
|
||||
StateSectorGetInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorOnChainInfo, error)
|
||||
@@ -427,7 +429,7 @@ type FullNode interface {
|
||||
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*paych.SignedVoucher, error)
|
||||
PaychVoucherAdd(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
|
||||
PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error)
|
||||
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher) (cid.Cid, error)
|
||||
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error)
|
||||
}
|
||||
|
||||
type FileRef struct {
|
||||
|
||||
+12
-10
@@ -120,16 +120,18 @@ type SectorLog struct {
|
||||
}
|
||||
|
||||
type SectorInfo struct {
|
||||
SectorID abi.SectorNumber
|
||||
State SectorState
|
||||
CommD *cid.Cid
|
||||
CommR *cid.Cid
|
||||
Proof []byte
|
||||
Deals []abi.DealID
|
||||
Ticket SealTicket
|
||||
Seed SealSeed
|
||||
Retries uint64
|
||||
ToUpgrade bool
|
||||
SectorID abi.SectorNumber
|
||||
State SectorState
|
||||
CommD *cid.Cid
|
||||
CommR *cid.Cid
|
||||
Proof []byte
|
||||
Deals []abi.DealID
|
||||
Ticket SealTicket
|
||||
Seed SealSeed
|
||||
PreCommitMsg *cid.Cid
|
||||
CommitMsg *cid.Cid
|
||||
Retries uint64
|
||||
ToUpgrade bool
|
||||
|
||||
LastErr string
|
||||
|
||||
|
||||
+3
-1
@@ -27,11 +27,13 @@ type WorkerAPI interface {
|
||||
|
||||
storage.Sealer
|
||||
|
||||
MoveStorage(ctx context.Context, sector abi.SectorID) error
|
||||
MoveStorage(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error
|
||||
|
||||
UnsealPiece(context.Context, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize, abi.SealRandomness, cid.Cid) error
|
||||
ReadPiece(context.Context, io.Writer, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) (bool, error)
|
||||
|
||||
StorageAddLocal(ctx context.Context, path string) error
|
||||
|
||||
Fetch(context.Context, abi.SectorID, stores.SectorFileType, stores.PathType, stores.AcquireMode) error
|
||||
|
||||
Closing(context.Context) (<-chan struct{}, error)
|
||||
|
||||
+16
-6
@@ -139,6 +139,7 @@ type FullNodeStruct struct {
|
||||
ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
|
||||
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
|
||||
ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
|
||||
ClientGetDealUpdates func(ctx context.Context) (<-chan api.DealInfo, error) `perm:"read"`
|
||||
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error `perm:"admin"`
|
||||
ClientRetrieveWithEvents func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) (<-chan marketevents.RetrievalEvent, error) `perm:"admin"`
|
||||
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
|
||||
@@ -214,7 +215,7 @@ type FullNodeStruct struct {
|
||||
PaychVoucherAdd func(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
|
||||
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*paych.SignedVoucher, error) `perm:"sign"`
|
||||
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
|
||||
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher) (cid.Cid, error) `perm:"sign"`
|
||||
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) `perm:"sign"`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +317,8 @@ type WorkerStruct struct {
|
||||
FinalizeSector func(context.Context, abi.SectorID, []storage.Range) error `perm:"admin"`
|
||||
ReleaseUnsealed func(ctx context.Context, sector abi.SectorID, safeToFree []storage.Range) error `perm:"admin"`
|
||||
Remove func(ctx context.Context, sector abi.SectorID) error `perm:"admin"`
|
||||
MoveStorage func(ctx context.Context, sector abi.SectorID) error `perm:"admin"`
|
||||
MoveStorage func(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error `perm:"admin"`
|
||||
StorageAddLocal func(ctx context.Context, path string) error `perm:"admin"`
|
||||
|
||||
UnsealPiece func(context.Context, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize, abi.SealRandomness, cid.Cid) error `perm:"admin"`
|
||||
ReadPiece func(context.Context, io.Writer, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) (bool, error) `perm:"admin"`
|
||||
@@ -433,6 +435,10 @@ func (c *FullNodeStruct) ClientListDeals(ctx context.Context) ([]api.DealInfo, e
|
||||
return c.Internal.ClientListDeals(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientGetDealUpdates(ctx context.Context) (<-chan api.DealInfo, error) {
|
||||
return c.Internal.ClientGetDealUpdates(ctx)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error {
|
||||
return c.Internal.ClientRetrieve(ctx, order, ref)
|
||||
}
|
||||
@@ -920,8 +926,8 @@ func (c *FullNodeStruct) PaychNewPayment(ctx context.Context, from, to address.A
|
||||
return c.Internal.PaychNewPayment(ctx, from, to, vouchers)
|
||||
}
|
||||
|
||||
func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (cid.Cid, error) {
|
||||
return c.Internal.PaychVoucherSubmit(ctx, ch, sv)
|
||||
func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, secret []byte, proof []byte) (cid.Cid, error) {
|
||||
return c.Internal.PaychVoucherSubmit(ctx, ch, sv, secret, proof)
|
||||
}
|
||||
|
||||
// StorageMinerStruct
|
||||
@@ -1214,8 +1220,12 @@ func (w *WorkerStruct) Remove(ctx context.Context, sector abi.SectorID) error {
|
||||
return w.Internal.Remove(ctx, sector)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) MoveStorage(ctx context.Context, sector abi.SectorID) error {
|
||||
return w.Internal.MoveStorage(ctx, sector)
|
||||
func (w *WorkerStruct) MoveStorage(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error {
|
||||
return w.Internal.MoveStorage(ctx, sector, types)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) StorageAddLocal(ctx context.Context, path string) error {
|
||||
return w.Internal.StorageAddLocal(ctx, path)
|
||||
}
|
||||
|
||||
func (w *WorkerStruct) UnsealPiece(ctx context.Context, id abi.SectorID, index storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize, randomness abi.SealRandomness, c cid.Cid) error {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
@@ -14,9 +15,9 @@ import (
|
||||
)
|
||||
|
||||
// NewCommonRPC creates a new http jsonrpc client.
|
||||
func NewCommonRPC(addr string, requestHeader http.Header) (api.Common, jsonrpc.ClientCloser, error) {
|
||||
func NewCommonRPC(ctx context.Context, addr string, requestHeader http.Header) (api.Common, jsonrpc.ClientCloser, error) {
|
||||
var res apistruct.CommonStruct
|
||||
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
|
||||
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
|
||||
[]interface{}{
|
||||
&res.Internal,
|
||||
},
|
||||
@@ -27,9 +28,9 @@ func NewCommonRPC(addr string, requestHeader http.Header) (api.Common, jsonrpc.C
|
||||
}
|
||||
|
||||
// NewFullNodeRPC creates a new http jsonrpc client.
|
||||
func NewFullNodeRPC(addr string, requestHeader http.Header) (api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
func NewFullNodeRPC(ctx context.Context, addr string, requestHeader http.Header) (api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
var res apistruct.FullNodeStruct
|
||||
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
|
||||
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
|
||||
[]interface{}{
|
||||
&res.CommonStruct.Internal,
|
||||
&res.Internal,
|
||||
@@ -39,9 +40,9 @@ func NewFullNodeRPC(addr string, requestHeader http.Header) (api.FullNode, jsonr
|
||||
}
|
||||
|
||||
// NewStorageMinerRPC creates a new http jsonrpc client for miner
|
||||
func NewStorageMinerRPC(addr string, requestHeader http.Header, opts ...jsonrpc.Option) (api.StorageMiner, jsonrpc.ClientCloser, error) {
|
||||
func NewStorageMinerRPC(ctx context.Context, addr string, requestHeader http.Header, opts ...jsonrpc.Option) (api.StorageMiner, jsonrpc.ClientCloser, error) {
|
||||
var res apistruct.StorageMinerStruct
|
||||
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
|
||||
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
|
||||
[]interface{}{
|
||||
&res.CommonStruct.Internal,
|
||||
&res.Internal,
|
||||
@@ -53,7 +54,7 @@ func NewStorageMinerRPC(addr string, requestHeader http.Header, opts ...jsonrpc.
|
||||
return &res, closer, err
|
||||
}
|
||||
|
||||
func NewWorkerRPC(addr string, requestHeader http.Header) (api.WorkerAPI, jsonrpc.ClientCloser, error) {
|
||||
func NewWorkerRPC(ctx context.Context, addr string, requestHeader http.Header) (api.WorkerAPI, jsonrpc.ClientCloser, error) {
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -69,7 +70,7 @@ func NewWorkerRPC(addr string, requestHeader http.Header) (api.WorkerAPI, jsonrp
|
||||
u.Path = path.Join(u.Path, "../streams/v0/push")
|
||||
|
||||
var res apistruct.WorkerStruct
|
||||
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
|
||||
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
|
||||
[]interface{}{
|
||||
&res.Internal,
|
||||
},
|
||||
|
||||
@@ -41,7 +41,7 @@ func (bm *BlockMiner) MineBlocks() {
|
||||
nulls := atomic.SwapInt64(&bm.nulls, 0)
|
||||
if err := bm.miner.MineOne(bm.ctx, miner.MineReq{
|
||||
InjectNulls: abi.ChainEpoch(nulls),
|
||||
Done: func(bool, error) {},
|
||||
Done: func(bool, abi.ChainEpoch, error) {},
|
||||
}); err != nil {
|
||||
bm.t.Error(err)
|
||||
}
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
|
||||
"github.com/filecoin-project/lotus/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
dag "github.com/ipfs/go-merkledag"
|
||||
dstest "github.com/ipfs/go-merkledag/test"
|
||||
unixfile "github.com/ipfs/go-unixfs/file"
|
||||
@@ -35,7 +36,7 @@ import (
|
||||
|
||||
var MineNext = miner.MineReq{
|
||||
InjectNulls: 0,
|
||||
Done: func(bool, error) {},
|
||||
Done: func(bool, abi.ChainEpoch, error) {},
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
+7
-9
@@ -31,22 +31,20 @@ func (ts *testSuite) testMining(t *testing.T) {
|
||||
newHeads, err := api.ChainNotify(ctx)
|
||||
require.NoError(t, err)
|
||||
initHead := (<-newHeads)[0]
|
||||
if initHead.Val.Height() != 2 {
|
||||
<-newHeads
|
||||
}
|
||||
baseHeight := initHead.Val.Height()
|
||||
|
||||
h1, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(2), h1.Height())
|
||||
require.Equal(t, int64(h1.Height()), int64(baseHeight))
|
||||
|
||||
err = sn[0].MineOne(ctx, MineNext)
|
||||
MineUntilBlock(ctx, t, apis[0], sn[0], nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
|
||||
h2, err := api.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(3), h2.Height())
|
||||
require.Greater(t, int64(h2.Height()), int64(h1.Height()))
|
||||
}
|
||||
|
||||
func (ts *testSuite) testMiningReal(t *testing.T) {
|
||||
@@ -70,7 +68,7 @@ func (ts *testSuite) testMiningReal(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(2), h1.Height())
|
||||
|
||||
err = sn[0].MineOne(ctx, MineNext)
|
||||
MineUntilBlock(ctx, t, apis[0], sn[0], nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
@@ -79,7 +77,7 @@ func (ts *testSuite) testMiningReal(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, abi.ChainEpoch(3), h2.Height())
|
||||
|
||||
err = sn[0].MineOne(ctx, MineNext)
|
||||
MineUntilBlock(ctx, t, apis[0], sn[0], nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
<-newHeads
|
||||
@@ -144,7 +142,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo
|
||||
complChan := minedTwo
|
||||
for atomic.LoadInt32(&mine) != 0 {
|
||||
wait := make(chan int)
|
||||
mdone := func(mined bool, err error) {
|
||||
mdone := func(mined bool, _ abi.ChainEpoch, err error) {
|
||||
n := 0
|
||||
if mined {
|
||||
n = 1
|
||||
|
||||
@@ -3,11 +3,13 @@ package test
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/miner"
|
||||
)
|
||||
|
||||
func SendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address.Address, amount abi.TokenAmount) {
|
||||
@@ -34,3 +36,51 @@ func SendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address.
|
||||
t.Fatal("did not successfully send money")
|
||||
}
|
||||
}
|
||||
|
||||
func MineUntilBlock(ctx context.Context, t *testing.T, fn TestNode, sn TestStorageNode, cb func(abi.ChainEpoch)) {
|
||||
for i := 0; i < 1000; i++ {
|
||||
var success bool
|
||||
var err error
|
||||
var epoch abi.ChainEpoch
|
||||
wait := make(chan struct{})
|
||||
mineErr := sn.MineOne(ctx, miner.MineReq{
|
||||
Done: func(win bool, ep abi.ChainEpoch, e error) {
|
||||
success = win
|
||||
err = e
|
||||
epoch = ep
|
||||
wait <- struct{}{}
|
||||
},
|
||||
})
|
||||
if mineErr != nil {
|
||||
t.Fatal(mineErr)
|
||||
}
|
||||
<-wait
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if success {
|
||||
// Wait until it shows up on the given full nodes ChainHead
|
||||
nloops := 50
|
||||
for i := 0; i < nloops; i++ {
|
||||
ts, err := fn.ChainHead(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ts.Height() == epoch {
|
||||
break
|
||||
}
|
||||
if i == nloops-1 {
|
||||
t.Fatal("block never managed to sync to node")
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
|
||||
if cb != nil {
|
||||
cb(epoch)
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Log("did not mine block, trying again", i)
|
||||
}
|
||||
t.Fatal("failed to mine 1000 times in a row...")
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSect
|
||||
defer close(done)
|
||||
for mine {
|
||||
build.Clock.Sleep(blocktime)
|
||||
if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, error) {
|
||||
if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) {
|
||||
|
||||
}}); err != nil {
|
||||
t.Error(err)
|
||||
@@ -216,6 +216,10 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
|
||||
sn, err := parts[0].Sectors.First()
|
||||
require.NoError(t, err)
|
||||
|
||||
all, err := parts[0].Sectors.All(2)
|
||||
require.NoError(t, err)
|
||||
fmt.Println("the sectors", all)
|
||||
|
||||
s = abi.SectorID{
|
||||
Miner: abi.ActorID(mid),
|
||||
Number: abi.SectorNumber(sn),
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ func buildType() string {
|
||||
}
|
||||
|
||||
// BuildVersion is the local build version, set by build system
|
||||
const BuildVersion = "0.5.2"
|
||||
const BuildVersion = "0.5.7"
|
||||
|
||||
func UserVersion() string {
|
||||
return BuildVersion + buildType() + CurrentCommit
|
||||
@@ -53,7 +53,7 @@ func (ve Version) EqMajorMinor(v2 Version) bool {
|
||||
}
|
||||
|
||||
// APIVersion is a semver version of the rpc api exposed
|
||||
var APIVersion Version = newVer(0, 12, 0)
|
||||
var APIVersion Version = newVer(0, 14, 0)
|
||||
|
||||
//nolint:varcheck,deadcode
|
||||
const (
|
||||
|
||||
@@ -37,6 +37,10 @@ func ValidateBlockValues(b RandomBeacon, h *types.BlockHeader, prevEntry types.B
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(h.BeaconEntries) == 0 {
|
||||
return xerrors.Errorf("expected to have beacon entries in this block, but didn't find any")
|
||||
}
|
||||
|
||||
last := h.BeaconEntries[len(h.BeaconEntries)-1]
|
||||
if last.Round != maxRound {
|
||||
return xerrors.Errorf("expected final beacon entry in block to be at round %d, got %d", maxRound, last.Round)
|
||||
|
||||
@@ -308,6 +308,12 @@ func (client *BlockSync) GetChainMessages(
|
||||
length uint64,
|
||||
) ([]*CompactedMessages, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "GetChainMessages")
|
||||
if span.IsRecordingEvents() {
|
||||
span.AddAttributes(
|
||||
trace.StringAttribute("tipset", fmt.Sprint(head.Cids())),
|
||||
trace.Int64Attribute("count", int64(length)),
|
||||
)
|
||||
}
|
||||
defer span.End()
|
||||
|
||||
req := &Request{
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
stdbig "math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -38,7 +39,7 @@ import (
|
||||
|
||||
var log = logging.Logger("messagepool")
|
||||
|
||||
const futureDebug = false
|
||||
var futureDebug = false
|
||||
|
||||
var rbfNumBig = types.NewInt(uint64((ReplaceByFeeRatioDefault - 1) * RbfDenom))
|
||||
var rbfDenomBig = types.NewInt(RbfDenom)
|
||||
@@ -47,6 +48,10 @@ const RbfDenom = 256
|
||||
|
||||
var RepublishInterval = pubsub.TimeCacheDuration + time.Duration(5*build.BlockDelaySecs+build.PropagationDelaySecs)*time.Second
|
||||
|
||||
var minimumBaseFee = types.NewInt(uint64(build.MinimumBaseFee))
|
||||
|
||||
var MaxActorPendingMessages = 1000
|
||||
|
||||
var (
|
||||
ErrMessageTooBig = errors.New("message too big")
|
||||
|
||||
@@ -54,12 +59,15 @@ var (
|
||||
|
||||
ErrNonceTooLow = errors.New("message nonce too low")
|
||||
|
||||
ErrGasFeeCapTooLow = errors.New("gas fee cap too low")
|
||||
|
||||
ErrNotEnoughFunds = errors.New("not enough funds to execute transaction")
|
||||
|
||||
ErrInvalidToAddr = errors.New("message had invalid to address")
|
||||
|
||||
ErrBroadcastAnyway = errors.New("broadcasting message despite validation fail")
|
||||
ErrRBFTooLowPremium = errors.New("replace by fee has too low GasPremium")
|
||||
ErrSoftValidationFailure = errors.New("validation failure")
|
||||
ErrRBFTooLowPremium = errors.New("replace by fee has too low GasPremium")
|
||||
ErrTooManyPendingMessages = errors.New("too many pending messages for actor")
|
||||
|
||||
ErrTryAgain = errors.New("state inconsistency while pushing message; please try again")
|
||||
)
|
||||
@@ -118,17 +126,19 @@ type MessagePool struct {
|
||||
}
|
||||
|
||||
type msgSet struct {
|
||||
msgs map[uint64]*types.SignedMessage
|
||||
nextNonce uint64
|
||||
msgs map[uint64]*types.SignedMessage
|
||||
nextNonce uint64
|
||||
requiredFunds *stdbig.Int
|
||||
}
|
||||
|
||||
func newMsgSet() *msgSet {
|
||||
return &msgSet{
|
||||
msgs: make(map[uint64]*types.SignedMessage),
|
||||
msgs: make(map[uint64]*types.SignedMessage),
|
||||
requiredFunds: stdbig.NewInt(0),
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool) (bool, error) {
|
||||
func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool, limit bool) (bool, error) {
|
||||
if len(ms.msgs) == 0 || m.Message.Nonce >= ms.nextNonce {
|
||||
ms.nextNonce = m.Message.Nonce + 1
|
||||
}
|
||||
@@ -150,21 +160,51 @@ func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool) (bool, error) {
|
||||
ErrRBFTooLowPremium)
|
||||
}
|
||||
}
|
||||
|
||||
ms.requiredFunds.Sub(ms.requiredFunds, exms.Message.RequiredFunds().Int)
|
||||
//ms.requiredFunds.Sub(ms.requiredFunds, exms.Message.Value.Int)
|
||||
}
|
||||
|
||||
if !has && limit && len(ms.msgs) > MaxActorPendingMessages {
|
||||
log.Errorf("too many pending messages from actor %s", m.Message.From)
|
||||
return false, ErrTooManyPendingMessages
|
||||
}
|
||||
|
||||
ms.msgs[m.Message.Nonce] = m
|
||||
ms.requiredFunds.Add(ms.requiredFunds, m.Message.RequiredFunds().Int)
|
||||
//ms.requiredFunds.Add(ms.requiredFunds, m.Message.Value.Int)
|
||||
|
||||
return !has, nil
|
||||
}
|
||||
|
||||
func (ms *msgSet) rm(nonce uint64) {
|
||||
m, has := ms.msgs[nonce]
|
||||
if has {
|
||||
ms.requiredFunds.Sub(ms.requiredFunds, m.Message.RequiredFunds().Int)
|
||||
//ms.requiredFunds.Sub(ms.requiredFunds, m.Message.Value.Int)
|
||||
delete(ms.msgs, nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *msgSet) getRequiredFunds(nonce uint64) types.BigInt {
|
||||
requiredFunds := new(stdbig.Int).Set(ms.requiredFunds)
|
||||
|
||||
m, has := ms.msgs[nonce]
|
||||
if has {
|
||||
requiredFunds.Sub(requiredFunds, m.Message.RequiredFunds().Int)
|
||||
//requiredFunds.Sub(requiredFunds, m.Message.Value.Int)
|
||||
}
|
||||
|
||||
return types.BigInt{Int: requiredFunds}
|
||||
}
|
||||
|
||||
func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*MessagePool, error) {
|
||||
cache, _ := lru.New2Q(build.BlsSignatureCacheSize)
|
||||
verifcache, _ := lru.New2Q(build.VerifSigCacheSize)
|
||||
|
||||
cfg, err := loadConfig(ds)
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error loading mpool config: %w", err)
|
||||
}
|
||||
return nil, xerrors.Errorf("error loading mpool config: %w", err)
|
||||
}
|
||||
|
||||
mp := &MessagePool{
|
||||
@@ -190,12 +230,7 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
|
||||
// enable initial prunes
|
||||
mp.pruneCooldown <- struct{}{}
|
||||
|
||||
if err := mp.loadLocal(); err != nil {
|
||||
log.Errorf("loading local messages: %+v", err)
|
||||
}
|
||||
|
||||
go mp.runLoop()
|
||||
|
||||
// load the current tipset and subscribe to head changes _before_ loading local messages
|
||||
mp.curTs = api.SubscribeHeadChanges(func(rev, app []*types.TipSet) error {
|
||||
err := mp.HeadChange(rev, app)
|
||||
if err != nil {
|
||||
@@ -204,6 +239,12 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
|
||||
return err
|
||||
})
|
||||
|
||||
if err := mp.loadLocal(); err != nil {
|
||||
log.Errorf("loading local messages: %+v", err)
|
||||
}
|
||||
|
||||
go mp.runLoop()
|
||||
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
@@ -256,7 +297,7 @@ func (mp *MessagePool) addLocal(m *types.SignedMessage, msgb []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *MessagePool) verifyMsgBeforePush(m *types.SignedMessage, epoch abi.ChainEpoch) error {
|
||||
func (mp *MessagePool) verifyMsgBeforeAdd(m *types.SignedMessage, epoch abi.ChainEpoch) error {
|
||||
minGas := vm.PricelistByEpoch(epoch).OnChainMessage(m.ChainLength())
|
||||
|
||||
if err := m.VMMessage().ValidForBlockInclusion(minGas.Total()); err != nil {
|
||||
@@ -277,25 +318,12 @@ func (mp *MessagePool) Push(m *types.SignedMessage) (cid.Cid, error) {
|
||||
<-mp.addSema
|
||||
}()
|
||||
|
||||
mp.curTsLk.Lock()
|
||||
curTs := mp.curTs
|
||||
epoch := curTs.Height()
|
||||
mp.curTsLk.Unlock()
|
||||
if err := mp.verifyMsgBeforePush(m, epoch); err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
msgb, err := m.Serialize()
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
mp.curTsLk.Lock()
|
||||
if mp.curTs != curTs {
|
||||
mp.curTsLk.Unlock()
|
||||
return cid.Undef, ErrTryAgain
|
||||
}
|
||||
|
||||
if err := mp.addTs(m, mp.curTs); err != nil {
|
||||
mp.curTsLk.Unlock()
|
||||
return cid.Undef, err
|
||||
@@ -318,7 +346,7 @@ func (mp *MessagePool) checkMessage(m *types.SignedMessage) error {
|
||||
return xerrors.Errorf("mpool message too large (%dB): %w", m.Size(), ErrMessageTooBig)
|
||||
}
|
||||
|
||||
// Perform syntaxtic validation, minGas=0 as we check if correctly in select messages
|
||||
// Perform syntactic validation, minGas=0 as we check the actual mingas before we add it
|
||||
if err := m.Message.ValidForBlockInclusion(0); err != nil {
|
||||
return xerrors.Errorf("message not valid for block inclusion: %w", err)
|
||||
}
|
||||
@@ -331,8 +359,12 @@ func (mp *MessagePool) checkMessage(m *types.SignedMessage) error {
|
||||
return ErrMessageValueTooHigh
|
||||
}
|
||||
|
||||
if m.Message.GasFeeCap.LessThan(minimumBaseFee) {
|
||||
return ErrGasFeeCapTooLow
|
||||
}
|
||||
|
||||
if err := mp.VerifyMsgSig(m); err != nil {
|
||||
log.Warnf("mpooladd signature verification failed: %s", err)
|
||||
log.Warnf("signature verification failed: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -392,48 +424,71 @@ func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *MessagePool) checkBalance(m *types.SignedMessage, curTs *types.TipSet) error {
|
||||
balance, err := mp.getStateBalance(m.Message.From, curTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to check sender balance: %s: %w", err, ErrSoftValidationFailure)
|
||||
}
|
||||
|
||||
requiredFunds := m.Message.RequiredFunds()
|
||||
if balance.LessThan(requiredFunds) {
|
||||
return xerrors.Errorf("not enough funds (required: %s, balance: %s): %w", types.FIL(requiredFunds), types.FIL(balance), ErrNotEnoughFunds)
|
||||
}
|
||||
|
||||
// add Value for soft failure check
|
||||
//requiredFunds = types.BigAdd(requiredFunds, m.Message.Value)
|
||||
|
||||
mset, ok := mp.pending[m.Message.From]
|
||||
if ok {
|
||||
requiredFunds = types.BigAdd(requiredFunds, mset.getRequiredFunds(m.Message.Nonce))
|
||||
}
|
||||
|
||||
if balance.LessThan(requiredFunds) {
|
||||
// Note: we fail here for ErrSoftValidationFailure to signal a soft failure because we might
|
||||
// be out of sync.
|
||||
return xerrors.Errorf("not enough funds including pending messages (required: %s, balance: %s): %w", types.FIL(requiredFunds), types.FIL(balance), ErrSoftValidationFailure)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error {
|
||||
snonce, err := mp.getStateNonce(m.Message.From, curTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrBroadcastAnyway)
|
||||
return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrSoftValidationFailure)
|
||||
}
|
||||
|
||||
if snonce > m.Message.Nonce {
|
||||
return xerrors.Errorf("minimum expected nonce is %d: %w", snonce, ErrNonceTooLow)
|
||||
}
|
||||
|
||||
balance, err := mp.getStateBalance(m.Message.From, curTs)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to check sender balance: %s: %w", err, ErrBroadcastAnyway)
|
||||
}
|
||||
|
||||
if balance.LessThan(m.Message.RequiredFunds()) {
|
||||
return xerrors.Errorf("not enough funds (required: %s, balance: %s): %w", types.FIL(m.Message.RequiredFunds()), types.FIL(balance), ErrNotEnoughFunds)
|
||||
}
|
||||
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
|
||||
return mp.addLocked(m)
|
||||
if err := mp.verifyMsgBeforeAdd(m, curTs.Height()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mp.checkBalance(m, curTs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mp.addLocked(m, true)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addSkipChecks(m *types.SignedMessage) error {
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
|
||||
return mp.addLocked(m)
|
||||
return mp.addLocked(m, false)
|
||||
}
|
||||
|
||||
func (mp *MessagePool) addLocked(m *types.SignedMessage) error {
|
||||
func (mp *MessagePool) addLocked(m *types.SignedMessage, limit bool) error {
|
||||
log.Debugf("mpooladd: %s %d", m.Message.From, m.Message.Nonce)
|
||||
if m.Signature.Type == crypto.SigTypeBLS {
|
||||
mp.blsSigCache.Add(m.Cid(), m.Signature)
|
||||
}
|
||||
|
||||
if m.Message.GasLimit > build.BlockGasLimit {
|
||||
return xerrors.Errorf("given message has too high of a gas limit")
|
||||
}
|
||||
|
||||
if _, err := mp.api.PutMessage(m); err != nil {
|
||||
log.Warnf("mpooladd cs.PutMessage failed: %s", err)
|
||||
return err
|
||||
@@ -450,7 +505,7 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage) error {
|
||||
mp.pending[m.Message.From] = mset
|
||||
}
|
||||
|
||||
incr, err := mset.add(m, mp)
|
||||
incr, err := mset.add(m, mp, limit)
|
||||
if err != nil {
|
||||
log.Info(err)
|
||||
return err
|
||||
@@ -561,6 +616,16 @@ func (mp *MessagePool) PushWithNonce(ctx context.Context, addr address.Address,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = mp.checkMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgb, err := msg.Serialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// reacquire the locks and check state for consistency
|
||||
mp.curTsLk.Lock()
|
||||
defer mp.curTsLk.Unlock()
|
||||
@@ -581,16 +646,15 @@ func (mp *MessagePool) PushWithNonce(ctx context.Context, addr address.Address,
|
||||
return nil, ErrTryAgain
|
||||
}
|
||||
|
||||
if err := mp.verifyMsgBeforePush(msg, mp.curTs.Height()); err != nil {
|
||||
if err := mp.verifyMsgBeforeAdd(msg, curTs.Height()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgb, err := msg.Serialize()
|
||||
if err != nil {
|
||||
if err := mp.checkBalance(msg, curTs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := mp.addLocked(msg); err != nil {
|
||||
if err := mp.addLocked(msg, true); err != nil {
|
||||
return nil, xerrors.Errorf("add locked failed: %w", err)
|
||||
}
|
||||
if err := mp.addLocal(msg, msgb); err != nil {
|
||||
@@ -624,7 +688,7 @@ func (mp *MessagePool) remove(from address.Address, nonce uint64) {
|
||||
|
||||
// NB: This deletes any message with the given nonce. This makes sense
|
||||
// as two messages with the same sender cannot have the same nonce
|
||||
delete(mset.msgs, nonce)
|
||||
mset.rm(nonce)
|
||||
|
||||
if len(mset.msgs) == 0 {
|
||||
delete(mp.pending, from)
|
||||
@@ -650,6 +714,10 @@ func (mp *MessagePool) Pending() ([]*types.SignedMessage, *types.TipSet) {
|
||||
mp.lk.Lock()
|
||||
defer mp.lk.Unlock()
|
||||
|
||||
return mp.allPending()
|
||||
}
|
||||
|
||||
func (mp *MessagePool) allPending() ([]*types.SignedMessage, *types.TipSet) {
|
||||
out := make([]*types.SignedMessage, 0)
|
||||
for a := range mp.pending {
|
||||
out = append(out, mp.pendingFor(a)...)
|
||||
@@ -657,6 +725,7 @@ func (mp *MessagePool) Pending() ([]*types.SignedMessage, *types.TipSet) {
|
||||
|
||||
return out, mp.curTs
|
||||
}
|
||||
|
||||
func (mp *MessagePool) PendingFor(a address.Address) ([]*types.SignedMessage, *types.TipSet) {
|
||||
mp.curTsLk.Lock()
|
||||
defer mp.curTsLk.Unlock()
|
||||
@@ -789,7 +858,9 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
|
||||
}
|
||||
|
||||
if len(revert) > 0 && futureDebug {
|
||||
msgs, ts := mp.Pending()
|
||||
mp.lk.Lock()
|
||||
msgs, ts := mp.allPending()
|
||||
mp.lk.Unlock()
|
||||
|
||||
buckets := map[address.Address]*statBucket{}
|
||||
|
||||
@@ -900,8 +971,6 @@ func (mp *MessagePool) runHeadChange(from *types.TipSet, to *types.TipSet, rmsgs
|
||||
}
|
||||
|
||||
for _, ts := range apply {
|
||||
mp.curTs = ts
|
||||
|
||||
for _, b := range ts.Blocks() {
|
||||
bmsgs, smsgs, err := mp.api.MessagesForBlock(b)
|
||||
if err != nil {
|
||||
@@ -974,6 +1043,7 @@ func (mp *MessagePool) Updates(ctx context.Context) (<-chan api.MpoolUpdate, err
|
||||
|
||||
go func() {
|
||||
defer mp.changes.Unsub(sub, localUpdates)
|
||||
defer close(out)
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -982,9 +1052,13 @@ func (mp *MessagePool) Updates(ctx context.Context) (<-chan api.MpoolUpdate, err
|
||||
case out <- u.(api.MpoolUpdate):
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-mp.closer:
|
||||
return
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-mp.closer:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"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"
|
||||
@@ -31,6 +32,8 @@ type testMpoolAPI struct {
|
||||
balance map[address.Address]types.BigInt
|
||||
|
||||
tipsets []*types.TipSet
|
||||
|
||||
published int
|
||||
}
|
||||
|
||||
func newTestMpoolAPI() *testMpoolAPI {
|
||||
@@ -90,10 +93,16 @@ func (tma *testMpoolAPI) PutMessage(m types.ChainMsg) (cid.Cid, error) {
|
||||
}
|
||||
|
||||
func (tma *testMpoolAPI) PubSubPublish(string, []byte) error {
|
||||
tma.published++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tma *testMpoolAPI) GetActorAfter(addr address.Address, ts *types.TipSet) (*types.Actor, error) {
|
||||
// regression check for load bug
|
||||
if ts == nil {
|
||||
panic("GetActorAfter called with nil tipset")
|
||||
}
|
||||
|
||||
balance, ok := tma.balance[addr]
|
||||
if !ok {
|
||||
balance = types.NewInt(1000e6)
|
||||
@@ -281,6 +290,11 @@ func TestMessagePoolMessagesInEachBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevertMessages(t *testing.T) {
|
||||
futureDebug = true
|
||||
defer func() {
|
||||
futureDebug = false
|
||||
}()
|
||||
|
||||
tma := newTestMpoolAPI()
|
||||
|
||||
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
@@ -359,6 +373,7 @@ func TestPruningSimple(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tma.setBalance(sender, 1) // in FIL
|
||||
target := mock.Address(1001)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
@@ -385,3 +400,257 @@ func TestPruningSimple(t *testing.T) {
|
||||
t.Fatal("expected only 5 messages in pool, got: ", len(msgs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadLocal(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// the actors
|
||||
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tma.setBalance(a1, 1) // in FIL
|
||||
tma.setBalance(a2, 1) // in FIL
|
||||
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
|
||||
msgs := make(map[cid.Cid]struct{})
|
||||
for i := 0; i < 10; i++ {
|
||||
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
||||
cid, err := mp.Push(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
msgs[cid] = struct{}{}
|
||||
}
|
||||
err = mp.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, err = New(tma, ds, "mptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pmsgs, _ := mp.Pending()
|
||||
if len(msgs) != len(pmsgs) {
|
||||
t.Fatalf("expected %d messages, but got %d", len(msgs), len(pmsgs))
|
||||
}
|
||||
|
||||
for _, m := range pmsgs {
|
||||
cid := m.Cid()
|
||||
_, ok := msgs[cid]
|
||||
if !ok {
|
||||
t.Fatal("unknown message")
|
||||
}
|
||||
|
||||
delete(msgs, cid)
|
||||
}
|
||||
|
||||
if len(msgs) > 0 {
|
||||
t.Fatalf("not all messages were laoded; missing %d messages", len(msgs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearAll(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// the actors
|
||||
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tma.setBalance(a1, 1) // in FIL
|
||||
tma.setBalance(a2, 1) // in FIL
|
||||
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
|
||||
for i := 0; i < 10; i++ {
|
||||
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
||||
_, err := mp.Push(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
m := makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
|
||||
mustAdd(t, mp, m)
|
||||
}
|
||||
|
||||
mp.Clear(true)
|
||||
|
||||
pending, _ := mp.Pending()
|
||||
if len(pending) > 0 {
|
||||
t.Fatalf("cleared the mpool, but got %d pending messages", len(pending))
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearNonLocal(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// the actors
|
||||
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tma.setBalance(a1, 1) // in FIL
|
||||
tma.setBalance(a2, 1) // in FIL
|
||||
|
||||
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
|
||||
for i := 0; i < 10; i++ {
|
||||
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
||||
_, err := mp.Push(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
m := makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
|
||||
mustAdd(t, mp, m)
|
||||
}
|
||||
|
||||
mp.Clear(false)
|
||||
|
||||
pending, _ := mp.Pending()
|
||||
if len(pending) != 10 {
|
||||
t.Fatalf("expected 10 pending messages, but got %d instead", len(pending))
|
||||
}
|
||||
|
||||
for _, m := range pending {
|
||||
if m.Message.From != a1 {
|
||||
t.Fatalf("expected message from %s but got one from %s instead", a1, m.Message.From)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdates(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// the actors
|
||||
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
|
||||
ch, err := mp.Updates(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
|
||||
|
||||
tma.setBalance(a1, 1) // in FIL
|
||||
tma.setBalance(a2, 1) // in FIL
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
||||
_, err := mp.Push(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, ok := <-ch
|
||||
if !ok {
|
||||
t.Fatal("expected update, but got a closed channel instead")
|
||||
}
|
||||
}
|
||||
|
||||
err = mp.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, ok := <-ch
|
||||
if ok {
|
||||
t.Fatal("expected closed channel, but got an update instead")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ func (mp *MessagePool) republishPendingMessages() error {
|
||||
|
||||
// we can't fit the current chain but there is gas to spare
|
||||
// trim it and push it down
|
||||
chain.Trim(gasLimit, mp, baseFee, ts, false)
|
||||
chain.Trim(gasLimit, mp, baseFee, ts)
|
||||
for j := i; j < len(chains)-1; j++ {
|
||||
if chains[j].Before(chains[j+1]) {
|
||||
break
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package messagepool
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/messagepool/gasguess"
|
||||
"github.com/filecoin-project/lotus/chain/wallet"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/ipfs/go-datastore"
|
||||
)
|
||||
|
||||
func TestRepubMessages(t *testing.T) {
|
||||
tma := newTestMpoolAPI()
|
||||
ds := datastore.NewMapDatastore()
|
||||
|
||||
mp, err := New(tma, ds, "mptest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// the actors
|
||||
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
|
||||
|
||||
tma.setBalance(a1, 1) // in FIL
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
|
||||
_, err := mp.Push(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if tma.published != 10 {
|
||||
t.Fatalf("expected to have published 10 messages, but got %d instead", tma.published)
|
||||
}
|
||||
|
||||
mp.repubTrigger <- struct{}{}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if tma.published != 20 {
|
||||
t.Fatalf("expected to have published 20 messages, but got %d instead", tma.published)
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,7 @@ tailLoop:
|
||||
for gasLimit >= minGas && last < len(chains) {
|
||||
// trim if necessary
|
||||
if chains[last].gasLimit > gasLimit {
|
||||
chains[last].Trim(gasLimit, mp, baseFee, ts, false)
|
||||
chains[last].Trim(gasLimit, mp, baseFee, ts)
|
||||
}
|
||||
|
||||
// push down if it hasn't been invalidated
|
||||
@@ -284,7 +284,7 @@ tailLoop:
|
||||
}
|
||||
|
||||
// dependencies fit, just trim it
|
||||
chain.Trim(gasLimit-depGasLimit, mp, baseFee, ts, false)
|
||||
chain.Trim(gasLimit-depGasLimit, mp, baseFee, ts)
|
||||
last += i
|
||||
continue tailLoop
|
||||
}
|
||||
@@ -389,7 +389,7 @@ func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.S
|
||||
tailLoop:
|
||||
for gasLimit >= minGas && last < len(chains) {
|
||||
// trim
|
||||
chains[last].Trim(gasLimit, mp, baseFee, ts, false)
|
||||
chains[last].Trim(gasLimit, mp, baseFee, ts)
|
||||
|
||||
// push down if it hasn't been invalidated
|
||||
if chains[last].valid {
|
||||
@@ -462,15 +462,27 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui
|
||||
}
|
||||
}
|
||||
|
||||
if len(chains) == 0 {
|
||||
return nil, gasLimit
|
||||
}
|
||||
|
||||
// 2. Sort the chains
|
||||
sort.Slice(chains, func(i, j int) bool {
|
||||
return chains[i].Before(chains[j])
|
||||
})
|
||||
|
||||
// 3. Merge chains until the block limit; we are willing to include negative performing chains
|
||||
// as these are messages from our own miners
|
||||
if len(chains) != 0 && chains[0].gasPerf < 0 {
|
||||
log.Warnw("all priority messages in mpool have negative gas performance", "bestGasPerf", chains[0].gasPerf)
|
||||
return nil, gasLimit
|
||||
}
|
||||
|
||||
// 3. Merge chains until the block limit, as long as they have non-negative gas performance
|
||||
last := len(chains)
|
||||
for i, chain := range chains {
|
||||
if chain.gasPerf < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if chain.gasLimit <= gasLimit {
|
||||
gasLimit -= chain.gasLimit
|
||||
result = append(result, chain.msgs...)
|
||||
@@ -484,8 +496,8 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui
|
||||
|
||||
tailLoop:
|
||||
for gasLimit >= minGas && last < len(chains) {
|
||||
// trim, without discarding negative performing messages
|
||||
chains[last].Trim(gasLimit, mp, baseFee, ts, true)
|
||||
// trim, discarding negative performing messages
|
||||
chains[last].Trim(gasLimit, mp, baseFee, ts)
|
||||
|
||||
// push down if it hasn't been invalidated
|
||||
if chains[last].valid {
|
||||
@@ -503,6 +515,12 @@ tailLoop:
|
||||
if !chain.valid {
|
||||
continue
|
||||
}
|
||||
|
||||
// if gasPerf < 0 we have no more profitable chains
|
||||
if chain.gasPerf < 0 {
|
||||
break tailLoop
|
||||
}
|
||||
|
||||
// does it fit in the bock?
|
||||
if chain.gasLimit <= gasLimit {
|
||||
gasLimit -= chain.gasLimit
|
||||
@@ -515,9 +533,9 @@ tailLoop:
|
||||
continue tailLoop
|
||||
}
|
||||
|
||||
// the merge loop ended after processing all the chains and we probably still have gas to spare
|
||||
// -- mark the end.
|
||||
last = len(chains)
|
||||
// the merge loop ended after processing all the chains and we probably still have gas to spare;
|
||||
// end the loop
|
||||
break
|
||||
}
|
||||
|
||||
return result, gasLimit
|
||||
@@ -755,9 +773,9 @@ func (mc *msgChain) Before(other *msgChain) bool {
|
||||
(mc.gasPerf == other.gasPerf && mc.gasReward.Cmp(other.gasReward) > 0)
|
||||
}
|
||||
|
||||
func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt, ts *types.TipSet, priority bool) {
|
||||
func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt, ts *types.TipSet) {
|
||||
i := len(mc.msgs) - 1
|
||||
for i >= 0 && (mc.gasLimit > gasLimit || (!priority && mc.gasPerf < 0)) {
|
||||
for i >= 0 && (mc.gasLimit > gasLimit || mc.gasPerf < 0) {
|
||||
gasReward := mp.getGasReward(mc.msgs[i], baseFee, ts)
|
||||
mc.gasReward = new(big.Int).Sub(mc.gasReward, gasReward)
|
||||
mc.gasLimit -= mc.msgs[i].Message.GasLimit
|
||||
|
||||
@@ -23,6 +23,11 @@ import (
|
||||
logging "github.com/ipfs/go-log"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// bump this for the selection tests
|
||||
MaxActorPendingMessages = 1000000
|
||||
}
|
||||
|
||||
func makeTestMessage(w *wallet.Wallet, from, to address.Address, nonce uint64, gasLimit int64, gasPrice uint64) *types.SignedMessage {
|
||||
msg := &types.Message{
|
||||
From: from,
|
||||
@@ -649,6 +654,73 @@ func TestPriorityMessageSelection(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityMessageSelection2(t *testing.T) {
|
||||
mp, tma := makeTestMpool()
|
||||
|
||||
// the actors
|
||||
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
block := tma.nextBlock()
|
||||
ts := mock.TipSet(block)
|
||||
tma.applyBlock(t, block)
|
||||
|
||||
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
|
||||
|
||||
tma.setBalance(a1, 1) // in FIL
|
||||
tma.setBalance(a2, 1) // in FIL
|
||||
|
||||
mp.cfg.PriorityAddrs = []address.Address{a1}
|
||||
|
||||
nMessages := int(2 * build.BlockGasLimit / gasLimit)
|
||||
for i := 0; i < nMessages; i++ {
|
||||
bias := (nMessages - i) / 3
|
||||
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(1+i%3+bias))
|
||||
mustAdd(t, mp, m)
|
||||
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(1+i%3+bias))
|
||||
mustAdd(t, mp, m)
|
||||
}
|
||||
|
||||
msgs, err := mp.SelectMessages(ts, 1.0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedMsgs := int(build.BlockGasLimit / gasLimit)
|
||||
if len(msgs) != expectedMsgs {
|
||||
t.Fatalf("expected %d messages but got %d", expectedMsgs, len(msgs))
|
||||
}
|
||||
|
||||
// all messages must be from a1
|
||||
nextNonce := uint64(0)
|
||||
for _, m := range msgs {
|
||||
if m.Message.From != a1 {
|
||||
t.Fatal("expected messages from a1 before messages from a2")
|
||||
}
|
||||
if m.Message.Nonce != nextNonce {
|
||||
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
|
||||
}
|
||||
nextNonce++
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimalMessageSelection1(t *testing.T) {
|
||||
// this test uses just a single actor sending messages with a low tq
|
||||
// the chain depenent merging algorithm should pick messages from the actor
|
||||
|
||||
@@ -157,7 +157,7 @@ func MinerSectorInfo(ctx context.Context, sm *StateManager, maddr address.Addres
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, xerrors.New("sector not found")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return sectorInfo, nil
|
||||
|
||||
+40
-22
@@ -3,6 +3,7 @@ package sub
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -40,6 +41,9 @@ import (
|
||||
|
||||
var log = logging.Logger("sub")
|
||||
|
||||
var ErrSoftFailure = errors.New("soft validation failure")
|
||||
var ErrInsufficientPower = errors.New("incoming block's miner does not have minimum power")
|
||||
|
||||
func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, bserv bserv.BlockService, cmgr connmgr.ConnManager) {
|
||||
for {
|
||||
msg, err := bsub.Next(ctx)
|
||||
@@ -258,16 +262,15 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
|
||||
|
||||
stats.Record(ctx, metrics.BlockReceived.M(1))
|
||||
|
||||
recordFailure := func(what string) {
|
||||
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, what))
|
||||
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
|
||||
recordFailureFlagPeer := func(what string) {
|
||||
recordFailure(ctx, metrics.BlockValidationFailure, what)
|
||||
bv.flagPeer(pid)
|
||||
}
|
||||
|
||||
blk, what, err := bv.decodeAndCheckBlock(msg)
|
||||
if err != nil {
|
||||
log.Error("got invalid block over pubsub: ", err)
|
||||
recordFailure(what)
|
||||
recordFailureFlagPeer(what)
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
@@ -275,7 +278,7 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
|
||||
err = bv.validateMsgMeta(ctx, blk)
|
||||
if err != nil {
|
||||
log.Warnf("error validating message metadata: %s", err)
|
||||
recordFailure("invalid_block_meta")
|
||||
recordFailureFlagPeer("invalid_block_meta")
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
@@ -288,11 +291,12 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
|
||||
// if we are synced and the miner is unknown, then the block is rejcected.
|
||||
key, err := bv.checkPowerAndGetWorkerKey(ctx, blk.Header)
|
||||
if err != nil {
|
||||
if bv.isChainNearSynced() {
|
||||
if err != ErrSoftFailure && bv.isChainNearSynced() {
|
||||
log.Warnf("received block from unknown miner or miner that doesn't meet min power over pubsub; rejecting message")
|
||||
recordFailure("unknown_miner")
|
||||
recordFailureFlagPeer("unknown_miner")
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
log.Warnf("cannot validate block message; unknown miner or miner that doesn't meet min power in unsynced chain")
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
@@ -300,13 +304,13 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
|
||||
err = sigs.CheckBlockSignature(ctx, blk.Header, key)
|
||||
if err != nil {
|
||||
log.Errorf("block signature verification failed: %s", err)
|
||||
recordFailure("signature_verification_failed")
|
||||
recordFailureFlagPeer("signature_verification_failed")
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
if blk.Header.ElectionProof.WinCount < 1 {
|
||||
log.Errorf("block is not claiming to be winning")
|
||||
recordFailure("not_winning")
|
||||
recordFailureFlagPeer("not_winning")
|
||||
return pubsub.ValidationReject
|
||||
}
|
||||
|
||||
@@ -473,19 +477,19 @@ func (bv *BlockValidator) checkPowerAndGetWorkerKey(ctx context.Context, bh *typ
|
||||
baseTs := bv.chain.GetHeaviestTipSet()
|
||||
lbts, err := stmgr.GetLookbackTipSetForRound(ctx, bv.stmgr, baseTs, bh.Height)
|
||||
if err != nil {
|
||||
log.Warnf("failed to load lookback tipset for incoming block")
|
||||
return address.Undef, err
|
||||
log.Warnf("failed to load lookback tipset for incoming block: %s", err)
|
||||
return address.Undef, ErrSoftFailure
|
||||
}
|
||||
|
||||
hmp, err := stmgr.MinerHasMinPower(ctx, bv.stmgr, bh.Miner, lbts)
|
||||
if err != nil {
|
||||
log.Warnf("failed to determine if incoming block's miner has minimum power")
|
||||
return address.Undef, err
|
||||
log.Warnf("failed to determine if incoming block's miner has minimum power: %s", err)
|
||||
return address.Undef, ErrSoftFailure
|
||||
}
|
||||
|
||||
if !hmp {
|
||||
log.Warnf("incoming block's miner does not have minimum power")
|
||||
return address.Undef, xerrors.New("incoming block's miner does not have minimum power")
|
||||
return address.Undef, ErrInsufficientPower
|
||||
}
|
||||
|
||||
return key, nil
|
||||
@@ -541,14 +545,16 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
|
||||
log.Debugf("failed to add message from network to message pool (From: %s, To: %s, Nonce: %d, Value: %s): %s", m.Message.From, m.Message.To, m.Message.Nonce, types.FIL(m.Message.Value), err)
|
||||
ctx, _ = tag.New(
|
||||
ctx,
|
||||
tag.Insert(metrics.FailureType, "add"),
|
||||
tag.Upsert(metrics.Local, "false"),
|
||||
)
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
recordFailure(ctx, metrics.MessageValidationFailure, "add")
|
||||
switch {
|
||||
case xerrors.Is(err, messagepool.ErrBroadcastAnyway):
|
||||
case xerrors.Is(err, messagepool.ErrSoftValidationFailure):
|
||||
fallthrough
|
||||
case xerrors.Is(err, messagepool.ErrRBFTooLowPremium):
|
||||
fallthrough
|
||||
case xerrors.Is(err, messagepool.ErrTooManyPendingMessages):
|
||||
fallthrough
|
||||
case xerrors.Is(err, messagepool.ErrNonceTooLow):
|
||||
return pubsub.ValidationIgnore
|
||||
default:
|
||||
@@ -560,37 +566,41 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
|
||||
}
|
||||
|
||||
func (mv *MessageValidator) validateLocalMessage(ctx context.Context, msg *pubsub.Message) pubsub.ValidationResult {
|
||||
ctx, _ = tag.New(
|
||||
ctx,
|
||||
tag.Upsert(metrics.Local, "true"),
|
||||
)
|
||||
// do some lightweight validation
|
||||
stats.Record(ctx, metrics.MessagePublished.M(1))
|
||||
|
||||
m, err := types.DecodeSignedMessage(msg.Message.GetData())
|
||||
if err != nil {
|
||||
log.Warnf("failed to decode local message: %s", err)
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
recordFailure(ctx, metrics.MessageValidationFailure, "decode")
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
|
||||
if m.Size() > 32*1024 {
|
||||
log.Warnf("local message is too large! (%dB)", m.Size())
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
recordFailure(ctx, metrics.MessageValidationFailure, "oversize")
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
|
||||
if m.Message.To == address.Undef {
|
||||
log.Warn("local message has invalid destination address")
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
recordFailure(ctx, metrics.MessageValidationFailure, "undef-addr")
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
|
||||
if !m.Message.Value.LessThan(types.TotalFilecoinInt) {
|
||||
log.Warnf("local messages has too high value: %s", m.Message.Value)
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
recordFailure(ctx, metrics.MessageValidationFailure, "value-too-high")
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
|
||||
if err := mv.mpool.VerifyMsgSig(m); err != nil {
|
||||
log.Warnf("signature verification failed for local message: %s", err)
|
||||
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
|
||||
recordFailure(ctx, metrics.MessageValidationFailure, "verify-sig")
|
||||
return pubsub.ValidationIgnore
|
||||
}
|
||||
|
||||
@@ -613,3 +623,11 @@ func HandleIncomingMessages(ctx context.Context, mpool *messagepool.MessagePool,
|
||||
// Do nothing... everything happens in validate
|
||||
}
|
||||
}
|
||||
|
||||
func recordFailure(ctx context.Context, metric *stats.Int64Measure, failureType string) {
|
||||
ctx, _ = tag.New(
|
||||
ctx,
|
||||
tag.Upsert(metrics.FailureType, failureType),
|
||||
)
|
||||
stats.Record(ctx, metric.M(1))
|
||||
}
|
||||
|
||||
+41
-2
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -52,6 +53,19 @@ import (
|
||||
//the theoretical max height based on systime are quickly rejected
|
||||
const MaxHeightDrift = 5
|
||||
|
||||
var defaultMessageFetchWindowSize = 200
|
||||
|
||||
func init() {
|
||||
if s := os.Getenv("LOTUS_BSYNC_MSG_WINDOW"); s != "" {
|
||||
val, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse LOTUS_BSYNC_MSG_WINDOW: %s", err)
|
||||
return
|
||||
}
|
||||
defaultMessageFetchWindowSize = val
|
||||
}
|
||||
}
|
||||
|
||||
var log = logging.Logger("chain")
|
||||
|
||||
var LocalIncoming = "incoming"
|
||||
@@ -109,6 +123,8 @@ type Syncer struct {
|
||||
receiptTracker *blockReceiptTracker
|
||||
|
||||
verifier ffiwrapper.Verifier
|
||||
|
||||
windowSize int
|
||||
}
|
||||
|
||||
// NewSyncer creates a new Syncer object.
|
||||
@@ -134,6 +150,7 @@ func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connm
|
||||
receiptTracker: newBlockReceiptTracker(),
|
||||
connmgr: connmgr,
|
||||
verifier: verifier,
|
||||
windowSize: defaultMessageFetchWindowSize,
|
||||
|
||||
incoming: pubsub.New(50),
|
||||
}
|
||||
@@ -641,7 +658,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
|
||||
validationStart := build.Clock.Now()
|
||||
defer func() {
|
||||
stats.Record(ctx, metrics.BlockValidationDurationMilliseconds.M(metrics.SinceInMilliseconds(validationStart)))
|
||||
log.Infow("block validation", "took", time.Since(validationStart), "height", b.Header.Height)
|
||||
log.Infow("block validation", "took", time.Since(validationStart), "height", b.Header.Height, "age", time.Since(time.Unix(int64(b.Header.Timestamp), 0)))
|
||||
}()
|
||||
|
||||
ctx, span := trace.StartSpan(ctx, "validateBlock")
|
||||
@@ -1399,7 +1416,8 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
|
||||
|
||||
span.AddAttributes(trace.Int64Attribute("num_headers", int64(len(headers))))
|
||||
|
||||
windowSize := 200
|
||||
windowSize := syncer.windowSize
|
||||
mainLoop:
|
||||
for i := len(headers) - 1; i >= 0; {
|
||||
fts, err := syncer.store.TryFillTipSet(headers[i])
|
||||
if err != nil {
|
||||
@@ -1427,6 +1445,12 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
|
||||
nreq := batchSize - len(bstout)
|
||||
bstips, err := syncer.Bsync.GetChainMessages(ctx, next, uint64(nreq))
|
||||
if err != nil {
|
||||
// TODO check errors for temporary nature
|
||||
if windowSize > 1 {
|
||||
windowSize /= 2
|
||||
log.Infof("error fetching messages: %s; reducing window size to %d and trying again", err, windowSize)
|
||||
continue mainLoop
|
||||
}
|
||||
return xerrors.Errorf("message processing failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -1461,9 +1485,24 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
|
||||
return xerrors.Errorf("message processing failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if i >= windowSize {
|
||||
newWindowSize := windowSize + 10
|
||||
if newWindowSize > int(blocksync.MaxRequestLength) {
|
||||
newWindowSize = int(blocksync.MaxRequestLength)
|
||||
}
|
||||
if newWindowSize > windowSize {
|
||||
windowSize = newWindowSize
|
||||
log.Infof("successfully fetched %d messages; increasing window size to %d", len(bstout), windowSize)
|
||||
}
|
||||
}
|
||||
|
||||
i -= batchSize
|
||||
}
|
||||
|
||||
// remember our window size
|
||||
syncer.windowSize = windowSize
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ type Runtime struct {
|
||||
|
||||
vm *VM
|
||||
state *state.StateTree
|
||||
msg *types.Message
|
||||
vmsg vmr.Message
|
||||
height abi.ChainEpoch
|
||||
cst cbor.IpldStore
|
||||
|
||||
@@ -97,7 +97,6 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
|
||||
ctx: ctx,
|
||||
vm: vm,
|
||||
state: vm.cstate,
|
||||
msg: msg,
|
||||
origin: origin,
|
||||
originNonce: originNonce,
|
||||
height: vm.blockHeight,
|
||||
|
||||
+136
-83
@@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -978,6 +979,10 @@ var clientListDeals = &cli.Command{
|
||||
Usage: "use color in display output",
|
||||
Value: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "watch",
|
||||
Usage: "watch deal updates in real-time, rather than a one time list",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
@@ -987,82 +992,97 @@ var clientListDeals = &cli.Command{
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verbose := cctx.Bool("verbose")
|
||||
color := cctx.Bool("color")
|
||||
watch := cctx.Bool("watch")
|
||||
|
||||
localDeals, err := api.ClientListDeals(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Slice(localDeals, func(i, j int) bool {
|
||||
return localDeals[i].CreationTime.Before(localDeals[j].CreationTime)
|
||||
})
|
||||
if watch {
|
||||
updates, err := api.ClientGetDealUpdates(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var deals []deal
|
||||
for _, v := range localDeals {
|
||||
if v.DealID == 0 {
|
||||
deals = append(deals, deal{
|
||||
LocalDeal: v,
|
||||
OnChainDealState: market.DealState{
|
||||
SectorStartEpoch: -1,
|
||||
LastUpdatedEpoch: -1,
|
||||
SlashEpoch: -1,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
onChain, err := api.StateMarketStorageDeal(ctx, v.DealID, head.Key())
|
||||
for {
|
||||
tm.Clear()
|
||||
tm.MoveCursor(1, 1)
|
||||
|
||||
err = outputStorageDeals(ctx, tm.Screen, api, localDeals, verbose, color)
|
||||
if err != nil {
|
||||
deals = append(deals, deal{LocalDeal: v})
|
||||
} else {
|
||||
deals = append(deals, deal{
|
||||
LocalDeal: v,
|
||||
OnChainDealState: onChain.State,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
tm.Flush()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case updated := <-updates:
|
||||
var found bool
|
||||
for i, existing := range localDeals {
|
||||
if existing.ProposalCid.Equals(updated.ProposalCid) {
|
||||
localDeals[i] = updated
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
localDeals = append(localDeals, updated)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color := cctx.Bool("color")
|
||||
return outputStorageDeals(ctx, os.Stdout, api, localDeals, cctx.Bool("verbose"), cctx.Bool("color"))
|
||||
},
|
||||
}
|
||||
|
||||
if cctx.Bool("verbose") {
|
||||
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "Created\tDealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tMessage\n")
|
||||
for _, d := range deals {
|
||||
onChain := "N"
|
||||
if d.OnChainDealState.SectorStartEpoch != -1 {
|
||||
onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch)
|
||||
}
|
||||
|
||||
slashed := "N"
|
||||
if d.OnChainDealState.SlashEpoch != -1 {
|
||||
slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch)
|
||||
}
|
||||
|
||||
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
|
||||
fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\n", d.LocalDeal.CreationTime.Format(time.Stamp), d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, dealStateString(color, d.LocalDeal.State), onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), price, d.LocalDeal.Duration, d.LocalDeal.Message)
|
||||
}
|
||||
return w.Flush()
|
||||
func dealFromDealInfo(ctx context.Context, full api.FullNode, head *types.TipSet, v api.DealInfo) deal {
|
||||
if v.DealID == 0 {
|
||||
return deal{
|
||||
LocalDeal: v,
|
||||
OnChainDealState: market.DealState{
|
||||
SectorStartEpoch: -1,
|
||||
LastUpdatedEpoch: -1,
|
||||
SlashEpoch: -1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
w := tablewriter.New(tablewriter.Col("DealCid"),
|
||||
tablewriter.Col("DealId"),
|
||||
tablewriter.Col("Provider"),
|
||||
tablewriter.Col("State"),
|
||||
tablewriter.Col("On Chain?"),
|
||||
tablewriter.Col("Slashed?"),
|
||||
tablewriter.Col("PieceCID"),
|
||||
tablewriter.Col("Size"),
|
||||
tablewriter.Col("Price"),
|
||||
tablewriter.Col("Duration"),
|
||||
tablewriter.NewLineCol("Message"))
|
||||
onChain, err := full.StateMarketStorageDeal(ctx, v.DealID, head.Key())
|
||||
if err != nil {
|
||||
return deal{LocalDeal: v}
|
||||
}
|
||||
|
||||
return deal{
|
||||
LocalDeal: v,
|
||||
OnChainDealState: onChain.State,
|
||||
}
|
||||
}
|
||||
|
||||
func outputStorageDeals(ctx context.Context, out io.Writer, full api.FullNode, localDeals []api.DealInfo, verbose bool, color bool) error {
|
||||
sort.Slice(localDeals, func(i, j int) bool {
|
||||
return localDeals[i].CreationTime.Before(localDeals[j].CreationTime)
|
||||
})
|
||||
|
||||
head, err := full.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var deals []deal
|
||||
for _, localDeal := range localDeals {
|
||||
deals = append(deals, dealFromDealInfo(ctx, full, head, localDeal))
|
||||
}
|
||||
|
||||
if verbose {
|
||||
w := tabwriter.NewWriter(out, 2, 4, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "Created\tDealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tMessage\n")
|
||||
for _, d := range deals {
|
||||
propcid := d.LocalDeal.ProposalCid.String()
|
||||
propcid = "..." + propcid[len(propcid)-8:]
|
||||
|
||||
onChain := "N"
|
||||
if d.OnChainDealState.SectorStartEpoch != -1 {
|
||||
onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch)
|
||||
@@ -1073,28 +1093,57 @@ var clientListDeals = &cli.Command{
|
||||
slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch)
|
||||
}
|
||||
|
||||
piece := d.LocalDeal.PieceCID.String()
|
||||
piece = "..." + piece[len(piece)-8:]
|
||||
|
||||
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
|
||||
fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\n", d.LocalDeal.CreationTime.Format(time.Stamp), d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, dealStateString(color, d.LocalDeal.State), onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), price, d.LocalDeal.Duration, d.LocalDeal.Message)
|
||||
}
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
w.Write(map[string]interface{}{
|
||||
"DealCid": propcid,
|
||||
"DealId": d.LocalDeal.DealID,
|
||||
"Provider": d.LocalDeal.Provider,
|
||||
"State": dealStateString(color, d.LocalDeal.State),
|
||||
"On Chain?": onChain,
|
||||
"Slashed?": slashed,
|
||||
"PieceCID": piece,
|
||||
"Size": types.SizeStr(types.NewInt(d.LocalDeal.Size)),
|
||||
"Price": price,
|
||||
"Duration": d.LocalDeal.Duration,
|
||||
"Message": d.LocalDeal.Message,
|
||||
})
|
||||
w := tablewriter.New(tablewriter.Col("DealCid"),
|
||||
tablewriter.Col("DealId"),
|
||||
tablewriter.Col("Provider"),
|
||||
tablewriter.Col("State"),
|
||||
tablewriter.Col("On Chain?"),
|
||||
tablewriter.Col("Slashed?"),
|
||||
tablewriter.Col("PieceCID"),
|
||||
tablewriter.Col("Size"),
|
||||
tablewriter.Col("Price"),
|
||||
tablewriter.Col("Duration"),
|
||||
tablewriter.NewLineCol("Message"))
|
||||
|
||||
for _, d := range deals {
|
||||
propcid := ellipsis(d.LocalDeal.ProposalCid.String(), 8)
|
||||
|
||||
onChain := "N"
|
||||
if d.OnChainDealState.SectorStartEpoch != -1 {
|
||||
onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch)
|
||||
}
|
||||
|
||||
return w.Flush(os.Stdout)
|
||||
},
|
||||
slashed := "N"
|
||||
if d.OnChainDealState.SlashEpoch != -1 {
|
||||
slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch)
|
||||
}
|
||||
|
||||
piece := ellipsis(d.LocalDeal.PieceCID.String(), 8)
|
||||
|
||||
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
|
||||
|
||||
w.Write(map[string]interface{}{
|
||||
"DealCid": propcid,
|
||||
"DealId": d.LocalDeal.DealID,
|
||||
"Provider": d.LocalDeal.Provider,
|
||||
"State": dealStateString(color, d.LocalDeal.State),
|
||||
"On Chain?": onChain,
|
||||
"Slashed?": slashed,
|
||||
"PieceCID": piece,
|
||||
"Size": types.SizeStr(types.NewInt(d.LocalDeal.Size)),
|
||||
"Price": price,
|
||||
"Duration": d.LocalDeal.Duration,
|
||||
"Message": d.LocalDeal.Message,
|
||||
})
|
||||
}
|
||||
|
||||
return w.Flush(out)
|
||||
}
|
||||
|
||||
func dealStateString(c bool, state storagemarket.StorageDealStatus) string {
|
||||
@@ -1352,11 +1401,8 @@ func channelStatusString(useColor bool, status datatransfer.Status) string {
|
||||
}
|
||||
|
||||
func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTransferChannel) map[string]interface{} {
|
||||
rootCid := channel.BaseCID.String()
|
||||
rootCid = "..." + rootCid[len(rootCid)-8:]
|
||||
|
||||
otherParty := channel.OtherPeer.String()
|
||||
otherParty = "..." + otherParty[len(otherParty)-8:]
|
||||
rootCid := ellipsis(channel.BaseCID.String(), 8)
|
||||
otherParty := ellipsis(channel.OtherPeer.String(), 8)
|
||||
|
||||
initiated := "N"
|
||||
if channel.IsInitiator {
|
||||
@@ -1365,7 +1411,7 @@ func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTr
|
||||
|
||||
voucher := channel.Voucher
|
||||
if len(voucher) > 40 {
|
||||
voucher = "..." + voucher[len(voucher)-37:]
|
||||
voucher = ellipsis(voucher, 37)
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
@@ -1379,3 +1425,10 @@ func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTr
|
||||
"Message": channel.Message,
|
||||
}
|
||||
}
|
||||
|
||||
func ellipsis(s string, length int) string {
|
||||
if length > 0 && len(s) > length {
|
||||
return "..." + s[len(s)-length:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
+21
-4
@@ -12,7 +12,7 @@ import (
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
@@ -75,6 +75,8 @@ func flagForAPI(t repo.RepoType) string {
|
||||
return "api"
|
||||
case repo.StorageMiner:
|
||||
return "miner-api"
|
||||
case repo.Worker:
|
||||
return "worker-api"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown repo type: %v", t))
|
||||
}
|
||||
@@ -86,6 +88,8 @@ func flagForRepo(t repo.RepoType) string {
|
||||
return "repo"
|
||||
case repo.StorageMiner:
|
||||
return "miner-repo"
|
||||
case repo.Worker:
|
||||
return "worker-repo"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown repo type: %v", t))
|
||||
}
|
||||
@@ -97,6 +101,8 @@ func envForRepo(t repo.RepoType) string {
|
||||
return "FULLNODE_API_INFO"
|
||||
case repo.StorageMiner:
|
||||
return "MINER_API_INFO"
|
||||
case repo.Worker:
|
||||
return "WORKER_API_INFO"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown repo type: %v", t))
|
||||
}
|
||||
@@ -109,6 +115,8 @@ func envForRepoDeprecation(t repo.RepoType) string {
|
||||
return "FULLNODE_API_INFO"
|
||||
case repo.StorageMiner:
|
||||
return "STORAGE_API_INFO"
|
||||
case repo.Worker:
|
||||
return "WORKER_API_INFO"
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown repo type: %v", t))
|
||||
}
|
||||
@@ -213,7 +221,7 @@ func GetAPI(ctx *cli.Context) (api.Common, jsonrpc.ClientCloser, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewCommonRPC(addr, headers)
|
||||
return client.NewCommonRPC(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
@@ -222,7 +230,7 @@ func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewFullNodeRPC(addr, headers)
|
||||
return client.NewFullNodeRPC(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func GetStorageMinerAPI(ctx *cli.Context, opts ...jsonrpc.Option) (api.StorageMiner, jsonrpc.ClientCloser, error) {
|
||||
@@ -231,7 +239,16 @@ func GetStorageMinerAPI(ctx *cli.Context, opts ...jsonrpc.Option) (api.StorageMi
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewStorageMinerRPC(addr, headers, opts...)
|
||||
return client.NewStorageMinerRPC(ctx.Context, addr, headers, opts...)
|
||||
}
|
||||
|
||||
func GetWorkerAPI(ctx *cli.Context) (api.WorkerAPI, jsonrpc.ClientCloser, error) {
|
||||
addr, headers, err := GetRawAPI(ctx, repo.Worker)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewWorkerRPC(ctx.Context, addr, headers)
|
||||
}
|
||||
|
||||
func DaemonContext(cctx *cli.Context) context.Context {
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ var logList = &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "List log systems",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
api, closer, err := GetAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -71,7 +71,7 @@ var logSetLevel = &cli.Command{
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
api, closer, err := GetAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ var mpoolClear = &cli.Command{
|
||||
|
||||
really := cctx.Bool("really-do-it")
|
||||
if !really {
|
||||
//nolint:golint
|
||||
return fmt.Errorf("--really-do-it must be specified for this action to have an effect; you have been warned")
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -468,7 +468,7 @@ var paychVoucherSubmitCmd = &cli.Command{
|
||||
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
mcid, err := api.PaychVoucherSubmit(ctx, ch, sv)
|
||||
mcid, err := api.PaychVoucherSubmit(ctx, ch, sv, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+99
-11
@@ -107,12 +107,14 @@ func TestPaymentChannelVouchers(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
|
||||
paymentCreator := nodes[0]
|
||||
paymentReceiver := nodes[1]
|
||||
creatorAddr := addrs[0]
|
||||
receiverAddr := addrs[1]
|
||||
|
||||
// Create mock CLI
|
||||
mockCLI := newMockCLI(t)
|
||||
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
|
||||
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
|
||||
|
||||
// creator: paych get <creator> <receiver> <amount>
|
||||
channelAmt := "100000"
|
||||
@@ -127,43 +129,123 @@ func TestPaymentChannelVouchers(t *testing.T) {
|
||||
// creator: paych voucher create <channel> <amount>
|
||||
// Note: implied --lane=0
|
||||
voucherAmt1 := 100
|
||||
vamt1 := strconv.Itoa(voucherAmt1)
|
||||
cmd = []string{chAddr.String(), vamt1}
|
||||
cmd = []string{chAddr.String(), strconv.Itoa(voucherAmt1)}
|
||||
voucher1 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
||||
vouchers = append(vouchers, voucherSpec{serialized: voucher1, lane: 0, amt: voucherAmt1})
|
||||
|
||||
// creator: paych voucher create <channel> <amount> --lane=5
|
||||
lane5 := "--lane=5"
|
||||
voucherAmt2 := 50
|
||||
vamt2 := strconv.Itoa(voucherAmt2)
|
||||
cmd = []string{lane5, chAddr.String(), vamt2}
|
||||
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt2)}
|
||||
voucher2 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
||||
vouchers = append(vouchers, voucherSpec{serialized: voucher2, lane: 5, amt: voucherAmt2})
|
||||
|
||||
// creator: paych voucher create <channel> <amount> --lane=5
|
||||
voucherAmt3 := 70
|
||||
vamt3 := strconv.Itoa(voucherAmt3)
|
||||
cmd = []string{lane5, chAddr.String(), vamt3}
|
||||
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt3)}
|
||||
voucher3 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
||||
vouchers = append(vouchers, voucherSpec{serialized: voucher3, lane: 5, amt: voucherAmt3})
|
||||
|
||||
// creator: paych voucher create <channel> <amount> --lane=5
|
||||
voucherAmt4 := 80
|
||||
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt4)}
|
||||
voucher4 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
|
||||
vouchers = append(vouchers, voucherSpec{serialized: voucher4, lane: 5, amt: voucherAmt4})
|
||||
|
||||
// creator: paych voucher list <channel> --export
|
||||
cmd = []string{"--export", chAddr.String()}
|
||||
list := creatorCLI.runCmd(paychVoucherListCmd, cmd)
|
||||
|
||||
// Check that voucher list output is correct
|
||||
// Check that voucher list output is correct on creator
|
||||
checkVoucherOutput(t, list, vouchers)
|
||||
|
||||
// creator: paych voucher best-spendable <channel>
|
||||
cmd = []string{"--export", chAddr.String()}
|
||||
bestSpendable := creatorCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
||||
|
||||
// Check that best spendable output is correct
|
||||
// Check that best spendable output is correct on creator
|
||||
bestVouchers := []voucherSpec{
|
||||
{serialized: voucher1, lane: 0, amt: voucherAmt1},
|
||||
{serialized: voucher3, lane: 5, amt: voucherAmt3},
|
||||
{serialized: voucher4, lane: 5, amt: voucherAmt4},
|
||||
}
|
||||
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
||||
|
||||
// receiver: paych voucher add <voucher>
|
||||
cmd = []string{chAddr.String(), voucher1}
|
||||
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
||||
|
||||
// receiver: paych voucher add <voucher>
|
||||
cmd = []string{chAddr.String(), voucher2}
|
||||
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
||||
|
||||
// receiver: paych voucher add <voucher>
|
||||
cmd = []string{chAddr.String(), voucher3}
|
||||
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
||||
|
||||
// receiver: paych voucher add <voucher>
|
||||
cmd = []string{chAddr.String(), voucher4}
|
||||
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
|
||||
|
||||
// receiver: paych voucher list <channel> --export
|
||||
cmd = []string{"--export", chAddr.String()}
|
||||
list = receiverCLI.runCmd(paychVoucherListCmd, cmd)
|
||||
|
||||
// Check that voucher list output is correct on receiver
|
||||
checkVoucherOutput(t, list, vouchers)
|
||||
|
||||
// receiver: paych voucher best-spendable <channel>
|
||||
cmd = []string{"--export", chAddr.String()}
|
||||
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
||||
|
||||
// Check that best spendable output is correct on receiver
|
||||
bestVouchers = []voucherSpec{
|
||||
{serialized: voucher1, lane: 0, amt: voucherAmt1},
|
||||
{serialized: voucher4, lane: 5, amt: voucherAmt4},
|
||||
}
|
||||
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
||||
|
||||
// receiver: paych voucher submit <channel> <voucher>
|
||||
cmd = []string{chAddr.String(), voucher1}
|
||||
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
|
||||
|
||||
// receiver: paych voucher best-spendable <channel>
|
||||
cmd = []string{"--export", chAddr.String()}
|
||||
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
||||
|
||||
// Check that best spendable output no longer includes submitted voucher
|
||||
bestVouchers = []voucherSpec{
|
||||
{serialized: voucher4, lane: 5, amt: voucherAmt4},
|
||||
}
|
||||
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
||||
|
||||
// There are three vouchers in lane 5: 50, 70, 80
|
||||
// Submit the voucher for 50. Best spendable should still be 80.
|
||||
// receiver: paych voucher submit <channel> <voucher>
|
||||
cmd = []string{chAddr.String(), voucher2}
|
||||
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
|
||||
|
||||
// receiver: paych voucher best-spendable <channel>
|
||||
cmd = []string{"--export", chAddr.String()}
|
||||
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
||||
|
||||
// Check that best spendable output still includes the voucher for 80
|
||||
bestVouchers = []voucherSpec{
|
||||
{serialized: voucher4, lane: 5, amt: voucherAmt4},
|
||||
}
|
||||
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
||||
|
||||
// Submit the voucher for 80
|
||||
// receiver: paych voucher submit <channel> <voucher>
|
||||
cmd = []string{chAddr.String(), voucher4}
|
||||
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
|
||||
|
||||
// receiver: paych voucher best-spendable <channel>
|
||||
cmd = []string{"--export", chAddr.String()}
|
||||
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
|
||||
|
||||
// Check that best spendable output no longer includes submitted voucher
|
||||
bestVouchers = []voucherSpec{}
|
||||
checkVoucherOutput(t, bestSpendable, bestVouchers)
|
||||
}
|
||||
|
||||
func checkVoucherOutput(t *testing.T, list string, vouchers []voucherSpec) {
|
||||
@@ -171,14 +253,20 @@ func checkVoucherOutput(t *testing.T, list string, vouchers []voucherSpec) {
|
||||
listVouchers := make(map[string]string)
|
||||
for _, line := range lines {
|
||||
parts := strings.Split(line, ";")
|
||||
serialized := strings.TrimSpace(parts[1])
|
||||
listVouchers[serialized] = strings.TrimSpace(parts[0])
|
||||
if len(parts) == 2 {
|
||||
serialized := strings.TrimSpace(parts[1])
|
||||
listVouchers[serialized] = strings.TrimSpace(parts[0])
|
||||
}
|
||||
}
|
||||
for _, vchr := range vouchers {
|
||||
res, ok := listVouchers[vchr.serialized]
|
||||
require.True(t, ok)
|
||||
require.Regexp(t, fmt.Sprintf("Lane %d", vchr.lane), res)
|
||||
require.Regexp(t, fmt.Sprintf("%d", vchr.amt), res)
|
||||
delete(listVouchers, vchr.serialized)
|
||||
}
|
||||
for _, vchr := range listVouchers {
|
||||
require.Fail(t, "Extra voucher "+vchr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
var pprofCmd = &cli.Command{
|
||||
|
||||
@@ -26,6 +26,11 @@ func main() {
|
||||
EnvVars: []string{"LOTUS_PATH"},
|
||||
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "api",
|
||||
EnvVars: []string{"FULLNODE_API_INFO"},
|
||||
Value: "",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "db",
|
||||
EnvVars: []string{"LOTUS_DB"},
|
||||
|
||||
@@ -96,12 +96,6 @@ func (p *Processor) HandleMarketChanges(ctx context.Context, marketTips ActorTip
|
||||
log.Fatalw("Failed to persist market actors", "error", err)
|
||||
}
|
||||
|
||||
// we persist the dealID <--> minerID,sectorID here since the dealID needs to be stored above first
|
||||
if err := p.storePreCommitDealInfo(p.sectorDealEvents); err != nil {
|
||||
close(p.sectorDealEvents)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.updateMarket(ctx, marketChanges); err != nil {
|
||||
log.Fatalw("Failed to update market actors", "error", err)
|
||||
}
|
||||
@@ -272,48 +266,6 @@ func (p *Processor) storeMarketActorDealProposals(ctx context.Context, marketTip
|
||||
|
||||
}
|
||||
|
||||
func (p *Processor) storePreCommitDealInfo(dealEvents <-chan *SectorDealEvent) error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`create temp table mds (like minerid_dealid_sectorid excluding constraints) on commit drop;`); err != nil {
|
||||
return xerrors.Errorf("Failed to create temp table for minerid_dealid_sectorid: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy mds (deal_id, miner_id, sector_id) from STDIN`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to prepare minerid_dealid_sectorid statement: %w", err)
|
||||
}
|
||||
|
||||
for sde := range dealEvents {
|
||||
for _, did := range sde.DealIDs {
|
||||
if _, err := stmt.Exec(
|
||||
uint64(did),
|
||||
sde.MinerID.String(),
|
||||
sde.SectorID,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return xerrors.Errorf("Failed to close miner sector deals statement: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into minerid_dealid_sectorid select * from mds on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("Failed to insert into miner deal sector table: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("Failed to commit miner deal sector table: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (p *Processor) updateMarketActorDealProposals(ctx context.Context, marketTip []marketActorInfo) error {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
|
||||
@@ -3,7 +3,6 @@ package processor
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/xerrors"
|
||||
@@ -120,10 +119,6 @@ func (p *Processor) persistMessagesAndReceipts(ctx context.Context, blocks map[c
|
||||
}
|
||||
|
||||
func (p *Processor) storeReceipts(recs map[mrec]*types.MessageReceipt) error {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
log.Debugw("Persisted Receipts", "duration", time.Since(start).String())
|
||||
}()
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -164,10 +159,6 @@ create temp table recs (like receipts excluding constraints) on commit drop;
|
||||
}
|
||||
|
||||
func (p *Processor) storeMsgInclusions(incls map[cid.Cid][]cid.Cid) error {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
log.Debugw("Persisted Message Inclusions", "duration", time.Since(start).String())
|
||||
}()
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -206,10 +197,6 @@ create temp table mi (like block_messages excluding constraints) on commit drop;
|
||||
}
|
||||
|
||||
func (p *Processor) storeMessages(msgs map[cid.Cid]*types.Message) error {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
log.Debugw("Persisted Messages", "duration", time.Since(start).String())
|
||||
}()
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -271,7 +271,11 @@ func (p *Processor) persistMiners(ctx context.Context, miners []minerActorInfo)
|
||||
preCommitEvents := make(chan *MinerSectorsEvent, 8)
|
||||
sectorEvents := make(chan *MinerSectorsEvent, 8)
|
||||
partitionEvents := make(chan *MinerSectorsEvent, 8)
|
||||
p.sectorDealEvents = make(chan *SectorDealEvent, 8)
|
||||
dealEvents := make(chan *SectorDealEvent, 8)
|
||||
|
||||
grp.Go(func() error {
|
||||
return p.storePreCommitDealInfo(dealEvents)
|
||||
})
|
||||
|
||||
grp.Go(func() error {
|
||||
return p.storeMinerSectorEvents(ctx, sectorEvents, preCommitEvents, partitionEvents)
|
||||
@@ -280,9 +284,9 @@ func (p *Processor) persistMiners(ctx context.Context, miners []minerActorInfo)
|
||||
grp.Go(func() error {
|
||||
defer func() {
|
||||
close(preCommitEvents)
|
||||
close(p.sectorDealEvents)
|
||||
close(dealEvents)
|
||||
}()
|
||||
return p.storeMinerPreCommitInfo(ctx, miners, preCommitEvents, p.sectorDealEvents)
|
||||
return p.storeMinerPreCommitInfo(ctx, miners, preCommitEvents, dealEvents)
|
||||
})
|
||||
|
||||
grp.Go(func() error {
|
||||
@@ -314,100 +318,107 @@ func (p *Processor) storeMinerPreCommitInfo(ctx context.Context, miners []minerA
|
||||
return xerrors.Errorf("Failed to prepare miner precommit info statement: %w", err)
|
||||
}
|
||||
|
||||
grp, _ := errgroup.WithContext(ctx)
|
||||
for _, m := range miners {
|
||||
minerSectors, err := adt.AsArray(p.ctxStore, m.state.Sectors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changes, err := p.getMinerPreCommitChanges(ctx, m)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
|
||||
continue
|
||||
} else {
|
||||
m := m
|
||||
grp.Go(func() error {
|
||||
minerSectors, err := adt.AsArray(p.ctxStore, m.state.Sectors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if changes == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
preCommitAdded := make([]uint64, len(changes.Added))
|
||||
for i, added := range changes.Added {
|
||||
if len(added.Info.DealIDs) > 0 {
|
||||
sectorDeals <- &SectorDealEvent{
|
||||
MinerID: m.common.addr,
|
||||
SectorID: uint64(added.Info.SectorNumber),
|
||||
DealIDs: added.Info.DealIDs,
|
||||
changes, err := p.getMinerPreCommitChanges(ctx, m)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if added.Info.ReplaceCapacity {
|
||||
if _, err := stmt.Exec(
|
||||
m.common.addr.String(),
|
||||
added.Info.SectorNumber,
|
||||
added.Info.SealedCID.String(),
|
||||
m.common.stateroot.String(),
|
||||
added.Info.SealRandEpoch,
|
||||
added.Info.Expiration,
|
||||
added.PreCommitDeposit.String(),
|
||||
added.PreCommitEpoch,
|
||||
added.DealWeight.String(),
|
||||
added.VerifiedDealWeight.String(),
|
||||
added.Info.ReplaceCapacity,
|
||||
added.Info.ReplaceSectorDeadline,
|
||||
added.Info.ReplaceSectorPartition,
|
||||
added.Info.ReplaceSectorNumber,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := stmt.Exec(
|
||||
m.common.addr.String(),
|
||||
added.Info.SectorNumber,
|
||||
added.Info.SealedCID.String(),
|
||||
m.common.stateroot.String(),
|
||||
added.Info.SealRandEpoch,
|
||||
added.Info.Expiration,
|
||||
added.PreCommitDeposit.String(),
|
||||
added.PreCommitEpoch,
|
||||
added.DealWeight.String(),
|
||||
added.VerifiedDealWeight.String(),
|
||||
added.Info.ReplaceCapacity,
|
||||
nil, // replace deadline
|
||||
nil, // replace partition
|
||||
nil, // replace sector
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
preCommitAdded[i] = uint64(added.Info.SectorNumber)
|
||||
}
|
||||
if len(preCommitAdded) > 0 {
|
||||
sectorEvents <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: preCommitAdded,
|
||||
Event: PreCommitAdded,
|
||||
}
|
||||
}
|
||||
var preCommitExpired []uint64
|
||||
for _, removed := range changes.Removed {
|
||||
var sector miner.SectorOnChainInfo
|
||||
if found, err := minerSectors.Get(uint64(removed.Info.SectorNumber), §or); err != nil {
|
||||
return err
|
||||
} else if !found {
|
||||
preCommitExpired = append(preCommitExpired, uint64(removed.Info.SectorNumber))
|
||||
}
|
||||
}
|
||||
if len(preCommitExpired) > 0 {
|
||||
sectorEvents <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: preCommitExpired,
|
||||
Event: PreCommitExpired,
|
||||
if changes == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
preCommitAdded := make([]uint64, len(changes.Added))
|
||||
for i, added := range changes.Added {
|
||||
if len(added.Info.DealIDs) > 0 {
|
||||
sectorDeals <- &SectorDealEvent{
|
||||
MinerID: m.common.addr,
|
||||
SectorID: uint64(added.Info.SectorNumber),
|
||||
DealIDs: added.Info.DealIDs,
|
||||
}
|
||||
}
|
||||
if added.Info.ReplaceCapacity {
|
||||
if _, err := stmt.Exec(
|
||||
m.common.addr.String(),
|
||||
added.Info.SectorNumber,
|
||||
added.Info.SealedCID.String(),
|
||||
m.common.stateroot.String(),
|
||||
added.Info.SealRandEpoch,
|
||||
added.Info.Expiration,
|
||||
added.PreCommitDeposit.String(),
|
||||
added.PreCommitEpoch,
|
||||
added.DealWeight.String(),
|
||||
added.VerifiedDealWeight.String(),
|
||||
added.Info.ReplaceCapacity,
|
||||
added.Info.ReplaceSectorDeadline,
|
||||
added.Info.ReplaceSectorPartition,
|
||||
added.Info.ReplaceSectorNumber,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := stmt.Exec(
|
||||
m.common.addr.String(),
|
||||
added.Info.SectorNumber,
|
||||
added.Info.SealedCID.String(),
|
||||
m.common.stateroot.String(),
|
||||
added.Info.SealRandEpoch,
|
||||
added.Info.Expiration,
|
||||
added.PreCommitDeposit.String(),
|
||||
added.PreCommitEpoch,
|
||||
added.DealWeight.String(),
|
||||
added.VerifiedDealWeight.String(),
|
||||
added.Info.ReplaceCapacity,
|
||||
nil, // replace deadline
|
||||
nil, // replace partition
|
||||
nil, // replace sector
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
preCommitAdded[i] = uint64(added.Info.SectorNumber)
|
||||
}
|
||||
if len(preCommitAdded) > 0 {
|
||||
sectorEvents <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: preCommitAdded,
|
||||
Event: PreCommitAdded,
|
||||
}
|
||||
}
|
||||
var preCommitExpired []uint64
|
||||
for _, removed := range changes.Removed {
|
||||
var sector miner.SectorOnChainInfo
|
||||
if found, err := minerSectors.Get(uint64(removed.Info.SectorNumber), §or); err != nil {
|
||||
return err
|
||||
} else if !found {
|
||||
preCommitExpired = append(preCommitExpired, uint64(removed.Info.SectorNumber))
|
||||
}
|
||||
}
|
||||
if len(preCommitExpired) > 0 {
|
||||
sectorEvents <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: preCommitExpired,
|
||||
Event: PreCommitExpired,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := grp.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
@@ -439,67 +450,75 @@ func (p *Processor) storeMinerSectorInfo(ctx context.Context, miners []minerActo
|
||||
return xerrors.Errorf("Failed to prepare miner sector info statement: %w", err)
|
||||
}
|
||||
|
||||
grp, _ := errgroup.WithContext(ctx)
|
||||
for _, m := range miners {
|
||||
changes, err := p.getMinerSectorChanges(ctx, m)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
|
||||
continue
|
||||
} else {
|
||||
m := m
|
||||
grp.Go(func() error {
|
||||
changes, err := p.getMinerSectorChanges(ctx, m)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
if changes == nil {
|
||||
continue
|
||||
}
|
||||
var sectorsAdded []uint64
|
||||
var ccAdded []uint64
|
||||
var extended []uint64
|
||||
for _, added := range changes.Added {
|
||||
// add the sector to the table
|
||||
if _, err := stmt.Exec(
|
||||
m.common.addr.String(),
|
||||
added.SectorNumber,
|
||||
added.SealedCID.String(),
|
||||
m.common.stateroot.String(),
|
||||
added.Activation.String(),
|
||||
added.Expiration.String(),
|
||||
added.DealWeight.String(),
|
||||
added.VerifiedDealWeight.String(),
|
||||
added.InitialPledge.String(),
|
||||
added.ExpectedDayReward.String(),
|
||||
added.ExpectedStoragePledge.String(),
|
||||
); err != nil {
|
||||
return err
|
||||
if changes == nil {
|
||||
return nil
|
||||
}
|
||||
if len(added.DealIDs) == 0 {
|
||||
ccAdded = append(ccAdded, uint64(added.SectorNumber))
|
||||
} else {
|
||||
sectorsAdded = append(sectorsAdded, uint64(added.SectorNumber))
|
||||
var sectorsAdded []uint64
|
||||
var ccAdded []uint64
|
||||
var extended []uint64
|
||||
for _, added := range changes.Added {
|
||||
// add the sector to the table
|
||||
if _, err := stmt.Exec(
|
||||
m.common.addr.String(),
|
||||
added.SectorNumber,
|
||||
added.SealedCID.String(),
|
||||
m.common.stateroot.String(),
|
||||
added.Activation.String(),
|
||||
added.Expiration.String(),
|
||||
added.DealWeight.String(),
|
||||
added.VerifiedDealWeight.String(),
|
||||
added.InitialPledge.String(),
|
||||
added.ExpectedDayReward.String(),
|
||||
added.ExpectedStoragePledge.String(),
|
||||
); err != nil {
|
||||
log.Errorw("writing miner sector changes statement", "error", err.Error())
|
||||
}
|
||||
if len(added.DealIDs) == 0 {
|
||||
ccAdded = append(ccAdded, uint64(added.SectorNumber))
|
||||
} else {
|
||||
sectorsAdded = append(sectorsAdded, uint64(added.SectorNumber))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, mod := range changes.Extended {
|
||||
extended = append(extended, uint64(mod.To.SectorNumber))
|
||||
}
|
||||
for _, mod := range changes.Extended {
|
||||
extended = append(extended, uint64(mod.To.SectorNumber))
|
||||
}
|
||||
|
||||
events <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: ccAdded,
|
||||
Event: CommitCapacityAdded,
|
||||
}
|
||||
events <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: sectorsAdded,
|
||||
Event: SectorAdded,
|
||||
}
|
||||
events <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: extended,
|
||||
Event: SectorExtended,
|
||||
}
|
||||
events <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: ccAdded,
|
||||
Event: CommitCapacityAdded,
|
||||
}
|
||||
events <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: sectorsAdded,
|
||||
Event: SectorAdded,
|
||||
}
|
||||
events <- &MinerSectorsEvent{
|
||||
MinerID: m.common.addr,
|
||||
StateRoot: m.common.stateroot,
|
||||
SectorIDs: extended,
|
||||
Event: SectorExtended,
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := grp.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
@@ -911,6 +930,48 @@ func (p *Processor) storeMinersActorInfoState(ctx context.Context, miners []mine
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (p *Processor) storePreCommitDealInfo(dealEvents <-chan *SectorDealEvent) error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`create temp table mds (like minerid_dealid_sectorid excluding constraints) on commit drop;`); err != nil {
|
||||
return xerrors.Errorf("Failed to create temp table for minerid_dealid_sectorid: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy mds (deal_id, miner_id, sector_id) from STDIN`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to prepare minerid_dealid_sectorid statement: %w", err)
|
||||
}
|
||||
|
||||
for sde := range dealEvents {
|
||||
for _, did := range sde.DealIDs {
|
||||
if _, err := stmt.Exec(
|
||||
uint64(did),
|
||||
sde.MinerID.String(),
|
||||
sde.SectorID,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return xerrors.Errorf("Failed to close miner sector deals statement: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into minerid_dealid_sectorid select * from mds on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("Failed to insert into miner deal sector table: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("Failed to commit miner deal sector table: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (p *Processor) storeMinersPower(miners []minerActorInfo) error {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
|
||||
@@ -47,8 +47,6 @@ func (p *Processor) subMpool(ctx context.Context) {
|
||||
msgs[v.Message.Message.Cid()] = &v.Message.Message
|
||||
}
|
||||
|
||||
log.Debugf("Processing %d mpool updates", len(msgs))
|
||||
|
||||
err := p.storeMessages(msgs)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/smoothing"
|
||||
@@ -15,7 +16,19 @@ import (
|
||||
type powerActorInfo struct {
|
||||
common actorInfo
|
||||
|
||||
epochSmoothingEstimate *smoothing.FilterEstimate
|
||||
totalRawBytes big.Int
|
||||
totalRawBytesCommitted big.Int
|
||||
totalQualityAdjustedBytes big.Int
|
||||
totalQualityAdjustedBytesCommitted big.Int
|
||||
totalPledgeCollateral big.Int
|
||||
|
||||
newRawBytes big.Int
|
||||
newQualityAdjustedBytes big.Int
|
||||
newPledgeCollateral big.Int
|
||||
newQAPowerSmoothed *smoothing.FilterEstimate
|
||||
|
||||
minerCount int64
|
||||
minerCountAboveMinimumPower int64
|
||||
}
|
||||
|
||||
func (p *Processor) setupPower() error {
|
||||
@@ -25,13 +38,27 @@ func (p *Processor) setupPower() error {
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
create table if not exists power_smoothing_estimates
|
||||
create table if not exists chain_power
|
||||
(
|
||||
state_root text not null
|
||||
constraint power_smoothing_estimates_pk
|
||||
primary key,
|
||||
position_estimate text not null,
|
||||
velocity_estimate text not null
|
||||
state_root text not null
|
||||
constraint power_smoothing_estimates_pk
|
||||
primary key,
|
||||
|
||||
new_raw_bytes_power text not null,
|
||||
new_qa_bytes_power text not null,
|
||||
new_pledge_collateral text not null,
|
||||
|
||||
total_raw_bytes_power text not null,
|
||||
total_raw_bytes_committed text not null,
|
||||
total_qa_bytes_power text not null,
|
||||
total_qa_bytes_committed text not null,
|
||||
total_pledge_collateral text not null,
|
||||
|
||||
qa_smoothed_position_estimate text not null,
|
||||
qa_smoothed_velocity_estimate text not null,
|
||||
|
||||
miner_count int not null,
|
||||
minimum_consensus_miner_count int not null
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
@@ -60,8 +87,8 @@ func (p *Processor) processPowerActors(ctx context.Context, powerTips ActorTips)
|
||||
}()
|
||||
|
||||
var out []powerActorInfo
|
||||
for tipset, powers := range powerTips {
|
||||
for _, act := range powers {
|
||||
for tipset, powerStates := range powerTips {
|
||||
for _, act := range powerStates {
|
||||
var pw powerActorInfo
|
||||
pw.common = act
|
||||
|
||||
@@ -80,7 +107,19 @@ func (p *Processor) processPowerActors(ctx context.Context, powerTips ActorTips)
|
||||
return nil, xerrors.Errorf("unmarshal state (@ %s): %w", pw.common.stateroot.String(), err)
|
||||
}
|
||||
|
||||
pw.epochSmoothingEstimate = powerActorState.ThisEpochQAPowerSmoothed
|
||||
pw.totalRawBytes = powerActorState.TotalRawBytePower
|
||||
pw.totalRawBytesCommitted = powerActorState.TotalBytesCommitted
|
||||
pw.totalQualityAdjustedBytes = powerActorState.TotalQualityAdjPower
|
||||
pw.totalQualityAdjustedBytesCommitted = powerActorState.TotalQABytesCommitted
|
||||
pw.totalPledgeCollateral = powerActorState.TotalPledgeCollateral
|
||||
|
||||
pw.newRawBytes = powerActorState.ThisEpochRawBytePower
|
||||
pw.newQualityAdjustedBytes = powerActorState.ThisEpochQualityAdjPower
|
||||
pw.newPledgeCollateral = powerActorState.ThisEpochPledgeCollateral
|
||||
pw.newQAPowerSmoothed = powerActorState.ThisEpochQAPowerSmoothed
|
||||
|
||||
pw.minerCount = powerActorState.MinerCount
|
||||
pw.minerCountAboveMinimumPower = powerActorState.MinerAboveMinPowerCount
|
||||
out = append(out, pw)
|
||||
}
|
||||
}
|
||||
@@ -88,46 +127,59 @@ func (p *Processor) processPowerActors(ctx context.Context, powerTips ActorTips)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (p *Processor) persistPowerActors(ctx context.Context, powers []powerActorInfo) error {
|
||||
func (p *Processor) persistPowerActors(ctx context.Context, powerStates []powerActorInfo) error {
|
||||
// NB: use errgroup when there is more than a single store operation
|
||||
return p.storePowerSmoothingEstimates(powers)
|
||||
return p.storePowerSmoothingEstimates(powerStates)
|
||||
}
|
||||
|
||||
func (p *Processor) storePowerSmoothingEstimates(powers []powerActorInfo) error {
|
||||
func (p *Processor) storePowerSmoothingEstimates(powerStates []powerActorInfo) error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("begin power_smoothing_estimates tx: %w", err)
|
||||
return xerrors.Errorf("begin chain_power tx: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`create temp table rse (like power_smoothing_estimates) on commit drop`); err != nil {
|
||||
return xerrors.Errorf("prep power_smoothing_estimates: %w", err)
|
||||
if _, err := tx.Exec(`create temp table cp (like chain_power) on commit drop`); err != nil {
|
||||
return xerrors.Errorf("prep chain_power: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy rse (state_root, position_estimate, velocity_estimate) from stdin;`)
|
||||
stmt, err := tx.Prepare(`copy cp (state_root, new_raw_bytes_power, new_qa_bytes_power, new_pledge_collateral, total_raw_bytes_power, total_raw_bytes_committed, total_qa_bytes_power, total_qa_bytes_committed, total_pledge_collateral, qa_smoothed_position_estimate, qa_smoothed_velocity_estimate, miner_count, minimum_consensus_miner_count) from stdin;`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("prepare tmp power_smoothing_estimates: %w", err)
|
||||
return xerrors.Errorf("prepare tmp chain_power: %w", err)
|
||||
}
|
||||
|
||||
for _, powerState := range powers {
|
||||
for _, ps := range powerStates {
|
||||
if _, err := stmt.Exec(
|
||||
powerState.common.stateroot.String(),
|
||||
powerState.epochSmoothingEstimate.PositionEstimate.String(),
|
||||
powerState.epochSmoothingEstimate.VelocityEstimate.String(),
|
||||
ps.common.stateroot.String(),
|
||||
ps.newRawBytes.String(),
|
||||
ps.newQualityAdjustedBytes.String(),
|
||||
ps.newPledgeCollateral.String(),
|
||||
|
||||
ps.totalRawBytes.String(),
|
||||
ps.totalRawBytesCommitted.String(),
|
||||
ps.totalQualityAdjustedBytes.String(),
|
||||
ps.totalQualityAdjustedBytesCommitted.String(),
|
||||
ps.totalPledgeCollateral.String(),
|
||||
|
||||
ps.newQAPowerSmoothed.PositionEstimate.String(),
|
||||
ps.newQAPowerSmoothed.VelocityEstimate.String(),
|
||||
|
||||
ps.minerCount,
|
||||
ps.minerCountAboveMinimumPower,
|
||||
); err != nil {
|
||||
return xerrors.Errorf("failed to store smoothing estimate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return xerrors.Errorf("close prepared power_smoothing_estimates: %w", err)
|
||||
return xerrors.Errorf("close prepared chain_power: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into power_smoothing_estimates select * from rse on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("insert power_smoothing_estimates from tmp: %w", err)
|
||||
if _, err := tx.Exec(`insert into chain_power select * from cp on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("insert chain_power from tmp: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("commit power_smoothing_estimates tx: %w", err)
|
||||
return xerrors.Errorf("commit chain_power tx: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -35,9 +35,6 @@ type Processor struct {
|
||||
|
||||
// number of blocks processed at a time
|
||||
batch int
|
||||
|
||||
// process communication channels
|
||||
sectorDealEvents chan *SectorDealEvent
|
||||
}
|
||||
|
||||
type ActorTips map[types.TipSetKey][]actorInfo
|
||||
@@ -152,7 +149,6 @@ func (p *Processor) Start(ctx context.Context) {
|
||||
log.Errorf("Failed to handle market changes: %w", err)
|
||||
return
|
||||
}
|
||||
log.Info("Processed Market Changes")
|
||||
}()
|
||||
|
||||
grp.Add(1)
|
||||
@@ -162,7 +158,6 @@ func (p *Processor) Start(ctx context.Context) {
|
||||
log.Errorf("Failed to handle miner changes: %w", err)
|
||||
return
|
||||
}
|
||||
log.Info("Processed Miner Changes")
|
||||
}()
|
||||
|
||||
grp.Add(1)
|
||||
@@ -172,7 +167,6 @@ func (p *Processor) Start(ctx context.Context) {
|
||||
log.Errorf("Failed to handle reward changes: %w", err)
|
||||
return
|
||||
}
|
||||
log.Info("Processed Reward Changes")
|
||||
}()
|
||||
|
||||
grp.Add(1)
|
||||
@@ -182,7 +176,6 @@ func (p *Processor) Start(ctx context.Context) {
|
||||
log.Errorf("Failed to handle power actor changes: %w", err)
|
||||
return
|
||||
}
|
||||
log.Info("Processes Power Changes")
|
||||
}()
|
||||
|
||||
grp.Add(1)
|
||||
@@ -192,7 +185,6 @@ func (p *Processor) Start(ctx context.Context) {
|
||||
log.Errorf("Failed to handle message changes: %w", err)
|
||||
return
|
||||
}
|
||||
log.Info("Processed Message Changes")
|
||||
}()
|
||||
|
||||
grp.Add(1)
|
||||
@@ -202,7 +194,6 @@ func (p *Processor) Start(ctx context.Context) {
|
||||
log.Errorf("Failed to handle common actor changes: %w", err)
|
||||
return
|
||||
}
|
||||
log.Info("Processed CommonActor Changes")
|
||||
}()
|
||||
|
||||
grp.Wait()
|
||||
@@ -214,7 +205,7 @@ func (p *Processor) Start(ctx context.Context) {
|
||||
if err := p.refreshViews(); err != nil {
|
||||
log.Errorw("Failed to refresh views", "error", err)
|
||||
}
|
||||
log.Infow("Processed Batch", "duration", time.Since(loopStart).String())
|
||||
log.Infow("Processed Batch Complete", "duration", time.Since(loopStart).String())
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
@@ -13,20 +12,23 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/smoothing"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
type rewardActorInfo struct {
|
||||
common actorInfo
|
||||
|
||||
// expected power in bytes during this epoch
|
||||
baselinePower big.Int
|
||||
cumSumBaselinePower big.Int
|
||||
cumSumRealizedPower big.Int
|
||||
|
||||
// base reward in attofil for each block found during this epoch
|
||||
baseBlockReward big.Int
|
||||
effectiveNetworkTime int64
|
||||
effectiveBaselinePower big.Int
|
||||
|
||||
epochSmoothingEstimate *smoothing.FilterEstimate
|
||||
newBaselinePower big.Int
|
||||
newBaseReward big.Int
|
||||
newSmoothingEstimate *smoothing.FilterEstimate
|
||||
|
||||
totalMinedReward big.Int
|
||||
}
|
||||
|
||||
func (p *Processor) setupRewards() error {
|
||||
@@ -36,34 +38,23 @@ func (p *Processor) setupRewards() error {
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`
|
||||
/*
|
||||
* captures base block reward per miner per state root and does not
|
||||
* include penalties or gas reward
|
||||
*/
|
||||
create table if not exists base_block_rewards
|
||||
(
|
||||
state_root text not null
|
||||
constraint block_rewards_pk
|
||||
primary key,
|
||||
base_block_reward numeric not null
|
||||
);
|
||||
|
||||
/* captures chain-specific power state for any given stateroot */
|
||||
create table if not exists chain_power
|
||||
create table if not exists chain_reward
|
||||
(
|
||||
state_root text not null
|
||||
constraint chain_power_pk
|
||||
constraint chain_reward_pk
|
||||
primary key,
|
||||
baseline_power text not null
|
||||
);
|
||||
cum_sum_baseline text not null,
|
||||
cum_sum_realized text not null,
|
||||
effective_network_time int not null,
|
||||
effective_baseline_power text not null,
|
||||
|
||||
create table if not exists reward_smoothing_estimates
|
||||
(
|
||||
state_root text not null
|
||||
constraint reward_smoothing_estimates_pk
|
||||
primary key,
|
||||
position_estimate text not null,
|
||||
velocity_estimate text not null
|
||||
new_baseline_power text not null,
|
||||
new_reward numeric not null,
|
||||
new_reward_smoothed_position_estimate text not null,
|
||||
new_reward_smoothed_velocity_estimate text not null,
|
||||
|
||||
total_mined_reward text not null
|
||||
);
|
||||
`); err != nil {
|
||||
return err
|
||||
@@ -113,9 +104,14 @@ func (p *Processor) processRewardActors(ctx context.Context, rewardTips ActorTip
|
||||
return nil, xerrors.Errorf("unmarshal state (@ %s): %w", rw.common.stateroot.String(), err)
|
||||
}
|
||||
|
||||
rw.baseBlockReward = rewardActorState.ThisEpochReward
|
||||
rw.baselinePower = rewardActorState.ThisEpochBaselinePower
|
||||
rw.epochSmoothingEstimate = rewardActorState.ThisEpochRewardSmoothed
|
||||
rw.cumSumBaselinePower = rewardActorState.CumsumBaseline
|
||||
rw.cumSumRealizedPower = rewardActorState.CumsumRealized
|
||||
rw.effectiveNetworkTime = int64(rewardActorState.EffectiveNetworkTime)
|
||||
rw.effectiveBaselinePower = rewardActorState.EffectiveBaselinePower
|
||||
rw.newBaselinePower = rewardActorState.ThisEpochBaselinePower
|
||||
rw.newBaseReward = rewardActorState.ThisEpochReward
|
||||
rw.newSmoothingEstimate = rewardActorState.ThisEpochRewardSmoothed
|
||||
rw.totalMinedReward = rewardActorState.TotalMined
|
||||
out = append(out, rw)
|
||||
}
|
||||
}
|
||||
@@ -145,8 +141,14 @@ func (p *Processor) processRewardActors(ctx context.Context, rewardTips ActorTip
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rw.baseBlockReward = rewardActorState.ThisEpochReward
|
||||
rw.baselinePower = rewardActorState.ThisEpochBaselinePower
|
||||
rw.cumSumBaselinePower = rewardActorState.CumsumBaseline
|
||||
rw.cumSumRealizedPower = rewardActorState.CumsumRealized
|
||||
rw.effectiveNetworkTime = int64(rewardActorState.EffectiveNetworkTime)
|
||||
rw.effectiveBaselinePower = rewardActorState.EffectiveBaselinePower
|
||||
rw.newBaselinePower = rewardActorState.ThisEpochBaselinePower
|
||||
rw.newBaseReward = rewardActorState.ThisEpochReward
|
||||
rw.newSmoothingEstimate = rewardActorState.ThisEpochRewardSmoothed
|
||||
rw.totalMinedReward = rewardActorState.TotalMined
|
||||
out = append(out, rw)
|
||||
}
|
||||
|
||||
@@ -159,149 +161,47 @@ func (p *Processor) persistRewardActors(ctx context.Context, rewards []rewardAct
|
||||
log.Debugw("Persisted Reward Actors", "duration", time.Since(start).String())
|
||||
}()
|
||||
|
||||
grp, ctx := errgroup.WithContext(ctx) //nolint
|
||||
|
||||
grp.Go(func() error {
|
||||
if err := p.storeChainPower(rewards); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
grp.Go(func() error {
|
||||
if err := p.storeBaseBlockReward(rewards); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
grp.Go(func() error {
|
||||
if err := p.storeRewardSmoothingEstimates(rewards); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return grp.Wait()
|
||||
}
|
||||
|
||||
func (p *Processor) storeChainPower(rewards []rewardActorInfo) error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("begin chain_power tx: %w", err)
|
||||
return xerrors.Errorf("begin chain_reward tx: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`create temp table cp (like chain_power excluding constraints) on commit drop`); err != nil {
|
||||
return xerrors.Errorf("prep chain_power temp: %w", err)
|
||||
if _, err := tx.Exec(`create temp table cr (like chain_reward excluding constraints) on commit drop`); err != nil {
|
||||
return xerrors.Errorf("prep chain_reward temp: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy cp (state_root, baseline_power) from STDIN`)
|
||||
stmt, err := tx.Prepare(`copy cr ( state_root, cum_sum_baseline, cum_sum_realized, effective_network_time, effective_baseline_power, new_baseline_power, new_reward, new_reward_smoothed_position_estimate, new_reward_smoothed_velocity_estimate, total_mined_reward) from STDIN`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("prepare tmp chain_power: %w", err)
|
||||
return xerrors.Errorf("prepare tmp chain_reward: %w", err)
|
||||
}
|
||||
|
||||
for _, rewardState := range rewards {
|
||||
if _, err := stmt.Exec(
|
||||
rewardState.common.stateroot.String(),
|
||||
rewardState.baselinePower.String(),
|
||||
rewardState.cumSumBaselinePower.String(),
|
||||
rewardState.cumSumRealizedPower.String(),
|
||||
rewardState.effectiveNetworkTime,
|
||||
rewardState.effectiveBaselinePower.String(),
|
||||
rewardState.newBaselinePower.String(),
|
||||
rewardState.newBaseReward.String(),
|
||||
rewardState.newSmoothingEstimate.PositionEstimate.String(),
|
||||
rewardState.newSmoothingEstimate.VelocityEstimate.String(),
|
||||
rewardState.totalMinedReward.String(),
|
||||
); err != nil {
|
||||
log.Errorw("failed to store chain power", "state_root", rewardState.common.stateroot, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return xerrors.Errorf("close prepared chain_power: %w", err)
|
||||
return xerrors.Errorf("close prepared chain_reward: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into chain_power select * from cp on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("insert chain_power from tmp: %w", err)
|
||||
if _, err := tx.Exec(`insert into chain_reward select * from cr on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("insert chain_reward from tmp: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("commit chain_power tx: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) storeBaseBlockReward(rewards []rewardActorInfo) error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("begin base_block_reward tx: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`create temp table bbr (like base_block_rewards excluding constraints) on commit drop`); err != nil {
|
||||
return xerrors.Errorf("prep base_block_reward temp: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy bbr (state_root, base_block_reward) from STDIN`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("prepare tmp base_block_reward: %w", err)
|
||||
}
|
||||
|
||||
for _, rewardState := range rewards {
|
||||
baseBlockReward := big.Div(rewardState.baseBlockReward, big.NewIntUnsigned(build.BlocksPerEpoch))
|
||||
if _, err := stmt.Exec(
|
||||
rewardState.common.stateroot.String(),
|
||||
baseBlockReward.String(),
|
||||
); err != nil {
|
||||
log.Errorw("failed to store base block reward", "state_root", rewardState.common.stateroot, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return xerrors.Errorf("close prepared base_block_reward: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into base_block_rewards select * from bbr on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("insert base_block_reward from tmp: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("commit base_block_reward tx: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) storeRewardSmoothingEstimates(rewards []rewardActorInfo) error {
|
||||
tx, err := p.db.Begin()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("begin reward_smoothing_estimates tx: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`create temp table rse (like reward_smoothing_estimates) on commit drop`); err != nil {
|
||||
return xerrors.Errorf("prep reward_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(`copy rse (state_root, position_estimate, velocity_estimate) from stdin;`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("prepare tmp reward_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
for _, rewardState := range rewards {
|
||||
if rewardState.epochSmoothingEstimate == nil {
|
||||
continue
|
||||
}
|
||||
if _, err := stmt.Exec(
|
||||
rewardState.common.stateroot.String(),
|
||||
rewardState.epochSmoothingEstimate.PositionEstimate.String(),
|
||||
rewardState.epochSmoothingEstimate.VelocityEstimate.String(),
|
||||
); err != nil {
|
||||
return xerrors.Errorf("failed to store smoothing estimate: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := stmt.Close(); err != nil {
|
||||
return xerrors.Errorf("close prepared reward_smoothing_estimates: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(`insert into reward_smoothing_estimates select * from rse on conflict do nothing`); err != nil {
|
||||
return xerrors.Errorf("insert reward_smoothing_estimates from tmp: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("commit reward_smoothing_estimates tx: %w", err)
|
||||
return xerrors.Errorf("commit chain_reward tx: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,20 +2,25 @@ package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/cmd/lotus-chainwatch/processor"
|
||||
"github.com/filecoin-project/lotus/cmd/lotus-chainwatch/scheduler"
|
||||
"github.com/filecoin-project/lotus/cmd/lotus-chainwatch/syncer"
|
||||
"github.com/filecoin-project/lotus/cmd/lotus-chainwatch/util"
|
||||
)
|
||||
|
||||
var runCmd = &cli.Command{
|
||||
@@ -39,9 +44,24 @@ var runCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
api, closer, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
var api api.FullNode
|
||||
var closer jsonrpc.ClientCloser
|
||||
var err error
|
||||
if tokenMaddr := cctx.String("api"); tokenMaddr != "" {
|
||||
toks := strings.Split(tokenMaddr, ":")
|
||||
if len(toks) != 2 {
|
||||
return fmt.Errorf("invalid api tokens, expected <token>:<maddr>, got: %s", tokenMaddr)
|
||||
}
|
||||
|
||||
api, closer, err = util.GetFullNodeAPIUsingCredentials(cctx.Context, toks[1], toks[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
api, closer, err = lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
@@ -70,7 +90,7 @@ var runCmd = &cli.Command{
|
||||
}
|
||||
db.SetMaxOpenConns(1350)
|
||||
|
||||
sync := syncer.NewSyncer(db, api)
|
||||
sync := syncer.NewSyncer(db, api, 1400)
|
||||
sync.Start(ctx)
|
||||
|
||||
proc := processor.NewProcessor(ctx, db, api, maxBatch)
|
||||
|
||||
@@ -3,7 +3,6 @@ package scheduler
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
@@ -24,9 +23,9 @@ func setupTopMinerByBaseRewardSchema(ctx context.Context, db *sql.DB) error {
|
||||
with total_rewards_by_miner as (
|
||||
select
|
||||
b.miner,
|
||||
sum(bbr.base_block_reward * b.win_count) as total_reward
|
||||
sum(cr.new_reward * b.win_count) as total_reward
|
||||
from blocks b
|
||||
inner join base_block_rewards bbr on b.parentstateroot = bbr.state_root
|
||||
inner join chain_reward cr on b.parentstateroot = cr.state_root
|
||||
group by 1
|
||||
) select
|
||||
rank() over (order by total_reward desc),
|
||||
@@ -43,17 +42,17 @@ func setupTopMinerByBaseRewardSchema(ctx context.Context, db *sql.DB) error {
|
||||
b."timestamp"as current_timestamp,
|
||||
max(b.height) as current_height
|
||||
from blocks b
|
||||
join base_block_rewards bbr on b.parentstateroot = bbr.state_root
|
||||
where bbr.base_block_reward is not null
|
||||
join chain_reward cr on b.parentstateroot = cr.state_root
|
||||
where cr.new_reward is not null
|
||||
group by 1
|
||||
order by 1 desc
|
||||
limit 1;
|
||||
`); err != nil {
|
||||
return xerrors.Errorf("create top_miner_by_base_reward views: %w", err)
|
||||
return xerrors.Errorf("create top_miners_by_base_reward views: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return xerrors.Errorf("committing top_miner_by_base_reward views; %w", err)
|
||||
return xerrors.Errorf("committing top_miners_by_base_reward views; %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -65,11 +64,6 @@ func refreshTopMinerByBaseReward(ctx context.Context, db *sql.DB) error {
|
||||
default:
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
defer func() {
|
||||
log.Debugw("refresh top_miners_by_base_reward", "duration", time.Since(t).String())
|
||||
}()
|
||||
|
||||
_, err := db.Exec("refresh materialized view top_miners_by_base_reward;")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("refresh top_miners_by_base_reward: %w", err)
|
||||
|
||||
@@ -40,7 +40,7 @@ func (s *Scheduler) Start(ctx context.Context) {
|
||||
|
||||
go func() {
|
||||
// run once on start after schema has initialized
|
||||
time.Sleep(5 * time.Second)
|
||||
time.Sleep(1 * time.Minute)
|
||||
if err := refreshTopMinerByBaseReward(ctx, s.db); err != nil {
|
||||
log.Errorw("failed to refresh top miner", "error", err)
|
||||
}
|
||||
|
||||
@@ -23,14 +23,17 @@ var log = logging.Logger("syncer")
|
||||
type Syncer struct {
|
||||
db *sql.DB
|
||||
|
||||
lookbackLimit uint64
|
||||
|
||||
headerLk sync.Mutex
|
||||
node api.FullNode
|
||||
}
|
||||
|
||||
func NewSyncer(db *sql.DB, node api.FullNode) *Syncer {
|
||||
func NewSyncer(db *sql.DB, node api.FullNode, lookbackLimit uint64) *Syncer {
|
||||
return &Syncer{
|
||||
db: db,
|
||||
node: node,
|
||||
db: db,
|
||||
node: node,
|
||||
lookbackLimit: lookbackLimit,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,40 +151,34 @@ create index if not exists state_heights_parentstateroot_index
|
||||
}
|
||||
|
||||
func (s *Syncer) Start(ctx context.Context) {
|
||||
if err := logging.SetLogLevel("syncer", "info"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Debug("Starting Syncer")
|
||||
|
||||
if err := s.setupSchemas(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// doing the initial sync here lets us avoid the HCCurrent case in the switch
|
||||
head, err := s.node.ChainHead(ctx)
|
||||
if err != nil {
|
||||
log.Fatalw("Failed to get chain head form lotus", "error", err)
|
||||
}
|
||||
|
||||
unsynced, err := s.unsyncedBlocks(ctx, head, time.Unix(0, 0))
|
||||
if err != nil {
|
||||
log.Fatalw("failed to gather unsynced blocks", "error", err)
|
||||
}
|
||||
|
||||
if err := s.storeHeaders(unsynced, true, time.Now()); err != nil {
|
||||
log.Fatalw("failed to store unsynced blocks", "error", err)
|
||||
}
|
||||
|
||||
// continue to keep the block headers table up to date.
|
||||
notifs, err := s.node.ChainNotify(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
lastSynced := time.Now()
|
||||
// we need to ensure that on a restart we don't reprocess the whole flarping chain
|
||||
blkCID, height, err := s.mostRecentlySyncedBlockHeight()
|
||||
if err != nil {
|
||||
log.Fatalw("failed to find most recently synced block", "error", err)
|
||||
}
|
||||
log.Infow("Found starting point for syncing", "blockCID", blkCID.String(), "height", height)
|
||||
sinceEpoch := uint64(height)
|
||||
go func() {
|
||||
for notif := range notifs {
|
||||
for _, change := range notif {
|
||||
switch change.Type {
|
||||
case store.HCApply:
|
||||
unsynced, err := s.unsyncedBlocks(ctx, change.Val, lastSynced)
|
||||
unsynced, err := s.unsyncedBlocks(ctx, change.Val, sinceEpoch)
|
||||
if err != nil {
|
||||
log.Errorw("failed to gather unsynced blocks", "error", err)
|
||||
}
|
||||
@@ -194,13 +191,13 @@ func (s *Syncer) Start(ctx context.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := s.storeHeaders(unsynced, true, lastSynced); err != nil {
|
||||
if err := s.storeHeaders(unsynced, true, time.Now()); err != nil {
|
||||
// so this is pretty bad, need some kind of retry..
|
||||
// for now just log an error and the blocks will be attempted again on next notifi
|
||||
log.Errorw("failed to store unsynced blocks", "error", err)
|
||||
}
|
||||
|
||||
lastSynced = time.Now()
|
||||
sinceEpoch = uint64(change.Val.Height())
|
||||
case store.HCRevert:
|
||||
log.Debug("revert todo")
|
||||
}
|
||||
@@ -209,12 +206,8 @@ func (s *Syncer) Start(ctx context.Context) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Syncer) unsyncedBlocks(ctx context.Context, head *types.TipSet, since time.Time) (map[cid.Cid]*types.BlockHeader, error) {
|
||||
// get a list of blocks we have already synced in the past 3 mins. This ensures we aren't returning the entire
|
||||
// table every time.
|
||||
lookback := since.Add(-(time.Minute * 3))
|
||||
log.Debugw("Gathering unsynced blocks", "since", lookback.String())
|
||||
hasList, err := s.syncedBlocks(lookback)
|
||||
func (s *Syncer) unsyncedBlocks(ctx context.Context, head *types.TipSet, since uint64) (map[cid.Cid]*types.BlockHeader, error) {
|
||||
hasList, err := s.syncedBlocks(since, s.lookbackLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -257,9 +250,8 @@ func (s *Syncer) unsyncedBlocks(ctx context.Context, head *types.TipSet, since t
|
||||
return toSync, nil
|
||||
}
|
||||
|
||||
func (s *Syncer) syncedBlocks(timestamp time.Time) (map[cid.Cid]struct{}, error) {
|
||||
// timestamp is used to return a configurable amount of rows based on when they were last added.
|
||||
rws, err := s.db.Query(`select cid FROM blocks_synced where synced_at > $1`, timestamp.Unix())
|
||||
func (s *Syncer) syncedBlocks(since, limit uint64) (map[cid.Cid]struct{}, error) {
|
||||
rws, err := s.db.Query(`select bs.cid FROM blocks_synced bs left join blocks b on b.cid = bs.cid where b.height <= $1 and bs.processed_at is not null limit $2`, since, limit)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to query blocks_synced: %w", err)
|
||||
}
|
||||
@@ -281,14 +273,43 @@ func (s *Syncer) syncedBlocks(timestamp time.Time) (map[cid.Cid]struct{}, error)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *Syncer) mostRecentlySyncedBlockHeight() (cid.Cid, int64, error) {
|
||||
rw := s.db.QueryRow(`
|
||||
select blocks_synced.cid, b.height
|
||||
from blocks_synced
|
||||
left join blocks b on blocks_synced.cid = b.cid
|
||||
where processed_at is not null
|
||||
order by height desc
|
||||
limit 1
|
||||
`)
|
||||
|
||||
var c string
|
||||
var h int64
|
||||
if err := rw.Scan(&c, &h); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return cid.Undef, 0, nil
|
||||
}
|
||||
return cid.Undef, -1, err
|
||||
}
|
||||
|
||||
ci, err := cid.Parse(c)
|
||||
if err != nil {
|
||||
return cid.Undef, -1, err
|
||||
}
|
||||
|
||||
return ci, h, nil
|
||||
}
|
||||
|
||||
func (s *Syncer) storeCirculatingSupply(ctx context.Context, tipset *types.TipSet) error {
|
||||
supply, err := s.node.StateCirculatingSupply(ctx, tipset.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ceInsert := `insert into chain_economics (parent_state_root, circulating_fil, vested_fil, mined_fil, burnt_fil, locked_fil)` +
|
||||
`values ('%s', '%s', '%s', '%s', '%s', '%s');`
|
||||
ceInsert := `insert into chain_economics (parent_state_root, circulating_fil, vested_fil, mined_fil, burnt_fil, locked_fil) ` +
|
||||
`values ('%s', '%s', '%s', '%s', '%s', '%s') on conflict on constraint chain_economics_pk do ` +
|
||||
`update set (circulating_fil, vested_fil, mined_fil, burnt_fil, locked_fil) = ('%[2]s', '%[3]s', '%[4]s', '%[5]s', '%[6]s') ` +
|
||||
`where chain_economics.parent_state_root = '%[1]s';`
|
||||
|
||||
if _, err := s.db.Exec(fmt.Sprintf(ceInsert,
|
||||
tipset.ParentState().String(),
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/client"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
func GetFullNodeAPIUsingCredentials(ctx context.Context, listenAddr, token string) (api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
parsedAddr, err := ma.NewMultiaddr(listenAddr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_, addr, err := manet.DialArgs(parsedAddr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client.NewFullNodeRPC(ctx, apiURI(addr), apiHeaders(token))
|
||||
}
|
||||
func apiURI(addr string) string {
|
||||
return "ws://" + addr + "/rpc/v0"
|
||||
}
|
||||
func apiHeaders(token string) http.Header {
|
||||
headers := http.Header{}
|
||||
headers.Add("Authorization", "Bearer "+token)
|
||||
return headers
|
||||
}
|
||||
+1
-250
@@ -1,77 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
rice "github.com/GeertJohan/go.rice"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/power"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
)
|
||||
|
||||
var log = logging.Logger("main")
|
||||
|
||||
var supportedSectors struct {
|
||||
SectorSizes []struct {
|
||||
Name string
|
||||
Value uint64
|
||||
Default bool
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
for supportedSector := range miner.SupportedProofTypes {
|
||||
sectorSize, err := supportedSector.SectorSize()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
supportedSectors.SectorSizes = append(supportedSectors.SectorSizes, struct {
|
||||
Name string
|
||||
Value uint64
|
||||
Default bool
|
||||
}{
|
||||
Name: sectorSize.ShortString(),
|
||||
Value: uint64(sectorSize),
|
||||
Default: false,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
sort.Slice(supportedSectors.SectorSizes[:], func(i, j int) bool {
|
||||
return supportedSectors.SectorSizes[i].Value < supportedSectors.SectorSizes[j].Value
|
||||
})
|
||||
|
||||
supportedSectors.SectorSizes[0].Default = true
|
||||
}
|
||||
|
||||
func main() {
|
||||
logging.SetLogLevel("*", "INFO")
|
||||
|
||||
@@ -144,11 +94,6 @@ var runCmd = &cli.Command{
|
||||
return xerrors.Errorf("parsing source address (provide correct --from flag!): %w", err)
|
||||
}
|
||||
|
||||
defaultMinerPeer, err := peer.Decode("12D3KooWJpBNhwgvoZ15EB1JwRTRpxgM9D2fwq6eEktrJJG74aP6")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h := &handler{
|
||||
ctx: ctx,
|
||||
api: nodeApi,
|
||||
@@ -162,23 +107,10 @@ var runCmd = &cli.Command{
|
||||
WalletRate: 15 * time.Minute,
|
||||
WalletBurst: 2,
|
||||
}),
|
||||
minerLimiter: NewLimiter(LimiterConfig{
|
||||
TotalRate: 500 * time.Millisecond,
|
||||
TotalBurst: build.BlockMessageLimit,
|
||||
IPRate: 10 * time.Minute,
|
||||
IPBurst: 2,
|
||||
WalletRate: 1 * time.Hour,
|
||||
WalletBurst: 2,
|
||||
}),
|
||||
defaultMinerPeer: defaultMinerPeer,
|
||||
}
|
||||
|
||||
http.Handle("/", http.FileServer(rice.MustFindBox("site").HTTPBox()))
|
||||
http.HandleFunc("/miner.html", h.minerhtml)
|
||||
http.HandleFunc("/send", h.send)
|
||||
http.HandleFunc("/mkminer", h.mkminer)
|
||||
http.HandleFunc("/msgwait", h.msgwait)
|
||||
http.HandleFunc("/msgwaitaddr", h.msgwaitaddr)
|
||||
|
||||
fmt.Printf("Open http://%s\n", cctx.String("front"))
|
||||
|
||||
@@ -198,41 +130,7 @@ type handler struct {
|
||||
from address.Address
|
||||
sendPerRequest types.FIL
|
||||
|
||||
limiter *Limiter
|
||||
minerLimiter *Limiter
|
||||
|
||||
defaultMinerPeer peer.ID
|
||||
}
|
||||
|
||||
func (h *handler) minerhtml(w http.ResponseWriter, r *http.Request) {
|
||||
f, err := rice.MustFindBox("site").Open("_miner.html")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
tmpl, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var executedTmpl bytes.Buffer
|
||||
|
||||
t, err := template.New("miner.html").Parse(string(tmpl))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
if err := t.Execute(&executedTmpl, supportedSectors); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := io.Copy(w, &executedTmpl); err != nil {
|
||||
log.Errorf("failed to write template to string %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
limiter *Limiter
|
||||
}
|
||||
|
||||
func (h *handler) send(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -287,150 +185,3 @@ func (h *handler) send(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
_, _ = w.Write([]byte(smsg.Cid().String()))
|
||||
}
|
||||
|
||||
func (h *handler) mkminer(w http.ResponseWriter, r *http.Request) {
|
||||
owner, err := address.NewFromString(r.FormValue("address"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if owner.Protocol() != address.BLS {
|
||||
http.Error(w,
|
||||
"Miner address must use BLS. A BLS address starts with the prefix 't3'."+
|
||||
"Please create a BLS address by running \"lotus wallet new bls\" while connected to a Lotus node.",
|
||||
http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
ssize, err := strconv.ParseInt(r.FormValue("sectorSize"), 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("%s: create actor start", owner)
|
||||
|
||||
// Limit based on wallet address
|
||||
limiter := h.minerLimiter.GetWalletLimiter(owner.String())
|
||||
if !limiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests)+": wallet limit", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
// Limit based on IP
|
||||
reqIP := r.Header.Get("X-Real-IP")
|
||||
if reqIP == "" {
|
||||
h, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
log.Errorf("could not get ip from: %s, err: %s", r.RemoteAddr, err)
|
||||
}
|
||||
reqIP = h
|
||||
}
|
||||
limiter = h.minerLimiter.GetIPLimiter(reqIP)
|
||||
if !limiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests)+": IP limit", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
// General limiter owner allow throttling all messages that can make it into the mpool
|
||||
if !h.minerLimiter.Allow() {
|
||||
http.Error(w, http.StatusText(http.StatusTooManyRequests)+": global limit", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
smsg, err := h.api.MpoolPushMessage(h.ctx, &types.Message{
|
||||
Value: types.BigInt(h.sendPerRequest),
|
||||
From: h.from,
|
||||
To: owner,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
http.Error(w, "pushfunds: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
log.Infof("%s: push funds %s", owner, smsg.Cid())
|
||||
|
||||
spt, err := ffiwrapper.SealProofTypeFromSectorSize(abi.SectorSize(ssize))
|
||||
if err != nil {
|
||||
http.Error(w, "sealprooftype: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
params, err := actors.SerializeParams(&power.CreateMinerParams{
|
||||
Owner: owner,
|
||||
Worker: owner,
|
||||
SealProofType: spt,
|
||||
Peer: abi.PeerID(h.defaultMinerPeer),
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
createStorageMinerMsg := &types.Message{
|
||||
To: builtin.StoragePowerActorAddr,
|
||||
From: h.from,
|
||||
Value: big.Zero(),
|
||||
|
||||
Method: builtin.MethodsPower.CreateMiner,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
signed, err := h.api.MpoolPushMessage(r.Context(), createStorageMinerMsg, nil)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("%s: create miner msg: %s", owner, signed.Cid())
|
||||
|
||||
http.Redirect(w, r, fmt.Sprintf("/wait.html?f=%s&m=%s&o=%s", signed.Cid(), smsg.Cid(), owner), http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (h *handler) msgwait(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := cid.Parse(r.FormValue("cid"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
mw, err := h.api.StateWaitMsg(r.Context(), c, build.MessageConfidence)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func (h *handler) msgwaitaddr(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := cid.Parse(r.FormValue("cid"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
mw, err := h.api.StateWaitMsg(r.Context(), c, build.MessageConfidence)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
http.Error(w, xerrors.Errorf("create miner failed: exit code %d", mw.Receipt.ExitCode).Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
var ma power.CreateMinerReturn
|
||||
if err := ma.UnmarshalCBOR(bytes.NewReader(mw.Receipt.Return)); err != nil {
|
||||
log.Errorf("%w", err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "{\"addr\": \"%s\"}", ma.IDAddress)
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Creating Miner - Lotus Fountain</title>
|
||||
<link rel="stylesheet" type="text/css" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="Index">
|
||||
<div class="Index-nodes">
|
||||
<div class="Index-node">
|
||||
[CREATING MINER]
|
||||
</div>
|
||||
<div class="Index-node" id="formnd">
|
||||
<form id="f" action='/mkminer' method='POST'>
|
||||
<span>Enter owner/worker address:</span>
|
||||
<input type='text' name='address' style="width: 300px" placeholder="t3...">
|
||||
<select name="sectorSize">
|
||||
{{range .SectorSizes}}
|
||||
<option {{if .Default}}selected{{end}} value="{{ .Value }}">{{ .Name }}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
<button type='submit'>Create Miner</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="plswait" style="display: none" class="Index-node">
|
||||
<b>Waiting for transaction on chain..</b>
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
<span>When creating miner, DO NOT REFRESH THE PAGE, wait for it to load. This can take more than 5min.</span>
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
<span>If you don't have an owner/worker address, you can create it by following <a target="_blank" href="https://lotu.sh/en+mining#get-started-22083">these instructions</a>.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Index-footer">
|
||||
<div>
|
||||
<a href="index.html">[Back]</a>
|
||||
<span style="float: right">Not dispensing real Filecoin tokens</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let f = document.getElementById('f')
|
||||
f.onsubmit = ev => {
|
||||
document.getElementById('plswait').style.display = 'block'
|
||||
document.getElementById('formnd').style.display = 'none'
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -13,9 +13,6 @@
|
||||
<div class="Index-node">
|
||||
<a href="funds.html">[Send Funds]</a>
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
<a href="miner.html">[Create Miner]</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Index-footer">
|
||||
<div>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Creating Miner (wait) - Lotus Fountain</title>
|
||||
<link rel="stylesheet" type="text/css" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="Index">
|
||||
<div class="Index-nodes">
|
||||
<div class="Index-node">
|
||||
[CREATING MINER]
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
Gas Funds: <span id="fcid"></span> - <span id="fstate">WAIT</span>
|
||||
</div>
|
||||
<div class="Index-node">
|
||||
Miner Actor: <span id="mcid"></span> - <span id="mstate">WAIT</span>
|
||||
</div>
|
||||
<div class="Index-node" style="display: none" id="fwait">
|
||||
New miners address is: <b id="actaddr">t</b>
|
||||
</div>
|
||||
<div class="Index-node" style="display: none" id="mwait">
|
||||
<div style="padding-bottom: 1em">To initialize the miner run the following command:</div>
|
||||
<div style="overflow-x: visible; white-space: nowrap; background: #353500">
|
||||
<code>lotus-miner init --actor=<span id="actaddr2">t</span> --owner=<span id="owner">t3</span></code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Index-footer">
|
||||
<div>
|
||||
<a href="index.html">[Back]</a>
|
||||
<span style="float: right">Not dispensing real Filecoin tokens</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
const fcid = document.getElementById('fcid')
|
||||
const mcid = document.getElementById('mcid')
|
||||
|
||||
fcid.innerText = params.get('f')
|
||||
mcid.innerText = params.get('m')
|
||||
|
||||
async function waitFunds() {
|
||||
await fetch('/msgwait?cid=' + params.get('f'))
|
||||
document.getElementById('fstate').innerText = "OK"
|
||||
|
||||
document.getElementById('fwait').style.display = 'block'
|
||||
}
|
||||
|
||||
async function waitMiner() {
|
||||
const resp = await fetch('/msgwaitaddr?cid=' + params.get('f'))
|
||||
document.getElementById('mstate').innerText = "OK"
|
||||
|
||||
const addr = (await resp.json()).addr
|
||||
|
||||
document.getElementById('actaddr').innerText = addr
|
||||
document.getElementById('actaddr2').innerText = addr
|
||||
document.getElementById('owner').innerText = params.get('o')
|
||||
|
||||
document.getElementById('mwait').style.display = 'block'
|
||||
}
|
||||
|
||||
waitFunds()
|
||||
waitMiner()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -146,7 +146,7 @@ var runCmd = &cli.Command{
|
||||
}()
|
||||
|
||||
ctx := context.Background()
|
||||
api, closer, err := stats.GetFullNodeAPI(cctx.String("lotus-path"))
|
||||
api, closer, err := stats.GetFullNodeAPI(cctx.Context, cctx.String("lotus-path"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
)
|
||||
|
||||
var infoCmd = &cli.Command{
|
||||
Name: "info",
|
||||
Usage: "Print worker info",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := lcli.GetWorkerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
ver, err := api.Version(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting version: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Worker version: ", ver)
|
||||
fmt.Print("CLI version: ")
|
||||
cli.VersionPrinter(cctx)
|
||||
fmt.Println()
|
||||
|
||||
info, err := api.Info(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting info: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Hostname: %s\n", info.Hostname)
|
||||
fmt.Printf("CPUs: %d; GPUs: %v\n", info.Resources.CPUs, info.Resources.GPUs)
|
||||
fmt.Printf("RAM: %s; Swap: %s\n", types.SizeStr(types.NewInt(info.Resources.MemPhysical)), types.SizeStr(types.NewInt(info.Resources.MemSwap)))
|
||||
fmt.Printf("Reserved memory: %s\n", types.SizeStr(types.NewInt(info.Resources.MemReserved)))
|
||||
fmt.Println()
|
||||
|
||||
paths, err := api.Paths(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting path info: %w", err)
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
fmt.Printf("%s:\n", path.ID)
|
||||
fmt.Printf("\tWeight: %d; Use: ", path.Weight)
|
||||
if path.CanSeal || path.CanStore {
|
||||
fmt.Printf("Weight: %d; Use: ", path.Weight)
|
||||
if path.CanSeal {
|
||||
fmt.Print("Seal ")
|
||||
}
|
||||
if path.CanStore {
|
||||
fmt.Print("Store")
|
||||
}
|
||||
fmt.Println("")
|
||||
} else {
|
||||
fmt.Print("Use: ReadOnly")
|
||||
}
|
||||
fmt.Printf("\tLocal: %s\n", path.LocalPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
@@ -46,10 +47,10 @@ const FlagWorkerRepoDeprecation = "workerrepo"
|
||||
func main() {
|
||||
lotuslog.SetupLogLevels()
|
||||
|
||||
log.Info("Starting lotus worker")
|
||||
|
||||
local := []*cli.Command{
|
||||
runCmd,
|
||||
infoCmd,
|
||||
storageCmd,
|
||||
}
|
||||
|
||||
app := &cli.App{
|
||||
@@ -153,6 +154,8 @@ var runCmd = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
log.Info("Starting lotus worker")
|
||||
|
||||
if !cctx.Bool("enable-gpu-proving") {
|
||||
if err := os.Setenv("BELLMAN_NO_GPU", "true"); err != nil {
|
||||
return xerrors.Errorf("could not set no-gpu env: %+v", err)
|
||||
@@ -342,6 +345,8 @@ var runCmd = &cli.Command{
|
||||
SealProof: spt,
|
||||
TaskTypes: taskTypes,
|
||||
}, remote, localStore, nodeApi),
|
||||
localStore: localStore,
|
||||
ls: lr,
|
||||
}
|
||||
|
||||
mux := mux.NewRouter()
|
||||
@@ -383,6 +388,32 @@ var runCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
{
|
||||
a, err := net.ResolveTCPAddr("tcp", address)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("parsing address: %w", err)
|
||||
}
|
||||
|
||||
ma, err := manet.FromNetAddr(a)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("creating api multiaddress: %w", err)
|
||||
}
|
||||
|
||||
if err := lr.SetAPIEndpoint(ma); err != nil {
|
||||
return xerrors.Errorf("setting api endpoint: %w", err)
|
||||
}
|
||||
|
||||
ainfo, err := lcli.GetAPIInfo(cctx, repo.StorageMiner)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("could not get miner API info: %w", err)
|
||||
}
|
||||
|
||||
// TODO: ideally this would be a token with some permissions dropped
|
||||
if err := lr.SetAPIToken(ainfo.Token); err != nil {
|
||||
return xerrors.Errorf("setting api token: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Waiting for tasks")
|
||||
|
||||
go func() {
|
||||
|
||||
@@ -3,19 +3,44 @@ package main
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
|
||||
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
|
||||
)
|
||||
|
||||
type worker struct {
|
||||
*sectorstorage.LocalWorker
|
||||
|
||||
localStore *stores.Local
|
||||
ls stores.LocalStorage
|
||||
}
|
||||
|
||||
func (w *worker) Version(context.Context) (build.Version, error) {
|
||||
return build.APIVersion, nil
|
||||
}
|
||||
|
||||
func (w *worker) StorageAddLocal(ctx context.Context, path string) error {
|
||||
path, err := homedir.Expand(path)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("expanding local path: %w", err)
|
||||
}
|
||||
|
||||
if err := w.localStore.OpenPath(ctx, path); err != nil {
|
||||
return xerrors.Errorf("opening local path: %w", err)
|
||||
}
|
||||
|
||||
if err := w.ls.SetStorage(func(sc *stores.StorageConfig) {
|
||||
sc.StoragePaths = append(sc.StoragePaths, stores.LocalPath{Path: path})
|
||||
}); err != nil {
|
||||
return xerrors.Errorf("get storage config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ storage.Sealer = &worker{}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
|
||||
)
|
||||
|
||||
const metaFile = "sectorstore.json"
|
||||
|
||||
var storageCmd = &cli.Command{
|
||||
Name: "storage",
|
||||
Usage: "manage sector storage",
|
||||
Subcommands: []*cli.Command{
|
||||
storageAttachCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var storageAttachCmd = &cli.Command{
|
||||
Name: "attach",
|
||||
Usage: "attach local storage path",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "init",
|
||||
Usage: "initialize the path first",
|
||||
},
|
||||
&cli.Uint64Flag{
|
||||
Name: "weight",
|
||||
Usage: "(for init) path weight",
|
||||
Value: 10,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "seal",
|
||||
Usage: "(for init) use path for sealing",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "store",
|
||||
Usage: "(for init) use path for long-term storage",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
nodeApi, closer, err := lcli.GetWorkerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
if !cctx.Args().Present() {
|
||||
return xerrors.Errorf("must specify storage path to attach")
|
||||
}
|
||||
|
||||
p, err := homedir.Expand(cctx.Args().First())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("expanding path: %w", err)
|
||||
}
|
||||
|
||||
if cctx.Bool("init") {
|
||||
if err := os.MkdirAll(p, 0755); err != nil {
|
||||
if !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := os.Stat(filepath.Join(p, metaFile))
|
||||
if !os.IsNotExist(err) {
|
||||
if err == nil {
|
||||
return xerrors.Errorf("path is already initialized")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := &stores.LocalStorageMeta{
|
||||
ID: stores.ID(uuid.New().String()),
|
||||
Weight: cctx.Uint64("weight"),
|
||||
CanSeal: cctx.Bool("seal"),
|
||||
CanStore: cctx.Bool("store"),
|
||||
}
|
||||
|
||||
if !(cfg.CanStore || cfg.CanSeal) {
|
||||
return xerrors.Errorf("must specify at least one of --store of --seal")
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("marshaling storage config: %w", err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(p, metaFile), b, 0644); err != nil {
|
||||
return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(p, metaFile), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nodeApi.StorageAddLocal(ctx, p)
|
||||
},
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func main() {
|
||||
height = h
|
||||
}
|
||||
|
||||
api, closer, err := stats.GetFullNodeAPI(repo)
|
||||
api, closer, err := stats.GetFullNodeAPI(ctx, repo)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ var actorSetPeeridCmd = &cli.Command{
|
||||
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
pid, err := peer.IDFromString(cctx.Args().Get(0))
|
||||
pid, err := peer.Decode(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse input as a peerId: %w", err)
|
||||
}
|
||||
|
||||
@@ -236,8 +236,10 @@ var stateList = []stateMeta{
|
||||
{col: 39, state: "Total"},
|
||||
{col: color.FgGreen, state: sealing.Proving},
|
||||
|
||||
{col: color.FgBlue, state: sealing.Empty},
|
||||
{col: color.FgBlue, state: sealing.WaitDeals},
|
||||
|
||||
{col: color.FgRed, state: sealing.UndefinedSectorState},
|
||||
{col: color.FgYellow, state: sealing.Empty},
|
||||
{col: color.FgYellow, state: sealing.Packing},
|
||||
{col: color.FgYellow, state: sealing.PreCommit1},
|
||||
{col: color.FgYellow, state: sealing.PreCommit2},
|
||||
@@ -245,9 +247,13 @@ var stateList = []stateMeta{
|
||||
{col: color.FgYellow, state: sealing.PreCommitWait},
|
||||
{col: color.FgYellow, state: sealing.WaitSeed},
|
||||
{col: color.FgYellow, state: sealing.Committing},
|
||||
{col: color.FgYellow, state: sealing.SubmitCommit},
|
||||
{col: color.FgYellow, state: sealing.CommitWait},
|
||||
{col: color.FgYellow, state: sealing.FinalizeSector},
|
||||
|
||||
{col: color.FgCyan, state: sealing.Removing},
|
||||
{col: color.FgCyan, state: sealing.Removed},
|
||||
|
||||
{col: color.FgRed, state: sealing.FailedUnrecoverable},
|
||||
{col: color.FgRed, state: sealing.SealPreCommit1Failed},
|
||||
{col: color.FgRed, state: sealing.SealPreCommit2Failed},
|
||||
@@ -259,6 +265,9 @@ var stateList = []stateMeta{
|
||||
{col: color.FgRed, state: sealing.Faulty},
|
||||
{col: color.FgRed, state: sealing.FaultReported},
|
||||
{col: color.FgRed, state: sealing.FaultedFinal},
|
||||
{col: color.FgRed, state: sealing.RemoveFailed},
|
||||
{col: color.FgRed, state: sealing.DealsExpired},
|
||||
{col: color.FgRed, state: sealing.RecoverDealIDs},
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
mux "github.com/gorilla/mux"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
|
||||
@@ -155,6 +155,9 @@ var sealingJobsCmd = &cli.Command{
|
||||
|
||||
// oldest first
|
||||
sort.Slice(lines, func(i, j int) bool {
|
||||
if lines[i].RunWait != lines[j].RunWait {
|
||||
return lines[i].RunWait < lines[j].RunWait
|
||||
}
|
||||
return lines[i].Start.Before(lines[j].Start)
|
||||
})
|
||||
|
||||
@@ -170,10 +173,14 @@ var sealingJobsCmd = &cli.Command{
|
||||
}
|
||||
|
||||
tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(tw, "ID\tSector\tWorker\tHostname\tTask\tTime\n")
|
||||
_, _ = fmt.Fprintf(tw, "ID\tSector\tWorker\tHostname\tTask\tState\tTime\n")
|
||||
|
||||
for _, l := range lines {
|
||||
_, _ = fmt.Fprintf(tw, "%d\t%d\t%d\t%s\t%s\t%s\n", l.ID, l.Sector.Number, l.wid, workerHostnames[l.wid], l.Task.Short(), time.Now().Sub(l.Start).Truncate(time.Millisecond*100))
|
||||
state := "running"
|
||||
if l.RunWait != 0 {
|
||||
state = fmt.Sprintf("assigned(%d)", l.RunWait-1)
|
||||
}
|
||||
_, _ = fmt.Fprintf(tw, "%d\t%d\t%d\t%s\t%s\t%s\t%s\n", l.ID, l.Sector.Number, l.wid, workerHostnames[l.wid], l.Task.Short(), state, time.Now().Sub(l.Start).Truncate(time.Millisecond*100))
|
||||
}
|
||||
|
||||
return tw.Flush()
|
||||
|
||||
@@ -97,6 +97,8 @@ var sectorsStatusCmd = &cli.Command{
|
||||
fmt.Printf("TicketH:\t%d\n", status.Ticket.Epoch)
|
||||
fmt.Printf("Seed:\t\t%x\n", status.Seed.Value)
|
||||
fmt.Printf("SeedH:\t\t%d\n", status.Seed.Epoch)
|
||||
fmt.Printf("Precommit:\t%s\n", status.PreCommitMsg)
|
||||
fmt.Printf("Commit:\t\t%s\n", status.CommitMsg)
|
||||
fmt.Printf("Proof:\t\t%x\n", status.Proof)
|
||||
fmt.Printf("Deals:\t\t%v\n", status.Deals)
|
||||
fmt.Printf("Retries:\t%d\n", status.Retries)
|
||||
|
||||
@@ -345,6 +345,16 @@ func ImportChain(r repo.Repo, fname string) error {
|
||||
return xerrors.Errorf("importing chain failed: %w", err)
|
||||
}
|
||||
|
||||
gb, err := cst.GetTipsetByHeight(context.TODO(), 0, ts, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cst.SetGenesis(gb.Blocks()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stm := stmgr.NewStateManager(cst)
|
||||
|
||||
log.Infof("validating imported chain...")
|
||||
|
||||
@@ -60,7 +60,9 @@ func init() {
|
||||
}
|
||||
|
||||
}
|
||||
// TODO: beacon
|
||||
|
||||
mbi, err := api.MinerGetBaseInfo(ctx, addr, head.Height()+1, head.Key())
|
||||
|
||||
ep := &types.ElectionProof{}
|
||||
ep.WinCount = ep.ComputeWinCount(types.NewInt(1), types.NewInt(1))
|
||||
for ep.WinCount == 0 {
|
||||
@@ -75,7 +77,7 @@ func init() {
|
||||
uts := head.MinTimestamp() + uint64(build.BlockDelaySecs)
|
||||
nheight := head.Height() + 1
|
||||
blk, err := api.MinerCreateBlock(ctx, &lapi.BlockTemplate{
|
||||
addr, head.Key(), ticket, ep, nil, msgs, nheight, uts, gen.ValidWpostForTesting,
|
||||
addr, head.Key(), ticket, ep, mbi.BeaconEntries, msgs, nheight, uts, gen.ValidWpostForTesting,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("creating block: %w", err)
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr-net"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"contrib.go.opencensus.io/exporter/prometheus"
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package conformance
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"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/blockstore"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/puppet"
|
||||
|
||||
"github.com/filecoin-project/test-vectors/chaos"
|
||||
"github.com/filecoin-project/test-vectors/schema"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
var (
|
||||
// BaseFee to use in the VM.
|
||||
// TODO make parametrisable through vector.
|
||||
BaseFee = abi.NewTokenAmount(100)
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
ctx context.Context
|
||||
vector *schema.TestVector
|
||||
}
|
||||
|
||||
func NewDriver(ctx context.Context, vector *schema.TestVector) *Driver {
|
||||
return &Driver{ctx: ctx, vector: vector}
|
||||
}
|
||||
|
||||
// ExecuteMessage executes a conformance test vector message in a temporary VM.
|
||||
func (d *Driver) ExecuteMessage(msg *types.Message, preroot cid.Cid, bs blockstore.Blockstore, epoch abi.ChainEpoch) (*vm.ApplyRet, cid.Cid, error) {
|
||||
vmOpts := &vm.VMOpts{
|
||||
StateBase: preroot,
|
||||
Epoch: epoch,
|
||||
Rand: &testRand{}, // TODO always succeeds; need more flexibility.
|
||||
Bstore: bs,
|
||||
Syscalls: mkFakedSigSyscalls(vm.Syscalls(ffiwrapper.ProofVerifier)), // TODO always succeeds; need more flexibility.
|
||||
CircSupplyCalc: nil,
|
||||
BaseFee: BaseFee,
|
||||
}
|
||||
|
||||
lvm, err := vm.NewVM(vmOpts)
|
||||
if err != nil {
|
||||
return nil, cid.Undef, err
|
||||
}
|
||||
|
||||
invoker := vm.NewInvoker()
|
||||
|
||||
// add support for the puppet and chaos actors.
|
||||
if puppetOn, ok := d.vector.Selector["puppet_actor"]; ok && puppetOn == "true" {
|
||||
invoker.Register(puppet.PuppetActorCodeID, puppet.Actor{}, puppet.State{})
|
||||
}
|
||||
if chaosOn, ok := d.vector.Selector["chaos_actor"]; ok && chaosOn == "true" {
|
||||
invoker.Register(chaos.ChaosActorCodeCID, chaos.Actor{}, chaos.State{})
|
||||
}
|
||||
|
||||
lvm.SetInvoker(invoker)
|
||||
|
||||
ret, err := lvm.ApplyMessage(d.ctx, msg)
|
||||
if err != nil {
|
||||
return nil, cid.Undef, err
|
||||
}
|
||||
|
||||
root, err := lvm.Flush(d.ctx)
|
||||
return ret, root, err
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package conformance
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
"github.com/filecoin-project/lotus/lib/blockstore"
|
||||
|
||||
"github.com/filecoin-project/statediff"
|
||||
"github.com/filecoin-project/test-vectors/schema"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/ipld/go-car"
|
||||
)
|
||||
|
||||
const (
|
||||
// EnvSkipConformance, if 1, skips the conformance test suite.
|
||||
EnvSkipConformance = "SKIP_CONFORMANCE"
|
||||
|
||||
// EnvCorpusRootDir is the name of the environment variable where the path
|
||||
// to an alternative corpus location can be provided.
|
||||
//
|
||||
// The default is defaultCorpusRoot.
|
||||
EnvCorpusRootDir = "CORPUS_DIR"
|
||||
|
||||
// defaultCorpusRoot is the directory where the test vector corpus is hosted.
|
||||
// It is mounted on the Lotus repo as a git submodule.
|
||||
//
|
||||
// When running this test, the corpus root can be overridden through the
|
||||
// -conformance.corpus CLI flag to run an alternate corpus.
|
||||
defaultCorpusRoot = "../extern/test-vectors/corpus"
|
||||
)
|
||||
|
||||
// ignore is a set of paths relative to root to skip.
|
||||
var ignore = map[string]struct{}{
|
||||
".git": {},
|
||||
"schema.json": {},
|
||||
}
|
||||
|
||||
// TestConformance is the entrypoint test that runs all test vectors found
|
||||
// in the corpus root directory.
|
||||
//
|
||||
// It locates all json files via a recursive walk, skipping over the ignore set,
|
||||
// as well as files beginning with _. It parses each file as a test vector, and
|
||||
// runs it via the Driver.
|
||||
func TestConformance(t *testing.T) {
|
||||
if skip := strings.TrimSpace(os.Getenv(EnvSkipConformance)); skip == "1" {
|
||||
t.SkipNow()
|
||||
}
|
||||
// corpusRoot is the effective corpus root path, taken from the `-conformance.corpus` CLI flag,
|
||||
// falling back to defaultCorpusRoot if not provided.
|
||||
corpusRoot := defaultCorpusRoot
|
||||
if dir := strings.TrimSpace(os.Getenv(EnvCorpusRootDir)); dir != "" {
|
||||
corpusRoot = dir
|
||||
}
|
||||
|
||||
var vectors []string
|
||||
err := filepath.Walk(corpusRoot+"/", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
filename := filepath.Base(path)
|
||||
rel, err := filepath.Rel(corpusRoot, path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, ok := ignore[rel]; ok {
|
||||
// skip over using the right error.
|
||||
if info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if info.IsDir() {
|
||||
// dive into directories.
|
||||
return nil
|
||||
}
|
||||
if filepath.Ext(path) != ".json" {
|
||||
// skip if not .json.
|
||||
return nil
|
||||
}
|
||||
if ignored := strings.HasPrefix(filename, "_"); ignored {
|
||||
// ignore files starting with _.
|
||||
t.Logf("ignoring: %s", rel)
|
||||
return nil
|
||||
}
|
||||
vectors = append(vectors, rel)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(vectors) == 0 {
|
||||
t.Fatalf("no test vectors found")
|
||||
}
|
||||
|
||||
// Run a test for each vector.
|
||||
for _, v := range vectors {
|
||||
path := filepath.Join(corpusRoot, v)
|
||||
raw, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read test raw file: %s", path)
|
||||
}
|
||||
|
||||
var vector schema.TestVector
|
||||
err = json.Unmarshal(raw, &vector)
|
||||
if err != nil {
|
||||
t.Errorf("failed to parse test vector %s: %s; skipping", path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
t.Run(v, func(t *testing.T) {
|
||||
for _, h := range vector.Hints {
|
||||
if h == schema.HintIncorrect {
|
||||
t.Logf("skipping vector marked as incorrect: %s", vector.Meta.ID)
|
||||
t.SkipNow()
|
||||
}
|
||||
}
|
||||
|
||||
// dispatch the execution depending on the vector class.
|
||||
switch vector.Class {
|
||||
case "message":
|
||||
executeMessageVector(t, &vector)
|
||||
default:
|
||||
t.Fatalf("test vector class not supported: %s", vector.Class)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// executeMessageVector executes a message-class test vector.
|
||||
func executeMessageVector(t *testing.T, vector *schema.TestVector) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
epoch = vector.Pre.Epoch
|
||||
root = vector.Pre.StateTree.RootCID
|
||||
)
|
||||
|
||||
bs := blockstore.NewTemporary()
|
||||
|
||||
// Read the base64-encoded CAR from the vector, and inflate the gzip.
|
||||
buf := bytes.NewReader(vector.CAR)
|
||||
r, err := gzip.NewReader(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to inflate gzipped CAR: %s", err)
|
||||
}
|
||||
defer r.Close() // nolint
|
||||
|
||||
// Load the CAR embedded in the test vector into the Blockstore.
|
||||
_, err = car.LoadCar(bs, r)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load state tree car from test vector: %s", err)
|
||||
}
|
||||
|
||||
// Create a new Driver.
|
||||
driver := NewDriver(ctx, vector)
|
||||
|
||||
// Apply every message.
|
||||
for i, m := range vector.ApplyMessages {
|
||||
msg, err := types.DecodeMessage(m.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to deserialize message: %s", err)
|
||||
}
|
||||
|
||||
// add an epoch if one's set.
|
||||
if m.Epoch != nil {
|
||||
epoch = *m.Epoch
|
||||
}
|
||||
|
||||
// Execute the message.
|
||||
var ret *vm.ApplyRet
|
||||
ret, root, err = driver.ExecuteMessage(msg, root, bs, epoch)
|
||||
if err != nil {
|
||||
t.Fatalf("fatal failure when executing message: %s", err)
|
||||
}
|
||||
|
||||
// Assert that the receipt matches what the test vector expects.
|
||||
receipt := vector.Post.Receipts[i]
|
||||
if expected, actual := receipt.ExitCode, ret.ExitCode; expected != actual {
|
||||
t.Errorf("exit code of msg %d did not match; expected: %s, got: %s", i, expected, actual)
|
||||
}
|
||||
if expected, actual := receipt.GasUsed, ret.GasUsed; expected != actual {
|
||||
t.Errorf("gas used of msg %d did not match; expected: %d, got: %d", i, expected, actual)
|
||||
}
|
||||
if expected, actual := []byte(receipt.ReturnValue), ret.Return; !bytes.Equal(expected, actual) {
|
||||
t.Errorf("return value of msg %d did not match; expected: %s, got: %s", i, base64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(actual))
|
||||
}
|
||||
}
|
||||
|
||||
// Once all messages are applied, assert that the final state root matches
|
||||
// the expected postcondition root.
|
||||
if root != vector.Post.StateTree.RootCID {
|
||||
color.NoColor = false // enable colouring.
|
||||
|
||||
t.Errorf("wrong post root cid; expected %v, but got %v", vector.Post.StateTree.RootCID, root)
|
||||
|
||||
var (
|
||||
a = color.New(color.FgMagenta, color.Bold).Sprint("(A) expected final state")
|
||||
b = color.New(color.FgYellow, color.Bold).Sprint("(B) actual final state")
|
||||
c = color.New(color.FgCyan, color.Bold).Sprint("(C) initial state")
|
||||
d1 = color.New(color.FgGreen, color.Bold).Sprint("[Δ1]")
|
||||
d2 = color.New(color.FgGreen, color.Bold).Sprint("[Δ2]")
|
||||
d3 = color.New(color.FgGreen, color.Bold).Sprint("[Δ3]")
|
||||
)
|
||||
|
||||
bold := color.New(color.Bold).SprintfFunc()
|
||||
|
||||
// run state diffs.
|
||||
t.Log(bold("=== dumping 3-way diffs between %s, %s, %s ===", a, b, c))
|
||||
|
||||
t.Log(bold("--- %s left: %s; right: %s ---", d1, a, b))
|
||||
t.Log(statediff.Diff(context.Background(), bs, vector.Post.StateTree.RootCID, root))
|
||||
|
||||
t.Log(bold("--- %s left: %s; right: %s ---", d2, c, b))
|
||||
t.Log(statediff.Diff(context.Background(), bs, vector.Pre.StateTree.RootCID, root))
|
||||
|
||||
t.Log(bold("--- %s left: %s; right: %s ---", d3, c, a))
|
||||
t.Log(statediff.Diff(context.Background(), bs, vector.Pre.StateTree.RootCID, vector.Post.StateTree.RootCID))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package conformance
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/vm"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime"
|
||||
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
)
|
||||
|
||||
type testRand struct{}
|
||||
|
||||
var _ vm.Rand = (*testRand)(nil)
|
||||
|
||||
func (r *testRand) GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||
return []byte("i_am_random_____i_am_random_____"), nil // 32 bytes.
|
||||
}
|
||||
|
||||
func (r *testRand) GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||
return []byte("i_am_random_____i_am_random_____"), nil // 32 bytes.
|
||||
}
|
||||
|
||||
type testSyscalls struct {
|
||||
runtime.Syscalls
|
||||
}
|
||||
|
||||
// TODO VerifySignature this will always succeed; but we want to be able to test failures too.
|
||||
func (fss *testSyscalls) VerifySignature(_ crypto.Signature, _ address.Address, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO VerifySeal this will always succeed; but we want to be able to test failures too.
|
||||
func (fss *testSyscalls) VerifySeal(_ abi.SealVerifyInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO VerifyPoSt this will always succeed; but we want to be able to test failures too.
|
||||
func (fss *testSyscalls) VerifyPoSt(_ abi.WindowPoStVerifyInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func mkFakedSigSyscalls(base vm.SyscallBuilder) vm.SyscallBuilder {
|
||||
return func(ctx context.Context, cstate *state.StateTree, cst cbor.IpldStore) runtime.Syscalls {
|
||||
return &testSyscalls{
|
||||
base(ctx, cstate, cst),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@
|
||||
* [ClientFindData](#ClientFindData)
|
||||
* [ClientGenCar](#ClientGenCar)
|
||||
* [ClientGetDealInfo](#ClientGetDealInfo)
|
||||
* [ClientGetDealUpdates](#ClientGetDealUpdates)
|
||||
* [ClientHasLocal](#ClientHasLocal)
|
||||
* [ClientImport](#ClientImport)
|
||||
* [ClientListDataTransfers](#ClientListDataTransfers)
|
||||
@@ -197,7 +198,7 @@ Response:
|
||||
```json
|
||||
{
|
||||
"Version": "string value",
|
||||
"APIVersion": 3072,
|
||||
"APIVersion": 3584,
|
||||
"BlockDelay": 42
|
||||
}
|
||||
```
|
||||
@@ -915,6 +916,42 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
### ClientGetDealUpdates
|
||||
ClientGetDealUpdates returns the status of updated deals
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs: `null`
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"ProposalCid": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
"State": 42,
|
||||
"Message": "string value",
|
||||
"Provider": "t01234",
|
||||
"DataRef": {
|
||||
"TransferType": "string value",
|
||||
"Root": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
"PieceCid": null,
|
||||
"PieceSize": 1024
|
||||
},
|
||||
"PieceCID": {
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
},
|
||||
"Size": 42,
|
||||
"PricePerEpoch": "0",
|
||||
"Duration": 42,
|
||||
"DealID": 5432,
|
||||
"CreationTime": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### ClientHasLocal
|
||||
ClientHasLocal indicates whether a certain CID is locally stored.
|
||||
|
||||
@@ -2437,7 +2474,9 @@ Inputs:
|
||||
"Type": 2,
|
||||
"Data": "Ynl0ZSBhcnJheQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"Ynl0ZSBhcnJheQ==",
|
||||
"Ynl0ZSBhcnJheQ=="
|
||||
]
|
||||
```
|
||||
|
||||
@@ -3603,7 +3642,7 @@ Response:
|
||||
```
|
||||
|
||||
### StateSectorGetInfo
|
||||
StateSectorGetInfo returns the on-chain info for the specified miner's sector
|
||||
StateSectorGetInfo returns the on-chain info for the specified miner's sector. Returns null in case the sector info isn't found
|
||||
NOTE: returned info.Expiration may not be accurate in some cases, use StateSectorExpiration to get accurate
|
||||
expiration epoch
|
||||
|
||||
|
||||
@@ -48,6 +48,11 @@ block heights that are very far from the current chain height, you may want to
|
||||
increase this.
|
||||
|
||||
|
||||
### `LOTUS_BSYNC_MSG_WINDOW`
|
||||
|
||||
Set the initial maximum window size for message fetching blocksync requests. If
|
||||
you have a slower internet connection and are having trouble syncing, you might
|
||||
try lowering this down to 10-20 for a 'poor' internet connection.
|
||||
|
||||
## Lotus Miner
|
||||
|
||||
|
||||
@@ -33,26 +33,6 @@ With your wallet address:
|
||||
- Press the Request button.
|
||||
- Run `/lotus-miner init --owner=<blsAddress> --worker=<blsAddress>`
|
||||
|
||||
The task will be complete when you see:
|
||||
|
||||
```sh
|
||||
New miners address is: <YOUR_NEW_MINING_ADDRESS>
|
||||
```
|
||||
|
||||
## Initialize the miner
|
||||
|
||||
In a CLI window, use the following command to start your miner:
|
||||
|
||||
```sh
|
||||
lotus-miner init --actor=ACTOR_VALUE_RECEIVED --owner=OWNER_VALUE_RECEIVED
|
||||
```
|
||||
|
||||
Example
|
||||
|
||||
```sh
|
||||
lotus-miner init --actor=t01424 --owner=t3spmep2xxsl33o4gxk7yjxcobyohzgj3vejzerug25iinbznpzob6a6kexcbeix73th6vjtzfq7boakfdtd6a
|
||||
```
|
||||
|
||||
You will have to wait some time for this operation to complete.
|
||||
|
||||
## Mining
|
||||
|
||||
@@ -133,7 +133,7 @@ type MpoolConfig struct {
|
||||
|
||||
The meaning of these fields is as follows:
|
||||
- `PriorityAddrs` -- these are the addresses of actors whose pending messages should always
|
||||
be included in a block during message selection, regardless of profitability.
|
||||
be included in a block during message selection, as long as they are profitable.
|
||||
Miners should configure their own worker addresses so that they include their own messages
|
||||
when they produce a new block.
|
||||
Default is empty.
|
||||
|
||||
Vendored
+2
-2
@@ -208,8 +208,8 @@ func (l *LocalWorker) Remove(ctx context.Context, sector abi.SectorID) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *LocalWorker) MoveStorage(ctx context.Context, sector abi.SectorID) error {
|
||||
if err := l.storage.MoveStorage(ctx, sector, l.scfg.SealProofType, stores.FTSealed|stores.FTCache); err != nil {
|
||||
func (l *LocalWorker) MoveStorage(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error {
|
||||
if err := l.storage.MoveStorage(ctx, sector, l.scfg.SealProofType, types); err != nil {
|
||||
return xerrors.Errorf("moving sealed data to storage: %w", err)
|
||||
}
|
||||
|
||||
|
||||
Vendored
+14
-20
@@ -6,8 +6,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
@@ -17,6 +16,7 @@ import (
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/sealtasks"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||
@@ -31,7 +31,7 @@ type URLs []string
|
||||
type Worker interface {
|
||||
ffiwrapper.StorageSealer
|
||||
|
||||
MoveStorage(ctx context.Context, sector abi.SectorID) error
|
||||
MoveStorage(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error
|
||||
|
||||
Fetch(ctx context.Context, s abi.SectorID, ft stores.SectorFileType, ptype stores.PathType, am stores.AcquireMode) error
|
||||
UnsealPiece(context.Context, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize, abi.SealRandomness, cid.Cid) error
|
||||
@@ -441,7 +441,7 @@ func (m *Manager) FinalizeSector(ctx context.Context, sector abi.SectorID, keepU
|
||||
err = m.sched.Schedule(ctx, sector, sealtasks.TTFetch, fetchSel,
|
||||
schedFetch(sector, stores.FTCache|stores.FTSealed|moveUnsealed, stores.PathStorage, stores.AcquireMove),
|
||||
func(ctx context.Context, w Worker) error {
|
||||
return w.MoveStorage(ctx, sector)
|
||||
return w.MoveStorage(ctx, sector, stores.FTCache|stores.FTSealed|moveUnsealed)
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("moving sector to storage: %w", err)
|
||||
@@ -463,25 +463,19 @@ func (m *Manager) Remove(ctx context.Context, sector abi.SectorID) error {
|
||||
return xerrors.Errorf("acquiring sector lock: %w", err)
|
||||
}
|
||||
|
||||
unsealed := stores.FTUnsealed
|
||||
{
|
||||
unsealedStores, err := m.index.StorageFindSector(ctx, sector, stores.FTUnsealed, 0, false)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("finding unsealed sector: %w", err)
|
||||
}
|
||||
var err error
|
||||
|
||||
if len(unsealedStores) == 0 { // can be already removed
|
||||
unsealed = stores.FTNone
|
||||
}
|
||||
if rerr := m.storage.Remove(ctx, sector, stores.FTSealed, true); rerr != nil {
|
||||
err = multierror.Append(err, xerrors.Errorf("removing sector (sealed): %w", rerr))
|
||||
}
|
||||
if rerr := m.storage.Remove(ctx, sector, stores.FTCache, true); rerr != nil {
|
||||
err = multierror.Append(err, xerrors.Errorf("removing sector (cache): %w", rerr))
|
||||
}
|
||||
if rerr := m.storage.Remove(ctx, sector, stores.FTUnsealed, true); rerr != nil {
|
||||
err = multierror.Append(err, xerrors.Errorf("removing sector (unsealed): %w", rerr))
|
||||
}
|
||||
|
||||
selector := newExistingSelector(m.index, sector, stores.FTCache|stores.FTSealed, false)
|
||||
|
||||
return m.sched.Schedule(ctx, sector, sealtasks.TTFinalize, selector,
|
||||
schedFetch(sector, stores.FTCache|stores.FTSealed|unsealed, stores.PathStorage, stores.AcquireMove),
|
||||
func(ctx context.Context, w Worker) error {
|
||||
return w.Remove(ctx, sector)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Manager) StorageLocal(ctx context.Context) (map[stores.ID]string, error) {
|
||||
|
||||
Vendored
+16
-45
@@ -3,12 +3,12 @@ package mock
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"sync"
|
||||
|
||||
"github.com/filecoin-project/go-bitfield"
|
||||
commcid "github.com/filecoin-project/go-fil-commcid"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
@@ -291,32 +291,29 @@ func (mgr *SectorMgr) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorI
|
||||
return generateFakePoSt(si, abi.RegisteredSealProof.RegisteredWindowPoStProof, randomness), skipped, nil
|
||||
}
|
||||
|
||||
func generateFakePoSt(sectorInfo []abi.SectorInfo, rpt func(abi.RegisteredSealProof) (abi.RegisteredPoStProof, error), randomness abi.PoStRandomness) []abi.PoStProof {
|
||||
sectors := bitfield.New()
|
||||
func generateFakePoStProof(sectorInfo []abi.SectorInfo, randomness abi.PoStRandomness) []byte {
|
||||
hasher := sha256.New()
|
||||
_, _ = hasher.Write(randomness)
|
||||
for _, info := range sectorInfo {
|
||||
sectors.Set(uint64(info.SectorNumber))
|
||||
err := info.MarshalCBOR(hasher)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return hasher.Sum(nil)
|
||||
|
||||
}
|
||||
|
||||
func generateFakePoSt(sectorInfo []abi.SectorInfo, rpt func(abi.RegisteredSealProof) (abi.RegisteredPoStProof, error), randomness abi.PoStRandomness) []abi.PoStProof {
|
||||
wp, err := rpt(sectorInfo[0].SealProof)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var proofBuf bytes.Buffer
|
||||
|
||||
_, err = proofBuf.Write(randomness)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := sectors.MarshalCBOR(&proofBuf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return []abi.PoStProof{
|
||||
{
|
||||
PoStProof: wp,
|
||||
ProofBytes: proofBuf.Bytes(),
|
||||
ProofBytes: generateFakePoStProof(sectorInfo, randomness),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -412,36 +409,10 @@ func (m mockVerif) VerifyWindowPoSt(ctx context.Context, info abi.WindowPoStVeri
|
||||
|
||||
proof := info.Proofs[0]
|
||||
|
||||
if !bytes.Equal(proof.ProofBytes[:len(info.Randomness)], info.Randomness) {
|
||||
return false, xerrors.Errorf("bad randomness")
|
||||
expected := generateFakePoStProof(info.ChallengedSectors, info.Randomness)
|
||||
if !bytes.Equal(proof.ProofBytes, expected) {
|
||||
return false, xerrors.Errorf("bad proof")
|
||||
}
|
||||
|
||||
sectors := bitfield.New()
|
||||
if err := sectors.UnmarshalCBOR(bytes.NewReader(proof.ProofBytes[len(info.Randomness):])); err != nil {
|
||||
return false, xerrors.Errorf("unmarshaling sectors bitfield from \"proof\": %w", err)
|
||||
}
|
||||
|
||||
challenged := bitfield.New()
|
||||
for _, sector := range info.ChallengedSectors {
|
||||
challenged.Set(uint64(sector.SectorNumber))
|
||||
}
|
||||
|
||||
{
|
||||
b1, err := sectors.MarshalJSON()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
b2, err := challenged.MarshalJSON()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(b1, b2) {
|
||||
return false, xerrors.Errorf("proven and challenged sector sets didn't match: %s != %s", string(b1), string(b2))
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -7,6 +7,11 @@ type requestQueue []*workerRequest
|
||||
func (q requestQueue) Len() int { return len(q) }
|
||||
|
||||
func (q requestQueue) Less(i, j int) bool {
|
||||
oneMuchLess, muchLess := q[i].taskType.MuchLess(q[j].taskType)
|
||||
if oneMuchLess {
|
||||
return muchLess
|
||||
}
|
||||
|
||||
if q[i].priority != q[j].priority {
|
||||
return q[i].priority > q[j].priority
|
||||
}
|
||||
|
||||
Vendored
+6
-6
@@ -22,17 +22,17 @@ func (r Resources) MultiThread() bool {
|
||||
|
||||
var ResourceTable = map[sealtasks.TaskType]map[abi.RegisteredSealProof]Resources{
|
||||
sealtasks.TTAddPiece: {
|
||||
abi.RegisteredSealProof_StackedDrg64GiBV1: Resources{ // This is probably a bit conservative
|
||||
MaxMemory: 64 << 30,
|
||||
MinMemory: 64 << 30,
|
||||
abi.RegisteredSealProof_StackedDrg64GiBV1: Resources{
|
||||
MaxMemory: 8 << 30,
|
||||
MinMemory: 8 << 30,
|
||||
|
||||
Threads: 1,
|
||||
|
||||
BaseMinMemory: 1 << 30,
|
||||
},
|
||||
abi.RegisteredSealProof_StackedDrg32GiBV1: Resources{ // This is probably a bit conservative
|
||||
MaxMemory: 32 << 30,
|
||||
MinMemory: 32 << 30,
|
||||
abi.RegisteredSealProof_StackedDrg32GiBV1: Resources{
|
||||
MaxMemory: 4 << 30,
|
||||
MinMemory: 4 << 30,
|
||||
|
||||
Threads: 1,
|
||||
|
||||
|
||||
Vendored
+134
-21
@@ -21,6 +21,7 @@ type schedPrioCtxKey int
|
||||
var SchedPriorityKey schedPrioCtxKey
|
||||
var DefaultSchedPriority = 0
|
||||
var SelectorTimeout = 5 * time.Second
|
||||
var InitWait = 3 * time.Second
|
||||
|
||||
var (
|
||||
SchedWindows = 2
|
||||
@@ -85,6 +86,9 @@ type workerHandle struct {
|
||||
|
||||
lk sync.Mutex
|
||||
|
||||
wndLk sync.Mutex
|
||||
activeWindows []*schedWindow
|
||||
|
||||
// stats / tracking
|
||||
wt *workTracker
|
||||
|
||||
@@ -123,6 +127,8 @@ type workerRequest struct {
|
||||
prepare WorkerAction
|
||||
work WorkerAction
|
||||
|
||||
start time.Time
|
||||
|
||||
index int // The index of the item in the heap.
|
||||
|
||||
indexHeap int
|
||||
@@ -147,7 +153,7 @@ func newScheduler(spt abi.RegisteredSealProof) *scheduler {
|
||||
workerClosing: make(chan WorkerID),
|
||||
|
||||
schedule: make(chan *workerRequest),
|
||||
windowRequests: make(chan *schedWindowRequest),
|
||||
windowRequests: make(chan *schedWindowRequest, 20),
|
||||
|
||||
schedQueue: &requestQueue{},
|
||||
|
||||
@@ -171,6 +177,8 @@ func (sh *scheduler) Schedule(ctx context.Context, sector abi.SectorID, taskType
|
||||
prepare: prepare,
|
||||
work: work,
|
||||
|
||||
start: time.Now(),
|
||||
|
||||
ret: ret,
|
||||
ctx: ctx,
|
||||
}:
|
||||
@@ -214,7 +222,12 @@ func (sh *scheduler) runSched() {
|
||||
|
||||
go sh.runWorkerWatcher()
|
||||
|
||||
iw := time.After(InitWait)
|
||||
var initialised bool
|
||||
|
||||
for {
|
||||
var doSched bool
|
||||
|
||||
select {
|
||||
case w := <-sh.newWorkers:
|
||||
sh.newWorker(w)
|
||||
@@ -224,22 +237,47 @@ func (sh *scheduler) runSched() {
|
||||
|
||||
case req := <-sh.schedule:
|
||||
sh.schedQueue.Push(req)
|
||||
sh.trySched()
|
||||
doSched = true
|
||||
|
||||
if sh.testSync != nil {
|
||||
sh.testSync <- struct{}{}
|
||||
}
|
||||
case req := <-sh.windowRequests:
|
||||
sh.openWindows = append(sh.openWindows, req)
|
||||
sh.trySched()
|
||||
|
||||
doSched = true
|
||||
case ireq := <-sh.info:
|
||||
ireq(sh.diag())
|
||||
|
||||
case <-iw:
|
||||
initialised = true
|
||||
iw = nil
|
||||
doSched = true
|
||||
case <-sh.closing:
|
||||
sh.schedClose()
|
||||
return
|
||||
}
|
||||
|
||||
if doSched && initialised {
|
||||
// First gather any pending tasks, so we go through the scheduling loop
|
||||
// once for every added task
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case req := <-sh.schedule:
|
||||
sh.schedQueue.Push(req)
|
||||
if sh.testSync != nil {
|
||||
sh.testSync <- struct{}{}
|
||||
}
|
||||
case req := <-sh.windowRequests:
|
||||
sh.openWindows = append(sh.openWindows, req)
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
sh.trySched()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +359,7 @@ func (sh *scheduler) trySched() {
|
||||
}
|
||||
|
||||
// TODO: allow bigger windows
|
||||
if !windows[wnd].allocated.canHandleRequest(needRes, windowRequest.worker, worker.info.Resources) {
|
||||
if !windows[wnd].allocated.canHandleRequest(needRes, windowRequest.worker, "schedAcceptable", worker.info.Resources) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -392,13 +430,17 @@ func (sh *scheduler) trySched() {
|
||||
log.Debugf("SCHED try assign sqi:%d sector %d to window %d", sqi, task.sector.Number, wnd)
|
||||
|
||||
// TODO: allow bigger windows
|
||||
if !windows[wnd].allocated.canHandleRequest(needRes, wid, wr) {
|
||||
if !windows[wnd].allocated.canHandleRequest(needRes, wid, "schedAssign", wr) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("SCHED ASSIGNED sqi:%d sector %d to window %d", sqi, task.sector.Number, wnd)
|
||||
log.Debugf("SCHED ASSIGNED sqi:%d sector %d task %s to window %d", sqi, task.sector.Number, task.taskType, wnd)
|
||||
|
||||
windows[wnd].allocated.add(wr, needRes)
|
||||
// TODO: We probably want to re-sort acceptableWindows here based on new
|
||||
// workerHandle.utilization + windows[wnd].allocated.utilization (workerHandle.utilization is used in all
|
||||
// task selectors, but not in the same way, so need to figure out how to do that in a non-O(n^2 way), and
|
||||
// without additional network roundtrips (O(n^2) could be avoided by turning acceptableWindows.[] into heaps))
|
||||
|
||||
selectedWindow = wnd
|
||||
break
|
||||
@@ -475,8 +517,6 @@ func (sh *scheduler) runWorker(wid WorkerID) {
|
||||
taskDone := make(chan struct{}, 1)
|
||||
windowsRequested := 0
|
||||
|
||||
var activeWindows []*schedWindow
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
|
||||
@@ -510,7 +550,9 @@ func (sh *scheduler) runWorker(wid WorkerID) {
|
||||
|
||||
select {
|
||||
case w := <-scheduledWindows:
|
||||
activeWindows = append(activeWindows, w)
|
||||
worker.wndLk.Lock()
|
||||
worker.activeWindows = append(worker.activeWindows, w)
|
||||
worker.wndLk.Unlock()
|
||||
case <-taskDone:
|
||||
log.Debugw("task done", "workerid", wid)
|
||||
case <-sh.closing:
|
||||
@@ -521,24 +563,38 @@ func (sh *scheduler) runWorker(wid WorkerID) {
|
||||
return
|
||||
}
|
||||
|
||||
worker.wndLk.Lock()
|
||||
|
||||
windowsRequested -= sh.workerCompactWindows(worker, wid)
|
||||
|
||||
assignLoop:
|
||||
// process windows in order
|
||||
for len(activeWindows) > 0 {
|
||||
// process tasks within a window in order
|
||||
for len(activeWindows[0].todo) > 0 {
|
||||
todo := activeWindows[0].todo[0]
|
||||
needRes := ResourceTable[todo.taskType][sh.spt]
|
||||
for len(worker.activeWindows) > 0 {
|
||||
firstWindow := worker.activeWindows[0]
|
||||
|
||||
// process tasks within a window, preferring tasks at lower indexes
|
||||
for len(firstWindow.todo) > 0 {
|
||||
sh.workersLk.RLock()
|
||||
|
||||
tidx := -1
|
||||
|
||||
worker.lk.Lock()
|
||||
ok := worker.preparing.canHandleRequest(needRes, wid, worker.info.Resources)
|
||||
for t, todo := range firstWindow.todo {
|
||||
needRes := ResourceTable[todo.taskType][sh.spt]
|
||||
if worker.preparing.canHandleRequest(needRes, wid, "startPreparing", worker.info.Resources) {
|
||||
tidx = t
|
||||
break
|
||||
}
|
||||
}
|
||||
worker.lk.Unlock()
|
||||
|
||||
if !ok {
|
||||
if tidx == -1 {
|
||||
sh.workersLk.RUnlock()
|
||||
break assignLoop
|
||||
}
|
||||
|
||||
todo := firstWindow.todo[tidx]
|
||||
|
||||
log.Debugf("assign worker sector %d", todo.sector.Number)
|
||||
err := sh.assignWorker(taskDone, wid, worker, todo)
|
||||
sh.workersLk.RUnlock()
|
||||
@@ -548,19 +604,76 @@ func (sh *scheduler) runWorker(wid WorkerID) {
|
||||
go todo.respond(xerrors.Errorf("assignWorker error: %w", err))
|
||||
}
|
||||
|
||||
activeWindows[0].todo = activeWindows[0].todo[1:]
|
||||
// Note: we're not freeing window.allocated resources here very much on purpose
|
||||
copy(firstWindow.todo[tidx:], firstWindow.todo[tidx+1:])
|
||||
firstWindow.todo[len(firstWindow.todo)-1] = nil
|
||||
firstWindow.todo = firstWindow.todo[:len(firstWindow.todo)-1]
|
||||
}
|
||||
|
||||
copy(activeWindows, activeWindows[1:])
|
||||
activeWindows[len(activeWindows)-1] = nil
|
||||
activeWindows = activeWindows[:len(activeWindows)-1]
|
||||
copy(worker.activeWindows, worker.activeWindows[1:])
|
||||
worker.activeWindows[len(worker.activeWindows)-1] = nil
|
||||
worker.activeWindows = worker.activeWindows[:len(worker.activeWindows)-1]
|
||||
|
||||
windowsRequested--
|
||||
}
|
||||
|
||||
worker.wndLk.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (sh *scheduler) workerCompactWindows(worker *workerHandle, wid WorkerID) int {
|
||||
// move tasks from older windows to newer windows if older windows
|
||||
// still can fit them
|
||||
if len(worker.activeWindows) > 1 {
|
||||
for wi, window := range worker.activeWindows[1:] {
|
||||
lower := worker.activeWindows[wi]
|
||||
var moved []int
|
||||
|
||||
for ti, todo := range window.todo {
|
||||
needRes := ResourceTable[todo.taskType][sh.spt]
|
||||
if !lower.allocated.canHandleRequest(needRes, wid, "compactWindows", worker.info.Resources) {
|
||||
continue
|
||||
}
|
||||
|
||||
moved = append(moved, ti)
|
||||
lower.todo = append(lower.todo, todo)
|
||||
lower.allocated.add(worker.info.Resources, needRes)
|
||||
window.allocated.free(worker.info.Resources, needRes)
|
||||
}
|
||||
|
||||
if len(moved) > 0 {
|
||||
newTodo := make([]*workerRequest, 0, len(window.todo)-len(moved))
|
||||
for i, t := range window.todo {
|
||||
if len(moved) > 0 && moved[0] == i {
|
||||
moved = moved[1:]
|
||||
continue
|
||||
}
|
||||
|
||||
newTodo = append(newTodo, t)
|
||||
}
|
||||
window.todo = newTodo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var compacted int
|
||||
var newWindows []*schedWindow
|
||||
|
||||
for _, window := range worker.activeWindows {
|
||||
if len(window.todo) == 0 {
|
||||
compacted++
|
||||
continue
|
||||
}
|
||||
|
||||
newWindows = append(newWindows, window)
|
||||
}
|
||||
|
||||
worker.activeWindows = newWindows
|
||||
|
||||
return compacted
|
||||
}
|
||||
|
||||
func (sh *scheduler) assignWorker(taskDone chan struct{}, wid WorkerID, w *workerHandle, req *workerRequest) error {
|
||||
needRes := ResourceTable[req.taskType][sh.spt]
|
||||
|
||||
|
||||
+21
-7
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func (a *activeResources) withResources(id WorkerID, wr storiface.WorkerResources, r Resources, locker sync.Locker, cb func() error) error {
|
||||
for !a.canHandleRequest(r, id, wr) {
|
||||
for !a.canHandleRequest(r, id, "withResources", wr) {
|
||||
if a.cond == nil {
|
||||
a.cond = sync.NewCond(locker)
|
||||
}
|
||||
@@ -52,37 +52,37 @@ func (a *activeResources) free(wr storiface.WorkerResources, r Resources) {
|
||||
a.memUsedMax -= r.MaxMemory
|
||||
}
|
||||
|
||||
func (a *activeResources) canHandleRequest(needRes Resources, wid WorkerID, res storiface.WorkerResources) bool {
|
||||
func (a *activeResources) canHandleRequest(needRes Resources, wid WorkerID, caller string, res storiface.WorkerResources) bool {
|
||||
|
||||
// TODO: dedupe needRes.BaseMinMemory per task type (don't add if that task is already running)
|
||||
minNeedMem := res.MemReserved + a.memUsedMin + needRes.MinMemory + needRes.BaseMinMemory
|
||||
if minNeedMem > res.MemPhysical {
|
||||
log.Debugf("sched: not scheduling on worker %d; not enough physical memory - need: %dM, have %dM", wid, minNeedMem/mib, res.MemPhysical/mib)
|
||||
log.Debugf("sched: not scheduling on worker %d for %s; not enough physical memory - need: %dM, have %dM", wid, caller, minNeedMem/mib, res.MemPhysical/mib)
|
||||
return false
|
||||
}
|
||||
|
||||
maxNeedMem := res.MemReserved + a.memUsedMax + needRes.MaxMemory + needRes.BaseMinMemory
|
||||
|
||||
if maxNeedMem > res.MemSwap+res.MemPhysical {
|
||||
log.Debugf("sched: not scheduling on worker %d; not enough virtual memory - need: %dM, have %dM", wid, maxNeedMem/mib, (res.MemSwap+res.MemPhysical)/mib)
|
||||
log.Debugf("sched: not scheduling on worker %d for %s; not enough virtual memory - need: %dM, have %dM", wid, caller, maxNeedMem/mib, (res.MemSwap+res.MemPhysical)/mib)
|
||||
return false
|
||||
}
|
||||
|
||||
if needRes.MultiThread() {
|
||||
if a.cpuUse > 0 {
|
||||
log.Debugf("sched: not scheduling on worker %d; multicore process needs %d threads, %d in use, target %d", wid, res.CPUs, a.cpuUse, res.CPUs)
|
||||
log.Debugf("sched: not scheduling on worker %d for %s; multicore process needs %d threads, %d in use, target %d", wid, caller, res.CPUs, a.cpuUse, res.CPUs)
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if a.cpuUse+uint64(needRes.Threads) > res.CPUs {
|
||||
log.Debugf("sched: not scheduling on worker %d; not enough threads, need %d, %d in use, target %d", wid, needRes.Threads, a.cpuUse, res.CPUs)
|
||||
log.Debugf("sched: not scheduling on worker %d for %s; not enough threads, need %d, %d in use, target %d", wid, caller, needRes.Threads, a.cpuUse, res.CPUs)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(res.GPUs) > 0 && needRes.CanGPU {
|
||||
if a.gpuUsed {
|
||||
log.Debugf("sched: not scheduling on worker %d; GPU in use", wid)
|
||||
log.Debugf("sched: not scheduling on worker %d for %s; GPU in use", wid, caller)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -108,3 +108,17 @@ func (a *activeResources) utilization(wr storiface.WorkerResources) float64 {
|
||||
|
||||
return max
|
||||
}
|
||||
|
||||
func (wh *workerHandle) utilization() float64 {
|
||||
wh.lk.Lock()
|
||||
u := wh.active.utilization(wh.info.Resources)
|
||||
u += wh.preparing.utilization(wh.info.Resources)
|
||||
wh.lk.Unlock()
|
||||
wh.wndLk.Lock()
|
||||
for _, window := range wh.activeWindows {
|
||||
u += window.allocated.utilization(wh.info.Resources)
|
||||
}
|
||||
wh.wndLk.Unlock()
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
Vendored
+127
-2
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -22,6 +23,10 @@ import (
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
)
|
||||
|
||||
func init() {
|
||||
InitWait = 10 * time.Millisecond
|
||||
}
|
||||
|
||||
func TestWithPriority(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -77,7 +82,7 @@ func (s *schedTestWorker) AddPiece(ctx context.Context, sector abi.SectorID, pie
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *schedTestWorker) MoveStorage(ctx context.Context, sector abi.SectorID) error {
|
||||
func (s *schedTestWorker) MoveStorage(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@@ -301,7 +306,8 @@ func TestSched(t *testing.T) {
|
||||
done: map[string]chan struct{}{},
|
||||
}
|
||||
|
||||
for _, task := range tasks {
|
||||
for i, task := range tasks {
|
||||
log.Info("TASK", i)
|
||||
task(t, sched, index, &rm)
|
||||
}
|
||||
|
||||
@@ -415,6 +421,45 @@ func TestSched(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
diag := func() task {
|
||||
return func(t *testing.T, s *scheduler, index *stores.Index, meta *runMeta) {
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
for _, request := range s.diag().Requests {
|
||||
log.Infof("!!! sDIAG: sid(%d) task(%s)", request.Sector.Number, request.TaskType)
|
||||
}
|
||||
|
||||
wj := (&Manager{sched: s}).WorkerJobs()
|
||||
|
||||
type line struct {
|
||||
storiface.WorkerJob
|
||||
wid uint64
|
||||
}
|
||||
|
||||
lines := make([]line, 0)
|
||||
|
||||
for wid, jobs := range wj {
|
||||
for _, job := range jobs {
|
||||
lines = append(lines, line{
|
||||
WorkerJob: job,
|
||||
wid: wid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// oldest first
|
||||
sort.Slice(lines, func(i, j int) bool {
|
||||
if lines[i].RunWait != lines[j].RunWait {
|
||||
return lines[i].RunWait < lines[j].RunWait
|
||||
}
|
||||
return lines[i].Start.Before(lines[j].Start)
|
||||
})
|
||||
|
||||
for _, l := range lines {
|
||||
log.Infof("!!! wDIAG: rw(%d) sid(%d) t(%s)", l.RunWait, l.Sector.Number, l.Task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run this one a bunch of times, it had a very annoying tendency to fail randomly
|
||||
for i := 0; i < 40; i++ {
|
||||
t.Run("pc1-pc2-prio", testFunc([]workerSpec{
|
||||
@@ -423,6 +468,8 @@ func TestSched(t *testing.T) {
|
||||
// fill queues
|
||||
twoPC1("w0", 0, taskStarted),
|
||||
twoPC1("w1", 2, taskNotScheduled),
|
||||
sched("w2", "fred", 4, sealtasks.TTPreCommit1),
|
||||
taskNotScheduled("w2"),
|
||||
|
||||
// windowed
|
||||
|
||||
@@ -435,10 +482,18 @@ func TestSched(t *testing.T) {
|
||||
sched("t3", "fred", 10, sealtasks.TTPreCommit2),
|
||||
taskNotScheduled("t3"),
|
||||
|
||||
diag(),
|
||||
|
||||
twoPC1Act("w0", taskDone),
|
||||
twoPC1Act("w1", taskStarted),
|
||||
taskNotScheduled("w2"),
|
||||
|
||||
twoPC1Act("w1", taskDone),
|
||||
taskStarted("w2"),
|
||||
|
||||
taskDone("w2"),
|
||||
|
||||
diag(),
|
||||
|
||||
taskStarted("t3"),
|
||||
taskNotScheduled("t1"),
|
||||
@@ -518,3 +573,73 @@ func BenchmarkTrySched(b *testing.B) {
|
||||
b.Run("1w-500q", test(1, 500))
|
||||
b.Run("200w-400q", test(200, 400))
|
||||
}
|
||||
|
||||
func TestWindowCompact(t *testing.T) {
|
||||
sh := scheduler{
|
||||
spt: abi.RegisteredSealProof_StackedDrg32GiBV1,
|
||||
}
|
||||
|
||||
test := func(start [][]sealtasks.TaskType, expect [][]sealtasks.TaskType) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
wh := &workerHandle{
|
||||
info: storiface.WorkerInfo{
|
||||
Resources: decentWorkerResources,
|
||||
},
|
||||
}
|
||||
|
||||
for _, windowTasks := range start {
|
||||
window := &schedWindow{}
|
||||
|
||||
for _, task := range windowTasks {
|
||||
window.todo = append(window.todo, &workerRequest{taskType: task})
|
||||
window.allocated.add(wh.info.Resources, ResourceTable[task][sh.spt])
|
||||
}
|
||||
|
||||
wh.activeWindows = append(wh.activeWindows, window)
|
||||
}
|
||||
|
||||
n := sh.workerCompactWindows(wh, 0)
|
||||
require.Equal(t, len(start)-len(expect), n)
|
||||
|
||||
for wi, tasks := range expect {
|
||||
var expectRes activeResources
|
||||
|
||||
for ti, task := range tasks {
|
||||
require.Equal(t, task, wh.activeWindows[wi].todo[ti].taskType, "%d, %d", wi, ti)
|
||||
expectRes.add(wh.info.Resources, ResourceTable[task][sh.spt])
|
||||
}
|
||||
|
||||
require.Equal(t, expectRes.cpuUse, wh.activeWindows[wi].allocated.cpuUse, "%d", wi)
|
||||
require.Equal(t, expectRes.gpuUsed, wh.activeWindows[wi].allocated.gpuUsed, "%d", wi)
|
||||
require.Equal(t, expectRes.memUsedMin, wh.activeWindows[wi].allocated.memUsedMin, "%d", wi)
|
||||
require.Equal(t, expectRes.memUsedMax, wh.activeWindows[wi].allocated.memUsedMax, "%d", wi)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("2-pc1-windows", test(
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit1}, {sealtasks.TTPreCommit1}},
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit1, sealtasks.TTPreCommit1}}),
|
||||
)
|
||||
|
||||
t.Run("1-window", test(
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit1, sealtasks.TTPreCommit1}},
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit1, sealtasks.TTPreCommit1}}),
|
||||
)
|
||||
|
||||
t.Run("2-pc2-windows", test(
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit2}, {sealtasks.TTPreCommit2}},
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit2}, {sealtasks.TTPreCommit2}}),
|
||||
)
|
||||
|
||||
t.Run("2pc1-pc1ap", test(
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit1, sealtasks.TTPreCommit1}, {sealtasks.TTPreCommit1, sealtasks.TTAddPiece}},
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit1, sealtasks.TTPreCommit1, sealtasks.TTAddPiece}, {sealtasks.TTPreCommit1}}),
|
||||
)
|
||||
|
||||
t.Run("2pc1-pc1appc2", test(
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit1, sealtasks.TTPreCommit1}, {sealtasks.TTPreCommit1, sealtasks.TTAddPiece, sealtasks.TTPreCommit2}},
|
||||
[][]sealtasks.TaskType{{sealtasks.TTPreCommit1, sealtasks.TTPreCommit1, sealtasks.TTAddPiece}, {sealtasks.TTPreCommit1, sealtasks.TTPreCommit2}}),
|
||||
)
|
||||
}
|
||||
|
||||
+15
-9
@@ -17,15 +17,15 @@ const (
|
||||
)
|
||||
|
||||
var order = map[TaskType]int{
|
||||
TTAddPiece: 7,
|
||||
TTPreCommit1: 6,
|
||||
TTPreCommit2: 5,
|
||||
TTCommit2: 4,
|
||||
TTCommit1: 3,
|
||||
TTFetch: 2,
|
||||
TTFinalize: 1,
|
||||
TTUnseal: 0,
|
||||
TTReadUnsealed: 0,
|
||||
TTAddPiece: 6, // least priority
|
||||
TTPreCommit1: 5,
|
||||
TTPreCommit2: 4,
|
||||
TTCommit2: 3,
|
||||
TTCommit1: 2,
|
||||
TTUnseal: 1,
|
||||
TTFetch: -1,
|
||||
TTReadUnsealed: -1,
|
||||
TTFinalize: -2, // most priority
|
||||
}
|
||||
|
||||
var shortNames = map[TaskType]string{
|
||||
@@ -43,6 +43,12 @@ var shortNames = map[TaskType]string{
|
||||
TTReadUnsealed: "RD ",
|
||||
}
|
||||
|
||||
func (a TaskType) MuchLess(b TaskType) (bool, bool) {
|
||||
oa, ob := order[a], order[b]
|
||||
oneNegative := oa^ob < 0
|
||||
return oneNegative, oa < ob
|
||||
}
|
||||
|
||||
func (a TaskType) Less(b TaskType) bool {
|
||||
return order[a] < order[b]
|
||||
}
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ func (s *allocSelector) Ok(ctx context.Context, task sealtasks.TaskType, spt abi
|
||||
}
|
||||
|
||||
func (s *allocSelector) Cmp(ctx context.Context, task sealtasks.TaskType, a, b *workerHandle) (bool, error) {
|
||||
return a.active.utilization(a.info.Resources) < b.active.utilization(b.info.Resources), nil
|
||||
return a.utilization() < b.utilization(), nil
|
||||
}
|
||||
|
||||
var _ WorkerSelector = &allocSelector{}
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ func (s *existingSelector) Ok(ctx context.Context, task sealtasks.TaskType, spt
|
||||
}
|
||||
|
||||
func (s *existingSelector) Cmp(ctx context.Context, task sealtasks.TaskType, a, b *workerHandle) (bool, error) {
|
||||
return a.active.utilization(a.info.Resources) < b.active.utilization(b.info.Resources), nil
|
||||
return a.utilization() < b.utilization(), nil
|
||||
}
|
||||
|
||||
var _ WorkerSelector = &existingSelector{}
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ func (s *taskSelector) Cmp(ctx context.Context, _ sealtasks.TaskType, a, b *work
|
||||
return len(atasks) < len(btasks), nil // prefer workers which can do less
|
||||
}
|
||||
|
||||
return a.active.utilization(a.info.Resources) < b.active.utilization(b.info.Resources), nil
|
||||
return a.utilization() < b.utilization(), nil
|
||||
}
|
||||
|
||||
var _ WorkerSelector = &allocSelector{}
|
||||
|
||||
Vendored
+17
-1
@@ -1,6 +1,8 @@
|
||||
package sectorstorage
|
||||
|
||||
import "github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||
import (
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||
)
|
||||
|
||||
func (m *Manager) WorkerStats() map[uint64]storiface.WorkerStats {
|
||||
m.sched.workersLk.RLock()
|
||||
@@ -29,6 +31,20 @@ func (m *Manager) WorkerJobs() map[uint64][]storiface.WorkerJob {
|
||||
|
||||
for id, handle := range m.sched.workers {
|
||||
out[uint64(id)] = handle.wt.Running()
|
||||
|
||||
handle.wndLk.Lock()
|
||||
for wi, window := range handle.activeWindows {
|
||||
for _, request := range window.todo {
|
||||
out[uint64(id)] = append(out[uint64(id)], storiface.WorkerJob{
|
||||
ID: 0,
|
||||
Sector: request.sector,
|
||||
Task: request.taskType,
|
||||
RunWait: wi + 1,
|
||||
Start: request.start,
|
||||
})
|
||||
}
|
||||
}
|
||||
handle.wndLk.Unlock()
|
||||
}
|
||||
|
||||
return out
|
||||
|
||||
+2
-1
@@ -37,5 +37,6 @@ type WorkerJob struct {
|
||||
Sector abi.SectorID
|
||||
Task sealtasks.TaskType
|
||||
|
||||
Start time.Time
|
||||
RunWait int // 0 - running, 1+ - assigned
|
||||
Start time.Time
|
||||
}
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ func (t *testWorker) Remove(ctx context.Context, sector abi.SectorID) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (t *testWorker) MoveStorage(ctx context.Context, sector abi.SectorID) error {
|
||||
func (t *testWorker) MoveStorage(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
|
||||
Vendored
+82
-3
@@ -135,12 +135,34 @@ func (t *DealInfo) MarshalCBOR(w io.Writer) error {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{163}); err != nil {
|
||||
if _, err := w.Write([]byte{164}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scratch := make([]byte, 9)
|
||||
|
||||
// t.PublishCid (cid.Cid) (struct)
|
||||
if len("PublishCid") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"PublishCid\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("PublishCid"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, string("PublishCid")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.PublishCid == nil {
|
||||
if _, err := w.Write(cbg.CborNull); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := cbg.WriteCidBuf(scratch, w, *t.PublishCid); err != nil {
|
||||
return xerrors.Errorf("failed to write cid field t.PublishCid: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// t.DealID (abi.DealID) (uint64)
|
||||
if len("DealID") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"DealID\" was too long")
|
||||
@@ -224,7 +246,30 @@ func (t *DealInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
}
|
||||
|
||||
switch name {
|
||||
// t.DealID (abi.DealID) (uint64)
|
||||
// t.PublishCid (cid.Cid) (struct)
|
||||
case "PublishCid":
|
||||
|
||||
{
|
||||
|
||||
b, err := br.ReadByte()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if b != cbg.CborNull[0] {
|
||||
if err := br.UnreadByte(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := cbg.ReadCid(br)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to read cid field t.PublishCid: %w", err)
|
||||
}
|
||||
|
||||
t.PublishCid = &c
|
||||
}
|
||||
|
||||
}
|
||||
// t.DealID (abi.DealID) (uint64)
|
||||
case "DealID":
|
||||
|
||||
{
|
||||
@@ -430,7 +475,7 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte{182}); err != nil {
|
||||
if _, err := w.Write([]byte{183}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -860,6 +905,29 @@ func (t *SectorInfo) MarshalCBOR(w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// t.Return (sealing.ReturnState) (string)
|
||||
if len("Return") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"Return\" was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len("Return"))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, string("Return")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(t.Return) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field t.Return was too long")
|
||||
}
|
||||
|
||||
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajTextString, uint64(len(t.Return))); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, string(t.Return)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.LastErr (string) (string)
|
||||
if len("LastErr") > cbg.MaxLength {
|
||||
return xerrors.Errorf("Value in field \"LastErr\" was too long")
|
||||
@@ -1362,6 +1430,17 @@ func (t *SectorInfo) UnmarshalCBOR(r io.Reader) error {
|
||||
}
|
||||
|
||||
}
|
||||
// t.Return (sealing.ReturnState) (string)
|
||||
case "Return":
|
||||
|
||||
{
|
||||
sval, err := cbg.ReadStringBuf(br, scratch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Return = ReturnState(sval)
|
||||
}
|
||||
// t.LastErr (string) (string)
|
||||
case "LastErr":
|
||||
|
||||
|
||||
Vendored
+13
-1
@@ -33,7 +33,7 @@ type ErrInvalidProof struct{ error }
|
||||
type ErrNoPrecommit struct{ error }
|
||||
type ErrCommitWaitFailed struct{ error }
|
||||
|
||||
func checkPieces(ctx context.Context, si SectorInfo, api SealingAPI) error {
|
||||
func checkPieces(ctx context.Context, maddr address.Address, si SectorInfo, api SealingAPI) error {
|
||||
tok, height, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return &ErrApi{xerrors.Errorf("getting chain head: %w", err)}
|
||||
@@ -55,6 +55,10 @@ func checkPieces(ctx context.Context, si SectorInfo, api SealingAPI) error {
|
||||
return &ErrInvalidDeals{xerrors.Errorf("getting deal %d for piece %d: %w", p.DealInfo.DealID, i, err)}
|
||||
}
|
||||
|
||||
if proposal.Provider != maddr {
|
||||
return &ErrInvalidDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers deal %d with wrong provider: %s != %s", i, len(si.Pieces), si.SectorNumber, p.DealInfo.DealID, proposal.Provider, maddr)}
|
||||
}
|
||||
|
||||
if proposal.PieceCID != p.Piece.PieceCID {
|
||||
return &ErrInvalidDeals{xerrors.Errorf("piece %d (of %d) of sector %d refers deal %d with wrong PieceCID: %x != %x", i, len(si.Pieces), si.SectorNumber, p.DealInfo.DealID, p.Piece.PieceCID, proposal.PieceCID)}
|
||||
}
|
||||
@@ -74,6 +78,10 @@ func checkPieces(ctx context.Context, si SectorInfo, api SealingAPI) error {
|
||||
// checkPrecommit checks that data commitment generated in the sealing process
|
||||
// matches pieces, and that the seal ticket isn't expired
|
||||
func checkPrecommit(ctx context.Context, maddr address.Address, si SectorInfo, tok TipSetToken, height abi.ChainEpoch, api SealingAPI) (err error) {
|
||||
if err := checkPieces(ctx, maddr, si, api); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commD, err := api.StateComputeDataCommitment(ctx, maddr, si.SectorType, si.dealIDs(), tok)
|
||||
if err != nil {
|
||||
return &ErrApi{xerrors.Errorf("calling StateComputeDataCommitment: %w", err)}
|
||||
@@ -176,5 +184,9 @@ func (m *Sealing) checkCommit(ctx context.Context, si SectorInfo, proof []byte,
|
||||
return &ErrInvalidProof{xerrors.New("invalid proof (compute error?)")}
|
||||
}
|
||||
|
||||
if err := checkPieces(ctx, m.maddr, si, m.api); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Vendored
+164
-99
@@ -17,9 +17,9 @@ import (
|
||||
)
|
||||
|
||||
func (m *Sealing) Plan(events []statemachine.Event, user interface{}) (interface{}, uint64, error) {
|
||||
next, err := m.plan(events, user.(*SectorInfo))
|
||||
next, processed, err := m.plan(events, user.(*SectorInfo))
|
||||
if err != nil || next == nil {
|
||||
return nil, uint64(len(events)), err
|
||||
return nil, processed, err
|
||||
}
|
||||
|
||||
return func(ctx statemachine.Context, si SectorInfo) error {
|
||||
@@ -30,10 +30,10 @@ func (m *Sealing) Plan(events []statemachine.Event, user interface{}) (interface
|
||||
}
|
||||
|
||||
return nil
|
||||
}, uint64(len(events)), nil // TODO: This processed event count is not very correct
|
||||
}, processed, nil // TODO: This processed event count is not very correct
|
||||
}
|
||||
|
||||
var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *SectorInfo) error{
|
||||
var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *SectorInfo) (uint64, error){
|
||||
// Sealing
|
||||
|
||||
UndefinedSectorState: planOne(
|
||||
@@ -49,31 +49,39 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto
|
||||
PreCommit1: planOne(
|
||||
on(SectorPreCommit1{}, PreCommit2),
|
||||
on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed),
|
||||
on(SectorPackingFailed{}, PackingFailed),
|
||||
on(SectorDealsExpired{}, DealsExpired),
|
||||
on(SectorInvalidDealIDs{}, RecoverDealIDs),
|
||||
),
|
||||
PreCommit2: planOne(
|
||||
on(SectorPreCommit2{}, PreCommitting),
|
||||
on(SectorSealPreCommit2Failed{}, SealPreCommit2Failed),
|
||||
on(SectorPackingFailed{}, PackingFailed),
|
||||
),
|
||||
PreCommitting: planOne(
|
||||
on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed),
|
||||
on(SectorPreCommitted{}, PreCommitWait),
|
||||
on(SectorChainPreCommitFailed{}, PreCommitFailed),
|
||||
on(SectorPreCommitLanded{}, WaitSeed),
|
||||
on(SectorDealsExpired{}, DealsExpired),
|
||||
on(SectorInvalidDealIDs{}, RecoverDealIDs),
|
||||
),
|
||||
PreCommitWait: planOne(
|
||||
on(SectorChainPreCommitFailed{}, PreCommitFailed),
|
||||
on(SectorPreCommitLanded{}, WaitSeed),
|
||||
on(SectorRetryPreCommit{}, PreCommitting),
|
||||
),
|
||||
WaitSeed: planOne(
|
||||
on(SectorSeedReady{}, Committing),
|
||||
on(SectorChainPreCommitFailed{}, PreCommitFailed),
|
||||
),
|
||||
Committing: planCommitting,
|
||||
SubmitCommit: planOne(
|
||||
on(SectorCommitSubmitted{}, CommitWait),
|
||||
on(SectorCommitFailed{}, CommitFailed),
|
||||
),
|
||||
CommitWait: planOne(
|
||||
on(SectorProving{}, FinalizeSector),
|
||||
on(SectorCommitFailed{}, CommitFailed),
|
||||
on(SectorRetrySubmitCommit{}, SubmitCommit),
|
||||
),
|
||||
|
||||
FinalizeSector: planOne(
|
||||
@@ -95,6 +103,8 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto
|
||||
on(SectorRetryWaitSeed{}, WaitSeed),
|
||||
on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed),
|
||||
on(SectorPreCommitLanded{}, WaitSeed),
|
||||
on(SectorDealsExpired{}, DealsExpired),
|
||||
on(SectorInvalidDealIDs{}, RecoverDealIDs),
|
||||
),
|
||||
ComputeProofFailed: planOne(
|
||||
on(SectorRetryComputeProof{}, Committing),
|
||||
@@ -109,22 +119,33 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto
|
||||
on(SectorChainPreCommitFailed{}, PreCommitFailed),
|
||||
on(SectorRetryPreCommit{}, PreCommitting),
|
||||
on(SectorRetryCommitWait{}, CommitWait),
|
||||
on(SectorDealsExpired{}, DealsExpired),
|
||||
on(SectorInvalidDealIDs{}, RecoverDealIDs),
|
||||
),
|
||||
FinalizeFailed: planOne(
|
||||
on(SectorRetryFinalize{}, FinalizeSector),
|
||||
),
|
||||
PackingFailed: planOne(), // TODO: Deprecated, remove
|
||||
DealsExpired: planOne(
|
||||
// SectorRemove (global)
|
||||
),
|
||||
RecoverDealIDs: planOne(
|
||||
onReturning(SectorUpdateDealIDs{}),
|
||||
),
|
||||
|
||||
// Post-seal
|
||||
|
||||
Proving: planOne(
|
||||
on(SectorFaultReported{}, FaultReported),
|
||||
on(SectorFaulty{}, Faulty),
|
||||
on(SectorRemove{}, Removing),
|
||||
),
|
||||
Removing: planOne(
|
||||
on(SectorRemoved{}, Removed),
|
||||
on(SectorRemoveFailed{}, RemoveFailed),
|
||||
),
|
||||
RemoveFailed: planOne(
|
||||
// SectorRemove (global)
|
||||
),
|
||||
Faulty: planOne(
|
||||
on(SectorFaultReported{}, FaultReported),
|
||||
),
|
||||
@@ -133,7 +154,7 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto
|
||||
Removed: final,
|
||||
}
|
||||
|
||||
func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(statemachine.Context, SectorInfo) error, error) {
|
||||
func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(statemachine.Context, SectorInfo) error, uint64, error) {
|
||||
/////
|
||||
// First process all events
|
||||
|
||||
@@ -170,11 +191,12 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta
|
||||
|
||||
p := fsmPlanners[state.State]
|
||||
if p == nil {
|
||||
return nil, xerrors.Errorf("planner for state %s not found", state.State)
|
||||
return nil, 0, xerrors.Errorf("planner for state %s not found", state.State)
|
||||
}
|
||||
|
||||
if err := p(events, state); err != nil {
|
||||
return nil, xerrors.Errorf("running planner for state %s failed: %w", state.State, err)
|
||||
processed, err := p(events, state)
|
||||
if err != nil {
|
||||
return nil, 0, xerrors.Errorf("running planner for state %s failed: %w", state.State, err)
|
||||
}
|
||||
|
||||
/////
|
||||
@@ -182,47 +204,50 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta
|
||||
|
||||
/*
|
||||
|
||||
* Empty <- incoming deals
|
||||
| |
|
||||
| v
|
||||
*<- WaitDeals <- incoming deals
|
||||
| |
|
||||
| v
|
||||
*<- Packing <- incoming committed capacity
|
||||
| |
|
||||
| v
|
||||
*<- PreCommit1 <--> SealPreCommit1Failed
|
||||
| | ^ ^^
|
||||
| | *----------++----\
|
||||
| v v || |
|
||||
*<- PreCommit2 --------++--> SealPreCommit2Failed
|
||||
| | ||
|
||||
| v /-------/|
|
||||
* PreCommitting <-----+---> PreCommitFailed
|
||||
| | | ^
|
||||
| v | |
|
||||
*<- WaitSeed -----------+-----/
|
||||
| ||| ^ |
|
||||
| ||| \--------*-----/
|
||||
| ||| |
|
||||
| vvv v----+----> ComputeProofFailed
|
||||
*<- Committing |
|
||||
| | ^--> CommitFailed
|
||||
| v ^
|
||||
*<- CommitWait ---/
|
||||
| |
|
||||
| v
|
||||
| FinalizeSector <--> FinalizeFailed
|
||||
| |
|
||||
| v
|
||||
*<- Proving
|
||||
|
|
||||
v
|
||||
FailedUnrecoverable
|
||||
* Empty <- incoming deals
|
||||
| |
|
||||
| v
|
||||
*<- WaitDeals <- incoming deals
|
||||
| |
|
||||
| v
|
||||
*<- Packing <- incoming committed capacity
|
||||
| |
|
||||
| v
|
||||
*<- PreCommit1 <--> SealPreCommit1Failed
|
||||
| | ^ ^^
|
||||
| | *----------++----\
|
||||
| v v || |
|
||||
*<- PreCommit2 --------++--> SealPreCommit2Failed
|
||||
| | ||
|
||||
| v /-------/|
|
||||
* PreCommitting <-----+---> PreCommitFailed
|
||||
| | | ^
|
||||
| v | |
|
||||
*<- WaitSeed -----------+-----/
|
||||
| ||| ^ |
|
||||
| ||| \--------*-----/
|
||||
| ||| |
|
||||
| vvv v----+----> ComputeProofFailed
|
||||
*<- Committing |
|
||||
| | ^--> CommitFailed
|
||||
| v ^
|
||||
| SubmitCommit |
|
||||
| | |
|
||||
| v |
|
||||
*<- CommitWait ---/
|
||||
| |
|
||||
| v
|
||||
| FinalizeSector <--> FinalizeFailed
|
||||
| |
|
||||
| v
|
||||
*<- Proving
|
||||
|
|
||||
v
|
||||
FailedUnrecoverable
|
||||
|
||||
UndefinedSectorState <- ¯\_(ツ)_/¯
|
||||
| ^
|
||||
*---------------------/
|
||||
UndefinedSectorState <- ¯\_(ツ)_/¯
|
||||
| ^
|
||||
*---------------------/
|
||||
|
||||
*/
|
||||
|
||||
@@ -235,51 +260,63 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta
|
||||
case WaitDeals:
|
||||
log.Infof("Waiting for deals %d", state.SectorNumber)
|
||||
case Packing:
|
||||
return m.handlePacking, nil
|
||||
return m.handlePacking, processed, nil
|
||||
case PreCommit1:
|
||||
return m.handlePreCommit1, nil
|
||||
return m.handlePreCommit1, processed, nil
|
||||
case PreCommit2:
|
||||
return m.handlePreCommit2, nil
|
||||
return m.handlePreCommit2, processed, nil
|
||||
case PreCommitting:
|
||||
return m.handlePreCommitting, nil
|
||||
return m.handlePreCommitting, processed, nil
|
||||
case PreCommitWait:
|
||||
return m.handlePreCommitWait, nil
|
||||
return m.handlePreCommitWait, processed, nil
|
||||
case WaitSeed:
|
||||
return m.handleWaitSeed, nil
|
||||
return m.handleWaitSeed, processed, nil
|
||||
case Committing:
|
||||
return m.handleCommitting, nil
|
||||
return m.handleCommitting, processed, nil
|
||||
case SubmitCommit:
|
||||
return m.handleSubmitCommit, processed, nil
|
||||
case CommitWait:
|
||||
return m.handleCommitWait, nil
|
||||
return m.handleCommitWait, processed, nil
|
||||
case FinalizeSector:
|
||||
return m.handleFinalizeSector, nil
|
||||
return m.handleFinalizeSector, processed, nil
|
||||
|
||||
// Handled failure modes
|
||||
case SealPreCommit1Failed:
|
||||
return m.handleSealPrecommit1Failed, nil
|
||||
return m.handleSealPrecommit1Failed, processed, nil
|
||||
case SealPreCommit2Failed:
|
||||
return m.handleSealPrecommit2Failed, nil
|
||||
return m.handleSealPrecommit2Failed, processed, nil
|
||||
case PreCommitFailed:
|
||||
return m.handlePreCommitFailed, nil
|
||||
return m.handlePreCommitFailed, processed, nil
|
||||
case ComputeProofFailed:
|
||||
return m.handleComputeProofFailed, nil
|
||||
return m.handleComputeProofFailed, processed, nil
|
||||
case CommitFailed:
|
||||
return m.handleCommitFailed, nil
|
||||
return m.handleCommitFailed, processed, nil
|
||||
case FinalizeFailed:
|
||||
return m.handleFinalizeFailed, nil
|
||||
return m.handleFinalizeFailed, processed, nil
|
||||
case PackingFailed: // DEPRECATED: remove this for the next reset
|
||||
state.State = DealsExpired
|
||||
fallthrough
|
||||
case DealsExpired:
|
||||
return m.handleDealsExpired, processed, nil
|
||||
case RecoverDealIDs:
|
||||
return m.handleRecoverDealIDs, processed, nil
|
||||
|
||||
// Post-seal
|
||||
case Proving:
|
||||
return m.handleProvingSector, nil
|
||||
return m.handleProvingSector, processed, nil
|
||||
case Removing:
|
||||
return m.handleRemoving, nil
|
||||
return m.handleRemoving, processed, nil
|
||||
case Removed:
|
||||
return nil, nil
|
||||
return nil, processed, nil
|
||||
|
||||
case RemoveFailed:
|
||||
return m.handleRemoveFailed, processed, nil
|
||||
|
||||
// Faults
|
||||
case Faulty:
|
||||
return m.handleFaulty, nil
|
||||
return m.handleFaulty, processed, nil
|
||||
case FaultReported:
|
||||
return m.handleFaultReported, nil
|
||||
return m.handleFaultReported, processed, nil
|
||||
|
||||
// Fatal errors
|
||||
case UndefinedSectorState:
|
||||
@@ -290,28 +327,29 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta
|
||||
log.Errorf("unexpected sector update state: %s", state.State)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return nil, processed, nil
|
||||
}
|
||||
|
||||
func planCommitting(events []statemachine.Event, state *SectorInfo) error {
|
||||
for _, event := range events {
|
||||
func planCommitting(events []statemachine.Event, state *SectorInfo) (uint64, error) {
|
||||
for i, event := range events {
|
||||
switch e := event.User.(type) {
|
||||
case globalMutator:
|
||||
if e.applyGlobal(state) {
|
||||
return nil
|
||||
return uint64(i + 1), nil
|
||||
}
|
||||
case SectorCommitted: // the normal case
|
||||
e.apply(state)
|
||||
state.State = CommitWait
|
||||
state.State = SubmitCommit
|
||||
case SectorSeedReady: // seed changed :/
|
||||
if e.SeedEpoch == state.SeedEpoch && bytes.Equal(e.SeedValue, state.SeedValue) {
|
||||
log.Warnf("planCommitting: got SectorSeedReady, but the seed didn't change")
|
||||
continue // or it didn't!
|
||||
}
|
||||
|
||||
log.Warnf("planCommitting: commit Seed changed")
|
||||
e.apply(state)
|
||||
state.State = Committing
|
||||
return nil
|
||||
return uint64(i + 1), nil
|
||||
case SectorComputeProofFailed:
|
||||
state.State = ComputeProofFailed
|
||||
case SectorSealPreCommit1Failed:
|
||||
@@ -321,10 +359,10 @@ func planCommitting(events []statemachine.Event, state *SectorInfo) error {
|
||||
case SectorRetryCommitWait:
|
||||
state.State = CommitWait
|
||||
default:
|
||||
return xerrors.Errorf("planCommitting got event of unknown type %T, events: %+v", event.User, events)
|
||||
return uint64(i), xerrors.Errorf("planCommitting got event of unknown type %T, events: %+v", event.User, events)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return uint64(len(events)), nil
|
||||
}
|
||||
|
||||
func (m *Sealing) restartSectors(ctx context.Context) error {
|
||||
@@ -338,12 +376,33 @@ func (m *Sealing) restartSectors(ctx context.Context) error {
|
||||
return xerrors.Errorf("getting the sealing delay: %w", err)
|
||||
}
|
||||
|
||||
m.unsealedInfoMap.lk.Lock()
|
||||
defer m.unsealedInfoMap.lk.Unlock()
|
||||
for _, sector := range trackedSectors {
|
||||
if err := m.sectors.Send(uint64(sector.SectorNumber), SectorRestart{}); err != nil {
|
||||
log.Errorf("restarting sector %d: %+v", sector.SectorNumber, err)
|
||||
}
|
||||
|
||||
if sector.State == WaitDeals {
|
||||
|
||||
// put the sector in the unsealedInfoMap
|
||||
if _, ok := m.unsealedInfoMap.infos[sector.SectorNumber]; ok {
|
||||
// something's funky here, but probably safe to move on
|
||||
log.Warnf("sector %v was already in the unsealedInfoMap when restarting", sector.SectorNumber)
|
||||
} else {
|
||||
ui := UnsealedSectorInfo{}
|
||||
for _, p := range sector.Pieces {
|
||||
if p.DealInfo != nil {
|
||||
ui.numDeals++
|
||||
}
|
||||
ui.stored += p.Piece.Size
|
||||
ui.pieceSizes = append(ui.pieceSizes, p.Piece.Size.Unpadded())
|
||||
}
|
||||
|
||||
m.unsealedInfoMap.infos[sector.SectorNumber] = ui
|
||||
}
|
||||
|
||||
// start a fresh timer for the sector
|
||||
if cfg.WaitDealsDelay > 0 {
|
||||
timer := time.NewTimer(cfg.WaitDealsDelay)
|
||||
go func() {
|
||||
@@ -365,31 +424,38 @@ func (m *Sealing) ForceSectorState(ctx context.Context, id abi.SectorNumber, sta
|
||||
return m.sectors.Send(id, SectorForceState{state})
|
||||
}
|
||||
|
||||
func final(events []statemachine.Event, state *SectorInfo) error {
|
||||
return xerrors.Errorf("didn't expect any events in state %s, got %+v", state.State, events)
|
||||
func final(events []statemachine.Event, state *SectorInfo) (uint64, error) {
|
||||
return 0, xerrors.Errorf("didn't expect any events in state %s, got %+v", state.State, events)
|
||||
}
|
||||
|
||||
func on(mut mutator, next SectorState) func() (mutator, SectorState) {
|
||||
return func() (mutator, SectorState) {
|
||||
return mut, next
|
||||
func on(mut mutator, next SectorState) func() (mutator, func(*SectorInfo) error) {
|
||||
return func() (mutator, func(*SectorInfo) error) {
|
||||
return mut, func(state *SectorInfo) error {
|
||||
state.State = next
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func planOne(ts ...func() (mut mutator, next SectorState)) func(events []statemachine.Event, state *SectorInfo) error {
|
||||
return func(events []statemachine.Event, state *SectorInfo) error {
|
||||
if len(events) != 1 {
|
||||
for _, event := range events {
|
||||
if gm, ok := event.User.(globalMutator); ok {
|
||||
gm.applyGlobal(state)
|
||||
return nil
|
||||
}
|
||||
func onReturning(mut mutator) func() (mutator, func(*SectorInfo) error) {
|
||||
return func() (mutator, func(*SectorInfo) error) {
|
||||
return mut, func(state *SectorInfo) error {
|
||||
if state.Return == "" {
|
||||
return xerrors.Errorf("return state not set")
|
||||
}
|
||||
return xerrors.Errorf("planner for state %s only has a plan for a single event only, got %+v", state.State, events)
|
||||
}
|
||||
|
||||
state.State = SectorState(state.Return)
|
||||
state.Return = ""
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func planOne(ts ...func() (mut mutator, next func(*SectorInfo) error)) func(events []statemachine.Event, state *SectorInfo) (uint64, error) {
|
||||
return func(events []statemachine.Event, state *SectorInfo) (uint64, error) {
|
||||
if gm, ok := events[0].User.(globalMutator); ok {
|
||||
gm.applyGlobal(state)
|
||||
return nil
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
for _, t := range ts {
|
||||
@@ -404,15 +470,14 @@ func planOne(ts ...func() (mut mutator, next SectorState)) func(events []statema
|
||||
}
|
||||
|
||||
events[0].User.(mutator).apply(state)
|
||||
state.State = next
|
||||
return nil
|
||||
return 1, next(state)
|
||||
}
|
||||
|
||||
_, ok := events[0].User.(Ignorable)
|
||||
if ok {
|
||||
return nil
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
return xerrors.Errorf("planner for state %s received unexpected event %T (%+v)", state.State, events[0].User, events[0])
|
||||
return 0, xerrors.Errorf("planner for state %s received unexpected event %T (%+v)", state.State, events[0].User, events[0])
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+39
-7
@@ -101,10 +101,6 @@ func (evt SectorPacked) apply(state *SectorInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
type SectorPackingFailed struct{ error }
|
||||
|
||||
func (evt SectorPackingFailed) apply(*SectorInfo) {}
|
||||
|
||||
type SectorPreCommit1 struct {
|
||||
PreCommit1Out storage.PreCommit1Out
|
||||
TicketValue abi.SealRandomness
|
||||
@@ -191,13 +187,28 @@ type SectorCommitFailed struct{ error }
|
||||
func (evt SectorCommitFailed) FormatError(xerrors.Printer) (next error) { return evt.error }
|
||||
func (evt SectorCommitFailed) apply(*SectorInfo) {}
|
||||
|
||||
type SectorRetrySubmitCommit struct{}
|
||||
|
||||
func (evt SectorRetrySubmitCommit) apply(*SectorInfo) {}
|
||||
|
||||
type SectorDealsExpired struct{ error }
|
||||
|
||||
func (evt SectorDealsExpired) FormatError(xerrors.Printer) (next error) { return evt.error }
|
||||
func (evt SectorDealsExpired) apply(*SectorInfo) {}
|
||||
|
||||
type SectorCommitted struct {
|
||||
Message cid.Cid
|
||||
Proof []byte
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
func (evt SectorCommitted) apply(state *SectorInfo) {
|
||||
state.Proof = evt.Proof
|
||||
}
|
||||
|
||||
type SectorCommitSubmitted struct {
|
||||
Message cid.Cid
|
||||
}
|
||||
|
||||
func (evt SectorCommitSubmitted) apply(state *SectorInfo) {
|
||||
state.CommitMessage = &evt.Message
|
||||
}
|
||||
|
||||
@@ -256,6 +267,24 @@ type SectorRetryCommitWait struct{}
|
||||
|
||||
func (evt SectorRetryCommitWait) apply(state *SectorInfo) {}
|
||||
|
||||
type SectorInvalidDealIDs struct {
|
||||
Return ReturnState
|
||||
}
|
||||
|
||||
func (evt SectorInvalidDealIDs) apply(state *SectorInfo) {
|
||||
state.Return = evt.Return
|
||||
}
|
||||
|
||||
type SectorUpdateDealIDs struct {
|
||||
Updates map[int]abi.DealID
|
||||
}
|
||||
|
||||
func (evt SectorUpdateDealIDs) apply(state *SectorInfo) {
|
||||
for i, id := range evt.Updates {
|
||||
state.Pieces[i].DealInfo.DealID = id
|
||||
}
|
||||
}
|
||||
|
||||
// Faults
|
||||
|
||||
type SectorFaulty struct{}
|
||||
@@ -274,7 +303,10 @@ type SectorFaultedFinal struct{}
|
||||
|
||||
type SectorRemove struct{}
|
||||
|
||||
func (evt SectorRemove) apply(state *SectorInfo) {}
|
||||
func (evt SectorRemove) applyGlobal(state *SectorInfo) bool {
|
||||
state.State = Removing
|
||||
return true
|
||||
}
|
||||
|
||||
type SectorRemoved struct{}
|
||||
|
||||
|
||||
Vendored
+11
-4
@@ -16,7 +16,7 @@ func init() {
|
||||
}
|
||||
|
||||
func (t *test) planSingle(evt interface{}) {
|
||||
_, err := t.s.plan([]statemachine.Event{{User: evt}}, t.state)
|
||||
_, _, err := t.s.plan([]statemachine.Event{{User: evt}}, t.state)
|
||||
require.NoError(t.t, err)
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ func TestHappyPath(t *testing.T) {
|
||||
require.Equal(m.t, m.state.State, Committing)
|
||||
|
||||
m.planSingle(SectorCommitted{})
|
||||
require.Equal(m.t, m.state.State, SubmitCommit)
|
||||
|
||||
m.planSingle(SectorCommitSubmitted{})
|
||||
require.Equal(m.t, m.state.State, CommitWait)
|
||||
|
||||
m.planSingle(SectorProving{})
|
||||
@@ -98,13 +101,16 @@ func TestSeedRevert(t *testing.T) {
|
||||
m.planSingle(SectorSeedReady{})
|
||||
require.Equal(m.t, m.state.State, Committing)
|
||||
|
||||
_, err := m.s.plan([]statemachine.Event{{User: SectorSeedReady{SeedValue: nil, SeedEpoch: 5}}, {User: SectorCommitted{}}}, m.state)
|
||||
_, _, err := m.s.plan([]statemachine.Event{{User: SectorSeedReady{SeedValue: nil, SeedEpoch: 5}}, {User: SectorCommitted{}}}, m.state)
|
||||
require.NoError(t, err)
|
||||
require.Equal(m.t, m.state.State, Committing)
|
||||
|
||||
// not changing the seed this time
|
||||
_, err = m.s.plan([]statemachine.Event{{User: SectorSeedReady{SeedValue: nil, SeedEpoch: 5}}, {User: SectorCommitted{}}}, m.state)
|
||||
_, _, err = m.s.plan([]statemachine.Event{{User: SectorSeedReady{SeedValue: nil, SeedEpoch: 5}}, {User: SectorCommitted{}}}, m.state)
|
||||
require.NoError(t, err)
|
||||
require.Equal(m.t, m.state.State, SubmitCommit)
|
||||
|
||||
m.planSingle(SectorCommitSubmitted{})
|
||||
require.Equal(m.t, m.state.State, CommitWait)
|
||||
|
||||
m.planSingle(SectorProving{})
|
||||
@@ -129,7 +135,8 @@ func TestPlanCommittingHandlesSectorCommitFailed(t *testing.T) {
|
||||
|
||||
events := []statemachine.Event{{User: SectorCommitFailed{}}}
|
||||
|
||||
require.NoError(t, planCommitting(events, m.state))
|
||||
_, err := planCommitting(events, m.state)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, CommitFailed, m.state.State)
|
||||
}
|
||||
|
||||
Vendored
+11
-3
@@ -141,7 +141,7 @@ func (m *Sealing) Stop(ctx context.Context) error {
|
||||
return m.sectors.Stop(ctx)
|
||||
}
|
||||
func (m *Sealing) AddPieceToAnySector(ctx context.Context, size abi.UnpaddedPieceSize, r io.Reader, d DealInfo) (abi.SectorNumber, abi.PaddedPieceSize, error) {
|
||||
log.Infof("Adding piece for deal %d", d.DealID)
|
||||
log.Infof("Adding piece for deal %d (publish msg: %s)", d.DealID, d.PublishCid)
|
||||
if (padreader.PaddedSize(uint64(size))) != size {
|
||||
return 0, 0, xerrors.Errorf("cannot allocate unpadded piece")
|
||||
}
|
||||
@@ -224,15 +224,23 @@ func (m *Sealing) Remove(ctx context.Context, sid abi.SectorNumber) error {
|
||||
|
||||
// Caller should NOT hold m.unsealedInfoMap.lk
|
||||
func (m *Sealing) StartPacking(sectorID abi.SectorNumber) error {
|
||||
// locking here ensures that when the SectorStartPacking event is sent, the sector won't be picked up anywhere else
|
||||
m.unsealedInfoMap.lk.Lock()
|
||||
defer m.unsealedInfoMap.lk.Unlock()
|
||||
|
||||
// cannot send SectorStartPacking to sectors that have already been packed, otherwise it will cause the state machine to exit
|
||||
if _, ok := m.unsealedInfoMap.infos[sectorID]; !ok {
|
||||
log.Warnf("call start packing, but sector %v not in unsealedInfoMap.infos, maybe have called", sectorID)
|
||||
return nil
|
||||
}
|
||||
log.Infof("Starting packing sector %d", sectorID)
|
||||
err := m.sectors.Send(uint64(sectorID), SectorStartPacking{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("send Starting packing event success sector %d", sectorID)
|
||||
|
||||
m.unsealedInfoMap.lk.Lock()
|
||||
delete(m.unsealedInfoMap.infos, sectorID)
|
||||
m.unsealedInfoMap.lk.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+7
-4
@@ -10,12 +10,13 @@ const (
|
||||
WaitDeals SectorState = "WaitDeals" // waiting for more pieces (deals) to be added to the sector
|
||||
Packing SectorState = "Packing" // sector not in sealStore, and not on chain
|
||||
PreCommit1 SectorState = "PreCommit1" // do PreCommit1
|
||||
PreCommit2 SectorState = "PreCommit2" // do PreCommit1
|
||||
PreCommit2 SectorState = "PreCommit2" // do PreCommit2
|
||||
PreCommitting SectorState = "PreCommitting" // on chain pre-commit
|
||||
PreCommitWait SectorState = "PreCommitWait" // waiting for precommit to land on chain
|
||||
WaitSeed SectorState = "WaitSeed" // waiting for seed
|
||||
Committing SectorState = "Committing"
|
||||
CommitWait SectorState = "CommitWait" // waiting for message to land on chain
|
||||
Committing SectorState = "Committing" // compute PoRep
|
||||
SubmitCommit SectorState = "SubmitCommit" // send commit message to the chain
|
||||
CommitWait SectorState = "CommitWait" // wait for the commit message to land on chain
|
||||
FinalizeSector SectorState = "FinalizeSector"
|
||||
Proving SectorState = "Proving"
|
||||
// error modes
|
||||
@@ -25,8 +26,10 @@ const (
|
||||
PreCommitFailed SectorState = "PreCommitFailed"
|
||||
ComputeProofFailed SectorState = "ComputeProofFailed"
|
||||
CommitFailed SectorState = "CommitFailed"
|
||||
PackingFailed SectorState = "PackingFailed"
|
||||
PackingFailed SectorState = "PackingFailed" // TODO: deprecated, remove
|
||||
FinalizeFailed SectorState = "FinalizeFailed"
|
||||
DealsExpired SectorState = "DealsExpired"
|
||||
RecoverDealIDs SectorState = "RecoverDealIDs"
|
||||
|
||||
Faulty SectorState = "Faulty" // sector is corrupted or gone for some reason
|
||||
FaultReported SectorState = "FaultReported" // sector has been declared as a fault on chain
|
||||
|
||||
+140
-1
@@ -1,12 +1,18 @@
|
||||
package sealing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-statemachine"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/market"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
|
||||
"github.com/filecoin-project/lotus/extern/sector-storage/zerocomm"
|
||||
)
|
||||
|
||||
const minRetryTime = 1 * time.Minute
|
||||
@@ -81,6 +87,11 @@ func (m *Sealing) handlePreCommitFailed(ctx statemachine.Context, sector SectorI
|
||||
return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("ticket expired error: %w", err)})
|
||||
case *ErrBadTicket:
|
||||
return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("bad expired: %w", err)})
|
||||
case *ErrInvalidDeals:
|
||||
log.Warnf("invalid deals in sector %d: %v", sector.SectorNumber, err)
|
||||
return ctx.Send(SectorInvalidDealIDs{Return: RetPreCommitFailed})
|
||||
case *ErrExpiredDeals:
|
||||
return ctx.Send(SectorDealsExpired{xerrors.Errorf("sector deals expired: %w", err)})
|
||||
case *ErrNoPrecommit:
|
||||
return ctx.Send(SectorRetryPreCommit{})
|
||||
case *ErrPrecommitOnChain:
|
||||
@@ -88,6 +99,7 @@ func (m *Sealing) handlePreCommitFailed(ctx statemachine.Context, sector SectorI
|
||||
case *ErrSectorNumberAllocated:
|
||||
log.Errorf("handlePreCommitFailed: sector number already allocated, not proceeding: %+v", err)
|
||||
// TODO: check if the sector is committed (not sure how we'd end up here)
|
||||
// TODO: check on-chain state, adjust local sector number counter to not give out allocated numbers
|
||||
return nil
|
||||
default:
|
||||
return xerrors.Errorf("checkPrecommit sanity check error: %w", err)
|
||||
@@ -157,7 +169,12 @@ func (m *Sealing) handleCommitFailed(ctx statemachine.Context, sector SectorInfo
|
||||
case *ErrExpiredTicket:
|
||||
return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("ticket expired error: %w", err)})
|
||||
case *ErrBadTicket:
|
||||
return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("bad expired: %w", err)})
|
||||
return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("bad ticket: %w", err)})
|
||||
case *ErrInvalidDeals:
|
||||
log.Warnf("invalid deals in sector %d: %v", sector.SectorNumber, err)
|
||||
return ctx.Send(SectorInvalidDealIDs{Return: RetCommitFailed})
|
||||
case *ErrExpiredDeals:
|
||||
return ctx.Send(SectorDealsExpired{xerrors.Errorf("sector deals expired: %w", err)})
|
||||
case nil:
|
||||
return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("no precommit: %w", err)})
|
||||
case *ErrPrecommitOnChain:
|
||||
@@ -192,6 +209,11 @@ func (m *Sealing) handleCommitFailed(ctx statemachine.Context, sector SectorInfo
|
||||
return ctx.Send(SectorRetryPreCommitWait{})
|
||||
case *ErrNoPrecommit:
|
||||
return ctx.Send(SectorRetryPreCommit{})
|
||||
case *ErrInvalidDeals:
|
||||
log.Warnf("invalid deals in sector %d: %v", sector.SectorNumber, err)
|
||||
return ctx.Send(SectorInvalidDealIDs{Return: RetCommitFailed})
|
||||
case *ErrExpiredDeals:
|
||||
return ctx.Send(SectorDealsExpired{xerrors.Errorf("sector deals expired: %w", err)})
|
||||
case *ErrCommitWaitFailed:
|
||||
if err := failedCooldown(ctx, sector); err != nil {
|
||||
return err
|
||||
@@ -221,3 +243,120 @@ func (m *Sealing) handleFinalizeFailed(ctx statemachine.Context, sector SectorIn
|
||||
|
||||
return ctx.Send(SectorRetryFinalize{})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleRemoveFailed(ctx statemachine.Context, sector SectorInfo) error {
|
||||
if err := failedCooldown(ctx, sector); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.Send(SectorRemove{})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleDealsExpired(ctx statemachine.Context, sector SectorInfo) error {
|
||||
// First make vary sure the sector isn't committed
|
||||
si, err := m.api.StateSectorGetInfo(ctx.Context(), m.maddr, sector.SectorNumber, nil)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting sector info: %w", err)
|
||||
}
|
||||
if si != nil {
|
||||
// TODO: this should never happen, but in case it does, try to go back to
|
||||
// the proving state after running some checks
|
||||
return xerrors.Errorf("sector is committed on-chain, but we're in DealsExpired")
|
||||
}
|
||||
|
||||
if sector.PreCommitInfo == nil {
|
||||
// TODO: Create a separate state which will remove those pieces, and go back to PC1
|
||||
log.Errorf("non-precommitted sector with expired deals, can't recover from this yet")
|
||||
}
|
||||
|
||||
// Not much to do here, we can't go back in time to commit this sector
|
||||
return ctx.Send(SectorRemove{})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleRecoverDealIDs(ctx statemachine.Context, sector SectorInfo) error {
|
||||
tok, height, err := m.api.ChainHead(ctx.Context())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting chain head: %w", err)
|
||||
}
|
||||
|
||||
var toFix []int
|
||||
|
||||
for i, p := range sector.Pieces {
|
||||
// if no deal is associated with the piece, ensure that we added it as
|
||||
// filler (i.e. ensure that it has a zero PieceCID)
|
||||
if p.DealInfo == nil {
|
||||
exp := zerocomm.ZeroPieceCommitment(p.Piece.Size.Unpadded())
|
||||
if !p.Piece.PieceCID.Equals(exp) {
|
||||
return xerrors.Errorf("sector %d piece %d had non-zero PieceCID %+v", sector.SectorNumber, i, p.Piece.PieceCID)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
proposal, err := m.api.StateMarketStorageDeal(ctx.Context(), p.DealInfo.DealID, tok)
|
||||
if err != nil {
|
||||
log.Warnf("getting deal %d for piece %d: %+v", p.DealInfo.DealID, i, err)
|
||||
toFix = append(toFix, i)
|
||||
continue
|
||||
}
|
||||
|
||||
if proposal.Provider != m.maddr {
|
||||
log.Warnf("piece %d (of %d) of sector %d refers deal %d with wrong provider: %s != %s", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID, proposal.Provider, m.maddr)
|
||||
toFix = append(toFix, i)
|
||||
continue
|
||||
}
|
||||
|
||||
if proposal.PieceCID != p.Piece.PieceCID {
|
||||
log.Warnf("piece %d (of %d) of sector %d refers deal %d with wrong PieceCID: %x != %x", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID, p.Piece.PieceCID, proposal.PieceCID)
|
||||
toFix = append(toFix, i)
|
||||
continue
|
||||
}
|
||||
|
||||
if p.Piece.Size != proposal.PieceSize {
|
||||
log.Warnf("piece %d (of %d) of sector %d refers deal %d with different size: %d != %d", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID, p.Piece.Size, proposal.PieceSize)
|
||||
toFix = append(toFix, i)
|
||||
continue
|
||||
}
|
||||
|
||||
if height >= proposal.StartEpoch {
|
||||
// TODO: check if we are in an early enough state (before precommit), try to remove the offending pieces
|
||||
// (tricky as we have to 'defragment' the sector while doing that, and update piece references for retrieval)
|
||||
return xerrors.Errorf("can't fix sector deals: piece %d (of %d) of sector %d refers expired deal %d - should start at %d, head %d", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID, proposal.StartEpoch, height)
|
||||
}
|
||||
}
|
||||
|
||||
updates := map[int]abi.DealID{}
|
||||
for _, i := range toFix {
|
||||
p := sector.Pieces[i]
|
||||
|
||||
if p.DealInfo.PublishCid == nil {
|
||||
// TODO: check if we are in an early enough state try to remove this piece
|
||||
log.Error("can't fix sector deals: piece %d (of %d) of sector %d has nil DealInfo.PublishCid (refers to deal %d)", i, len(sector.Pieces), sector.SectorNumber, p.DealInfo.DealID)
|
||||
// Not much to do here (and this can only happen for old spacerace sectors)
|
||||
return ctx.Send(SectorRemove{})
|
||||
}
|
||||
|
||||
ml, err := m.api.StateSearchMsg(ctx.Context(), *p.DealInfo.PublishCid)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("looking for publish deal message %s (sector %d, piece %d): %w", *p.DealInfo.PublishCid, sector.SectorNumber, i, err)
|
||||
}
|
||||
|
||||
if ml.Receipt.ExitCode != exitcode.Ok {
|
||||
return xerrors.Errorf("looking for publish deal message %s (sector %d, piece %d): non-ok exit code: %s", *p.DealInfo.PublishCid, sector.SectorNumber, i, ml.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
var retval market.PublishStorageDealsReturn
|
||||
if err := retval.UnmarshalCBOR(bytes.NewReader(ml.Receipt.Return)); err != nil {
|
||||
return xerrors.Errorf("looking for publish deal message: unmarshaling message return: %w", err)
|
||||
}
|
||||
|
||||
if len(retval.IDs) != 1 {
|
||||
// market currently only ever sends messages with 1 deal
|
||||
return xerrors.Errorf("can't recover dealIDs from publish deal message with more than 1 deal")
|
||||
}
|
||||
|
||||
updates[i] = retval.IDs[0]
|
||||
}
|
||||
|
||||
// Not much to do here, we can't go back in time to commit this sector
|
||||
return ctx.Send(SectorUpdateDealIDs{Updates: updates})
|
||||
}
|
||||
|
||||
+40
-14
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
|
||||
"github.com/filecoin-project/specs-storage/storage"
|
||||
)
|
||||
|
||||
@@ -79,15 +80,16 @@ func (m *Sealing) getTicket(ctx statemachine.Context, sector SectorInfo) (abi.Se
|
||||
}
|
||||
|
||||
func (m *Sealing) handlePreCommit1(ctx statemachine.Context, sector SectorInfo) error {
|
||||
if err := checkPieces(ctx.Context(), sector, m.api); err != nil { // Sanity check state
|
||||
if err := checkPieces(ctx.Context(), m.maddr, sector, m.api); err != nil { // Sanity check state
|
||||
switch err.(type) {
|
||||
case *ErrApi:
|
||||
log.Errorf("handlePreCommit1: api error, not proceeding: %+v", err)
|
||||
return nil
|
||||
case *ErrInvalidDeals:
|
||||
return ctx.Send(SectorPackingFailed{xerrors.Errorf("invalid dealIDs in sector: %w", err)})
|
||||
log.Warnf("invalid deals in sector %d: %v", sector.SectorNumber, err)
|
||||
return ctx.Send(SectorInvalidDealIDs{Return: RetPreCommit1})
|
||||
case *ErrExpiredDeals: // Probably not much we can do here, maybe re-pack the sector?
|
||||
return ctx.Send(SectorPackingFailed{xerrors.Errorf("expired dealIDs in sector: %w", err)})
|
||||
return ctx.Send(SectorDealsExpired{xerrors.Errorf("expired dealIDs in sector: %w", err)})
|
||||
default:
|
||||
return xerrors.Errorf("checkPieces sanity check error: %w", err)
|
||||
}
|
||||
@@ -155,6 +157,11 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf
|
||||
return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("ticket expired: %w", err)})
|
||||
case *ErrBadTicket:
|
||||
return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("bad ticket: %w", err)})
|
||||
case *ErrInvalidDeals:
|
||||
log.Warnf("invalid deals in sector %d: %v", sector.SectorNumber, err)
|
||||
return ctx.Send(SectorInvalidDealIDs{Return: RetPreCommitting})
|
||||
case *ErrExpiredDeals:
|
||||
return ctx.Send(SectorDealsExpired{xerrors.Errorf("sector deals expired: %w", err)})
|
||||
case *ErrPrecommitOnChain:
|
||||
return ctx.Send(SectorPreCommitLanded{TipSet: tok}) // we re-did precommit
|
||||
case *ErrSectorNumberAllocated:
|
||||
@@ -226,11 +233,18 @@ func (m *Sealing) handlePreCommitWait(ctx statemachine.Context, sector SectorInf
|
||||
return ctx.Send(SectorChainPreCommitFailed{err})
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
switch mw.Receipt.ExitCode {
|
||||
case exitcode.Ok:
|
||||
// this is what we expect
|
||||
case exitcode.SysErrOutOfGas:
|
||||
// gas estimator guessed a wrong number
|
||||
return ctx.Send(SectorRetryPreCommit{})
|
||||
default:
|
||||
log.Error("sector precommit failed: ", mw.Receipt.ExitCode)
|
||||
err := xerrors.Errorf("sector precommit failed: %d", mw.Receipt.ExitCode)
|
||||
return ctx.Send(SectorChainPreCommitFailed{err})
|
||||
}
|
||||
|
||||
log.Info("precommit message landed on chain: ", sector.SectorNumber)
|
||||
|
||||
return ctx.Send(SectorPreCommitLanded{TipSet: mw.TipSetTok})
|
||||
@@ -326,21 +340,25 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo)
|
||||
return ctx.Send(SectorComputeProofFailed{xerrors.Errorf("computing seal proof failed(2): %w", err)})
|
||||
}
|
||||
|
||||
return ctx.Send(SectorCommitted{
|
||||
Proof: proof,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo) error {
|
||||
tok, _, err := m.api.ChainHead(ctx.Context())
|
||||
if err != nil {
|
||||
log.Errorf("handleCommitting: api error, not proceeding: %+v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := m.checkCommit(ctx.Context(), sector, proof, tok); err != nil {
|
||||
if err := m.checkCommit(ctx.Context(), sector, sector.Proof, tok); err != nil {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("commit check error: %w", err)})
|
||||
}
|
||||
|
||||
// TODO: Consider splitting states and persist proof for faster recovery
|
||||
|
||||
params := &miner.ProveCommitSectorParams{
|
||||
SectorNumber: sector.SectorNumber,
|
||||
Proof: proof,
|
||||
Proof: sector.Proof,
|
||||
}
|
||||
|
||||
enc := new(bytes.Buffer)
|
||||
@@ -372,14 +390,13 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo)
|
||||
collateral = big.Zero()
|
||||
}
|
||||
|
||||
// TODO: check seed / ticket are up to date
|
||||
// TODO: check seed / ticket / deals are up to date
|
||||
mcid, err := m.api.SendMsg(ctx.Context(), waddr, m.maddr, builtin.MethodsMiner.ProveCommitSector, collateral, m.feeCfg.MaxCommitGasFee, enc.Bytes())
|
||||
if err != nil {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("pushing message to mpool: %w", err)})
|
||||
}
|
||||
|
||||
return ctx.Send(SectorCommitted{
|
||||
Proof: proof,
|
||||
return ctx.Send(SectorCommitSubmitted{
|
||||
Message: mcid,
|
||||
})
|
||||
}
|
||||
@@ -395,13 +412,22 @@ func (m *Sealing) handleCommitWait(ctx statemachine.Context, sector SectorInfo)
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("failed to wait for porep inclusion: %w", err)})
|
||||
}
|
||||
|
||||
if mw.Receipt.ExitCode != 0 {
|
||||
switch mw.Receipt.ExitCode {
|
||||
case exitcode.Ok:
|
||||
// this is what we expect
|
||||
case exitcode.SysErrOutOfGas:
|
||||
// gas estimator guessed a wrong number
|
||||
return ctx.Send(SectorRetrySubmitCommit{})
|
||||
default:
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("submitting sector proof failed (exit=%d, msg=%s) (t:%x; s:%x(%d); p:%x)", mw.Receipt.ExitCode, sector.CommitMessage, sector.TicketValue, sector.SeedValue, sector.SeedEpoch, sector.Proof)})
|
||||
}
|
||||
|
||||
_, err = m.api.StateSectorGetInfo(ctx.Context(), m.maddr, sector.SectorNumber, mw.TipSetTok)
|
||||
si, err := m.api.StateSectorGetInfo(ctx.Context(), m.maddr, sector.SectorNumber, mw.TipSetTok)
|
||||
if err != nil {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("proof validation failed, sector not found in sector set after cron: %w", err)})
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("proof validation failed, calling StateSectorGetInfo: %w", err)})
|
||||
}
|
||||
if si == nil {
|
||||
return ctx.Send(SectorCommitFailed{xerrors.Errorf("proof validation failed, sector not found in sector set after cron")})
|
||||
}
|
||||
|
||||
return ctx.Send(SectorProving{})
|
||||
|
||||
Vendored
+13
@@ -30,6 +30,7 @@ type Piece struct {
|
||||
|
||||
// DealInfo is a tuple of deal identity and its schedule
|
||||
type DealInfo struct {
|
||||
PublishCid *cid.Cid
|
||||
DealID abi.DealID
|
||||
DealSchedule DealSchedule
|
||||
KeepUnsealed bool
|
||||
@@ -53,6 +54,15 @@ type Log struct {
|
||||
Kind string
|
||||
}
|
||||
|
||||
type ReturnState string
|
||||
|
||||
const (
|
||||
RetPreCommit1 = ReturnState(PreCommit1)
|
||||
RetPreCommitting = ReturnState(PreCommitting)
|
||||
RetPreCommitFailed = ReturnState(PreCommitFailed)
|
||||
RetCommitFailed = ReturnState(CommitFailed)
|
||||
)
|
||||
|
||||
type SectorInfo struct {
|
||||
State SectorState
|
||||
SectorNumber abi.SectorNumber
|
||||
@@ -90,6 +100,9 @@ type SectorInfo struct {
|
||||
// Faults
|
||||
FaultReportMsg *cid.Cid
|
||||
|
||||
// Recovery
|
||||
Return ReturnState
|
||||
|
||||
// Debug
|
||||
LastErr string
|
||||
|
||||
|
||||
+4
@@ -72,6 +72,10 @@ func (m *Sealing) tryUpgradeSector(ctx context.Context, params *miner.SectorPreC
|
||||
log.Errorf("error calling StateSectorGetInfo for replaced sector: %+v", err)
|
||||
return big.Zero()
|
||||
}
|
||||
if ri == nil {
|
||||
log.Errorf("couldn't find sector info for sector to replace: %+v", replace)
|
||||
return big.Zero()
|
||||
}
|
||||
|
||||
if params.Expiration < ri.Expiration {
|
||||
// TODO: Some limit on this
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user