Compare commits

..
180 Commits
Author SHA1 Message Date
Whyrusleeping 7b258eddc4 Merge pull request #975 from filecoin-project/feat/cli-slashing
chain slash-consensus command
2020-01-24 14:14:03 -08:00
Jakub Sztandera 3bf0bd6ce9 Merge pull request #1153 from filecoin-project/feat/version-bump
Bump version following blocksync bugfixes
2020-01-24 14:10:17 -08:00
whyrusleeping 0ed981be19 Bump version following blocksync bugfixes 2020-01-24 13:58:57 -08:00
Łukasz Magiera 77113844a2 chain slash-consensus command 2020-01-24 13:58:06 -08:00
Whyrusleeping 9da4296e21 Merge pull request #745 from filcloud/fix/pr-docker
add docker support
2020-01-24 13:48:30 -08:00
Łukasz Magiera 9fc5f0cd9d Merge pull request #1124 from filecoin-project/feat/sector-recovery
sealing: Implement some common error states
2020-01-24 21:25:03 +01:00
Łukasz Magiera 2f2e7be12a sealing: docstrings for sanity-checks 2020-01-24 21:15:02 +01:00
Whyrusleeping 9892bf4379 Merge pull request #1147 from filecoin-project/upd/badger
Update badger
2020-01-24 11:49:36 -08:00
Whyrusleeping 351a683812 Merge pull request #1151 from filecoin-project/lotus-health-v2
refactor lotus-health agent for robustness
2020-01-24 11:44:47 -08:00
ognots 445b7f3388 refactor lotus-health agent for robustness
add retry logic when calls to API fail.
if API reconnects fail, restart lotus-daemon as it means lotus-daemon is likely unhealthy.

wait for lotus node's chain to sync during each check cycle, to avoid restarting lotus-daemon if needing to sync.

handle SIGTERM properly.

general cleanup and refactor of code, getting ready of unnecessary channels
2020-01-24 11:46:47 -05:00
Łukasz Magiera 9e95372e5d Merge pull request #1149 from filecoin-project/fix/peermgr-ts
make expand peers thread safe
2020-01-24 17:38:35 +01:00
Łukasz Magiera b5660b58b3 Merge remote-tracking branch 'origin/master' into feat/sector-recovery 2020-01-24 01:53:58 +01:00
Jakub Sztandera b472ea371a Merge pull request #1150 from filecoin-project/feat/sync-dashboard
Sync dashboard to what we have on stats.testnet.filecoin.io
2020-01-23 16:21:11 -08:00
Travis Person 6703bd2c71 Sync dashboard to what we have on stats.testnet.filecoin.io 2020-01-24 00:13:15 +00:00
Jakub Sztandera 36388de4e1 make expand peers thread safe
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-23 15:40:17 -08:00
Jakub Sztandera 10159ba1bb Merge pull request #1145 from filecoin-project/fix/blocksync-spam
limit blocksync response sizes
2020-01-23 15:21:01 -08:00
Jakub Sztandera 792a2cad82 limit blocksync response sizes
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-23 14:24:52 -08:00
Jakub Sztandera 7f1b12d29e Merge pull request #1137 from filecoin-project/feat/chainwatch-active-deals
chainwatch: Track deal activations
2020-01-23 13:21:39 -08:00
Jakub Sztandera 23b46a05f7 Update badger
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-23 12:46:32 -08:00
Jakub Sztandera cd14a5eb40 Merge pull request #1142 from filecoin-project/feat/bs2
Add incremental timeouts to blocksync fetching
2020-01-23 12:44:08 -08:00
Jakub Sztandera 2256e62648 Improve warning
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-23 12:39:23 -08:00
Jakub Sztandera 6b42a78c3f Cleanup
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-23 12:35:57 -08:00
Łukasz Magiera 6b2b22782b sealing: PreCommitFailed can go to SealFailed 2020-01-23 18:45:57 +01:00
Łukasz Magiera de52d3cadd sealing: Handlef for PreCommitFailed 2020-01-23 18:34:04 +01:00
Łukasz Magiera 7e43c40529 storageminer: Use tabwriter in sectors list 2020-01-23 17:15:45 +01:00
Łukasz Magiera df9dfa9d0c sealing: Don't infinite-loop on fatal errors 2020-01-23 17:11:58 +01:00
Łukasz Magiera 08a2e0f82c sealing: Errors are hard 2020-01-23 17:02:55 +01:00
Łukasz Magiera 01dc9c767e sealing: Error types that can actually be checked 2020-01-23 16:57:50 +01:00
Łukasz Magiera 67b441838b sealing: Actually call handleSealFailed 2020-01-23 16:47:33 +01:00
Łukasz Magiera 94f2948020 sealing: implement handler for sealFailed 2020-01-23 16:38:10 +01:00
Łukasz Magiera f5540195de worker: Fix progress bars 2020-01-23 15:38:36 +01:00
Łukasz Magiera 558e4c7669 Fix tests after sectorbuilder update 2020-01-23 15:33:28 +01:00
Łukasz Magiera c3a5da8586 storageminer: log flag for sector status 2020-01-23 15:18:05 +01:00
Łukasz Magiera 02c8ab7839 sealing: Check ticket expiration 2020-01-23 15:05:44 +01:00
Łukasz Magiera 8414569428 Merge remote-tracking branch 'origin/master' into feat/sector-recovery 2020-01-23 14:29:44 +01:00
Łukasz Magiera bd97eb98c5 Merge pull request #1141 from filecoin-project/fix/no-cgo
Make it buld without cgo
2020-01-23 14:10:56 +01:00
Łukasz Magiera e67fbda305 mod tidy 2020-01-23 13:29:43 +01:00
Łukasz Magiera 26e6bc32ee sealing: check deal expiration 2020-01-23 13:17:45 +01:00
Łukasz Magiera 11a6bff416 miner: Handle stop signal when sleeping 2020-01-23 11:10:42 +01:00
Łukasz Magiera e67ee227a3 sealing: Nonzero exit on commit isn't fatal 2020-01-23 11:02:20 +01:00
Łukasz Magiera f8ec1d9d22 Merge remote-tracking branch 'origin/master' into feat/sector-recovery 2020-01-23 10:52:10 +01:00
Łukasz Magiera 7fb3360d6f storage: Fix handing stop call 2020-01-23 10:46:46 +01:00
Jakub Sztandera cb35625a53 Remove log
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-22 16:04:33 -08:00
Jakub Sztandera 26870501a7 mod tidy
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-22 16:04:13 -08:00
Jakub Sztandera f5d061f34f Add incremental timeouts to blocksync fetching
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-22 15:57:42 -08:00
Jakub Sztandera 362fbbf913 Add blockheader_cgo file
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-22 15:55:52 -08:00
Jakub Sztandera f8d5f610b6 Merge pull request #1140 from filecoin-project/feat/profiling-work
wire up tracing into state tree flush, add pprof flag
2020-01-22 15:47:19 -08:00
Łukasz Magiera e43174f2af sealing: Not getting seal ticket isn't fatal 2020-01-22 22:16:45 +01:00
whyrusleeping 16523c2ef9 fixup tests 2020-01-22 12:56:40 -08:00
Łukasz Magiera d3a9006272 sealing: Wire up sector event log 2020-01-22 21:29:19 +01:00
Jakub Sztandera a404d535ee Make it buld without cgo
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-22 12:03:12 -08:00
whyrusleeping dcd01df2d3 wire up tracing into state tree flush, add pprof flag 2020-01-22 11:53:06 -08:00
Łukasz Magiera ba5a5d1248 sealing: wire up checkPieces and checkSeal 2020-01-22 20:47:29 +01:00
Łukasz Magiera 052b090bbf sealing: Fix planOne for global events 2020-01-22 19:30:56 +01:00
Łukasz Magiera 4654d9c192 chainwatch: Util routines / views 2020-01-22 16:12:47 +01:00
Łukasz Magiera 1e877eae86 sealing: Add Log field to SectorInfo 2020-01-22 03:41:39 +01:00
Łukasz Magiera 3c934ea21f Merge branch 'master' into feat/sector-recovery 2020-01-22 03:27:11 +01:00
Łukasz Magiera 52718e514c chainwatch: Track deal activations 2020-01-22 03:22:12 +01:00
Łukasz Magiera 4310cc23ce Merge pull request #1135 from filecoin-project/feat/listmsgs-cids
cli: state: --cids flag for list-messages
2020-01-22 00:33:53 +01:00
Łukasz Magiera bfb1cc6379 cli: state: --cids flag for list-messages 2020-01-21 23:54:01 +01:00
Jakub Sztandera cbfc675f4f Merge pull request #1120 from dtynn/fix/current_commit_flag
fix CurrentCommit in Makefile
2020-01-21 13:42:43 -08:00
Jakub Sztandera e7b5d678a9 rearange quotes
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-21 13:40:30 -08:00
ognots effacec817 range over right index to prevent bounds errors
the test scenario 'healthyHeadCheckWindow5' was causing index out of bounds errors.
the second range function in checkWindow was iterating over the incorrect slice of cids.
should be comparing latest items in slices first
2020-01-21 16:07:53 -05:00
Łukasz Magiera 3a89388039 Merge pull request #1134 from filecoin-project/feat/multisig-cli
add a CLI for multisig wallets
2020-01-21 21:05:41 +01:00
WhyrusleepingandŁukasz Magiera 3366c02a36 Update cli/multisig.go
Co-Authored-By: Łukasz Magiera <magik6k@users.noreply.github.com>
2020-01-21 11:52:59 -08:00
WhyrusleepingandŁukasz Magiera a26cc96a49 Update cli/multisig.go
Co-Authored-By: Łukasz Magiera <magik6k@users.noreply.github.com>
2020-01-21 11:52:50 -08:00
whyrusleeping 6d9ac84359 add a CLI for multisig wallets 2020-01-21 11:02:51 -08:00
Łukasz Magiera 882507e915 Merge pull request #1133 from filecoin-project/fix/build
Fix master build
2020-01-21 19:58:08 +01:00
Łukasz Magiera 08e1b52281 Fix master build 2020-01-21 19:51:17 +01:00
Łukasz Magiera cf153f89df Merge pull request #1131 from filecoin-project/feat/soft-map-cbg
cbor-gen: Soft struct-map unmarshaling
2020-01-21 19:44:39 +01:00
Łukasz Magiera d2401bc339 Update cbor-gen, error on unknown fields 2020-01-21 17:28:55 +01:00
Łukasz Magiera fd0bfc8777 Merge pull request #1089 from filecoin-project/feat/state-call-cmd
implement state call command for easy method calling
2020-01-21 17:16:54 +01:00
Łukasz Magiera 7b295d7bb0 Merge pull request #1115 from filecoin-project/feat/chainwatch-json-states
chainwatch: Store json states
2020-01-21 17:15:33 +01:00
Łukasz Magiera b6563ffc29 Merge pull request #1123 from filecoin-project/fix/jsonrpc-chclose-panic
jsonrpc: Fix channel closing race
2020-01-21 17:14:55 +01:00
Jakub Sztandera 2594ff6233 Merge pull request #1121 from filecoin-project/fix/mpool-stat-errs
mpool stat: Don't exit on StateGetActor error
2020-01-21 17:13:57 +01:00
Jakub Sztandera 807d803a81 Merge pull request #1130 from filecoin-project/fix/fpost-no-candidates
fpost: More logging
2020-01-21 17:07:22 +01:00
Łukasz Magiera 2076cb106d cobr-gen: Soft struct-map unmarshaling 2020-01-21 17:05:10 +01:00
Łukasz Magiera 5b304fa20a fpost: More logging 2020-01-21 16:01:42 +01:00
Łukasz Magiera fbc0330fa8 jsonrpc: Fix channel registration deadlock 2020-01-21 14:48:17 +01:00
Łukasz Magiera f5bfaff003 Merge pull request #1128 from filecoin-project/feat/chain-export-2
Implement chain import and exporting
2020-01-21 03:35:24 +01:00
Łukasz Magiera eac17a8328 mod tidy 2020-01-21 03:35:03 +01:00
Łukasz Magiera f9c9316990 Merge pull request #1127 from filecoin-project/bug/lotus-health
fixes health agent bug
2020-01-21 03:29:51 +01:00
whyrusleeping d9f5f6e2c6 add flag to halt after chain importing 2020-01-20 17:58:58 -08:00
whyrusleeping 955b755055 wire up importing 2020-01-20 17:53:55 -08:00
ognots 0c6e4c6c40 fixes health agent bug
was passing wrong variable to updateWindow function argument.
also updates duplicate function comment
2020-01-20 20:34:13 -05:00
whyrusleeping 57812fc61d chain export should work now 2020-01-20 15:51:28 -08:00
whyrusleeping 62f05d4c14 WIP: chain export 2020-01-20 15:51:28 -08:00
Łukasz Magiera e4de5c55ac sealing: Some state renaming 2020-01-20 23:04:46 +01:00
Łukasz Magiera efaf2c720b sealing: WIP SectorInfo sanity checks 2020-01-20 23:04:27 +01:00
Łukasz Magiera d1f419c9d1 jsonrpc: Fix channel closing race 2020-01-20 19:21:16 +01:00
Łukasz Magiera c5ece2a892 Merge pull request #1119 from dtynn/fix/some_sealing_problems
small fixes
2020-01-20 17:05:14 +01:00
Łukasz Magiera 7712d97270 mpool stat: Don't exit on StateGetActor error 2020-01-20 16:18:02 +01:00
dtynn 8e90d5fb17 fix CurrentCommit in Makefile 2020-01-20 18:22:42 +08:00
dtynn b2fcca87ff small fixes 2020-01-20 16:23:56 +08:00
Łukasz Magiera a46a9bb42e chainwatch: Store deals 2020-01-20 01:49:52 +01:00
Łukasz Magiera 62d661f898 chainwatch: Store json states 2020-01-19 22:48:19 +01:00
Łukasz Magiera 4436148fa1 Merge pull request #1117 from filecoin-project/fix/dont-override-loglvl
lotuslog: Don't override log levels
2020-01-19 21:38:36 +01:00
Łukasz Magiera 3886e050b0 lotuslog: don't override log levels 2020-01-19 21:18:35 +01:00
Łukasz Magiera ff4b0c8f5d Merge pull request #1110 from filecoin-project/fix/nil-newts-fpost-panic
fpost: Don't panic with nil new tipset
2020-01-19 21:17:51 +01:00
Łukasz Magiera f82ee0a2a7 Merge pull request #1116 from filecoin-project/fix/v0.2.5
Correctly update proving set & sector set with amt2
2020-01-19 17:53:59 +01:00
Łukasz Magiera b5d603dd46 Cleaner fork logic in onSuccessfulPoSt2 2020-01-19 17:44:44 +01:00
Łukasz Magiera 0b8ae006d3 Schedule the fork at 11000 2020-01-19 17:37:28 +01:00
Łukasz Magiera 55fd0a4e60 stmgr: Exetute miner amt after correctly switching to amt2 in miner code 2020-01-19 17:27:43 +01:00
Łukasz Magiera 3c4acbc6cb Merge pull request #1107 from dtynn/feat/bootybay-fork
correctly update proving set & sector set with amt2
2020-01-19 17:22:23 +01:00
Jakub Sztandera 8224aab851 Merge pull request #1112 from zhy2006/master
fix: calculate expected cost for sorting peers
2020-01-19 16:00:48 +01:00
Jakub Sztandera 28604f5a4c Merge pull request #1114 from filecoin-project/feat/bench-nits
bench: Don't print unseal results when unsealing is skipped
2020-01-19 15:58:38 +01:00
Łukasz Magiera 533081454a bench: Don't print unseal results when unsealing is skipped 2020-01-19 15:56:37 +01:00
Łukasz Magiera e94e64652b Merge pull request #1054 from filecoin-project/feat/event-states
Sector state machine changes
2020-01-19 15:53:07 +01:00
Yang 489ecef47b fix: calculate expected cost for sorting peers 2020-01-19 13:48:15 +08:00
dtynn 8b42ca76f8 add onSuccessfulPoSt2 2020-01-19 08:22:12 +08:00
Łukasz Magiera be2d138aac fpost: Don't panic with nil new tipset 2020-01-18 16:00:22 +01:00
Łukasz Magiera 1e782b3de2 Merge branch 'master' into feat/event-states 2020-01-18 14:40:59 +01:00
dtynn 8bd62165f5 RemoveFromSectorSet2 is declared but never used, remove redclared one 2020-01-18 20:08:44 +08:00
dtynn ae8a9a93bf correctly update proving set & sector set with amt2 after some fork height 2020-01-18 19:41:34 +08:00
Łukasz Magiera f7ab2103ce Merge pull request #1106 from terorie/debug-trust-params
params_debug.go: remove $TRUST_PARAMS override
2020-01-18 12:26:37 +01:00
Richard Patel c3d5a9848b params_debug.go: remove $TRUST_PARAMS override 2020-01-18 10:38:51 +01:00
Whyrusleeping 6d6d82a304 Merge pull request #1104 from filecoin-project/feat/frigid-fork
Catch cases where new old miners with bad code were getting made
2020-01-17 22:11:09 -08:00
whyrusleeping bc83c0221b bump that version 2020-01-17 21:45:51 -08:00
whyrusleeping c6bf252a21 use right fork trigger height 2020-01-17 21:35:45 -08:00
whyrusleeping f9ea6969c3 Catch cases where new old miners with bad code were getting made 2020-01-17 21:20:56 -08:00
whyrusleeping 9e956ff9f4 handle integers and peer IDs 2020-01-17 18:27:29 -08:00
whyrusleeping 0a45e97699 implement state call command for easy method calling 2020-01-17 18:27:29 -08:00
Whyrusleeping 3962d4204b Merge pull request #1096 from filecoin-project/fix/msgset-add-error
Add missing fields to duplicate message error
2020-01-17 16:22:20 -08:00
Jakub Sztandera fd76216652 Merge pull request #1101 from filecoin-project/patch/faulty-declare
fixup bitfield serialization in manual faulty sector handler
2020-01-17 18:19:35 +01:00
whyrusleeping 2a7dcb6e17 fixup bitfield serialization in manual faulty sector handler 2020-01-17 00:13:00 -08:00
Whyrusleeping b02eaee0d4 Merge pull request #1100 from filecoin-project/feat/blizzar-fork
The Blizzard Fork
2020-01-16 23:28:01 -08:00
whyrusleeping 6ccf24ce6c Fix miner tests, push back fork time 2020-01-16 23:10:47 -08:00
whyrusleeping c6186a03f1 quick mechanism to prevent miners from submitting slashable blocks 2020-01-16 22:58:10 -08:00
whyrusleeping ee4f503e10 Set fork height 2020-01-16 22:47:27 -08:00
whyrusleeping 0c227e89b2 fix last miner test, go mod tidy 2020-01-16 22:39:23 -08:00
whyrusleeping 128933a655 fix slashing test 2020-01-16 22:27:56 -08:00
whyrusleeping 055b215bc7 don't allow slashing with old blocks 2020-01-16 22:14:00 -08:00
whyrusleeping 611dd77442 properly filter messages during compute state 2020-01-16 19:36:54 -08:00
whyrusleeping fb173c058e WIP: blizzard fork changes and test utilities 2020-01-16 18:33:43 -08:00
whyrusleeping 2edb32979f move fork constant and tag fork branch with comment 2020-01-16 15:11:08 -08:00
whyrusleeping e3bed8d12a Catch passing duplicate block to arbitrate consensus fault 2020-01-16 15:11:08 -08:00
Jakub Sztandera 84de47377c Intoroduce AMT v2 and amtFsck
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-01-16 23:21:16 +01:00
Travis Person 06c99d0e18 Add missing fields to duplicate message error 2020-01-16 17:13:00 +00:00
Łukasz Magiera 0aebec6697 Merge branch 'master' into feat/event-states 2020-01-16 03:59:14 +01:00
Łukasz Magiera d4c50b8ea0 sealing: gofmt 2020-01-16 03:54:57 +01:00
Łukasz Magiera cc424d64fe sealing: Handle events based on current state 2020-01-16 03:53:59 +01:00
Jakub Sztandera 48ea36d37f Merge pull request #1092 from filecoin-project/feat/fountain-additional-pledge-collateral
Add more collateral to CreateStorageMiner call in fountain
2020-01-16 02:32:12 +01:00
Łukasz Magiera ffdd436b52 sealing: Handle seed changes more correctly 2020-01-16 02:25:49 +01:00
Travis Person ec9286eb4b Add more collateral to CreateStorageMiner call in fountain 2020-01-16 01:04:04 +00:00
Travis Person 68f8d60323 Adding fields to warning in vm ApplyMessage (#1090)
* Adding fields to warning in vm ApplyMessage
2020-01-16 01:36:16 +01:00
Jakub Sztandera 5cabb068c0 Merge pull request #1091 from filecoin-project/fix/fountain-rice
Fix fountain rice command
2020-01-16 01:35:01 +01:00
Travis Person 01079b0815 Fix fountain rice command 2020-01-16 00:29:23 +00:00
Whyrusleeping 7e15aa243e Merge pull request #1084 from jsign/resolveforks
new api to calculate arbitrary forks revert and applys
2020-01-15 12:55:14 -08:00
Łukasz Magiera e9ca6c0871 sealing: More cleanup 2020-01-15 21:53:14 +01:00
Łukasz Magiera 604bf64ef6 Move miner sealing logic into a separate package 2020-01-15 21:49:11 +01:00
Łukasz Magiera 925e057acb Some sector fsm tests 2020-01-15 21:04:02 +01:00
Jakub Sztandera c47e96ca45 Merge pull request #1088 from jiaruihuang/master
a bit more than the minimum collateral at the time of miner creation
2020-01-15 18:53:34 +01:00
Jiarui.Huang 6354a8ce03 So we should pass a bit more than the minimum collateral at the time of miner creation
Even 0.1% more would be enough# Please enter the commit message for your changes. Lines starting
2020-01-16 01:30:01 +08:00
Jakub Sztandera 85ec972abb Merge pull request #1087 from terorie/fix-go-flags
Makefile: fix $GOFLAGS
2020-01-15 15:29:23 +01:00
Richard Patel a8d38ad82f Makefile: fix $GOFLAGS 2020-01-15 14:56:53 +01:00
Łukasz Magiera be6badc6a5 Merge pull request #1086 from filecoin-project/fix/worker-cleanup-sealed
worker: Remove staging sectors in PreCommit
2020-01-15 02:50:21 +01:00
Łukasz Magiera cf9275a2a2 worker: Remove staging sectors in PreCommit 2020-01-15 02:37:37 +01:00
Łukasz Magiera 66f707c243 Merge pull request #1085 from filecoin-project/fix/worker-cleanup-sealed
worker: Cleanup sealed sector data in commit
2020-01-15 02:29:14 +01:00
Łukasz Magiera 1163e46622 worker: Cleanup sealed sector data in commit 2020-01-15 02:24:13 +01:00
jsign 08b99ff413 new api to calculate arbitrary forks revert and applys
Signed-off-by: jsign <jsign.uy@gmail.com>
2020-01-14 21:24:08 -03:00
Łukasz Magiera 23472aa4f2 statemachine: don't force users to see reflect 2020-01-15 01:08:20 +01:00
Łukasz Magiera 05edf50931 Merge branch 'master' into feat/event-states 2020-01-15 00:35:52 +01:00
Łukasz Magiera a8e39962d0 Merge pull request #1053 from filecoin-project/lotus-health
health agent to monitor lotus
2020-01-14 18:41:51 +01:00
ognots 5a42bb166e add rice for good health
allows lotus-health to be compiled and distributed properly
2020-01-14 12:18:45 -05:00
ognots 3953227702 use lotus cli and GetFullNodeAPI
also some other minor bug fixes
2020-01-14 12:18:45 -05:00
ognots d8d8ce7526 health agent to monitor lotus
watch if chain head changes in a given window of api polls
allows setting a threshold of how many times the chain head can remain
unchanged before failing health check
also can set interval for polling chain head
on failure, restarts systemd unit
2020-01-14 12:18:45 -05:00
Jakub Sztandera 95e754601e Merge pull request #1080 from filecoin-project/fix/nil-sigs
Fix nil signature verification
2020-01-14 17:15:45 +01:00
Łukasz Magiera 5e11da7316 build: bump version 2020-01-14 16:53:05 +01:00
Łukasz Magiera 24e36852af Fix nil signature verification 2020-01-14 16:52:01 +01:00
Łukasz Magiera 458acbf33d mod tidy 2020-01-14 16:39:25 +01:00
Łukasz Magiera 8a9707788c Merge branch 'master' into feat/event-states 2020-01-14 16:31:10 +01:00
Łukasz Magiera 2fecf3a8f1 Fix tests 2020-01-13 21:51:34 +01:00
Łukasz Magiera 2ef8c1ae0c statemachine: Better naming 2020-01-13 18:44:59 +01:00
Łukasz Magiera 2fea9433d8 sectors: Correctly set CommitMessage 2020-01-13 18:18:52 +01:00
Łukasz Magiera e06c19b51c update deps 2020-01-13 17:39:54 +01:00
Łukasz Magiera 34abc3d023 Merge branch 'feat/testnet2' into feat/event-states 2020-01-13 15:52:43 +01:00
Łukasz Magiera f6d41ee77d Port sector state maching to evtsm 2020-01-10 20:24:15 +01:00
Łukasz Magiera da2a11ed76 Event-based state machine 2020-01-09 22:23:31 +01:00
ldoublewood 86f078dbea restore Makefile 2019-12-05 14:47:23 +08:00
ldoublewood f739b7e377 add docker support 2019-12-05 11:31:32 +08:00
108 changed files with 6598 additions and 1764 deletions
+1
View File
@@ -0,0 +1 @@
.gitignore
+1
View File
@@ -2,6 +2,7 @@
/lotus-storage-miner
/lotus-seal-worker
/lotus-seed
/lotus-health
/pond
/townhall
/fountain
+91
View File
@@ -0,0 +1,91 @@
FROM golang:1.13.4-buster
MAINTAINER ldoublewood <ldoublewood@gmail.com>
ENV SRC_DIR /lotus
RUN apt-get update && apt-get install -y && apt-get install -y ca-certificates llvm clang mesa-opencl-icd ocl-icd-opencl-dev
RUN curl -sSf https://sh.rustup.rs | sh -s -- -y
# Get su-exec, a very minimal tool for dropping privileges,
# and tini, a very minimal init daemon for containers
ENV SUEXEC_VERSION v0.2
ENV TINI_VERSION v0.18.0
RUN set -x \
&& cd /tmp \
&& git clone https://github.com/ncopa/su-exec.git \
&& cd su-exec \
&& git checkout -q $SUEXEC_VERSION \
&& make \
&& cd /tmp \
&& wget -q -O tini https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini \
&& chmod +x tini
# Download packages first so they can be cached.
COPY go.mod go.sum $SRC_DIR/
COPY extern/ $SRC_DIR/extern/
RUN cd $SRC_DIR \
&& go mod download
COPY Makefile $SRC_DIR
# Because extern/filecoin-ffi building script need to get version number from git
COPY .git/ $SRC_DIR/.git/
COPY .gitmodules $SRC_DIR/
# Download dependence first
RUN cd $SRC_DIR \
&& mkdir $SRC_DIR/build \
&& . $HOME/.cargo/env \
&& make clean \
&& make deps
COPY . $SRC_DIR
# Build the thing.
RUN cd $SRC_DIR \
&& . $HOME/.cargo/env \
&& make
# Now comes the actual target image, which aims to be as small as possible.
FROM busybox:1-glibc
MAINTAINER ldoublewood <ldoublewood@gmail.com>
# Get the executable binary and TLS CAs from the build container.
ENV SRC_DIR /lotus
COPY --from=0 $SRC_DIR/lotus /usr/local/bin/lotus
COPY --from=0 $SRC_DIR/lotus-storage-miner /usr/local/bin/lotus-storage-miner
COPY --from=0 /tmp/su-exec/su-exec /sbin/su-exec
COPY --from=0 /tmp/tini /sbin/tini
COPY --from=0 /etc/ssl/certs /etc/ssl/certs
# This shared lib (part of glibc) doesn't seem to be included with busybox.
COPY --from=0 /lib/x86_64-linux-gnu/libdl-2.28.so /lib/libdl.so.2
COPY --from=0 /lib/x86_64-linux-gnu/libutil-2.28.so /lib/libutil.so.1
COPY --from=0 /usr/lib/x86_64-linux-gnu/libOpenCL.so.1.0.0 /lib/libOpenCL.so.1
COPY --from=0 /lib/x86_64-linux-gnu/librt-2.28.so /lib/librt.so.1
COPY --from=0 /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/libgcc_s.so.1
# WS port
EXPOSE 1234
# P2P port
EXPOSE 5678
# Create the home directory and switch to a non-privileged user.
ENV HOME_PATH /data
ENV PARAMCACHE_PATH /var/tmp/filecoin-proof-parameters
RUN mkdir -p $HOME_PATH \
&& adduser -D -h $HOME_PATH -u 1000 -G users lotus \
&& chown lotus:users $HOME_PATH
VOLUME $HOME_PATH
VOLUME $PARAMCACHE_PATH
# Execute the daemon subcommand by default
CMD ["/sbin/tini", "--", "lotus", "daemon"]
+10 -2
View File
@@ -14,7 +14,7 @@ MODULES:=
CLEAN:=
BINS:=
GOFLAGS+=-ldflags="-X "github.com/filecoin-project/lotus/build".CurrentCommit=+git$(subst -,.,$(shell git describe --always --match=NeVeRmAtCh --dirty 2>/dev/null || git rev-parse --short HEAD 2>/dev/null))"
GOFLAGS+=-ldflags=-X="github.com/filecoin-project/lotus/build".CurrentCommit="+git$(subst -,.,$(shell git describe --always --match=NeVeRmAtCh --dirty 2>/dev/null || git rev-parse --short HEAD 2>/dev/null))"
## FFI
@@ -124,7 +124,7 @@ BINS+=townhall
fountain:
rm -f fountain
go build -o fountain ./cmd/lotus-fountain
go run github.com/GeertJohan/go.rice/rice append --exec fountain -i ./cmd/lotus-fountain
go run github.com/GeertJohan/go.rice/rice append --exec fountain -i ./cmd/lotus-fountain -i ./build
.PHONY: fountain
BINS+=fountain
@@ -149,6 +149,14 @@ stats:
.PHONY: stats
BINS+=stats
health:
rm -f lotus-health
go build -o lotus-health ./cmd/lotus-health
go run github.com/GeertJohan/go.rice/rice append --exec lotus-health -i ./build
.PHONY: health
BINS+=health
# MISC
buildall: $(BINS)
+9 -1
View File
@@ -37,6 +37,8 @@ type FullNode interface {
ChainTipSetWeight(context.Context, *types.TipSet) (types.BigInt, error)
ChainGetNode(ctx context.Context, p string) (interface{}, error)
ChainGetMessage(context.Context, cid.Cid) (*types.Message, error)
ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*store.HeadChange, error)
ChainExport(context.Context, *types.TipSet) (<-chan []byte, error)
// syncer
SyncState(context.Context) (*SyncState, error)
@@ -93,7 +95,7 @@ type FullNode interface {
//ClientListAsks() []Ask
// if tipset is nil, we'll use heaviest
StateCall(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error)
StateCall(context.Context, *types.Message, *types.TipSet) (*MethodCall, error)
StateReplay(context.Context, *types.TipSet, cid.Cid) (*ReplayResults, error)
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
StateReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*ActorState, error)
@@ -118,6 +120,7 @@ type FullNode interface {
StateChangedActors(context.Context, cid.Cid, cid.Cid) (map[string]types.Actor, error)
StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error)
StateMinerSectorCount(context.Context, address.Address, *types.TipSet) (MinerSectors, error)
StateCompute(context.Context, uint64, []*types.Message, *types.TipSet) (cid.Cid, error)
MarketEnsureAvailable(context.Context, address.Address, types.BigInt) error
// MarketFreeBalance
@@ -268,6 +271,11 @@ type ReplayResults struct {
Error string
}
type MethodCall struct {
types.MessageReceipt
Error string
}
type ActiveSync struct {
Base *types.TipSet
Target *types.TipSet
+15 -3
View File
@@ -19,7 +19,7 @@ const (
Unsealed // sealing / queued
PreCommitting // on chain pre-commit
PreCommitted // waiting for seed
WaitSeed // waiting for seed
Committing
CommitWait // waiting for message to land on chain
Proving
@@ -45,7 +45,7 @@ const (
PreCommitFailed
SealCommitFailed
CommitFailed
_
PackingFailed
_
_
_
@@ -61,7 +61,7 @@ var SectorStates = []string{
Packing: "Packing",
Unsealed: "Unsealed",
PreCommitting: "PreCommitting",
PreCommitted: "PreCommitted",
WaitSeed: "WaitSeed",
Committing: "Committing",
CommitWait: "CommitWait",
Proving: "Proving",
@@ -70,6 +70,7 @@ var SectorStates = []string{
PreCommitFailed: "PreCommitFailed",
SealCommitFailed: "SealCommitFailed",
CommitFailed: "CommitFailed",
PackingFailed: "PackingFailed",
FailedUnrecoverable: "FailedUnrecoverable",
@@ -107,6 +108,15 @@ type StorageMiner interface {
WorkerDone(ctx context.Context, task uint64, res sectorbuilder.SealRes) error
}
type SectorLog struct {
Kind string
Timestamp uint64
Trace string
Message string
}
type SectorInfo struct {
SectorID uint64
State SectorState
@@ -119,6 +129,8 @@ type SectorInfo struct {
Retries uint64
LastErr string
Log []SectorLog
}
type SealedRef struct {
+32 -17
View File
@@ -40,21 +40,23 @@ type FullNodeStruct struct {
CommonStruct
Internal struct {
ChainNotify func(context.Context) (<-chan []*store.HeadChange, error) `perm:"read"`
ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"`
ChainGetRandomness func(context.Context, types.TipSetKey, int64) ([]byte, error) `perm:"read"`
ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"`
ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
ChainGetBlockMessages func(context.Context, cid.Cid) (*api.BlockMessages, error) `perm:"read"`
ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
ChainGetParentMessages func(context.Context, cid.Cid) ([]api.Message, error) `perm:"read"`
ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"`
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"`
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
ChainTipSetWeight func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"`
ChainGetNode func(ctx context.Context, p string) (interface{}, error) `perm:"read"`
ChainGetMessage func(context.Context, cid.Cid) (*types.Message, error) `perm:"read"`
ChainNotify func(context.Context) (<-chan []*store.HeadChange, error) `perm:"read"`
ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"`
ChainGetRandomness func(context.Context, types.TipSetKey, int64) ([]byte, error) `perm:"read"`
ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"`
ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
ChainGetBlockMessages func(context.Context, cid.Cid) (*api.BlockMessages, error) `perm:"read"`
ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
ChainGetParentMessages func(context.Context, cid.Cid) ([]api.Message, error) `perm:"read"`
ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"`
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"`
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
ChainTipSetWeight func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"`
ChainGetNode func(ctx context.Context, p string) (interface{}, error) `perm:"read"`
ChainGetMessage func(context.Context, cid.Cid) (*types.Message, error) `perm:"read"`
ChainGetPath func(context.Context, types.TipSetKey, types.TipSetKey) ([]*store.HeadChange, error) `perm:"read"`
ChainExport func(context.Context, *types.TipSet) (<-chan []byte, error) `perm:"read"`
SyncState func(context.Context) (*api.SyncState, error) `perm:"read"`
SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"`
@@ -97,7 +99,7 @@ type FullNodeStruct struct {
StateMinerPeerID func(ctx context.Context, m address.Address, ts *types.TipSet) (peer.ID, error) `perm:"read"`
StateMinerElectionPeriodStart func(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error) `perm:"read"`
StateMinerSectorSize func(context.Context, address.Address, *types.TipSet) (uint64, error) `perm:"read"`
StateCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
StateCall func(context.Context, *types.Message, *types.TipSet) (*api.MethodCall, error) `perm:"read"`
StateReplay func(context.Context, *types.TipSet, cid.Cid) (*api.ReplayResults, error) `perm:"read"`
StateGetActor func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) `perm:"read"`
StateReadState func(context.Context, *types.Actor, *types.TipSet) (*api.ActorState, error) `perm:"read"`
@@ -114,6 +116,7 @@ type FullNodeStruct struct {
StateGetReceipt func(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
StateMinerSectorCount func(context.Context, address.Address, *types.TipSet) (api.MinerSectors, error) `perm:"read"`
StateListMessages func(ctx context.Context, match *types.Message, ts *types.TipSet, toht uint64) ([]cid.Cid, error) `perm:"read"`
StateCompute func(context.Context, uint64, []*types.Message, *types.TipSet) (cid.Cid, error) `perm:"read"`
MarketEnsureAvailable func(context.Context, address.Address, types.BigInt) error `perm:"sign"`
@@ -355,6 +358,14 @@ func (c *FullNodeStruct) ChainGetMessage(ctx context.Context, mc cid.Cid) (*type
return c.Internal.ChainGetMessage(ctx, mc)
}
func (c *FullNodeStruct) ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*store.HeadChange, error) {
return c.Internal.ChainGetPath(ctx, from, to)
}
func (c *FullNodeStruct) ChainExport(ctx context.Context, ts *types.TipSet) (<-chan []byte, error) {
return c.Internal.ChainExport(ctx, ts)
}
func (c *FullNodeStruct) SyncState(ctx context.Context) (*api.SyncState, error) {
return c.Internal.SyncState(ctx)
}
@@ -399,7 +410,7 @@ func (c *FullNodeStruct) StateMinerSectorSize(ctx context.Context, actor address
return c.Internal.StateMinerSectorSize(ctx, actor, ts)
}
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.MethodCall, error) {
return c.Internal.StateCall(ctx, msg, ts)
}
@@ -462,6 +473,10 @@ func (c *FullNodeStruct) StateListMessages(ctx context.Context, match *types.Mes
return c.Internal.StateListMessages(ctx, match, ts, toht)
}
func (c *FullNodeStruct) StateCompute(ctx context.Context, height uint64, msgs []*types.Message, ts *types.TipSet) (cid.Cid, error) {
return c.Internal.StateCompute(ctx, height, msgs, ts)
}
func (c *FullNodeStruct) MarketEnsureAvailable(ctx context.Context, addr address.Address, amt types.BigInt) error {
return c.Internal.MarketEnsureAvailable(ctx, addr, amt)
}
+155 -186
View File
@@ -98,114 +98,95 @@ func (t *PaymentInfo) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("cbor input should be of type map")
}
if extra != 3 {
return fmt.Errorf("cbor input had wrong number of fields")
if extra > cbg.MaxLength {
return fmt.Errorf("PaymentInfo: map struct too large (%d)", extra)
}
var name string
n := extra
// t.Channel (address.Address) (struct)
for i := uint64(0); i < n; i++ {
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
name = string(sval)
}
if name != "Channel" {
return fmt.Errorf("expected struct map entry %s to be Channel", name)
}
{
if err := t.Channel.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.ChannelMessage (cid.Cid) (struct)
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
name = string(sval)
}
if name != "ChannelMessage" {
return fmt.Errorf("expected struct map entry %s to be ChannelMessage", name)
}
{
pb, err := br.PeekByte()
if err != nil {
return err
}
if pb == cbg.CborNull[0] {
var nbuf [1]byte
if _, err := br.Read(nbuf[:]); err != nil {
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
} else {
c, err := cbg.ReadCid(br)
name = string(sval)
}
switch name {
// t.Channel (address.Address) (struct)
case "Channel":
{
if err := t.Channel.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.ChannelMessage (cid.Cid) (struct)
case "ChannelMessage":
{
pb, err := br.PeekByte()
if err != nil {
return err
}
if pb == cbg.CborNull[0] {
var nbuf [1]byte
if _, err := br.Read(nbuf[:]); err != nil {
return err
}
} else {
c, err := cbg.ReadCid(br)
if err != nil {
return xerrors.Errorf("failed to read cid field t.ChannelMessage: %w", err)
}
t.ChannelMessage = &c
}
}
// t.Vouchers ([]*types.SignedVoucher) (slice)
case "Vouchers":
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return xerrors.Errorf("failed to read cid field t.ChannelMessage: %w", err)
return err
}
t.ChannelMessage = &c
if extra > cbg.MaxLength {
return fmt.Errorf("t.Vouchers: array too large (%d)", extra)
}
if maj != cbg.MajArray {
return fmt.Errorf("expected cbor array")
}
if extra > 0 {
t.Vouchers = make([]*types.SignedVoucher, extra)
}
for i := 0; i < int(extra); i++ {
var v types.SignedVoucher
if err := v.UnmarshalCBOR(br); err != nil {
return err
}
t.Vouchers[i] = &v
}
default:
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
}
}
// t.Vouchers ([]*types.SignedVoucher) (slice)
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
name = string(sval)
}
if name != "Vouchers" {
return fmt.Errorf("expected struct map entry %s to be Vouchers", name)
}
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if extra > cbg.MaxLength {
return fmt.Errorf("t.Vouchers: array too large (%d)", extra)
}
if maj != cbg.MajArray {
return fmt.Errorf("expected cbor array")
}
if extra > 0 {
t.Vouchers = make([]*types.SignedVoucher, extra)
}
for i := 0; i < int(extra); i++ {
var v types.SignedVoucher
if err := v.UnmarshalCBOR(br); err != nil {
return err
}
t.Vouchers[i] = &v
}
return nil
}
func (t *SealedRef) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
@@ -276,84 +257,66 @@ func (t *SealedRef) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("cbor input should be of type map")
}
if extra != 3 {
return fmt.Errorf("cbor input had wrong number of fields")
if extra > cbg.MaxLength {
return fmt.Errorf("SealedRef: map struct too large (%d)", extra)
}
var name string
n := extra
// t.SectorID (uint64) (uint64)
for i := uint64(0); i < n; i++ {
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
name = string(sval)
}
name = string(sval)
}
switch name {
// t.SectorID (uint64) (uint64)
case "SectorID":
if name != "SectorID" {
return fmt.Errorf("expected struct map entry %s to be SectorID", name)
}
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.SectorID = uint64(extra)
// t.Offset (uint64) (uint64)
case "Offset":
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.SectorID = uint64(extra)
// t.Offset (uint64) (uint64)
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.Offset = uint64(extra)
// t.Size (uint64) (uint64)
case "Size":
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.Size = uint64(extra)
default:
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
}
name = string(sval)
}
if name != "Offset" {
return fmt.Errorf("expected struct map entry %s to be Offset", name)
}
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.Offset = uint64(extra)
// t.Size (uint64) (uint64)
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
name = string(sval)
}
if name != "Size" {
return fmt.Errorf("expected struct map entry %s to be Size", name)
}
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.Size = uint64(extra)
return nil
}
func (t *SealedRefs) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
@@ -401,50 +364,56 @@ func (t *SealedRefs) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("cbor input should be of type map")
}
if extra != 1 {
return fmt.Errorf("cbor input had wrong number of fields")
if extra > cbg.MaxLength {
return fmt.Errorf("SealedRefs: map struct too large (%d)", extra)
}
var name string
n := extra
// t.Refs ([]api.SealedRef) (slice)
for i := uint64(0); i < n; i++ {
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
name = string(sval)
}
name = string(sval)
}
switch name {
// t.Refs ([]api.SealedRef) (slice)
case "Refs":
if name != "Refs" {
return fmt.Errorf("expected struct map entry %s to be Refs", name)
}
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if extra > cbg.MaxLength {
return fmt.Errorf("t.Refs: array too large (%d)", extra)
}
if extra > cbg.MaxLength {
return fmt.Errorf("t.Refs: array too large (%d)", extra)
}
if maj != cbg.MajArray {
return fmt.Errorf("expected cbor array")
}
if extra > 0 {
t.Refs = make([]SealedRef, extra)
}
for i := 0; i < int(extra); i++ {
if maj != cbg.MajArray {
return fmt.Errorf("expected cbor array")
}
if extra > 0 {
t.Refs = make([]SealedRef, extra)
}
for i := 0; i < int(extra); i++ {
var v SealedRef
if err := v.UnmarshalCBOR(br); err != nil {
return err
}
var v SealedRef
if err := v.UnmarshalCBOR(br); err != nil {
return err
t.Refs[i] = v
}
default:
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
}
t.Refs[i] = v
}
return nil
-1
View File
@@ -65,7 +65,6 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
defer close(done)
for mine {
time.Sleep(blocktime)
fmt.Println("mining a block now")
if err := sn[0].MineOne(ctx); err != nil {
t.Error(err)
}
+5 -1
View File
@@ -1,3 +1,7 @@
package build
// No forks yet \o/
const ForkBlizzardHeight = 6288
const ForkFrigidHeight = 7950
const ForkBootyBayHeight = 11000
-6
View File
@@ -2,8 +2,6 @@
package build
import "os"
var SectorSizes = []uint64{1024}
// Seconds
@@ -31,7 +29,3 @@ const InteractivePoRepConfidence = 6
// Bytes
var MinimumMinerPower uint64 = 2 << 10 // 2KiB
func init() {
os.Setenv("TRUST_PARAMS", "1")
}
+1
View File
@@ -105,3 +105,4 @@ const BlsSignatureCacheSize = 40000
// Limits
const BlockMessageLimit = 512
const MinerMaxSectors = 1 << 48
+1 -1
View File
@@ -5,7 +5,7 @@ import "fmt"
var CurrentCommit string
// BuildVersion is the local build version, set by build system
const BuildVersion = "0.2.1"
const BuildVersion = "0.2.6"
var UserVersion = BuildVersion + CurrentCommit
+1 -1
View File
@@ -161,7 +161,7 @@ func (ia InitActor) Exec(act *types.Actor, vmctx types.VMContext, p *ExecParams)
func IsBuiltinActor(code cid.Cid) bool {
switch code {
case StorageMarketCodeCid, StoragePowerCodeCid, StorageMinerCodeCid, AccountCodeCid, InitCodeCid, MultisigCodeCid, PaymentChannelCodeCid:
case StorageMarketCodeCid, StoragePowerCodeCid, StorageMinerCodeCid, StorageMiner2CodeCid, AccountCodeCid, InitCodeCid, MultisigCodeCid, PaymentChannelCodeCid:
return true
default:
return false
+2 -4
View File
@@ -22,8 +22,6 @@ import (
"golang.org/x/xerrors"
)
const MaxSectors = 1 << 48
type StorageMinerActor struct{}
type StorageMinerActorState struct {
@@ -571,7 +569,7 @@ func SectorIsUnique(ctx context.Context, s types.Storage, sroot cid.Cid, sid uin
}
func AddToSectorSet(ctx context.Context, blks amt.Blocks, ss cid.Cid, sectorID uint64, commR, commD []byte) (cid.Cid, ActorError) {
if sectorID >= MaxSectors {
if sectorID >= build.MinerMaxSectors {
return cid.Undef, aerrors.Newf(25, "sector ID out of range: %d", sectorID)
}
ssr, err := amt.LoadAMT(blks, ss)
@@ -594,7 +592,7 @@ func AddToSectorSet(ctx context.Context, blks amt.Blocks, ss cid.Cid, sectorID u
}
func GetFromSectorSet(ctx context.Context, s types.Storage, ss cid.Cid, sectorID uint64) (bool, []byte, []byte, ActorError) {
if sectorID >= MaxSectors {
if sectorID >= build.MinerMaxSectors {
return false, nil, nil, aerrors.Newf(25, "sector ID out of range: %d", sectorID)
}
+865
View File
@@ -0,0 +1,865 @@
package actors
import (
"bytes"
"context"
"fmt"
ffi "github.com/filecoin-project/filecoin-ffi"
amt2 "github.com/filecoin-project/go-amt-ipld/v2"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
)
type StorageMinerActor2 struct{}
func (sma StorageMinerActor2) Exports() []interface{} {
return []interface{}{
1: sma.StorageMinerConstructor,
2: sma.PreCommitSector,
3: sma.ProveCommitSector,
4: sma.SubmitFallbackPoSt,
//5: sma.SlashStorageFault,
//6: sma.GetCurrentProvingSet,
//7: sma.ArbitrateDeal,
//8: sma.DePledge,
9: sma.GetOwner,
10: sma.GetWorkerAddr,
11: sma.GetPower, // TODO: Remove
12: sma.GetPeerID,
13: sma.GetSectorSize,
14: sma.UpdatePeerID,
//15: sma.ChangeWorker,
16: sma.IsSlashed,
17: sma.CheckMiner,
18: sma.DeclareFaults,
19: sma.SlashConsensusFault,
20: sma.SubmitElectionPoSt,
}
}
func (sma StorageMinerActor2) StorageMinerConstructor(act *types.Actor, vmctx types.VMContext, params *StorageMinerConstructorParams) ([]byte, ActorError) {
minerInfo := &MinerInfo{
Owner: params.Owner,
Worker: params.Worker,
PeerID: params.PeerID,
SectorSize: params.SectorSize,
}
minfocid, err := vmctx.Storage().Put(minerInfo)
if err != nil {
return nil, err
}
var self StorageMinerActorState
sectors := amt2.NewAMT(types.WrapStorage(vmctx.Storage()))
scid, serr := sectors.Flush()
if serr != nil {
return nil, aerrors.HandleExternalError(serr, "initializing AMT")
}
self.Sectors = scid
self.ProvingSet = scid
self.Info = minfocid
storage := vmctx.Storage()
c, err := storage.Put(&self)
if err != nil {
return nil, err
}
if err := storage.Commit(EmptyCBOR, c); err != nil {
return nil, err
}
return nil, nil
}
func (sma StorageMinerActor2) PreCommitSector(act *types.Actor, vmctx types.VMContext, params *SectorPreCommitInfo) ([]byte, ActorError) {
ctx := vmctx.Context()
oldstate, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
if params.SealEpoch >= vmctx.BlockHeight()+build.SealRandomnessLookback {
return nil, aerrors.Newf(1, "sector commitment must be based off past randomness (%d >= %d)", params.SealEpoch, vmctx.BlockHeight()+build.SealRandomnessLookback)
}
if vmctx.BlockHeight()-params.SealEpoch+build.SealRandomnessLookback > build.SealRandomnessLookbackLimit {
return nil, aerrors.Newf(2, "sector commitment must be recent enough (was %d)", vmctx.BlockHeight()-params.SealEpoch+build.SealRandomnessLookback)
}
mi, err := loadMinerInfo(vmctx, self)
if err != nil {
return nil, err
}
if vmctx.Message().From != mi.Worker {
return nil, aerrors.New(1, "not authorized to precommit sector for miner")
}
// make sure the miner isnt trying to submit a pre-existing sector
unique, err := SectorIsUnique(ctx, vmctx.Storage(), self.Sectors, params.SectorNumber)
if err != nil {
return nil, err
}
if !unique {
return nil, aerrors.New(3, "sector already committed!")
}
// Power of the miner after adding this sector
futurePower := types.BigAdd(self.Power, types.NewInt(mi.SectorSize))
collateralRequired := CollateralForPower(futurePower)
// TODO: grab from market?
if act.Balance.LessThan(collateralRequired) {
return nil, aerrors.New(4, "not enough collateral")
}
self.PreCommittedSectors[uintToStringKey(params.SectorNumber)] = &PreCommittedSector{
Info: *params,
ReceivedEpoch: vmctx.BlockHeight(),
}
if len(self.PreCommittedSectors) > 4096 {
return nil, aerrors.New(5, "too many precommitted sectors")
}
nstate, err := vmctx.Storage().Put(self)
if err != nil {
return nil, err
}
if err := vmctx.Storage().Commit(oldstate, nstate); err != nil {
return nil, err
}
return nil, nil
}
func (sma StorageMinerActor2) ProveCommitSector(act *types.Actor, vmctx types.VMContext, params *SectorProveCommitInfo) ([]byte, ActorError) {
ctx := vmctx.Context()
oldstate, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
mi, err := loadMinerInfo(vmctx, self)
if err != nil {
return nil, err
}
if vmctx.Message().From != mi.Worker {
return nil, aerrors.New(1, "not authorized to submit sector proof for miner")
}
us, ok := self.PreCommittedSectors[uintToStringKey(params.SectorID)]
if !ok {
return nil, aerrors.New(1, "no pre-commitment found for sector")
}
if us.ReceivedEpoch+build.InteractivePoRepDelay >= vmctx.BlockHeight() {
return nil, aerrors.New(2, "too early for proof submission")
}
delete(self.PreCommittedSectors, uintToStringKey(params.SectorID))
// TODO: ensure normalization to ID address
maddr := vmctx.Message().To
if vmctx.BlockHeight()-us.Info.SealEpoch > build.MaxSealLookback {
return nil, aerrors.Newf(5, "source randomness for sector SealEpoch too far in past (epoch %d)", us.Info.SealEpoch)
}
if vmctx.BlockHeight()-us.ReceivedEpoch > build.MaxSealLookback {
return nil, aerrors.Newf(6, "source randomness for sector ReceivedEpoch too far in past (epoch %d)", us.ReceivedEpoch)
}
ticket, err := vmctx.GetRandomness(us.Info.SealEpoch - build.SealRandomnessLookback)
if err != nil {
return nil, aerrors.Wrap(err, "failed to get ticket randomness")
}
seed, err := vmctx.GetRandomness(us.ReceivedEpoch + build.InteractivePoRepDelay)
if err != nil {
return nil, aerrors.Wrap(err, "failed to get randomness for prove sector commitment")
}
enc, err := SerializeParams(&ComputeDataCommitmentParams{
DealIDs: params.DealIDs,
SectorSize: mi.SectorSize,
})
if err != nil {
return nil, aerrors.Wrap(err, "failed to serialize ComputeDataCommitmentParams")
}
commD, err := vmctx.Send(StorageMarketAddress, SMAMethods.ComputeDataCommitment, types.NewInt(0), enc)
if err != nil {
return nil, aerrors.Wrapf(err, "failed to compute data commitment (sector %d, deals: %v)", params.SectorID, params.DealIDs)
}
if ok, err := vmctx.Sys().ValidatePoRep(ctx, maddr, mi.SectorSize, commD, us.Info.CommR, ticket, params.Proof, seed, params.SectorID); err != nil {
return nil, err
} else if !ok {
return nil, aerrors.Newf(2, "porep proof was invalid (t:%x; s:%x(%d); p:%s)", ticket, seed, us.ReceivedEpoch+build.InteractivePoRepDelay, truncateHexPrint(params.Proof))
}
// Note: There must exist a unique index in the miner's sector set for each
// sector ID. The `faults`, `recovered`, and `done` parameters of the
// SubmitPoSt method express indices into this sector set.
nssroot, err := AddToSectorSet2(ctx, types.WrapStorage(vmctx.Storage()), self.Sectors, params.SectorID, us.Info.CommR, commD)
if err != nil {
return nil, err
}
self.Sectors = nssroot
// if miner is not mining, start their proving period now
// Note: As written here, every miners first PoSt will only be over one sector.
// We could set up a 'grace period' for starting mining that would allow miners
// to submit several sectors for their first proving period. Alternatively, we
// could simply make the 'PreCommitSector' call take multiple sectors at a time.
//
// Note: Proving period is a function of sector size; small sectors take less
// time to prove than large sectors do. Sector size is selected when pledging.
pss, lerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
if lerr != nil {
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
}
if pss.Count == 0 && !self.Active {
self.ProvingSet = self.Sectors
// TODO: probably want to wait until the miner is above a certain
// threshold before starting this
self.ElectionPeriodStart = vmctx.BlockHeight()
}
nstate, err := vmctx.Storage().Put(self)
if err != nil {
return nil, err
}
if err := vmctx.Storage().Commit(oldstate, nstate); err != nil {
return nil, err
}
activateParams, err := SerializeParams(&ActivateStorageDealsParams{
Deals: params.DealIDs,
})
if err != nil {
return nil, err
}
_, err = vmctx.Send(StorageMarketAddress, SMAMethods.ActivateStorageDeals, types.NewInt(0), activateParams)
return nil, aerrors.Wrapf(err, "calling ActivateStorageDeals failed")
}
func (sma StorageMinerActor2) SubmitFallbackPoSt(act *types.Actor, vmctx types.VMContext, params *SubmitFallbackPoStParams) ([]byte, ActorError) {
oldstate, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
mi, err := loadMinerInfo(vmctx, self)
if err != nil {
return nil, err
}
if vmctx.Message().From != mi.Worker {
return nil, aerrors.New(1, "not authorized to submit post for miner")
}
/*
// TODO: handle fees
msgVal := vmctx.Message().Value
if msgVal.LessThan(feesRequired) {
return nil, aerrors.New(2, "not enough funds to pay post submission fees")
}
if msgVal.GreaterThan(feesRequired) {
_, err := vmctx.Send(vmctx.Message().From, 0,
types.BigSub(msgVal, feesRequired), nil)
if err != nil {
return nil, aerrors.Wrap(err, "could not refund excess fees")
}
}
*/
var seed [sectorbuilder.CommLen]byte
{
randHeight := self.ElectionPeriodStart + build.FallbackPoStDelay
if vmctx.BlockHeight() <= randHeight {
// TODO: spec, retcode
return nil, aerrors.Newf(1, "submit fallback PoSt called too early (%d < %d)", vmctx.BlockHeight(), randHeight)
}
rand, err := vmctx.GetRandomness(randHeight)
if err != nil {
return nil, aerrors.Wrap(err, "could not get randomness for PoST")
}
if len(rand) < len(seed) {
return nil, aerrors.Escalate(fmt.Errorf("randomness too small (%d < %d)",
len(rand), len(seed)), "improper randomness")
}
copy(seed[:], rand)
}
pss, lerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
if lerr != nil {
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
}
ss, lerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
if lerr != nil {
return nil, aerrors.HandleExternalError(lerr, "could not load proving set node")
}
faults, nerr := self.FaultSet.AllMap(2 * ss.Count)
if nerr != nil {
return nil, aerrors.Absorb(err, 5, "RLE+ invalid")
}
activeFaults := uint64(0)
var sectorInfos []ffi.PublicSectorInfo
if err := pss.ForEach(func(id uint64, v *cbg.Deferred) error {
if faults[id] {
activeFaults++
return nil
}
var comms [][]byte
if err := cbor.DecodeInto(v.Raw, &comms); err != nil {
return xerrors.New("could not decode comms")
}
si := ffi.PublicSectorInfo{
SectorID: id,
}
commR := comms[0]
if len(commR) != len(si.CommR) {
return xerrors.Errorf("commR length is wrong: %d", len(commR))
}
copy(si.CommR[:], commR)
sectorInfos = append(sectorInfos, si)
return nil
}); err != nil {
return nil, aerrors.Absorb(err, 3, "could not decode sectorset")
}
proverID := vmctx.Message().To // TODO: normalize to ID address
var candidates []sectorbuilder.EPostCandidate
for _, t := range params.Candidates {
var partial [32]byte
copy(partial[:], t.Partial)
candidates = append(candidates, sectorbuilder.EPostCandidate{
PartialTicket: partial,
SectorID: t.SectorID,
SectorChallengeIndex: t.ChallengeIndex,
})
}
if ok, lerr := vmctx.Sys().VerifyFallbackPost(vmctx.Context(), mi.SectorSize,
sectorbuilder.NewSortedPublicSectorInfo(sectorInfos), seed[:], params.Proof, candidates, proverID, activeFaults); !ok || lerr != nil {
if lerr != nil {
// TODO: study PoST errors
return nil, aerrors.Absorb(lerr, 4, "PoST error")
}
if !ok {
return nil, aerrors.New(4, "PoST invalid")
}
}
// Post submission is successful!
if err := onSuccessfulPoSt2(self, vmctx, activeFaults); err != nil {
return nil, err
}
c, err := vmctx.Storage().Put(self)
if err != nil {
return nil, err
}
if err := vmctx.Storage().Commit(oldstate, c); err != nil {
return nil, err
}
return nil, nil
}
func (sma StorageMinerActor2) GetPower(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
_, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
return self.Power.Bytes(), nil
}
func SectorIsUnique2(ctx context.Context, s types.Storage, sroot cid.Cid, sid uint64) (bool, ActorError) {
found, _, _, err := GetFromSectorSet2(ctx, s, sroot, sid)
if err != nil {
return false, err
}
return !found, nil
}
func AddToSectorSet2(ctx context.Context, blks amt2.Blocks, ss cid.Cid, sectorID uint64, commR, commD []byte) (cid.Cid, ActorError) {
if sectorID >= build.MinerMaxSectors {
return cid.Undef, aerrors.Newf(25, "sector ID out of range: %d", sectorID)
}
ssr, err := amt2.LoadAMT(blks, ss)
if err != nil {
return cid.Undef, aerrors.HandleExternalError(err, "could not load sector set node")
}
// TODO: Spec says to use SealCommitment, and construct commD from deals each time,
// but that would make SubmitPoSt way, way more expensive
if err := ssr.Set(sectorID, [][]byte{commR, commD}); err != nil {
return cid.Undef, aerrors.HandleExternalError(err, "failed to set commitment in sector set")
}
ncid, err := ssr.Flush()
if err != nil {
return cid.Undef, aerrors.HandleExternalError(err, "failed to flush sector set")
}
return ncid, nil
}
func GetFromSectorSet2(ctx context.Context, s types.Storage, ss cid.Cid, sectorID uint64) (bool, []byte, []byte, ActorError) {
if sectorID >= build.MinerMaxSectors {
return false, nil, nil, aerrors.Newf(25, "sector ID out of range: %d", sectorID)
}
ssr, err := amt2.LoadAMT(types.WrapStorage(s), ss)
if err != nil {
return false, nil, nil, aerrors.HandleExternalError(err, "could not load sector set node")
}
var comms [][]byte
err = ssr.Get(sectorID, &comms)
if err != nil {
if _, ok := err.(*amt2.ErrNotFound); ok {
return false, nil, nil, nil
}
return false, nil, nil, aerrors.HandleExternalError(err, "failed to find sector in sector set")
}
if len(comms) != 2 {
return false, nil, nil, aerrors.Newf(20, "sector set entry should only have 2 elements")
}
return true, comms[0], comms[1], nil
}
func RemoveFromSectorSet2(ctx context.Context, s types.Storage, ss cid.Cid, ids []uint64) (cid.Cid, aerrors.ActorError) {
ssr, err := amt2.LoadAMT(types.WrapStorage(s), ss)
if err != nil {
return cid.Undef, aerrors.HandleExternalError(err, "could not load sector set node")
}
for _, id := range ids {
if err := ssr.Delete(id); err != nil {
log.Warnf("failed to delete sector %d from set: %s", id, err)
}
}
ncid, err := ssr.Flush()
if err != nil {
return cid.Undef, aerrors.HandleExternalError(err, "failed to flush sector set")
}
return ncid, nil
}
func (sma StorageMinerActor2) GetWorkerAddr(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
_, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
mi, err := loadMinerInfo(vmctx, self)
if err != nil {
return nil, err
}
return mi.Worker.Bytes(), nil
}
func (sma StorageMinerActor2) GetOwner(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
_, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
mi, err := loadMinerInfo(vmctx, self)
if err != nil {
return nil, err
}
return mi.Owner.Bytes(), nil
}
func (sma StorageMinerActor2) GetPeerID(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
_, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
mi, err := loadMinerInfo(vmctx, self)
if err != nil {
return nil, err
}
return []byte(mi.PeerID), nil
}
func (sma StorageMinerActor2) UpdatePeerID(act *types.Actor, vmctx types.VMContext, params *UpdatePeerIDParams) ([]byte, ActorError) {
oldstate, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
mi, err := loadMinerInfo(vmctx, self)
if err != nil {
return nil, err
}
if vmctx.Message().From != mi.Worker {
return nil, aerrors.New(2, "only the mine worker may update the peer ID")
}
mi.PeerID = params.PeerID
mic, err := vmctx.Storage().Put(mi)
if err != nil {
return nil, err
}
self.Info = mic
c, err := vmctx.Storage().Put(self)
if err != nil {
return nil, err
}
if err := vmctx.Storage().Commit(oldstate, c); err != nil {
return nil, err
}
return nil, nil
}
func (sma StorageMinerActor2) GetSectorSize(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
_, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
mi, err := loadMinerInfo(vmctx, self)
if err != nil {
return nil, err
}
return types.NewInt(mi.SectorSize).Bytes(), nil
}
func (sma StorageMinerActor2) IsSlashed(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, ActorError) {
_, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
return cbg.EncodeBool(self.SlashedAt != 0), nil
}
// TODO: better name
func (sma StorageMinerActor2) CheckMiner(act *types.Actor, vmctx types.VMContext, params *CheckMinerParams) ([]byte, ActorError) {
if vmctx.Message().From != StoragePowerAddress {
return nil, aerrors.New(2, "only the storage power actor can check miner")
}
oldstate, self, err := loadState(vmctx)
if err != nil {
return nil, err
}
if !isLate(vmctx.BlockHeight(), self) {
// Everything's fine
return nil, nil
}
if self.SlashedAt != 0 {
// Don't slash more than necessary
return nil, nil
}
if params.NetworkPower.Equals(self.Power) {
// Don't break the network when there's only one miner left
log.Warnf("can't slash miner %s for missed PoSt, no power would be left in the network", vmctx.Message().To)
return nil, nil
}
// Slash for being late
self.SlashedAt = vmctx.BlockHeight()
nstate, err := vmctx.Storage().Put(self)
if err != nil {
return nil, err
}
if err := vmctx.Storage().Commit(oldstate, nstate); err != nil {
return nil, err
}
var out bytes.Buffer
if err := self.Power.MarshalCBOR(&out); err != nil {
return nil, aerrors.HandleExternalError(err, "marshaling return value")
}
return out.Bytes(), nil
}
func (sma StorageMinerActor2) DeclareFaults(act *types.Actor, vmctx types.VMContext, params *DeclareFaultsParams) ([]byte, ActorError) {
oldstate, self, aerr := loadState(vmctx)
if aerr != nil {
return nil, aerr
}
mi, aerr := loadMinerInfo(vmctx, self)
if aerr != nil {
return nil, aerr
}
if vmctx.Message().From != mi.Worker {
return nil, aerrors.New(1, "not authorized to declare faults for miner")
}
nfaults, err := types.MergeBitFields(params.Faults, self.FaultSet)
if err != nil {
return nil, aerrors.Absorb(err, 1, "failed to merge bitfields")
}
ss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
if nerr != nil {
return nil, aerrors.HandleExternalError(nerr, "failed to load sector set")
}
cf, nerr := nfaults.Count()
if nerr != nil {
return nil, aerrors.Absorb(nerr, 2, "could not decode RLE+")
}
if cf > 2*ss.Count {
return nil, aerrors.Newf(3, "too many declared faults: %d > %d", cf, 2*ss.Count)
}
self.FaultSet = nfaults
self.LastFaultSubmission = vmctx.BlockHeight()
nstate, aerr := vmctx.Storage().Put(self)
if aerr != nil {
return nil, aerr
}
if err := vmctx.Storage().Commit(oldstate, nstate); err != nil {
return nil, err
}
return nil, nil
}
func (sma StorageMinerActor2) SlashConsensusFault(act *types.Actor, vmctx types.VMContext, params *MinerSlashConsensusFault) ([]byte, ActorError) {
if vmctx.Message().From != StoragePowerAddress {
return nil, aerrors.New(1, "SlashConsensusFault may only be called by the storage market actor")
}
slashedCollateral := params.SlashedCollateral
if slashedCollateral.LessThan(act.Balance) {
slashedCollateral = act.Balance
}
// Some of the slashed collateral should be paid to the slasher
// GROWTH_RATE determines how fast the slasher share of slashed collateral will increase as block elapses
// current GROWTH_RATE results in SLASHER_SHARE reaches 1 after 30 blocks
// TODO: define arithmetic precision and rounding for this operation
blockElapsed := vmctx.BlockHeight() - params.AtHeight
slasherShare := slasherShare(params.SlashedCollateral, blockElapsed)
burnPortion := types.BigSub(slashedCollateral, slasherShare)
_, err := vmctx.Send(vmctx.Message().From, 0, slasherShare, nil)
if err != nil {
return nil, aerrors.Wrap(err, "failed to pay slasher")
}
_, err = vmctx.Send(BurntFundsAddress, 0, burnPortion, nil)
if err != nil {
return nil, aerrors.Wrap(err, "failed to burn funds")
}
// TODO: this still allows the miner to commit sectors and submit posts,
// their users could potentially be unaffected, but the miner will never be
// able to mine a block again
// One potential issue: the miner will have to pay back the slashed
// collateral to continue submitting PoSts, which includes pledge
// collateral that they no longer really 'need'
return nil, nil
}
func (sma StorageMinerActor2) SubmitElectionPoSt(act *types.Actor, vmctx types.VMContext, params *struct{}) ([]byte, aerrors.ActorError) {
if vmctx.Message().From != NetworkAddress {
return nil, aerrors.Newf(1, "submit election post can only be called by the storage power actor")
}
oldstate, self, aerr := loadState(vmctx)
if aerr != nil {
return nil, aerr
}
if self.SlashedAt != 0 {
return nil, aerrors.New(1, "slashed miners can't perform election PoSt")
}
pss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
if nerr != nil {
return nil, aerrors.HandleExternalError(nerr, "failed to load proving set")
}
ss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
if nerr != nil {
return nil, aerrors.HandleExternalError(nerr, "failed to load proving set")
}
faults, nerr := self.FaultSet.AllMap(2 * ss.Count)
if nerr != nil {
return nil, aerrors.Absorb(nerr, 1, "invalid bitfield (fatal?)")
}
activeFaults := uint64(0)
for f := range faults {
if f > amt2.MaxIndex {
continue
}
var comms [][]byte
err := pss.Get(f, &comms)
if err != nil {
var notfound *amt2.ErrNotFound
if !xerrors.As(err, &notfound) {
return nil, aerrors.HandleExternalError(err, "failed to find sector in sector set")
}
continue
}
activeFaults++
}
if err := onSuccessfulPoSt2(self, vmctx, activeFaults); err != nil { // TODO
return nil, err
}
ncid, err := vmctx.Storage().Put(self)
if err != nil {
return nil, err
}
if err := vmctx.Storage().Commit(oldstate, ncid); err != nil {
return nil, err
}
return nil, nil
}
func onSuccessfulPoSt2(self *StorageMinerActorState, vmctx types.VMContext, activeFaults uint64) aerrors.ActorError {
// FORK
if vmctx.BlockHeight() < build.ForkBootyBayHeight {
return onSuccessfulPoSt(self, vmctx, activeFaults)
}
var mi MinerInfo
if err := vmctx.Storage().Get(self.Info, &mi); err != nil {
return err
}
pss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.ProvingSet)
if nerr != nil {
return aerrors.HandleExternalError(nerr, "failed to load proving set")
}
ss, nerr := amt2.LoadAMT(types.WrapStorage(vmctx.Storage()), self.Sectors)
if nerr != nil {
return aerrors.HandleExternalError(nerr, "failed to load sector set")
}
faults, nerr := self.FaultSet.All(2 * ss.Count)
if nerr != nil {
return aerrors.Absorb(nerr, 1, "invalid bitfield (fatal?)")
}
self.FaultSet = types.NewBitField()
oldPower := self.Power
newPower := types.BigMul(types.NewInt(pss.Count-activeFaults), types.NewInt(mi.SectorSize))
// If below the minimum size requirement, miners have zero power
if newPower.LessThan(types.NewInt(build.MinimumMinerPower)) {
newPower = types.NewInt(0)
}
self.Power = newPower
delta := types.BigSub(self.Power, oldPower)
if self.SlashedAt != 0 {
self.SlashedAt = 0
delta = self.Power
}
prevSlashingDeadline := self.ElectionPeriodStart + build.SlashablePowerDelay
if !self.Active && newPower.GreaterThan(types.NewInt(0)) {
self.Active = true
prevSlashingDeadline = 0
}
if !(oldPower.IsZero() && newPower.IsZero()) {
enc, err := SerializeParams(&UpdateStorageParams{
Delta: delta,
NextSlashDeadline: vmctx.BlockHeight() + build.SlashablePowerDelay,
PreviousSlashDeadline: prevSlashingDeadline,
})
if err != nil {
return err
}
_, err = vmctx.Send(StoragePowerAddress, SPAMethods.UpdateStorage, types.NewInt(0), enc)
if err != nil {
return aerrors.Wrap(err, "updating storage failed")
}
self.ElectionPeriodStart = vmctx.BlockHeight()
}
var ncid cid.Cid
var err aerrors.ActorError
ncid, err = RemoveFromSectorSet2(vmctx.Context(), vmctx.Storage(), self.Sectors, faults)
if err != nil {
return err
}
self.Sectors = ncid
self.ProvingSet = ncid
return nil
}
+1 -1
View File
@@ -13,7 +13,7 @@ import (
"github.com/ipfs/go-hamt-ipld"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-cbor-util"
cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
+22 -1
View File
@@ -85,7 +85,13 @@ func (spa StoragePowerActor) CreateStorageMiner(act *types.Actor, vmctx types.VM
return nil, aerrors.Newf(1, "not enough funds passed to cover required miner collateral (needed %s, got %s)", reqColl, vmctx.Message().Value)
}
encoded, err := CreateExecParams(StorageMinerCodeCid, &StorageMinerConstructorParams{
// FORK
minerCid := StorageMinerCodeCid
if vmctx.BlockHeight() > build.ForkFrigidHeight {
minerCid = StorageMiner2CodeCid
}
encoded, err := CreateExecParams(minerCid, &StorageMinerConstructorParams{
Owner: params.Owner,
Worker: params.Worker,
SectorSize: params.SectorSize,
@@ -138,6 +144,21 @@ func (spa StoragePowerActor) ArbitrateConsensusFault(act *types.Actor, vmctx typ
return nil, aerrors.New(2, "blocks must be from the same miner")
}
// FORK
if vmctx.BlockHeight() > build.ForkBlizzardHeight {
if params.Block1.Height <= build.ForkBlizzardHeight {
return nil, aerrors.New(10, "cannot slash miners with blocks from before blizzard")
}
if params.Block2.Height <= build.ForkBlizzardHeight {
return nil, aerrors.New(11, "cannot slash miners with blocks from before blizzard")
}
if params.Block1.Cid() == params.Block2.Cid() {
return nil, aerrors.New(3, "blocks must be different")
}
}
rval, err := vmctx.Send(params.Block1.Miner, MAMethods.GetWorkerAddr, types.NewInt(0), nil)
if err != nil {
return nil, aerrors.Wrap(err, "failed to get miner worker")
+9 -1
View File
@@ -93,7 +93,15 @@ func TestStorageMarketCreateAndSlashMiner(t *testing.T) {
signBlock(t, h.w, workerAddr, b1)
signBlock(t, h.w, workerAddr, b2)
h.BlockHeight = build.ForkBlizzardHeight + 1
ret, _ := h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.ArbitrateConsensusFault,
&ArbitrateConsensusFaultParams{
Block1: b1,
Block2: b1,
})
assert.Equal(t, uint8(3), ret.ExitCode, "should have failed with exit 3")
ret, _ = h.Invoke(t, ownerAddr, StoragePowerAddress, SPAMethods.ArbitrateConsensusFault,
&ArbitrateConsensusFaultParams{
Block1: b1,
Block2: b2,
@@ -145,7 +153,7 @@ func cheatStorageMarketTotal(t *testing.T, vm *vm.VM, bs bstore.Blockstore) {
func fakeBlock(t *testing.T, minerAddr address.Address, ts uint64) *types.BlockHeader {
c := fakeCid(t, 1)
return &types.BlockHeader{Height: 5, Miner: minerAddr, Timestamp: ts, ParentStateRoot: c, Messages: c, ParentMessageReceipts: c, BLSAggregate: types.Signature{Type: types.KTBLS}}
return &types.BlockHeader{Height: 8000, Miner: minerAddr, Timestamp: ts, ParentStateRoot: c, Messages: c, ParentMessageReceipts: c, BLSAggregate: types.Signature{Type: types.KTBLS}}
}
func fakeCid(t *testing.T, s int) cid.Cid {
+2
View File
@@ -12,6 +12,7 @@ var CronCodeCid cid.Cid
var StoragePowerCodeCid cid.Cid
var StorageMarketCodeCid cid.Cid
var StorageMinerCodeCid cid.Cid
var StorageMiner2CodeCid cid.Cid
var MultisigCodeCid cid.Cid
var InitCodeCid cid.Cid
var PaymentChannelCodeCid cid.Cid
@@ -46,6 +47,7 @@ func init() {
StoragePowerCodeCid = mustSum("fil/1/power")
StorageMarketCodeCid = mustSum("fil/1/market")
StorageMinerCodeCid = mustSum("fil/1/miner")
StorageMiner2CodeCid = mustSum("fil/1/miner/2")
MultisigCodeCid = mustSum("fil/1/multisig")
InitCodeCid = mustSum("fil/1/init")
PaymentChannelCodeCid = mustSum("fil/1/paych")
+1 -1
View File
@@ -45,7 +45,7 @@ func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address, bstore.Blockstore)
t.Fatal(err)
}
stateroot, err := st.Flush()
stateroot, err := st.Flush(context.TODO())
if err != nil {
t.Fatal(err)
}
+3 -2
View File
@@ -3,10 +3,11 @@ package actors_test
import (
"bytes"
"context"
"github.com/filecoin-project/go-sectorbuilder"
"math/rand"
"testing"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/ipfs/go-cid"
dstore "github.com/ipfs/go-datastore"
hamt "github.com/ipfs/go-hamt-ipld"
@@ -185,7 +186,7 @@ func NewHarness(t *testing.T, options ...HarnessOpt) *Harness {
t.Fatal(err)
}
stateroot, err := st.Flush()
stateroot, err := st.Flush(context.TODO())
if err != nil {
t.Fatal(err)
}
+36 -17
View File
@@ -3,17 +3,17 @@ package blocksync
import (
"bufio"
"context"
"time"
"github.com/libp2p/go-libp2p-core/protocol"
"go.opencensus.io/trace"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-cbor-util"
cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log/v2"
inet "github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
@@ -25,11 +25,7 @@ type NewStreamFunc func(context.Context, peer.ID, ...protocol.ID) (inet.Stream,
const BlockSyncProtocolID = "/fil/sync/blk/0.0.1"
func init() {
cbor.RegisterCborType(BlockSyncRequest{})
cbor.RegisterCborType(BlockSyncResponse{})
cbor.RegisterCborType(BSTipSet{})
}
const BlockSyncMaxRequestLength = 800
type BlockSyncService struct {
cs *store.ChainStore
@@ -55,8 +51,17 @@ func ParseBSOptions(optfield uint64) *BSOptions {
}
const (
BSOptBlocks = 1 << 0
BSOptMessages = 1 << 1
BSOptBlocks = 1 << iota
BSOptMessages
)
const (
StatusOK = uint64(0)
StatusPartial = uint64(101)
StatusNotFound = uint64(201)
StatusGoAway = uint64(202)
StatusInternalError = uint64(203)
StatusBadRequest = uint64(204)
)
type BlockSyncResponse struct {
@@ -93,28 +98,30 @@ func (bss *BlockSyncService) HandleStream(s inet.Stream) {
log.Warnf("failed to read block sync request: %s", err)
return
}
log.Infof("block sync request for: %s %d", req.Start, req.RequestLength)
log.Infow("block sync request", "start", req.Start, "len", req.RequestLength)
resp, err := bss.processRequest(ctx, &req)
resp, err := bss.processRequest(ctx, s.Conn().RemotePeer(), &req)
if err != nil {
log.Warn("failed to process block sync request: ", err)
return
}
writeDeadline := 60 * time.Second
s.SetDeadline(time.Now().Add(writeDeadline))
if err := cborutil.WriteCborRPC(s, resp); err != nil {
log.Warn("failed to write back response for handle stream: ", err)
log.Warnw("failed to write back response for handle stream", "err", err, "peer", s.Conn().RemotePeer())
return
}
}
func (bss *BlockSyncService) processRequest(ctx context.Context, req *BlockSyncRequest) (*BlockSyncResponse, error) {
func (bss *BlockSyncService) processRequest(ctx context.Context, p peer.ID, req *BlockSyncRequest) (*BlockSyncResponse, error) {
_, span := trace.StartSpan(ctx, "blocksync.ProcessRequest")
defer span.End()
opts := ParseBSOptions(req.Options)
if len(req.Start) == 0 {
return &BlockSyncResponse{
Status: 204,
Status: StatusBadRequest,
Message: "no cids given in blocksync request",
}, nil
}
@@ -122,20 +129,32 @@ func (bss *BlockSyncService) processRequest(ctx context.Context, req *BlockSyncR
span.AddAttributes(
trace.BoolAttribute("blocks", opts.IncludeBlocks),
trace.BoolAttribute("messages", opts.IncludeMessages),
trace.Int64Attribute("reqlen", int64(req.RequestLength)),
)
chain, err := bss.collectChainSegment(types.NewTipSetKey(req.Start...), req.RequestLength, opts)
reqlen := req.RequestLength
if reqlen > BlockSyncMaxRequestLength {
log.Warnw("limiting blocksync request length", "orig", req.RequestLength, "peer", p)
reqlen = BlockSyncMaxRequestLength
}
chain, err := bss.collectChainSegment(types.NewTipSetKey(req.Start...), reqlen, opts)
if err != nil {
log.Warn("encountered error while responding to block sync request: ", err)
return &BlockSyncResponse{
Status: 203,
Status: StatusInternalError,
Message: err.Error(),
}, nil
}
status := StatusOK
if reqlen < req.RequestLength {
status = StatusPartial
}
return &BlockSyncResponse{
Chain: chain,
Status: 0,
Status: status,
}, nil
}
+20 -12
View File
@@ -18,9 +18,10 @@ import (
"go.opencensus.io/trace"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-cbor-util"
cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
incrt "github.com/filecoin-project/lotus/lib/increadtimeout"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/peermgr"
)
@@ -44,15 +45,15 @@ func NewBlockSyncClient(bserv dtypes.ChainBlockService, h host.Host, pmgr peermg
func (bs *BlockSync) processStatus(req *BlockSyncRequest, res *BlockSyncResponse) error {
switch res.Status {
case 101: // Partial Response
case StatusPartial: // Partial Response
return xerrors.Errorf("not handling partial blocksync responses yet")
case 201: // req.Start not found
case StatusNotFound: // req.Start not found
return xerrors.Errorf("not found")
case 202: // Go Away
case StatusGoAway: // Go Away
return xerrors.Errorf("not handling 'go away' blocksync responses yet")
case 203: // Internal Error
case StatusInternalError: // Internal Error
return xerrors.Errorf("block sync peer errored: %s", res.Message)
case 204:
case StatusBadRequest:
return xerrors.Errorf("block sync request invalid: %s", res.Message)
default:
return xerrors.Errorf("unrecognized response code: %d", res.Status)
@@ -194,11 +195,16 @@ func (bs *BlockSync) GetChainMessages(ctx context.Context, h *types.TipSet, coun
continue
}
if res.Status == 0 {
if res.Status == StatusOK {
bs.syncPeers.logGlobalSuccess(time.Since(start))
return res.Chain, nil
}
if res.Status == StatusPartial {
log.Warn("dont yet handle partial responses")
continue
}
err = bs.processStatus(req, res)
if err != nil {
log.Warnf("BlockSync peer %s response was an error: %s", p.String(), err)
@@ -241,16 +247,18 @@ func (bs *BlockSync) sendRequestToPeer(ctx context.Context, p peer.ID, req *Bloc
bs.RemovePeer(p)
return nil, xerrors.Errorf("failed to open stream to peer: %w", err)
}
s.SetDeadline(time.Now().Add(10 * time.Second))
defer s.SetDeadline(time.Time{})
s.SetWriteDeadline(time.Now().Add(5 * time.Second))
if err := cborutil.WriteCborRPC(s, req); err != nil {
s.SetWriteDeadline(time.Time{})
bs.syncPeers.logFailure(p, time.Since(start))
return nil, err
}
s.SetWriteDeadline(time.Time{})
var res BlockSyncResponse
if err := cborutil.ReadCborRPC(bufio.NewReader(s), &res); err != nil {
r := incrt.New(s, 50<<10, 5*time.Second)
if err := cborutil.ReadCborRPC(bufio.NewReader(r), &res); err != nil {
bs.syncPeers.logFailure(p, time.Since(start))
return nil, err
}
@@ -455,7 +463,7 @@ func (bpt *bsPeerTracker) prefSortedPeers() []peer.ID {
getPeerInitLat := func(p peer.ID) float64 {
var res float64
if bpt.pmgr != nil {
if lat, ok := bpt.pmgr.GetPeerLatency(out[i]); ok {
if lat, ok := bpt.pmgr.GetPeerLatency(p); ok {
res = float64(lat)
}
}
@@ -476,7 +484,7 @@ func (bpt *bsPeerTracker) prefSortedPeers() []peer.ID {
failRateJ := float64(pj.failures) / float64(pj.failures+pj.successes)
costJ = float64(pj.averageTime) + failRateJ*float64(bpt.avgGlobalTime)
} else {
costI = getPeerInitLat(out[i])
costJ = getPeerInitLat(out[j])
}
return costI < costJ
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"io"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
cid "github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
+3 -3
View File
@@ -254,7 +254,7 @@ func SetupStorageMarketActor(bs bstore.Blockstore, sroot cid.Cid, deals []actors
return cid.Undef, xerrors.Errorf("set storage market actor: %w", err)
}
return state.Flush()
return state.Flush(context.TODO())
}
type GenMinerCfg struct {
@@ -563,7 +563,7 @@ func MakeGenesisBlock(bs bstore.Blockstore, sys *types.VMSyscalls, balances map[
return nil, xerrors.Errorf("make initial state tree failed: %w", err)
}
stateroot, err := state.Flush()
stateroot, err := state.Flush(ctx)
if err != nil {
return nil, xerrors.Errorf("flush state tree failed: %w", err)
}
@@ -673,5 +673,5 @@ func AdjustInitActorStartID(ctx context.Context, bs blockstore.Blockstore, state
return cid.Undef, err
}
return tree.Flush()
return tree.Flush(ctx)
}
+1 -1
View File
@@ -94,7 +94,7 @@ func (ms *msgSet) add(m *types.SignedMessage) error {
if _, has := ms.msgs[m.Message.Nonce]; has {
if m.Cid() != ms.msgs[m.Message.Nonce].Cid() {
log.Error("Add with duplicate nonce")
return xerrors.Errorf("message to %s with nonce %d already in mpool")
return xerrors.Errorf("message to %s with nonce %d already in mpool", m.Message.To, m.Message.Nonce)
}
}
ms.msgs[m.Message.Nonce] = m
+13 -6
View File
@@ -7,6 +7,7 @@ import (
"github.com/ipfs/go-cid"
hamt "github.com/ipfs/go-hamt-ipld"
logging "github.com/ipfs/go-log/v2"
"go.opencensus.io/trace"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
@@ -116,23 +117,29 @@ func (st *StateTree) GetActor(addr address.Address) (*types.Actor, error) {
return &act, nil
}
func (st *StateTree) Flush() (cid.Cid, error) {
func (st *StateTree) Flush(ctx context.Context) (cid.Cid, error) {
ctx, span := trace.StartSpan(ctx, "stateTree.Flush")
defer span.End()
for addr, act := range st.actorcache {
if err := st.root.Set(context.TODO(), string(addr.Bytes()), act); err != nil {
if err := st.root.Set(ctx, string(addr.Bytes()), act); err != nil {
return cid.Undef, err
}
}
st.actorcache = make(map[address.Address]*types.Actor)
if err := st.root.Flush(context.TODO()); err != nil {
if err := st.root.Flush(ctx); err != nil {
return cid.Undef, err
}
return st.Store.Put(context.TODO(), st.root)
return st.Store.Put(ctx, st.root)
}
func (st *StateTree) Snapshot() error {
ss, err := st.Flush()
func (st *StateTree) Snapshot(ctx context.Context) error {
ctx, span := trace.StartSpan(ctx, "stateTree.SnapShot")
defer span.End()
ss, err := st.Flush(ctx)
if err != nil {
return err
}
+3 -2
View File
@@ -1,6 +1,7 @@
package state
import (
"context"
"testing"
address "github.com/filecoin-project/go-address"
@@ -60,7 +61,7 @@ func BenchmarkStateTreeSetFlush(b *testing.B) {
if err != nil {
b.Fatal(err)
}
if _, err := st.Flush(); err != nil {
if _, err := st.Flush(context.TODO()); err != nil {
b.Fatal(err)
}
}
@@ -88,7 +89,7 @@ func BenchmarkStateTree10kGetActor(b *testing.B) {
}
}
if _, err := st.Flush(); err != nil {
if _, err := st.Flush(context.TODO()); err != nil {
b.Fatal(err)
}
+9 -3
View File
@@ -8,13 +8,14 @@ import (
"go.opencensus.io/trace"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
)
func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, r vm.Rand, bheight uint64) (*types.MessageReceipt, error) {
func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, r vm.Rand, bheight uint64) (*api.MethodCall, error) {
ctx, span := trace.StartSpan(ctx, "statemanager.CallRaw")
defer span.End()
@@ -54,14 +55,19 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate
return nil, xerrors.Errorf("apply message failed: %w", err)
}
var errs string
if ret.ActorErr != nil {
errs = ret.ActorErr.Error()
log.Warnf("chain call failed: %s", ret.ActorErr)
}
return &ret.MessageReceipt, nil
return &api.MethodCall{
MessageReceipt: ret.MessageReceipt,
Error: errs,
}, nil
}
func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.MethodCall, error) {
if ts == nil {
ts = sm.cs.GetHeaviestTipSet()
}
+135
View File
@@ -3,14 +3,149 @@ package stmgr
import (
"context"
amt "github.com/filecoin-project/go-amt-ipld/v2"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/state"
"github.com/ipfs/go-cid"
hamt "github.com/ipfs/go-hamt-ipld"
blockstore "github.com/ipfs/go-ipfs-blockstore"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
)
func (sm *StateManager) handleStateForks(ctx context.Context, pstate cid.Cid, height, parentH uint64) (_ cid.Cid, err error) {
for i := parentH; i < height; i++ {
switch i {
case build.ForkBlizzardHeight:
log.Warnw("Executing blizzard fork logic", "height", i)
pstate, err = fixBlizzardAMTBug(ctx, sm, pstate)
if err != nil {
return cid.Undef, xerrors.Errorf("blizzard bug fix failed: %w", err)
}
case build.ForkFrigidHeight:
log.Warnw("Executing frigid fork logic", "height", i)
pstate, err = fixBlizzardAMTBug(ctx, sm, pstate)
if err != nil {
return cid.Undef, xerrors.Errorf("frigid bug fix failed: %w", err)
}
case build.ForkBootyBayHeight:
log.Warnw("Executing booty bay fork logic", "height", i)
pstate, err = fixBlizzardAMTBug(ctx, sm, pstate)
if err != nil {
return cid.Undef, xerrors.Errorf("booty bay bug fix failed: %w", err)
}
}
}
return pstate, nil
}
/*
1) Iterate through each miner in the chain:
1.1) Fixup their sector set and proving set
1.2) Change their code cid to point to the new miner actor code
*/
func fixBlizzardAMTBug(ctx context.Context, sm *StateManager, pstate cid.Cid) (cid.Cid, error) {
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
st, err := state.LoadStateTree(cst, pstate)
if err != nil {
return cid.Undef, err
}
spa, err := st.GetActor(actors.StoragePowerAddress)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to get storage power actor: %w", err)
}
var spast actors.StoragePowerState
if err := cst.Get(ctx, spa.Head, &spast); err != nil {
return cid.Undef, err
}
miners, err := actors.MinerSetList(ctx, cst, spast.Miners)
if err != nil {
return cid.Undef, err
}
for _, m := range miners {
mact, err := st.GetActor(m)
if err != nil {
return cid.Undef, xerrors.Errorf("getting miner actor to fix: %w", err)
}
nhead, err := fixMiner(ctx, cst, sm.cs.Blockstore(), mact.Head)
if err != nil {
return cid.Undef, xerrors.Errorf("fixing miner: %w", err)
}
if nhead != mact.Head {
log.Warnf("Miner %s had changes", m)
}
mact.Head = nhead
mact.Code = actors.StorageMiner2CodeCid
if err := st.SetActor(m, mact); err != nil {
return cid.Undef, err
}
}
return st.Flush(ctx)
}
func fixMiner(ctx context.Context, cst *hamt.CborIpldStore, bs blockstore.Blockstore, mscid cid.Cid) (cid.Cid, error) {
var mstate actors.StorageMinerActorState
if err := cst.Get(ctx, mscid, &mstate); err != nil {
return cid.Undef, xerrors.Errorf("failed to load miner actor state: %w", err)
}
amts := amt.WrapBlockstore(bs)
nsectors, err := amtFsck(amts, mstate.Sectors)
if err != nil {
return cid.Undef, xerrors.Errorf("error fsck'ing sector set: %w", err)
}
mstate.Sectors = nsectors
nproving, err := amtFsck(amts, mstate.ProvingSet)
if err != nil {
return cid.Undef, xerrors.Errorf("error fsck'ing proving set: %w", err)
}
mstate.ProvingSet = nproving
nmcid, err := cst.Put(ctx, &mstate)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to put modified miner state: %w", err)
}
return nmcid, nil
}
func amtFsck(s amt.Blocks, ss cid.Cid) (cid.Cid, error) {
a, err := amt.LoadAMT(s, ss)
if err != nil {
return cid.Undef, xerrors.Errorf("could not load AMT: %w", a)
}
b := amt.NewAMT(s)
err = a.ForEach(func(id uint64, data *cbg.Deferred) error {
err := b.Set(id, data)
if err != nil {
return xerrors.Errorf("could not copy at idx (%d): %w", id, err)
}
return nil
})
if err != nil {
return cid.Undef, xerrors.Errorf("could not copy: %w", err)
}
nss, err := b.Flush()
if err != nil {
return cid.Undef, xerrors.Errorf("could not flush: %w", err)
}
return nss, nil
}
+29
View File
@@ -607,3 +607,32 @@ func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address,
return b[0], nil
}
func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) error {
tschain := []*types.TipSet{ts}
for ts.Height() != 0 {
next, err := sm.cs.LoadTipSet(ts.Parents())
if err != nil {
return err
}
tschain = append(tschain, next)
ts = next
}
lastState := tschain[len(tschain)-1].ParentState()
for i := len(tschain) - 1; i >= 0; i-- {
cur := tschain[i]
log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids())
if cur.ParentState() != lastState {
return xerrors.Errorf("tipset chain had state mismatch at height %d", cur.Height())
}
st, _, err := sm.TipSetState(ctx, cur)
if err != nil {
return err
}
lastState = st
}
return nil
}
+36
View File
@@ -10,7 +10,9 @@ import (
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
amt "github.com/filecoin-project/go-amt-ipld"
cid "github.com/ipfs/go-cid"
@@ -311,3 +313,37 @@ func LoadSectorsFromSet(ctx context.Context, bs blockstore.Blockstore, ssc cid.C
return sset, nil
}
func ComputeState(ctx context.Context, sm *StateManager, height uint64, msgs []*types.Message, ts *types.TipSet) (cid.Cid, error) {
if ts == nil {
ts = sm.cs.GetHeaviestTipSet()
}
base, _, err := sm.TipSetState(ctx, ts)
if err != nil {
return cid.Undef, err
}
fstate, err := sm.handleStateForks(ctx, base, height, ts.Height())
if err != nil {
return cid.Undef, err
}
r := store.NewChainRand(sm.cs, ts.Cids(), height)
vmi, err := vm.NewVM(fstate, height, r, actors.NetworkAddress, sm.cs.Blockstore(), sm.cs.VMSys())
if err != nil {
return cid.Undef, err
}
for i, msg := range msgs {
ret, err := vmi.ApplyMessage(ctx, msg)
if err != nil {
return cid.Undef, xerrors.Errorf("applying message %s: %w", msg.Cid(), err)
}
if ret.ExitCode != 0 {
log.Infof("compute state apply message %d failed (exit: %d): %s", i, ret.ExitCode, ret.ActorErr)
}
}
return vmi.Flush(ctx)
}
+109
View File
@@ -1,10 +1,12 @@
package store
import (
"bytes"
"context"
"crypto/sha256"
"encoding/binary"
"encoding/json"
"io"
"sync"
"github.com/filecoin-project/go-address"
@@ -19,11 +21,16 @@ import (
lru "github.com/hashicorp/golang-lru"
block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice"
car "github.com/ipfs/go-car"
"github.com/ipfs/go-cid"
dstore "github.com/ipfs/go-datastore"
hamt "github.com/ipfs/go-hamt-ipld"
blockstore "github.com/ipfs/go-ipfs-blockstore"
bstore "github.com/ipfs/go-ipfs-blockstore"
format "github.com/ipfs/go-ipld-format"
logging "github.com/ipfs/go-log/v2"
dag "github.com/ipfs/go-merkledag"
cbg "github.com/whyrusleeping/cbor-gen"
pubsub "github.com/whyrusleeping/pubsub"
"golang.org/x/xerrors"
@@ -730,6 +737,30 @@ func (cs *ChainStore) readMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error)
return blscids, secpkcids, nil
}
func (cs *ChainStore) GetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*HeadChange, error) {
fts, err := cs.LoadTipSet(from)
if err != nil {
return nil, xerrors.Errorf("loading from tipset %s: %w", from, err)
}
tts, err := cs.LoadTipSet(to)
if err != nil {
return nil, xerrors.Errorf("loading to tipset %s: %w", to, err)
}
revert, apply, err := cs.ReorgOps(fts, tts)
if err != nil {
return nil, xerrors.Errorf("error getting tipset branches: %w", err)
}
path := make([]*HeadChange, len(revert)+len(apply))
for i, r := range revert {
path[i] = &HeadChange{Type: HCRevert, Val: r}
}
for j, i := 0, len(apply)-1; i >= 0; j, i = j+1, i-1 {
path[j+len(revert)] = &HeadChange{Type: HCApply, Val: apply[i]}
}
return path, nil
}
func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
blscids, secpkcids, err := cs.readMsgMetaCids(b.Messages)
if err != nil {
@@ -893,6 +924,84 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h uint64, ts *types
}
}
func recurseLinks(bs blockstore.Blockstore, root cid.Cid, in []cid.Cid) ([]cid.Cid, error) {
data, err := bs.Get(root)
if err != nil {
return nil, err
}
top, err := cbg.ScanForLinks(bytes.NewReader(data.RawData()))
if err != nil {
return nil, err
}
in = append(in, top...)
for _, c := range top {
var err error
in, err = recurseLinks(bs, c, in)
if err != nil {
return nil, err
}
}
return in, nil
}
func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, w io.Writer) error {
if ts == nil {
ts = cs.GetHeaviestTipSet()
}
bsrv := blockservice.New(cs.bs, nil)
dserv := dag.NewDAGService(bsrv)
return car.WriteCarWithWalker(ctx, dserv, ts.Cids(), w, func(nd format.Node) ([]*format.Link, error) {
var b types.BlockHeader
if err := b.UnmarshalCBOR(bytes.NewBuffer(nd.RawData())); err != nil {
return nil, err
}
var out []*format.Link
for _, p := range b.Parents {
out = append(out, &format.Link{Cid: p})
}
cids, err := recurseLinks(cs.bs, b.Messages, nil)
if err != nil {
return nil, err
}
for _, c := range cids {
out = append(out, &format.Link{Cid: c})
}
if b.Height == 0 {
cids, err := recurseLinks(cs.bs, b.ParentStateRoot, nil)
if err != nil {
return nil, err
}
for _, c := range cids {
out = append(out, &format.Link{Cid: c})
}
}
return out, nil
})
}
func (cs *ChainStore) Import(r io.Reader) (*types.TipSet, error) {
header, err := car.LoadCar(cs.Blockstore(), r)
if err != nil {
return nil, xerrors.Errorf("loadcar failed: %w", err)
}
root, err := cs.LoadTipSet(types.NewTipSetKey(header.Roots...))
if err != nil {
return nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err)
}
return root, nil
}
type chainRand struct {
cs *ChainStore
blks []cid.Cid
+1 -1
View File
@@ -73,7 +73,7 @@ func prepSyncTest(t testing.TB, h int) *syncTestUtil {
g, err := gen.NewGenerator()
if err != nil {
t.Fatal(err)
t.Fatalf("%+v", err)
}
ctx, cancel := context.WithCancel(context.Background())
-14
View File
@@ -2,7 +2,6 @@ package types
import (
"bytes"
"context"
"math/big"
"github.com/filecoin-project/go-sectorbuilder"
@@ -11,7 +10,6 @@ import (
"github.com/ipfs/go-cid"
"github.com/minio/sha256-simd"
"github.com/multiformats/go-multihash"
"go.opencensus.io/trace"
xerrors "golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
@@ -116,18 +114,6 @@ func (blk *BlockHeader) SigningBytes() ([]byte, error) {
return blkcopy.Serialize()
}
func (blk *BlockHeader) CheckBlockSignature(ctx context.Context, worker address.Address) error {
_, span := trace.StartSpan(ctx, "checkBlockSignature")
defer span.End()
sigb, err := blk.SigningBytes()
if err != nil {
return xerrors.Errorf("failed to get block signing bytes: %w", err)
}
return blk.BlockSig.Verify(worker, sigb)
}
type MsgMeta struct {
BlsMessages cid.Cid
SecpkMessages cid.Cid
+27
View File
@@ -0,0 +1,27 @@
//+build cgo
package types
import (
"context"
"github.com/filecoin-project/go-address"
"go.opencensus.io/trace"
"golang.org/x/xerrors"
)
func (blk *BlockHeader) CheckBlockSignature(ctx context.Context, worker address.Address) error {
_, span := trace.StartSpan(ctx, "checkBlockSignature")
defer span.End()
if blk.BlockSig == nil {
return xerrors.New("block signature not present")
}
sigb, err := blk.SigningBytes()
if err != nil {
return xerrors.Errorf("failed to get block signing bytes: %w", err)
}
return blk.BlockSig.Verify(worker, sigb)
}
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"io"
"math"
"github.com/ipfs/go-cid"
cid "github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
+3
View File
@@ -116,5 +116,8 @@ func (s *Signature) UnmarshalCBOR(br io.Reader) error {
}
func (s *Signature) Equals(o *Signature) bool {
if s == nil || o == nil {
return s == o
}
return s.Type == o.Type && bytes.Equal(s.Data, o.Data)
}
+5
View File
@@ -9,9 +9,14 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-crypto"
"github.com/minio/blake2b-simd"
"golang.org/x/xerrors"
)
func (s *Signature) Verify(addr address.Address, msg []byte) error {
if s == nil {
return xerrors.Errorf("signature is nil")
}
if addr.Protocol() == address.ID {
return fmt.Errorf("must resolve ID addresses before using them to verify a signature")
}
+2 -2
View File
@@ -59,7 +59,7 @@ func NewState() *StateWrapper {
if err != nil {
panic(err) // Never returns error, the error return should be removed.
}
root, err := treeImpl.Flush()
root, err := treeImpl.Flush(context.TODO())
if err != nil {
panic(err)
}
@@ -208,7 +208,7 @@ func (s *StateWrapper) Signer() *keyStore {
// Flushes a state tree to storage and sets this state's root to that tree's root CID.
func (s *StateWrapper) flush(tree *state.StateTree) (err error) {
s.stateRoot, err = tree.Flush()
s.stateRoot, err = tree.Flush(context.TODO())
return
}
+5
View File
@@ -35,6 +35,7 @@ func newInvoker() *invoker {
inv.register(actors.StoragePowerCodeCid, actors.StoragePowerActor{}, actors.StoragePowerState{})
inv.register(actors.StorageMarketCodeCid, actors.StorageMarketActor{}, actors.StorageMarketState{})
inv.register(actors.StorageMinerCodeCid, actors.StorageMinerActor{}, actors.StorageMinerActorState{})
inv.register(actors.StorageMiner2CodeCid, actors.StorageMinerActor2{}, actors.StorageMinerActorState{})
inv.register(actors.MultisigCodeCid, actors.MultiSigActor{}, actors.MultiSigActorState{})
inv.register(actors.PaymentChannelCodeCid, actors.PaymentChannelActor{}, actors.PaymentChannelActorState{})
@@ -160,6 +161,10 @@ func DecodeParams(b []byte, out interface{}) error {
}
func DumpActorState(code cid.Cid, b []byte) (interface{}, error) {
if code == actors.AccountCodeCid { // Account code special case
return nil, nil
}
i := newInvoker() // TODO: register builtins in init block
typ, ok := i.builtInState[code]
+3 -3
View File
@@ -430,7 +430,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
}
st := vm.cstate
if err := st.Snapshot(); err != nil {
if err := st.Snapshot(ctx); err != nil {
return nil, xerrors.Errorf("snapshot failed: %w", err)
}
@@ -467,7 +467,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
}
if actorErr != nil {
log.Warnf("[from=%s,to=%s,n=%d,m=%d,h=%d] Send actor error: %+v", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
log.Warnw("Send actor error", "from", msg.From, "to", msg.To, "nonce", msg.Nonce, "method", msg.Method, "height", vm.blockHeight, "error", fmt.Sprintf("%+v", actorErr))
}
var errcode uint8
@@ -534,7 +534,7 @@ func (vm *VM) Flush(ctx context.Context) (cid.Cid, error) {
from := vm.buf
to := vm.buf.Read()
root, err := vm.cstate.Flush()
root, err := vm.cstate.Flush(ctx)
if err != nil {
return cid.Undef, xerrors.Errorf("flushing vm: %w", err)
}
+117
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"time"
@@ -12,6 +13,7 @@ import (
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
types "github.com/filecoin-project/lotus/chain/types"
)
@@ -26,6 +28,8 @@ var chainCmd = &cli.Command{
chainSetHeadCmd,
chainListCmd,
chainGetCmd,
chainExportCmd,
slashConsensusFault,
},
}
@@ -172,6 +176,10 @@ var chainGetMsgCmd = &cli.Command{
Name: "getmessage",
Usage: "Get and print a message by its cid",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("must pass a cid of a message to get")
}
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
@@ -385,3 +393,112 @@ func printTipSet(format string, ts *types.TipSet) {
fmt.Println(format)
}
var chainExportCmd = &cli.Command{
Name: "export",
Usage: "export chain to a car file",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "tipset",
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
if !cctx.Args().Present() {
return fmt.Errorf("must specify filename to export chain to")
}
fi, err := os.Create(cctx.Args().First())
if err != nil {
return err
}
defer fi.Close()
ts, err := loadTipSet(ctx, cctx, api)
if err != nil {
return err
}
stream, err := api.ChainExport(ctx, ts)
if err != nil {
return err
}
for b := range stream {
_, err := fi.Write(b)
if err != nil {
return err
}
}
return nil
},
}
var slashConsensusFault = &cli.Command{
Name: "slash-consensus",
Usage: "Report consensus fault",
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
c1, err := cid.Parse(cctx.Args().Get(0))
if err != nil {
return xerrors.Errorf("parsing cid 1: %w", err)
}
b1, err := api.ChainGetBlock(ctx, c1)
if err != nil {
return xerrors.Errorf("getting block 1: %w", err)
}
c2, err := cid.Parse(cctx.Args().Get(0))
if err != nil {
return xerrors.Errorf("parsing cid 2: %w", err)
}
b2, err := api.ChainGetBlock(ctx, c2)
if err != nil {
return xerrors.Errorf("getting block 2: %w", err)
}
def, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
params, err := actors.SerializeParams(&actors.ArbitrateConsensusFaultParams{
Block1: b1,
Block2: b2,
})
msg := &types.Message{
To: actors.StoragePowerAddress,
From: def,
Value: types.NewInt(0),
GasPrice: types.NewInt(1),
GasLimit: types.NewInt(10000000),
Method: actors.SPAMethods.ArbitrateConsensusFault,
Params: params,
}
smsg, err := api.MpoolPushMessage(ctx, msg)
if err != nil {
return err
}
fmt.Println(smsg.Cid())
return nil
},
}
+1
View File
@@ -202,6 +202,7 @@ var Commands = []*cli.Command{
clientCmd,
fetchParamCmd,
mpoolCmd,
multisigCmd,
netCmd,
paychCmd,
sendCmd,
+2 -1
View File
@@ -124,7 +124,8 @@ var mpoolStat = &cli.Command{
for a, bkt := range buckets {
act, err := api.StateGetActor(ctx, a, ts)
if err != nil {
return err
fmt.Printf("%s, err: %s\n", a, err)
continue
}
cur := act.Nonce
+400
View File
@@ -0,0 +1,400 @@
package cli
import (
"bytes"
"encoding/hex"
"fmt"
"os"
"strconv"
"text/tabwriter"
"github.com/filecoin-project/go-address"
actors "github.com/filecoin-project/lotus/chain/actors"
types "github.com/filecoin-project/lotus/chain/types"
cbg "github.com/whyrusleeping/cbor-gen"
"gopkg.in/urfave/cli.v2"
)
var multisigCmd = &cli.Command{
Name: "msig",
Usage: "Interact with a multisig wallet",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "source",
Usage: "specify the account to send propose from",
},
},
Subcommands: []*cli.Command{
msigCreateCmd,
msigInspectCmd,
msigProposeCmd,
msigApproveCmd,
},
}
var msigCreateCmd = &cli.Command{
Name: "create",
Usage: "Create a new multisig wallet",
Flags: []cli.Flag{
&cli.Uint64Flag{
Name: "required",
},
&cli.StringFlag{
Name: "value",
Usage: "initial funds to give to multisig",
Value: "0",
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
var addrs []address.Address
for _, a := range cctx.Args().Slice() {
addr, err := address.NewFromString(a)
if err != nil {
return err
}
addrs = append(addrs, addr)
}
// get the address we're going to use to create the multisig (can be one of the above, as long as they have funds)
sendAddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
val := cctx.String("value")
filval, err := types.ParseFIL(val)
if err != nil {
return err
}
required := cctx.Uint64("required")
if required == 0 {
required = uint64(len(addrs))
}
// Set up constructor parameters for multisig
msigParams := &actors.MultiSigConstructorParams{
Signers: addrs,
Required: required,
}
enc, err := actors.SerializeParams(msigParams)
if err != nil {
return err
}
// new actors are created by invoking 'exec' on the init actor with the constructor params
execParams := &actors.ExecParams{
Code: actors.MultisigCodeCid,
Params: enc,
}
enc, err = actors.SerializeParams(execParams)
if err != nil {
return err
}
// now we create the message to send this with
msg := types.Message{
To: actors.InitAddress,
From: sendAddr,
Method: actors.IAMethods.Exec,
Params: enc,
GasPrice: types.NewInt(1),
GasLimit: types.NewInt(1000000),
Value: types.BigInt(filval),
}
// send the message out to the network
smsg, err := api.MpoolPushMessage(ctx, &msg)
if err != nil {
return err
}
// wait for it to get mined into a block
wait, err := api.StateWaitMsg(ctx, smsg.Cid())
if err != nil {
return err
}
// check it executed successfully
if wait.Receipt.ExitCode != 0 {
fmt.Println("actor creation failed!")
return err
}
// get address of newly created miner
msigaddr, err := address.NewFromBytes(wait.Receipt.Return)
if err != nil {
return err
}
fmt.Println("Created new multisig: ", msigaddr.String())
// TODO: maybe register this somewhere
return nil
},
}
var msigInspectCmd = &cli.Command{
Name: "inspect",
Usage: "Inspect a multisig wallet",
Flags: []cli.Flag{},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
if !cctx.Args().Present() {
return fmt.Errorf("must specify address of multisig to inspect")
}
maddr, err := address.NewFromString(cctx.Args().First())
if err != nil {
return err
}
act, err := api.StateGetActor(ctx, maddr, nil)
if err != nil {
return err
}
obj, err := api.ChainReadObj(ctx, act.Head)
if err != nil {
return err
}
var mstate actors.MultiSigActorState
if err := mstate.UnmarshalCBOR(bytes.NewReader(obj)); err != nil {
return err
}
fmt.Printf("Balance: %sfil\n", types.FIL(act.Balance))
fmt.Printf("Threshold: %d / %d\n", mstate.Required, len(mstate.Signers))
fmt.Println("Signers:")
for _, s := range mstate.Signers {
fmt.Printf("\t%s\n", s)
}
fmt.Println("Transactions: ", len(mstate.Transactions))
if len(mstate.Transactions) > 0 {
w := tabwriter.NewWriter(os.Stdout, 8, 4, 0, ' ', 0)
fmt.Fprintf(w, "ID\tState\tTo\tValue\tMethod\tParams\n")
for _, tx := range mstate.Transactions {
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%d\t%x\n", tx.TxID, state(tx), tx.To, types.FIL(tx.Value), tx.Method, tx.Params)
}
w.Flush()
}
return nil
},
}
func state(tx actors.MTransaction) string {
if tx.Complete {
return "done"
}
if tx.Canceled {
return "canceled"
}
return "pending"
}
var msigProposeCmd = &cli.Command{
Name: "propose",
Usage: "Propose a multisig transaction",
Flags: []cli.Flag{},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
if cctx.Args().Len() < 3 {
return fmt.Errorf("must pass multisig address, destination, and value")
}
if cctx.Args().Len() > 3 && cctx.Args().Len() != 5 {
return fmt.Errorf("usage: msig propose <msig addr> <desination> <value> [ <method> <params> ]")
}
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
dest, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
value, err := types.ParseFIL(cctx.Args().Get(2))
if err != nil {
return err
}
var method uint64
var params []byte
if cctx.Args().Len() == 5 {
m, err := strconv.ParseUint(cctx.Args().Get(3), 10, 64)
if err != nil {
return err
}
method = m
p, err := hex.DecodeString(cctx.Args().Get(4))
if err != nil {
return err
}
params = p
}
enc, err := actors.SerializeParams(&actors.MultiSigProposeParams{
To: dest,
Value: types.BigInt(value),
Method: method,
Params: params,
})
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("source") {
f, err := address.NewFromString(cctx.String("source"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
msg := &types.Message{
To: msig,
From: from,
Value: types.NewInt(0),
Method: actors.MultiSigMethods.Propose,
Params: enc,
GasLimit: types.NewInt(100000),
GasPrice: types.NewInt(1),
}
smsg, err := api.MpoolPushMessage(ctx, msg)
if err != nil {
return err
}
fmt.Println("send proposal in message: ", smsg.Cid())
wait, err := api.StateWaitMsg(ctx, smsg.Cid())
if err != nil {
return err
}
if wait.Receipt.ExitCode != 0 {
return fmt.Errorf("proposal returned exit %d", wait.Receipt.ExitCode)
}
_, v, err := cbg.CborReadHeader(bytes.NewReader(wait.Receipt.Return))
if err != nil {
return err
}
fmt.Printf("Transaction ID: %d\n", v)
return nil
},
}
var msigApproveCmd = &cli.Command{
Name: "approve",
Usage: "Approve a multisig transaction",
Flags: []cli.Flag{},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
if cctx.Args().Len() < 2 {
return fmt.Errorf("must pass multisig address and transaction ID")
}
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
enc, err := actors.SerializeParams(&actors.MultiSigTxID{
TxID: txid,
})
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("source") {
f, err := address.NewFromString(cctx.String("source"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
msg := &types.Message{
To: msig,
From: from,
Value: types.NewInt(0),
Method: actors.MultiSigMethods.Approve,
Params: enc,
GasLimit: types.NewInt(100000),
GasPrice: types.NewInt(1),
}
smsg, err := api.MpoolPushMessage(ctx, msg)
if err != nil {
return err
}
fmt.Println("sent approval in message: ", smsg.Cid())
wait, err := api.StateWaitMsg(ctx, smsg.Cid())
if err != nil {
return err
}
if wait.Receipt.ExitCode != 0 {
return fmt.Errorf("approve returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
+1 -1
View File
@@ -15,7 +15,7 @@ var sendCmd = &cli.Command{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "source",
Usage: "optinally specifiy the account to send funds from",
Usage: "optionally specify the account to send funds from",
},
},
Action: func(cctx *cli.Context) error {
+290
View File
@@ -1,17 +1,24 @@
package cli
import (
"bytes"
"context"
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
actors "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/miner"
"github.com/libp2p/go-libp2p-core/peer"
"golang.org/x/xerrors"
"github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
"gopkg.in/urfave/cli.v2"
)
@@ -37,6 +44,8 @@ var stateCmd = &cli.Command{
stateSectorSizeCmd,
stateReadStateCmd,
stateListMessagesCmd,
stateComputeStateCmd,
stateCallCmd,
},
}
@@ -526,6 +535,10 @@ var stateListMessagesCmd = &cli.Command{
Name: "toheight",
Usage: "don't look before given block height",
},
&cli.BoolFlag{
Name: "cids",
Usage: "print message CIDs instead of messages",
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
@@ -566,6 +579,11 @@ var stateListMessagesCmd = &cli.Command{
}
for _, c := range msgs {
if cctx.Bool("cids") {
fmt.Println(c.String())
continue
}
m, err := api.ChainGetMessage(ctx, c)
if err != nil {
return err
@@ -580,3 +598,275 @@ var stateListMessagesCmd = &cli.Command{
return nil
},
}
var stateComputeStateCmd = &cli.Command{
Name: "compute-state",
Usage: "Perform state computations",
Flags: []cli.Flag{
&cli.Uint64Flag{
Name: "height",
Usage: "set the height to compute state at",
},
&cli.BoolFlag{
Name: "apply-mpool-messages",
Usage: "apply messages from the mempool to the computed state",
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
ts, err := loadTipSet(ctx, cctx, api)
if err != nil {
return err
}
h := cctx.Uint64("height")
if h == 0 {
if ts == nil {
head, err := api.ChainHead(ctx)
if err != nil {
return err
}
ts = head
}
h = ts.Height()
}
var msgs []*types.Message
if cctx.Bool("apply-mpool-messages") {
pmsgs, err := api.MpoolPending(ctx, ts)
if err != nil {
return err
}
pmsgs, err = miner.SelectMessages(ctx, api.StateGetActor, ts, pmsgs)
if err != nil {
return err
}
for _, sm := range pmsgs {
msgs = append(msgs, &sm.Message)
}
}
nstate, err := api.StateCompute(ctx, h, msgs, ts)
if err != nil {
return err
}
fmt.Println("computed state cid: ", nstate)
return nil
},
}
var stateCallCmd = &cli.Command{
Name: "call",
Usage: "Invoke a method on an actor locally",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "",
Value: actors.NetworkAddress.String(),
},
&cli.StringFlag{
Name: "value",
Usage: "specify value field for invocation",
Value: "0",
},
&cli.StringFlag{
Name: "ret",
Usage: "specify how to parse output (auto, raw, addr, big)",
Value: "auto",
},
},
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() < 2 {
return fmt.Errorf("must specify at least actor and method to invoke")
}
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
toa, err := address.NewFromString(cctx.Args().First())
if err != nil {
return fmt.Errorf("given 'to' address %q was invalid: %w", cctx.Args().First(), err)
}
froma, err := address.NewFromString(cctx.String("from"))
if err != nil {
return fmt.Errorf("given 'from' address %q was invalid: %w", cctx.String("from"), err)
}
ts, err := loadTipSet(ctx, cctx, api)
if err != nil {
return err
}
method, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return fmt.Errorf("must pass method as a number")
}
value, err := types.ParseFIL(cctx.String("value"))
if err != nil {
return fmt.Errorf("failed to parse 'value': %s", err)
}
act, err := api.StateGetActor(ctx, toa, ts)
if err != nil {
return fmt.Errorf("failed to lookup target actor: %s", err)
}
params, err := parseParamsForMethod(act.Code, method, cctx.Args().Slice()[2:])
if err != nil {
return fmt.Errorf("failed to parse params: %s", err)
}
ret, err := api.StateCall(ctx, &types.Message{
From: froma,
To: toa,
Value: types.BigInt(value),
GasLimit: types.NewInt(10000000000),
GasPrice: types.NewInt(0),
Method: method,
Params: params,
}, ts)
if err != nil {
return fmt.Errorf("state call failed: %s", err)
}
if ret.ExitCode != 0 {
return fmt.Errorf("invocation failed (exit: %d): %s", ret.ExitCode, ret.Error)
}
s, err := formatOutput(cctx.String("ret"), ret.Return)
if err != nil {
return fmt.Errorf("failed to format output: %s", err)
}
fmt.Printf("return: %s\n", s)
return nil
},
}
func formatOutput(t string, val []byte) (string, error) {
switch t {
case "raw", "hex":
return fmt.Sprintf("%x", val), nil
case "address", "addr", "a":
a, err := address.NewFromBytes(val)
if err != nil {
return "", err
}
return a.String(), nil
case "big", "int", "bigint":
bi := types.BigFromBytes(val)
return bi.String(), nil
case "fil":
bi := types.FIL(types.BigFromBytes(val))
return bi.String(), nil
case "pid", "peerid", "peer":
pid, err := peer.IDFromBytes(val)
if err != nil {
return "", err
}
return pid.Pretty(), nil
case "auto":
if len(val) == 0 {
return "", nil
}
a, err := address.NewFromBytes(val)
if err == nil {
return "address: " + a.String(), nil
}
pid, err := peer.IDFromBytes(val)
if err == nil {
return "peerID: " + pid.Pretty(), nil
}
bi := types.BigFromBytes(val)
return "bigint: " + bi.String(), nil
default:
return "", fmt.Errorf("unrecognized output type: %q", t)
}
}
func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, error) {
if len(args) == 0 {
return nil, nil
}
var f interface{}
switch act {
case actors.StorageMarketCodeCid:
f = actors.StorageMarketActor{}.Exports()[method]
case actors.StorageMinerCodeCid:
f = actors.StorageMinerActor{}.Exports()[method]
case actors.StoragePowerCodeCid:
f = actors.StoragePowerActor{}.Exports()[method]
case actors.MultisigCodeCid:
f = actors.MultiSigActor{}.Exports()[method]
case actors.PaymentChannelCodeCid:
f = actors.PaymentChannelActor{}.Exports()[method]
default:
return nil, fmt.Errorf("the lazy devs didnt add support for that actor to this call yet")
}
rf := reflect.TypeOf(f)
if rf.NumIn() != 3 {
return nil, fmt.Errorf("expected referenced method to have three arguments")
}
paramObj := rf.In(2).Elem()
if paramObj.NumField() != len(args) {
return nil, fmt.Errorf("not enough arguments given to call that method (expecting %d)", paramObj.NumField())
}
p := reflect.New(paramObj)
for i := 0; i < len(args); i++ {
switch paramObj.Field(i).Type {
case reflect.TypeOf(address.Address{}):
a, err := address.NewFromString(args[i])
if err != nil {
return nil, fmt.Errorf("failed to parse address: %s", err)
}
p.Elem().Field(i).Set(reflect.ValueOf(a))
case reflect.TypeOf(uint64(0)):
val, err := strconv.ParseUint(args[i], 10, 64)
if err != nil {
return nil, err
}
p.Elem().Field(i).Set(reflect.ValueOf(val))
case reflect.TypeOf(peer.ID("")):
pid, err := peer.IDB58Decode(args[i])
if err != nil {
return nil, fmt.Errorf("failed to parse peer ID: %s", err)
}
p.Elem().Field(i).Set(reflect.ValueOf(pid))
default:
return nil, fmt.Errorf("unsupported type for call (TODO): %s", paramObj.Field(i).Type)
}
}
m := p.Interface().(cbg.CBORMarshaler)
buf := new(bytes.Buffer)
if err := m.MarshalCBOR(buf); err != nil {
return nil, fmt.Errorf("failed to marshal param object: %s", err)
}
return buf.Bytes(), nil
}
+5 -3
View File
@@ -303,8 +303,8 @@ func main() {
epost2 := time.Now()
if !bytes.Equal(proof1, proof2) {
log.Warn("separate epost calls returned different proof values (this might be bad)")
if bytes.Equal(proof1, proof2) {
log.Warn("separate epost calls returned the same proof values (this might be bad)")
}
ok, err := sectorbuilder.ProofVerifier.VerifyElectionPost(context.TODO(), sectorSize, sinfos, challenge[:], proof1, candidates[:1], maddr)
@@ -351,7 +351,9 @@ func main() {
fmt.Printf("seal: preCommit: %s (%s)\n", bo.SealingResults[0].PreCommit, bps(bo.SectorSize, bo.SealingResults[0].PreCommit))
fmt.Printf("seal: commit: %s (%s)\n", bo.SealingResults[0].Commit, bps(bo.SectorSize, bo.SealingResults[0].Commit))
fmt.Printf("seal: verify: %s\n", bo.SealingResults[0].Verify)
fmt.Printf("unseal: %s (%s)\n", bo.SealingResults[0].Unseal, bps(bo.SectorSize, bo.SealingResults[0].Unseal))
if !c.Bool("skip-unseal") {
fmt.Printf("unseal: %s (%s)\n", bo.SealingResults[0].Unseal, bps(bo.SectorSize, bo.SealingResults[0].Unseal))
}
}
fmt.Printf("generate candidates: %s (%s)\n", bo.PostGenerateCandidates, bps(bo.SectorSize*uint64(len(bo.SealingResults)), bo.PostGenerateCandidates))
fmt.Printf("compute epost proof (cold): %s\n", bo.PostEProofCold)
+274 -23
View File
@@ -2,16 +2,18 @@ package main
import (
"database/sql"
"encoding/hex"
"fmt"
"github.com/filecoin-project/lotus/api"
"golang.org/x/xerrors"
"sync"
"time"
"github.com/filecoin-project/go-address"
"github.com/ipfs/go-cid"
_ "github.com/lib/pq"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
)
@@ -75,13 +77,22 @@ create table if not exists blocks
eprof bytea,
prand bytea,
ep0partial bytea,
ep0sector bigint not null,
ep0challangei bigint not null
ep0sector numeric not null,
ep0challangei numeric not null
);
create unique index if not exists block_cid_uindex
on blocks (cid);
create materialized view if not exists state_heights
as select distinct height, parentstateroot from blocks;
create unique index if not exists state_heights_uindex
on state_heights (height);
create index if not exists state_heights_height_index
on state_heights (parentstateroot);
create table if not exists id_address_map
(
id text not null,
@@ -117,6 +128,38 @@ create index if not exists id_address_map_address_index
create index if not exists id_address_map_id_index
on id_address_map (id);
create or replace function actor_tips(epoch bigint)
returns table (id text,
code text,
head text,
nonce int,
balance text,
stateroot text,
height bigint,
parentstateroot text) as
$body$
select distinct on (id) * from actors
inner join state_heights sh on sh.parentstateroot = stateroot
where height < $1
order by id, height desc;
$body$ language sql;
create table if not exists actor_states
(
head text not null,
code text not null,
state json not null
);
create unique index if not exists actor_states_head_code_uindex
on actor_states (head, code);
create index if not exists actor_states_head_index
on actor_states (head);
create index if not exists actor_states_code_head_index
on actor_states (head, code);
create table if not exists messages
(
cid text not null
@@ -124,11 +167,11 @@ create table if not exists messages
primary key,
"from" text not null,
"to" text not null,
nonce int not null,
nonce bigint not null,
value text not null,
gasprice int not null,
gaslimit int not null,
method int,
gasprice bigint not null,
gaslimit bigint not null,
method bigint,
params bytea
);
@@ -184,7 +227,7 @@ create table if not exists miner_heads
worker text not null,
peerid text not null,
sectorsize bigint not null,
power bigint not null,
power decimal not null,
active bool,
ppe bigint not null,
slashed_at bigint not null,
@@ -192,6 +235,73 @@ create table if not exists miner_heads
primary key (head, addr)
);
create or replace function miner_tips(epoch bigint)
returns table (head text,
addr text,
stateroot text,
sectorset text,
setsize decimal,
provingset text,
provingsize decimal,
owner text,
worker text,
peerid text,
sectorsize bigint,
power decimal,
active bool,
ppe bigint,
slashed_at bigint,
height bigint,
parentstateroot text) as
$body$
select distinct on (addr) * from miner_heads
inner join state_heights sh on sh.parentstateroot = stateroot
where height < $1
order by addr, height desc;
$body$ language sql;
create table if not exists deals
(
id int not null,
pieceRef text not null,
pieceSize bigint not null,
client text not null,
provider text not null,
expiration decimal not null,
duration decimal not null,
epochPrice decimal not null,
collateral decimal not null,
constraint deals_pk
primary key (id)
);
create index if not exists deals_client_index
on deals (client);
create unique index if not exists deals_id_uindex
on deals (id);
create index if not exists deals_pieceRef_index
on deals (pieceRef);
create index if not exists deals_provider_index
on deals (provider);
create table if not exists deal_activations
(
deal bigint not null
constraint deal_activations_deals_id_fk
references deals,
activation_epoch bigint not null,
constraint deal_activations_pk
primary key (deal)
);
create index if not exists deal_activations_activation_epoch_index
on deal_activations (activation_epoch);
create unique index if not exists deal_activations_deal_uindex
on deal_activations (deal);
`)
if err != nil {
return err
@@ -226,17 +336,15 @@ func (st *storage) hasList() map[cid.Cid]struct{} {
return out
}
func (st *storage) storeActors(actors map[address.Address]map[types.Actor]cid.Cid) error {
func (st *storage) storeActors(actors map[address.Address]map[types.Actor]actorInfo) error {
// Basic
tx, err := st.db.Begin()
if err != nil {
return err
}
if _, err := tx.Exec(`
create temp table a (like actors excluding constraints) on commit drop;
`); err != nil {
create temp table a (like actors excluding constraints) on commit drop;
`); err != nil {
return xerrors.Errorf("prep temp: %w", err)
}
@@ -247,7 +355,7 @@ create temp table a (like actors excluding constraints) on commit drop;
for addr, acts := range actors {
for act, st := range acts {
if _, err := stmt.Exec(addr.String(), act.Code.String(), act.Head.String(), act.Nonce, act.Balance.String(), st.String()); err != nil {
if _, err := stmt.Exec(addr.String(), act.Code.String(), act.Head.String(), act.Nonce, act.Balance.String(), st.stateroot.String()); err != nil {
return err
}
}
@@ -261,7 +369,47 @@ create temp table a (like actors excluding constraints) on commit drop;
return xerrors.Errorf("actor put: %w", err)
}
return tx.Commit()
if err := tx.Commit(); err != nil {
return err
}
// States
tx, err = st.db.Begin()
if err != nil {
return err
}
if _, err := tx.Exec(`
create temp table a (like actor_states excluding constraints) on commit drop;
`); err != nil {
return xerrors.Errorf("prep temp: %w", err)
}
stmt, err = tx.Prepare(`copy a (head, code, state) from stdin `)
if err != nil {
return err
}
for _, acts := range actors {
for act, st := range acts {
if _, err := stmt.Exec(act.Head.String(), act.Code.String(), st.state); err != nil {
return err
}
}
}
if err := stmt.Close(); err != nil {
return err
}
if _, err := tx.Exec(`insert into actor_states select * from a on conflict do nothing `); err != nil {
return xerrors.Errorf("actor put: %w", err)
}
if err := tx.Commit(); err != nil {
return err
}
return nil
}
func (st *storage) storeMiners(miners map[minerKey]*minerInfo) error {
@@ -602,11 +750,8 @@ func (st *storage) storeMpoolInclusions(msgs []api.MpoolUpdate) error {
}
if _, err := tx.Exec(`
create temp table mi (like mpool_messages excluding constraints) on commit drop;
`); err != nil {
create temp table mi (like mpool_messages excluding constraints) on commit drop;
`); err != nil {
return xerrors.Errorf("prep temp: %w", err)
}
@@ -639,6 +784,112 @@ create temp table mi (like mpool_messages excluding constraints) on commit drop;
return tx.Commit()
}
func (st *storage) storeDeals(deals map[string]actors.OnChainDeal) error {
tx, err := st.db.Begin()
if err != nil {
return err
}
if _, err := tx.Exec(`
create temp table d (like deals excluding constraints) on commit drop;
`); err != nil {
return xerrors.Errorf("prep temp: %w", err)
}
stmt, err := tx.Prepare(`copy d (id, pieceref, piecesize, client, "provider", expiration, duration, epochprice, collateral) from stdin `)
if err != nil {
return err
}
var bloat uint64
for id, deal := range deals {
if len(deal.PieceRef) > 40 {
bloat += uint64(len(deal.PieceRef))
continue
}
if _, err := stmt.Exec(
id,
hex.EncodeToString(deal.PieceRef),
deal.PieceSize,
deal.Client.String(),
deal.Provider.String(),
fmt.Sprint(deal.ProposalExpiration),
fmt.Sprint(deal.Duration),
deal.StoragePricePerEpoch.String(),
deal.StorageCollateral.String(),
); err != nil {
return err
}
}
if bloat > 0 {
log.Warnf("deal PieceRefs had %d bytes of garbage", bloat)
}
if err := stmt.Close(); err != nil {
return err
}
if _, err := tx.Exec(`insert into deals select * from d on conflict do nothing `); err != nil {
return xerrors.Errorf("actor put: %w", err)
}
if err := tx.Commit(); err != nil {
return err
}
// Activations
tx, err = st.db.Begin()
if err != nil {
return err
}
if _, err := tx.Exec(`
create temp table d (like deal_activations excluding constraints) on commit drop;
`); err != nil {
return xerrors.Errorf("prep temp: %w", err)
}
stmt, err = tx.Prepare(`copy d (deal, activation_epoch) from stdin `)
if err != nil {
return err
}
for id, deal := range deals {
if deal.ActivationEpoch == 0 {
continue
}
if _, err := stmt.Exec(
id,
deal.ActivationEpoch,
); err != nil {
return err
}
}
if err := stmt.Close(); err != nil {
return err
}
if _, err := tx.Exec(`insert into deal_activations select * from d on conflict do nothing `); err != nil {
return xerrors.Errorf("actor put: %w", err)
}
if err := tx.Commit(); err != nil {
return err
}
return nil
}
func (st *storage) refreshViews() error {
if _, err := st.db.Exec(`refresh materialized view state_heights`); err != nil {
return err
}
return nil
}
func (st *storage) close() error {
return st.db.Close()
}
+63 -7
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"container/list"
"context"
"encoding/json"
"math"
"sync"
@@ -60,9 +61,14 @@ type minerInfo struct {
psize uint64
}
type actorInfo struct {
stateroot cid.Cid
state string
}
func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipSet) {
addresses := map[address.Address]address.Address{}
actors := map[address.Address]map[types.Actor]cid.Cid{}
actors := map[address.Address]map[types.Actor]actorInfo{}
var alk sync.Mutex
log.Infof("Getting synced block list")
@@ -150,12 +156,26 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
log.Error(err)
return
}
ast, err := api.StateReadState(ctx, act, ts)
if err != nil {
log.Error(err)
return
}
state, err := json.Marshal(ast.State)
if err != nil {
log.Error(err)
return
}
alk.Lock()
_, ok := actors[addr]
if !ok {
actors[addr] = map[types.Actor]cid.Cid{}
actors[addr] = map[types.Actor]actorInfo{}
}
actors[addr][*act] = actorInfo{
stateroot: bh.ParentStateRoot,
state: string(state),
}
actors[addr][*act] = bh.ParentStateRoot
addresses[addr] = address.Undef
alk.Unlock()
})
@@ -181,13 +201,26 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
log.Error(err)
return
}
ast, err := api.StateReadState(ctx, &act, ts)
if err != nil {
log.Error(err)
return
}
state, err := json.Marshal(ast.State)
if err != nil {
log.Error(err)
return
}
alk.Lock()
_, ok := actors[addr]
if !ok {
actors[addr] = map[types.Actor]cid.Cid{}
actors[addr] = map[types.Actor]actorInfo{}
}
actors[addr][act] = actorInfo{
stateroot: bh.ParentStateRoot,
state: string(state),
}
actors[addr][act] = bh.ParentStateRoot
addresses[addr] = address.Undef
alk.Unlock()
}
@@ -221,14 +254,14 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
for addr, m := range actors {
for actor, c := range m {
if actor.Code != actors2.StorageMinerCodeCid {
if !(actor.Code == actors2.StorageMinerCodeCid || actor.Code == actors2.StorageMiner2CodeCid) {
continue
}
miners[minerKey{
addr: addr,
act: actor,
stateroot: c,
stateroot: c.stateroot,
}] = &minerInfo{}
}
}
@@ -322,6 +355,29 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipS
log.Infof("Sync stage done")
}
log.Infof("Get deals")
// TODO: incremental, gather expired
deals, err := api.StateMarketDeals(ctx, ts)
if err != nil {
log.Error(err)
return
}
log.Infof("Store deals")
if err := st.storeDeals(deals); err != nil {
log.Error(err)
return
}
log.Infof("Refresh views")
if err := st.refreshViews(); err != nil {
log.Error(err)
return
}
log.Infof("Sync done")
}
+1 -1
View File
@@ -275,7 +275,7 @@ func (h *handler) mkminer(w http.ResponseWriter, r *http.Request) {
createStorageMinerMsg := &types.Message{
To: actors.StoragePowerAddress,
From: h.from,
Value: collateral,
Value: types.BigAdd(collateral, types.BigDiv(collateral, types.NewInt(100))),
Method: actors.SPAMethods.CreateStorageMiner,
Params: params,
+262
View File
@@ -0,0 +1,262 @@
package main
import (
"context"
"errors"
"os"
"os/signal"
"syscall"
"time"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/lib/jsonrpc"
cid "github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log"
"gopkg.in/urfave/cli.v2"
)
type CidWindow [][]cid.Cid
var log = logging.Logger("lotus-health")
func main() {
logging.SetLogLevel("*", "INFO")
log.Info("Starting health agent")
local := []*cli.Command{
watchHeadCmd,
}
app := &cli.App{
Name: "lotus-health",
Usage: "Tools for monitoring lotus daemon health",
Version: build.UserVersion,
Commands: local,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
EnvVars: []string{"LOTUS_PATH"},
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
},
},
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
return
}
}
var watchHeadCmd = &cli.Command{
Name: "watch-head",
Flags: []cli.Flag{
&cli.IntFlag{
Name: "threshold",
Value: 3,
Usage: "number of times head remains unchanged before failing health check",
},
&cli.IntFlag{
Name: "interval",
Value: build.BlockDelay,
Usage: "interval in seconds between chain head checks",
},
&cli.StringFlag{
Name: "systemd-unit",
Value: "lotus-daemon.service",
Usage: "systemd unit name to restart on health check failure",
},
&cli.IntFlag{
Name: "api-timeout",
Value: build.BlockDelay,
Usage: "timeout between API retries",
},
&cli.IntFlag{
Name: "api-retries",
Value: 8,
Usage: "number of API retry attemps",
},
},
Action: func(c *cli.Context) error {
var headCheckWindow CidWindow
threshold := c.Int("threshold")
interval := time.Duration(c.Int("interval")) * time.Second
name := c.String("systemd-unit")
apiRetries := c.Int("api-retries")
apiTimeout := time.Duration(c.Int("api-timeout")) * time.Second
nCh := make(chan interface{}, 1)
sCh := make(chan os.Signal, 1)
signal.Notify(sCh, os.Interrupt, syscall.SIGTERM)
api, closer, err := getFullNodeAPI(c, apiRetries, apiTimeout)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(c)
go func() {
for {
log.Info("Waiting for sync to complete")
if err := waitForSyncComplete(ctx, api, apiRetries, apiTimeout); err != nil {
nCh <- err
return
}
headCheckWindow, err = updateWindow(ctx, api, headCheckWindow, threshold, apiRetries, apiTimeout)
if err != nil {
log.Warn("Failed to connect to API. Restarting systemd service")
nCh <- nil
return
}
ok := checkWindow(headCheckWindow, threshold)
if !ok {
log.Warn("Chain head has not updated. Restarting systemd service")
nCh <- nil
break
}
log.Info("Chain head is healthy")
time.Sleep(interval)
}
return
}()
restart, err := notifyHandler(name, nCh, sCh)
if err != nil {
return err
}
if restart != "done" {
return errors.New("Systemd unit failed to restart:" + restart)
}
log.Info("Restarting health agent")
// Exit health agent and let supervisor restart health agent
// Restarting lotus systemd unit kills api connection
os.Exit(130)
return nil
},
}
/*
* reads channel of slices of Cids
* compares slices of Cids when len is greater or equal to `t` - threshold
* if all slices are equal, head has not updated and returns false
*/
func checkWindow(window CidWindow, t int) bool {
var dup int
windowLen := len(window)
if windowLen >= t {
cidWindow:
for i := range window {
next := windowLen - 1 - i
// if array length is different, head is changing
if next >= 1 && len(window[next]) != len(window[next-1]) {
break cidWindow
}
// if cids are different, head is changing
for j := range window[next] {
if next >= 1 && window[next][j] != window[next-1][j] {
break cidWindow
}
}
if i < (t - 1) {
dup++
}
}
if dup == (t - 1) {
return false
}
}
return true
}
/*
* returns a slice of slices of Cids
* len of slice <= `t` - threshold
*/
func updateWindow(ctx context.Context, a api.FullNode, w CidWindow, t int, r int, to time.Duration) (CidWindow, error) {
head, err := getHead(ctx, a, r, to)
if err != nil {
return nil, err
}
window := appendCIDsToWindow(w, head.Cids(), t)
return window, err
}
/*
* get chain head from API
* retries if API no available
* returns tipset
*/
func getHead(ctx context.Context, a api.FullNode, r int, t time.Duration) (*types.TipSet, error) {
for i := 0; i < r; i++ {
head, err := a.ChainHead(ctx)
if err != nil && i == (r-1) {
return nil, err
}
if err != nil {
log.Warnf("Call to API failed. Retrying in %.0fs", t.Seconds())
time.Sleep(t)
continue
}
return head, err
}
return nil, nil
}
/*
* appends slice of Cids to window slice
* keeps a fixed window slice size, dropping older slices
* returns new window
*/
func appendCIDsToWindow(w CidWindow, c []cid.Cid, t int) CidWindow {
offset := len(w) - t + 1
if offset >= 0 {
return append(w[offset:], c)
}
return append(w, c)
}
/*
* wait for node to sync
*/
func waitForSyncComplete(ctx context.Context, a api.FullNode, r int, t time.Duration) error {
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(3 * time.Second):
head, err := getHead(ctx, a, r, t)
if err != nil {
return err
}
if time.Now().Unix()-int64(head.MinTimestamp()) < build.BlockDelay {
return nil
}
}
}
}
/*
* A thin wrapper around lotus cli GetFullNodeAPI
* Adds retry logic
*/
func getFullNodeAPI(ctx *cli.Context, r int, t time.Duration) (api.FullNode, jsonrpc.ClientCloser, error) {
for i := 0; i < r; i++ {
api, closer, err := lcli.GetFullNodeAPI(ctx)
if err != nil && i == (r-1) {
return nil, nil, err
}
if err != nil {
log.Warnf("API connection failed. Retrying in %.0fs", t.Seconds())
time.Sleep(t)
continue
}
return api, closer, err
}
return nil, nil, nil
}
+166
View File
@@ -0,0 +1,166 @@
package main
import (
"testing"
cid "github.com/ipfs/go-cid"
mh "github.com/multiformats/go-multihash"
"github.com/stretchr/testify/assert"
)
func TestAppendCIDsToWindow(t *testing.T) {
assert := assert.New(t)
var window CidWindow
threshold := 3
cid0 := makeCID("0")
cid1 := makeCID("1")
cid2 := makeCID("2")
cid3 := makeCID("3")
window = appendCIDsToWindow(window, []cid.Cid{cid0}, threshold)
window = appendCIDsToWindow(window, []cid.Cid{cid1}, threshold)
window = appendCIDsToWindow(window, []cid.Cid{cid2}, threshold)
window = appendCIDsToWindow(window, []cid.Cid{cid3}, threshold)
assert.Len(window, 3)
assert.Equal(window[0][0], cid1)
assert.Equal(window[1][0], cid2)
assert.Equal(window[2][0], cid3)
}
func TestCheckWindow(t *testing.T) {
assert := assert.New(t)
threshold := 3
var healthyHeadCheckWindow CidWindow
healthyHeadCheckWindow = appendCIDsToWindow(healthyHeadCheckWindow, []cid.Cid{
makeCID("abcd"),
}, threshold)
healthyHeadCheckWindow = appendCIDsToWindow(healthyHeadCheckWindow, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
}, threshold)
healthyHeadCheckWindow = appendCIDsToWindow(healthyHeadCheckWindow, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
}, threshold)
ok := checkWindow(healthyHeadCheckWindow, threshold)
assert.True(ok)
var healthyHeadCheckWindow1 CidWindow
healthyHeadCheckWindow1 = appendCIDsToWindow(healthyHeadCheckWindow1, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
}, threshold)
healthyHeadCheckWindow1 = appendCIDsToWindow(healthyHeadCheckWindow1, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
makeCID("abcd"),
}, threshold)
healthyHeadCheckWindow1 = appendCIDsToWindow(healthyHeadCheckWindow1, []cid.Cid{
makeCID("abcd"),
}, threshold)
ok = checkWindow(healthyHeadCheckWindow1, threshold)
assert.True(ok)
var healthyHeadCheckWindow2 CidWindow
healthyHeadCheckWindow2 = appendCIDsToWindow(healthyHeadCheckWindow2, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
}, threshold)
healthyHeadCheckWindow2 = appendCIDsToWindow(healthyHeadCheckWindow2, []cid.Cid{
makeCID("abcd"),
}, threshold)
ok = checkWindow(healthyHeadCheckWindow2, threshold)
assert.True(ok)
var healthyHeadCheckWindow3 CidWindow
healthyHeadCheckWindow3 = appendCIDsToWindow(healthyHeadCheckWindow3, []cid.Cid{
makeCID("abcd"),
}, threshold)
healthyHeadCheckWindow3 = appendCIDsToWindow(healthyHeadCheckWindow3, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
}, threshold)
ok = checkWindow(healthyHeadCheckWindow3, threshold)
assert.True(ok)
var healthyHeadCheckWindow4 CidWindow
healthyHeadCheckWindow4 = appendCIDsToWindow(healthyHeadCheckWindow4, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
}, threshold)
ok = checkWindow(healthyHeadCheckWindow4, threshold)
assert.True(ok)
var healthyHeadCheckWindow5 CidWindow
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
makeCID("bbff"),
}, 5)
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
makeCID("bbcd"),
makeCID("bbfe"),
}, 5)
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
makeCID("abcd"),
}, 5)
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
makeCID("cbcd"),
makeCID("cbfe"),
}, 5)
healthyHeadCheckWindow5 = appendCIDsToWindow(healthyHeadCheckWindow5, []cid.Cid{
makeCID("cbcd"),
makeCID("cbfe"),
}, 5)
ok = checkWindow(healthyHeadCheckWindow5, threshold)
assert.True(ok)
var unhealthyHeadCheckWindow CidWindow
unhealthyHeadCheckWindow = appendCIDsToWindow(unhealthyHeadCheckWindow, []cid.Cid{
makeCID("abcd"),
makeCID("fbcd"),
}, threshold)
unhealthyHeadCheckWindow = appendCIDsToWindow(unhealthyHeadCheckWindow, []cid.Cid{
makeCID("abcd"),
makeCID("fbcd"),
}, threshold)
unhealthyHeadCheckWindow = appendCIDsToWindow(unhealthyHeadCheckWindow, []cid.Cid{
makeCID("abcd"),
makeCID("fbcd"),
}, threshold)
ok = checkWindow(unhealthyHeadCheckWindow, threshold)
assert.False(ok)
var unhealthyHeadCheckWindow1 CidWindow
unhealthyHeadCheckWindow1 = appendCIDsToWindow(unhealthyHeadCheckWindow1, []cid.Cid{
makeCID("abcd"),
makeCID("fbcd"),
}, threshold)
unhealthyHeadCheckWindow1 = appendCIDsToWindow(unhealthyHeadCheckWindow1, []cid.Cid{
makeCID("abcd"),
makeCID("fbcd"),
}, threshold)
ok = checkWindow(unhealthyHeadCheckWindow1, threshold)
assert.True(ok)
var unhealthyHeadCheckWindow2 CidWindow
unhealthyHeadCheckWindow2 = appendCIDsToWindow(unhealthyHeadCheckWindow2, []cid.Cid{
makeCID("abcd"),
}, threshold)
unhealthyHeadCheckWindow2 = appendCIDsToWindow(unhealthyHeadCheckWindow2, []cid.Cid{
makeCID("abcd"),
}, threshold)
unhealthyHeadCheckWindow2 = appendCIDsToWindow(unhealthyHeadCheckWindow2, []cid.Cid{
makeCID("abcd"),
}, threshold)
ok = checkWindow(unhealthyHeadCheckWindow2, threshold)
assert.False(ok)
}
func makeCID(s string) cid.Cid {
h1, err := mh.Sum([]byte(s), mh.SHA2_256, -1)
if err != nil {
log.Fatal(err)
}
return cid.NewCidV1(0x55, h1)
}
+31
View File
@@ -0,0 +1,31 @@
package main
import (
"os"
"github.com/coreos/go-systemd/dbus"
)
func notifyHandler(n string, ch chan interface{}, sCh chan os.Signal) (string, error) {
select {
// alerts to restart systemd unit
case <-ch:
statusCh := make(chan string, 1)
c, err := dbus.New()
if err != nil {
return "", err
}
_, err = c.TryRestartUnit(n, "fail", statusCh)
if err != nil {
return "", err
}
select {
case result := <-statusCh:
return result, nil
}
// SIGTERM
case <-sCh:
os.Exit(1)
return "", nil
}
}
+8
View File
@@ -116,6 +116,10 @@ func (w *worker) processTask(ctx context.Context, task sectorbuilder.WorkerTask)
if err := w.push("cache", task.SectorID); err != nil {
return errRes(xerrors.Errorf("pushing precommited data: %w", err))
}
if err := w.remove("staging", task.SectorID); err != nil {
return errRes(xerrors.Errorf("cleaning up staged sector: %w", err))
}
case sectorbuilder.WorkerCommit:
proof, err := w.sb.SealCommit(ctx, task.SectorID, task.SealTicket, task.SealSeed, task.Pieces, task.Rspco)
if err != nil {
@@ -127,6 +131,10 @@ func (w *worker) processTask(ctx context.Context, task sectorbuilder.WorkerTask)
if err := w.push("cache", task.SectorID); err != nil {
return errRes(xerrors.Errorf("pushing precommited data: %w", err))
}
if err := w.remove("sealed", task.SectorID); err != nil {
return errRes(xerrors.Errorf("cleaning up sealed sector: %w", err))
}
}
return res
+15 -2
View File
@@ -15,6 +15,14 @@ import (
"github.com/filecoin-project/lotus/lib/tarutil"
)
func (w *worker) sizeForType(typ string) int64 {
size := int64(w.sb.SectorSize())
if typ == "cache" {
size *= 10
}
return size
}
func (w *worker) fetch(typ string, sectorID uint64) error {
outname := filepath.Join(w.repo, typ, w.sb.SectorName(sectorID))
@@ -37,7 +45,7 @@ func (w *worker) fetch(typ string, sectorID uint64) error {
return xerrors.Errorf("non-200 code: %d", resp.StatusCode)
}
bar := pb.New64(resp.ContentLength)
bar := pb.New64(w.sizeForType(typ))
bar.ShowPercent = true
bar.ShowSpeed = true
bar.Units = pb.U_BYTES
@@ -88,7 +96,7 @@ func (w *worker) push(typ string, sectorID uint64) error {
return xerrors.Errorf("opening push reader: %w", err)
}
bar := pb.New64(0)
bar := pb.New64(w.sizeForType(typ))
bar.ShowPercent = true
bar.ShowSpeed = true
bar.ShowCounters = true
@@ -125,6 +133,11 @@ func (w *worker) push(typ string, sectorID uint64) error {
}
// TODO: keep files around for later stages of sealing
return w.remove(typ, sectorID)
}
func (w *worker) remove(typ string, sectorID uint64) error {
filename := filepath.Join(w.repo, typ, w.sb.SectorName(sectorID))
return os.RemoveAll(filename)
}
+12 -11
View File
@@ -11,7 +11,12 @@ import (
"path/filepath"
"strconv"
"github.com/filecoin-project/go-address"
cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/go-fil-markets/storagemarket"
deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
paramfetch "github.com/filecoin-project/go-paramfetch"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
badger "github.com/ipfs/go-ds-badger2"
@@ -21,11 +26,6 @@ import (
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-address"
cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/go-fil-markets/storagemarket"
deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
"github.com/filecoin-project/go-sectorbuilder"
lapi "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
@@ -38,6 +38,7 @@ import (
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/repo"
"github.com/filecoin-project/lotus/storage"
"github.com/filecoin-project/lotus/storage/sealing"
)
var initCmd = &cli.Command{
@@ -242,17 +243,17 @@ func migratePreSealMeta(ctx context.Context, api lapi.FullNode, presealDir strin
}
for _, sector := range meta.Sectors {
sectorKey := datastore.NewKey(storage.SectorStorePrefix).ChildString(fmt.Sprint(sector.SectorID))
sectorKey := datastore.NewKey(sealing.SectorStorePrefix).ChildString(fmt.Sprint(sector.SectorID))
dealID, err := findMarketDealID(ctx, api, sector.Deal)
if err != nil {
return xerrors.Errorf("finding storage deal for pre-sealed sector %d: %w", sector.SectorID, err)
}
info := &storage.SectorInfo{
info := &sealing.SectorInfo{
State: lapi.Proving,
SectorID: sector.SectorID,
Pieces: []storage.Piece{
Pieces: []sealing.Piece{
{
DealID: dealID,
Size: meta.SectorSize,
@@ -262,9 +263,9 @@ func migratePreSealMeta(ctx context.Context, api lapi.FullNode, presealDir strin
CommD: sector.CommD[:],
CommR: sector.CommR[:],
Proof: nil,
Ticket: storage.SealTicket{},
Ticket: sealing.SealTicket{},
PreCommitMessage: nil,
Seed: storage.SealSeed{},
Seed: sealing.SealSeed{},
CommitMessage: nil,
}
@@ -556,7 +557,7 @@ func createStorageMiner(ctx context.Context, api lapi.FullNode, peerid peer.ID,
createStorageMinerMsg := &types.Message{
To: actors.StoragePowerAddress,
From: owner,
Value: collateral,
Value: types.BigAdd(collateral, types.BigDiv(collateral, types.NewInt(100))),
Method: actors.SPAMethods.CreateStorageMiner,
Params: params,
+26 -3
View File
@@ -2,8 +2,11 @@ package main
import (
"fmt"
"os"
"sort"
"strconv"
"text/tabwriter"
"time"
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
@@ -41,6 +44,12 @@ var sectorsCmd = &cli.Command{
var sectorsStatusCmd = &cli.Command{
Name: "status",
Usage: "Get the seal status of a sector by its ID",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "log",
Usage: "display event log",
},
},
Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
@@ -77,6 +86,17 @@ var sectorsStatusCmd = &cli.Command{
if status.LastErr != "" {
fmt.Printf("Last Error:\t\t%s\n", status.LastErr)
}
if cctx.Bool("log") {
fmt.Printf("--------\nEvent Log:\n")
for i, l := range status.Log {
fmt.Printf("%d.\t%s:\t[%s]\t%s\n", i, time.Unix(int64(l.Timestamp), 0), l.Kind, l.Message)
if l.Trace != "" {
fmt.Printf("\t%s\n", l.Trace)
}
}
}
return nil
},
}
@@ -131,17 +151,19 @@ var sectorsListCmd = &cli.Command{
return list[i] < list[j]
})
w := tabwriter.NewWriter(os.Stdout, 8, 4, 1, ' ', 0)
for _, s := range list {
st, err := nodeApi.SectorsStatus(ctx, s)
if err != nil {
fmt.Printf("%d:\tError: %s\n", s, err)
fmt.Fprintf(w, "%d:\tError: %s\n", s, err)
continue
}
_, inSSet := commitedIDs[s]
_, inPSet := provingIDs[s]
fmt.Printf("%d: %s\tsSet: %s\tpSet: %s\ttktH: %d\tseedH: %d\tdeals: %v\n",
fmt.Fprintf(w, "%d: %s\tsSet: %s\tpSet: %s\ttktH: %d\tseedH: %d\tdeals: %v\n",
s,
api.SectorStates[st.State],
yesno(inSSet),
@@ -151,7 +173,8 @@ var sectorsListCmd = &cli.Command{
st.Deals,
)
}
return nil
return w.Flush()
},
}
+88
View File
@@ -5,14 +5,21 @@ package main
import (
"context"
"io/ioutil"
"os"
"runtime/pprof"
paramfetch "github.com/filecoin-project/go-paramfetch"
"github.com/filecoin-project/go-sectorbuilder"
blockstore "github.com/ipfs/go-ipfs-blockstore"
"github.com/multiformats/go-multiaddr"
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/modules"
"github.com/filecoin-project/lotus/node/modules/testing"
@@ -56,8 +63,32 @@ var DaemonCmd = &cli.Command{
Name: "bootstrap",
Value: true,
},
&cli.StringFlag{
Name: "import-chain",
Usage: "on first run, load chain from given file",
},
&cli.BoolFlag{
Name: "halt-after-import",
Usage: "halt the process after importing chain from file",
},
&cli.StringFlag{
Name: "pprof",
Usage: "specify name of file for writing cpu profile to",
},
},
Action: func(cctx *cli.Context) error {
if prof := cctx.String("pprof"); prof != "" {
profile, err := os.Create(prof)
if err != nil {
return err
}
if err := pprof.StartCPUProfile(profile); err != nil {
return err
}
defer pprof.StopCPUProfile()
}
ctx := context.Background()
r, err := repo.NewFS(cctx.String("repo"))
if err != nil {
@@ -81,6 +112,16 @@ var DaemonCmd = &cli.Command{
}
}
chainfile := cctx.String("import-chain")
if chainfile != "" {
if err := ImportChain(r, chainfile); err != nil {
return err
}
if cctx.Bool("halt-after-import") {
return nil
}
}
genesis := node.Options()
if len(genBytes) > 0 {
genesis = node.Override(new(modules.Genesis), modules.LoadGenesis(genBytes))
@@ -129,3 +170,50 @@ var DaemonCmd = &cli.Command{
return serveRPC(api, stop, endpoint)
},
}
func ImportChain(r repo.Repo, fname string) error {
fi, err := os.Open(fname)
if err != nil {
return err
}
lr, err := r.Lock(repo.FullNode)
if err != nil {
return err
}
defer lr.Close()
ds, err := lr.Datastore("/blocks")
if err != nil {
return err
}
mds, err := lr.Datastore("/metadata")
if err != nil {
return err
}
bs := blockstore.NewBlockstore(ds)
cst := store.NewChainStore(bs, mds, vm.Syscalls(sectorbuilder.ProofVerifier))
log.Info("importing chain from file...")
ts, err := cst.Import(fi)
if err != nil {
return xerrors.Errorf("importing chain failed: %w", err)
}
stm := stmgr.NewStateManager(cst)
log.Infof("validating imported chain...")
if err := stm.ValidateChain(context.TODO(), ts); err != nil {
return xerrors.Errorf("chain validation failed: %w", err)
}
log.Info("accepting %s as new head", ts.Cids())
if err := cst.SetHead(ts); err != nil {
return err
}
return nil
}
+17 -6
View File
@@ -10,8 +10,9 @@ import (
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/blocksync"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/statemachine"
"github.com/filecoin-project/lotus/paych"
"github.com/filecoin-project/lotus/storage"
"github.com/filecoin-project/lotus/storage/sealing"
)
func main() {
@@ -121,14 +122,24 @@ func main() {
os.Exit(1)
}
err = gen.WriteMapEncodersToFile("./storage/cbor_gen.go", "storage",
storage.SealTicket{},
storage.SealSeed{},
storage.Piece{},
storage.SectorInfo{},
err = gen.WriteMapEncodersToFile("./storage/sealing/cbor_gen.go", "sealing",
sealing.SealTicket{},
sealing.SealSeed{},
sealing.Piece{},
sealing.SectorInfo{},
sealing.Log{},
)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = gen.WriteMapEncodersToFile("./lib/statemachine/cbor_gen.go", "statemachine",
statemachine.TestState{},
statemachine.TestEvent{},
)
if err != nil {
fmt.Printf("%+v\n", err)
os.Exit(1)
}
}
+9 -5
View File
@@ -8,17 +8,19 @@ require (
github.com/GeertJohan/go.rice v1.0.0
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/coreos/go-systemd v0.0.0-00010101000000-000000000000
github.com/docker/go-units v0.4.0
github.com/filecoin-project/chain-validation v0.0.3
github.com/filecoin-project/filecoin-ffi v0.0.0-20191219131535-bb699517a590
github.com/filecoin-project/go-address v0.0.0-20191219011437-af739c490b4f
github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5
github.com/filecoin-project/go-amt-ipld v0.0.0-20191205011053-79efc22d6cdc
github.com/filecoin-project/go-amt-ipld/v2 v2.0.0
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
github.com/filecoin-project/go-data-transfer v0.0.0-20191219005021-4accf56bd2ce
github.com/filecoin-project/go-fil-markets v0.0.0-20200114015428-74d100f305f8
github.com/filecoin-project/go-paramfetch v0.0.1
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200114015900-4103afa82689
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200123143044-d9cc96c53c55
github.com/filecoin-project/go-statestore v0.1.0
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
github.com/go-ole/go-ole v1.2.4 // indirect
@@ -30,10 +32,10 @@ require (
github.com/ipfs/go-bitswap v0.1.8
github.com/ipfs/go-block-format v0.0.2
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c
github.com/ipfs/go-car v0.0.3-0.20191203022317-23b0a85fd1b1
github.com/ipfs/go-car v0.0.3-0.20200121013634-f188c0e24291
github.com/ipfs/go-cid v0.0.4
github.com/ipfs/go-datastore v0.3.1
github.com/ipfs/go-ds-badger2 v0.0.0-20200108185345-7f650e6b2521
github.com/ipfs/go-ds-badger2 v0.0.0-20200123200730-d75eb2678a5d
github.com/ipfs/go-filestore v0.0.2
github.com/ipfs/go-fs-lock v0.0.1
github.com/ipfs/go-graphsync v0.0.4
@@ -84,7 +86,7 @@ require (
github.com/prometheus/common v0.2.0
github.com/stretchr/testify v1.4.0
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba
github.com/whyrusleeping/cbor-gen v0.0.0-20200106232624-282db0d37dbe
github.com/whyrusleeping/cbor-gen v0.0.0-20200121162646-b63bacf5eaf8
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
github.com/whyrusleeping/pubsub v0.0.0-20131020042734-02de8aa2db3d
go.opencensus.io v0.22.2
@@ -109,3 +111,5 @@ require (
replace github.com/golangci/golangci-lint => github.com/golangci/golangci-lint v1.18.0
replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi
replace github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
+22 -51
View File
@@ -3,14 +3,12 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
contrib.go.opencensus.io/exporter/jaeger v0.1.0 h1:WNc9HbA38xEQmsI40Tjd/MNU/g8byN2Of7lwIjv0Jdc=
contrib.go.opencensus.io/exporter/jaeger v0.1.0/go.mod h1:VYianECmuFPwU37O699Vc1GOcy+y8kOsfaxHRImmjbA=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
@@ -61,6 +59,8 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-semver v0.2.1-0.20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
@@ -77,10 +77,10 @@ github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzA
github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgraph-io/badger v1.6.0 h1:DshxFxZWXUcO0xX476VJC07Xsr6ZCBVRHKZ93Oh7Evo=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgraph-io/badger/v2 v2.0.1 h1:+D6dhIqC6jIeCclnxMHqk4HPuXgrRN5UfBsLR4dNQ3A=
github.com/dgraph-io/badger/v2 v2.0.1/go.mod h1:YoRSIp1LmAJ7zH7tZwRvjNMUYLxB4wl3ebYkaIruZ04=
github.com/dgraph-io/ristretto v0.0.0-20191025175511-c1f00be0418e h1:aeUNgwup7PnDOBAD1BOKAqzb/W/NksOj6r3dwKKuqfg=
github.com/dgraph-io/ristretto v0.0.0-20191025175511-c1f00be0418e/go.mod h1:edzKIzGvqUCMzhTVWbiTSe75zD9Xxq0GtSBtFmaUTZs=
github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20200120142413-c3333a5a830e h1:Jz7uYxTCDVrtL5tzPxPu6o7Ybhom8Az7sWmjUO1OkQc=
github.com/dgraph-io/badger/v2 v2.0.1-rc1.0.20200120142413-c3333a5a830e/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM=
github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3 h1:MQLRM35Pp0yAyBYksjbj1nZI/w6eyRY/mWoM1sFf4kU=
github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
@@ -91,7 +91,6 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.8.0 h1:5bzFgL+oy7JITMTxUPJ00n7VxmYd/PdMp5mHFX40/RY=
github.com/fatih/color v1.8.0/go.mod h1:3l45GVGkyrnYNl9HoIjnp2NnNWvh6hLAqD8yTfGjnw8=
@@ -100,35 +99,26 @@ github.com/filecoin-project/chain-validation v0.0.3 h1:luT/8kJ0WdMIqQ9Bm31W4JkuY
github.com/filecoin-project/chain-validation v0.0.3/go.mod h1:NCEGFjcWRjb8akWFSOXvU6n2efkWIqAeOKU6o5WBGQw=
github.com/filecoin-project/go-address v0.0.0-20191219011437-af739c490b4f h1:L2jaVU8TvWTx7iZPhlYvUE8vkoOnj778XuKavz8W36g=
github.com/filecoin-project/go-address v0.0.0-20191219011437-af739c490b4f/go.mod h1:rCbpXPva2NKF9/J4X6sr7hbKBgQCxyFtRj7KOZqoIms=
github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5 h1:/MmWluswvDIbuPvBct4q6HeQgVm62O2DzWYTB38kt4A=
github.com/filecoin-project/go-address v0.0.0-20200107215422-da8eea2842b5/go.mod h1:SAOwJoakQ8EPjwNIsiakIQKsoKdkcbx8U3IapgCg9R0=
github.com/filecoin-project/go-amt-ipld v0.0.0-20191205011053-79efc22d6cdc h1:cODZD2YzpTUtrOSxbEnWFcQHidNRZiRdvLxySjGvG/M=
github.com/filecoin-project/go-amt-ipld v0.0.0-20191205011053-79efc22d6cdc/go.mod h1:KsFPWjF+UUYl6n9A+qbg4bjFgAOneicFZtDH/LQEX2U=
github.com/filecoin-project/go-amt-ipld/v2 v2.0.0 h1:rQ7GTJbWE5XqUCPiBgs4gbTKD2wC7MynZhSaf7ZzlUM=
github.com/filecoin-project/go-amt-ipld/v2 v2.0.0/go.mod h1:PAZ5tvSfMfWE327osqFXKm7cBpCpBk2Nh0qKsJUmjjk=
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:av5fw6wmm58FYMgJeoB/lK9XXrgdugYiTqkdxjTy9k8=
github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
github.com/filecoin-project/go-data-transfer v0.0.0-20191219005021-4accf56bd2ce h1:Jdejrx6XVSTRy2PiX08HCU5y68p3wx2hNMJJc/J7kZY=
github.com/filecoin-project/go-data-transfer v0.0.0-20191219005021-4accf56bd2ce/go.mod h1:b14UWxhxVCAjrQUYvVGrQRRsjAh79wXYejw9RbUcAww=
github.com/filecoin-project/go-fil-markets v0.0.0-20200114015428-74d100f305f8 h1:g3oodvSz+Ou+ObwcVBB2wyt8SHdWpwzMiNJ19U1zZNA=
github.com/filecoin-project/go-fil-markets v0.0.0-20200114015428-74d100f305f8/go.mod h1:c8NTjvFVy1Ud02mmGDjOiMeawY2t6ALfrrdvAB01FQc=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878 h1:YicJT9xhPzZ1SBGiJFNUCkfwqK/G9vFyY1ytKBSjNJA=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
github.com/filecoin-project/go-paramfetch v0.0.0-20200102181131-b20d579f2878/go.mod h1:40kI2Gv16mwcRsHptI3OAV4nlOEU7wVDc4RgMylNFjU=
github.com/filecoin-project/go-paramfetch v0.0.1 h1:gV7bs5YaqlgpGFMiLxInGK2L1FyCXUE0rimz4L7ghoE=
github.com/filecoin-project/go-paramfetch v0.0.1/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc=
github.com/filecoin-project/go-sectorbuilder v0.0.1 h1:yiLSEprWA1E43DFTSCXLSuCstYuDKiI6RCXiYz4GaRs=
github.com/filecoin-project/go-sectorbuilder v0.0.1/go.mod h1:3OZ4E3B2OuwhJjtxR4r7hPU9bCfB+A+hm4alLEsaeDc=
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200114015900-4103afa82689 h1:2cT5bhm/5I0RY+HBIPdRRrtjCwLj33Qx6DHRs9TCslY=
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200114015900-4103afa82689/go.mod h1:3OZ4E3B2OuwhJjtxR4r7hPU9bCfB+A+hm4alLEsaeDc=
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200123143044-d9cc96c53c55 h1:XChPRKPZL+/N6a3ccLmjCJ7JrR+SFLFJDllv0BkxW4I=
github.com/filecoin-project/go-sectorbuilder v0.0.2-0.20200123143044-d9cc96c53c55/go.mod h1:ahsryULdwYoZ94K09HcfqX3QBwevWVldENSV/EdCbNg=
github.com/filecoin-project/go-statestore v0.1.0 h1:t56reH59843TwXHkMcwyuayStBIiWBRilQjQ+5IiwdQ=
github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
@@ -142,6 +132,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -219,8 +211,9 @@ github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbR
github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c h1:lN5IQA07VtLiTLAp/Scezp1ljFhXErC6yq4O1cu+yJ0=
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I=
github.com/ipfs/go-car v0.0.3-0.20191203022317-23b0a85fd1b1 h1:Nq8xEW+2KZq7IkRlkOh0rTEUI8FgunhMoLj5EMkJzbQ=
github.com/ipfs/go-car v0.0.3-0.20191203022317-23b0a85fd1b1/go.mod h1:rmd887mJxQRDfndfDEY3Liyx8gQVyfFFRSHdsnDSAlk=
github.com/ipfs/go-car v0.0.3-0.20200121013634-f188c0e24291 h1:Yy0dcFWw8oDV/WJ4S/rkMQRWnJ3tGr9EbgDDv2JhVQw=
github.com/ipfs/go-car v0.0.3-0.20200121013634-f188c0e24291/go.mod h1:AG6sBpd2PWMccpAG7XLFBBQ/4rfBEtzUNeO2GSMesYk=
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
@@ -230,7 +223,6 @@ github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj
github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
github.com/ipfs/go-datastore v0.1.1 h1:F4k0TkTAZGLFzBOrVKDAvch6JZtuN4NHkfdcEZL50aI=
github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
github.com/ipfs/go-datastore v0.3.1 h1:SS1t869a6cctoSYmZXUk8eL6AzVXgASmKIWFNQkQ1jU=
github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw=
@@ -238,10 +230,9 @@ github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=
github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s=
github.com/ipfs/go-ds-badger v0.0.7 h1:NMyh88Q50HG6/S2YD58DLkq0c0/ZQPMbSojONH+PRf4=
github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk=
github.com/ipfs/go-ds-badger2 v0.0.0-20200108185345-7f650e6b2521 h1:GSygNXXIRXFVntMR3Yr30imf40H9cYDAw72Qb0za2nE=
github.com/ipfs/go-ds-badger2 v0.0.0-20200108185345-7f650e6b2521/go.mod h1:oDKWqmQbLyyml3mgMtHgB7qAf6cQOEsJPRLFzfwKQ5Q=
github.com/ipfs/go-ds-badger2 v0.0.0-20200123200730-d75eb2678a5d h1:/Pn9fMp6ih2M/3vDeA2H+8mkTFQSpLxFuIM1HJD3Zlw=
github.com/ipfs/go-ds-badger2 v0.0.0-20200123200730-d75eb2678a5d/go.mod h1:sTQFaWUoW0OvhXzfHnQ9j39L6fdlqDkptDYcpC1XrYE=
github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc=
github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8=
github.com/ipfs/go-filestore v0.0.2 h1:pcYwpjtXXwirtbjBXKVJM9CTa9F7/8v1EkfnDaHTO3s=
@@ -289,15 +280,9 @@ github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dC
github.com/ipfs/go-ipld-format v0.0.2 h1:OVAGlyYT6JPZ0pEfGntFPS40lfrDmaDbQwNHEY2G9Zs=
github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k=
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
github.com/ipfs/go-log v1.0.0 h1:BW3LQIiZzpNyolt84yvKNCd3FU+AK4VDw1hnHR+1aiI=
github.com/ipfs/go-log v1.0.0/go.mod h1:JO7RzlMK6rA+CIxFMLOuB6Wf5b81GDiKElL7UPSIKjA=
github.com/ipfs/go-log v1.0.1 h1:5lIEEOQTk/vd1WuPFBRqz2mcp+5G1fMVcW+Ib/H5Hfo=
github.com/ipfs/go-log v1.0.1 h1:5lIEEOQTk/vd1WuPFBRqz2mcp+5G1fMVcW+Ib/H5Hfo=
github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I=
github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I=
github.com/ipfs/go-log/v2 v2.0.1 h1:mnR9XFltezAtO8A6tj5U7nKkRzhEQNEw/wT11U2HhPM=
github.com/ipfs/go-log/v2 v2.0.1 h1:mnR9XFltezAtO8A6tj5U7nKkRzhEQNEw/wT11U2HhPM=
github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
github.com/ipfs/go-log/v2 v2.0.2 h1:xguurydRdfKMJjKyxNXNU8lYP0VZH1NUwJRwUorjuEw=
github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
@@ -320,6 +305,7 @@ github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb h1:tmWYgjltxwM7PD
github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k=
github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E=
github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0=
github.com/ipld/go-ipld-prime v0.0.1/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w=
github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785 h1:fASnkvtR+SmB2y453RxmDD3Uvd4LonVUgFGk9JoDaZs=
github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w=
github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5 h1:lSip43rAdyGA+yRQuy6ju0ucZkWpYc1F2CTQtZTVW/4=
@@ -457,7 +443,6 @@ github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4T
github.com/libp2p/go-libp2p-peerstore v0.0.6/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20=
github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY=
github.com/libp2p/go-libp2p-peerstore v0.1.2/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI=
github.com/libp2p/go-libp2p-peerstore v0.1.3 h1:wMgajt1uM2tMiqf4M+4qWKVyyFc8SfA+84VV9glZq1M=
github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI=
github.com/libp2p/go-libp2p-peerstore v0.1.4 h1:d23fvq5oYMJ/lkkbO4oTwBp/JP+I/1m5gZJobNXCE/k=
github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs=
@@ -479,7 +464,6 @@ github.com/libp2p/go-libp2p-routing-helpers v0.1.0/go.mod h1:oUs0h39vNwYtYXnQWOT
github.com/libp2p/go-libp2p-secio v0.0.3/go.mod h1:hS7HQ00MgLhRO/Wyu1bTX6ctJKhVpm+j2/S2A5UqYb0=
github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8=
github.com/libp2p/go-libp2p-secio v0.1.1/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8=
github.com/libp2p/go-libp2p-secio v0.2.0 h1:ywzZBsWEEz2KNTn5RtzauEDq5RFEefPsttXYwAWqHng=
github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g=
github.com/libp2p/go-libp2p-secio v0.2.1 h1:eNWbJTdyPA7NxhP7J3c5lT97DC5d+u+IldkgCYFTPVA=
github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8=
@@ -530,7 +514,6 @@ github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FW
github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
github.com/libp2p/go-reuseport-transport v0.0.2 h1:WglMwyXyBu61CMkjCCtnmqNqnjib0GIEjMiHTwR/KN4=
github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs=
github.com/libp2p/go-stream-muxer v0.0.1 h1:Ce6e2Pyu+b5MC1k3eeFtAax0pW4gc6MosYSLV05UeLw=
github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14=
github.com/libp2p/go-stream-muxer v0.1.0/go.mod h1:8JAVsjeRBCWwPoZeH0W1imLOcriqXJyFvB0mR4A04sQ=
github.com/libp2p/go-stream-muxer-multistream v0.1.1/go.mod h1:zmGdfkQ1AzOECIAcccoL8L//laqawOsO03zX8Sa+eGw=
@@ -558,7 +541,6 @@ github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNA
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -716,7 +698,6 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
github.com/urfave/cli/v2 v2.0.0 h1:+HU9SCbu8GnEUFtIBfuUNXN39ofWViIEJIp6SURMpCg=
github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
@@ -731,14 +712,14 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20190910031516-c1cbffdb01bb/go.mod h1:x
github.com/whyrusleeping/cbor-gen v0.0.0-20190917003517-d78d67427694/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
github.com/whyrusleeping/cbor-gen v0.0.0-20191116002219-891f55cd449d/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
github.com/whyrusleeping/cbor-gen v0.0.0-20191212224538-d370462a7e8a/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
github.com/whyrusleeping/cbor-gen v0.0.0-20191216205031-b047b6acb3c0 h1:efb/4CnrubzNGqQOeHErxyQ6rIsJb7GcgeSDF7fqWeI=
github.com/whyrusleeping/cbor-gen v0.0.0-20191216205031-b047b6acb3c0/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
github.com/whyrusleeping/cbor-gen v0.0.0-20200106232624-282db0d37dbe h1:n7En1uyDtknjLRDXebWlPGJoHvwL8AkNcSQzuOoYYYQ=
github.com/whyrusleeping/cbor-gen v0.0.0-20200106232624-282db0d37dbe/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
github.com/whyrusleeping/cbor-gen v0.0.0-20200121162646-b63bacf5eaf8 h1:PHZv8Nu+95MBVNu3qSgg3ncxIv8hy4DzGAOBR9xYQRc=
github.com/whyrusleeping/cbor-gen v0.0.0-20200121162646-b63bacf5eaf8/go.mod h1:xdlJQaiqipF0HW+Mzpg7XRM3fWbGvfgFlcppuvlkIvY=
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc h1:9lDbC6Rz4bwmou+oE6Dt4Cb2BGMur5eR/GYptkKUVHo=
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8=
github.com/whyrusleeping/go-smux-multiplex v3.0.16+incompatible/go.mod h1:34LEDbeKFZInPUrAG+bjuJmUXONGdEFW7XL0SpTY1y4=
@@ -760,12 +741,10 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50=
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -781,7 +760,6 @@ go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E=
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
@@ -802,7 +780,6 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -811,7 +788,6 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
@@ -832,12 +808,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -868,7 +842,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50 h1:YvQ10rzcqWXLlJZ3XCUoO25savxmscf4+SC+ZqiCHhA=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -891,7 +864,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361 h1:RIIXAeV6GvDBuADKumTODatUqANFZ+5BPMnzsy4hulY=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200108195415-316d2f248479 h1:csuS+MHeEA2eWhyjQCMaPMq4z1+/PohkBSjJZHSIbOE=
golang.org/x/tools v0.0.0-20200108195415-316d2f248479/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -911,7 +883,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+73
View File
@@ -0,0 +1,73 @@
package incrt
import (
"io"
"time"
logging "github.com/ipfs/go-log/v2"
)
var log = logging.Logger("incrt")
var now = time.Now
type ReaderDeadline interface {
Read([]byte) (int, error)
SetReadDeadline(time.Time) error
}
type incrt struct {
rd ReaderDeadline
waitPerByte time.Duration
wait time.Duration
maxWait time.Duration
}
// New creates an Incremental Reader Timeout, with minimum sustained speed of
// minSpeed bytes per second and with maximum wait of maxWait
func New(rd ReaderDeadline, minSpeed int64, maxWait time.Duration) io.Reader {
return &incrt{
rd: rd,
waitPerByte: time.Second / time.Duration(minSpeed),
wait: maxWait,
maxWait: maxWait,
}
}
type errNoWait struct{}
func (err errNoWait) Error() string {
return "wait time exceeded"
}
func (err errNoWait) Timeout() bool {
return true
}
func (crt *incrt) Read(buf []byte) (int, error) {
start := now()
if crt.wait == 0 {
return 0, errNoWait{}
}
err := crt.rd.SetReadDeadline(start.Add(crt.wait))
if err != nil {
log.Warnf("unable to set daedline: %+v", err)
}
n, err := crt.rd.Read(buf)
crt.rd.SetReadDeadline(time.Time{})
if err == nil {
dur := now().Sub(start)
crt.wait -= dur
crt.wait += time.Duration(n) * crt.waitPerByte
if crt.wait < 0 {
crt.wait = 0
}
if crt.wait > crt.maxWait {
crt.wait = crt.maxWait
}
}
return n, err
}
+17 -8
View File
@@ -99,7 +99,7 @@ func (h handlers) register(namespace string, r interface{}) {
// Handle
type rpcErrFunc func(w func(func(io.Writer)), req *request, code int, err error)
type chanOut func(reflect.Value) interface{}
type chanOut func(reflect.Value, int64) error
func (h handlers) handleReader(ctx context.Context, r io.Reader, w io.Writer, rpcError rpcErrFunc) {
wf := func(cb func(io.Writer)) {
@@ -222,16 +222,25 @@ func (h handlers) handle(ctx context.Context, req request, w func(func(io.Writer
if handler.valOut != -1 {
resp.Result = callResult[handler.valOut].Interface()
}
if resp.Result != nil && reflect.TypeOf(resp.Result).Kind() == reflect.Chan {
// Channel responses are sent from channel control goroutine.
// Sending responses here could cause deadlocks on writeLk, or allow
// sending channel messages before this rpc call returns
w(func(w io.Writer) {
if resp.Result != nil && reflect.TypeOf(resp.Result).Kind() == reflect.Chan {
// this must happen in the writer callback, otherwise we may start sending
// channel messages before we send this response
//noinspection GoNilness // already checked above
resp.Result = chOut(callResult[handler.valOut])
//noinspection GoNilness // already checked above
err = chOut(callResult[handler.valOut], *req.ID)
if err == nil {
return // channel goroutine handles responding
}
log.Warnf("failed to setup channel in RPC call to '%s': %+v", req.Method, err)
resp.Error = &respError{
Code: 1,
Message: err.(error).Error(),
}
}
w(func(w io.Writer) {
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Error(err)
return
+46
View File
@@ -375,5 +375,51 @@ func TestChan(t *testing.T) {
serverHandler.wait <- struct{}{}
_, ok = <-sub
require.Equal(t, false, ok)
}
func TestControlChanDeadlock(t *testing.T) {
for r := 0; r < 20; r++ {
testControlChanDeadlock(t)
}
}
func testControlChanDeadlock(t *testing.T) {
var client struct {
Sub func(context.Context, int, int) (<-chan int, error)
}
n := 5000
serverHandler := &ChanHandler{
wait: make(chan struct{}, n),
}
rpcServer := NewServer()
rpcServer.Register("ChanHandler", serverHandler)
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
require.NoError(t, err)
defer closer()
for i := 0; i < n; i++ {
serverHandler.wait <- struct{}{}
}
ctx, _ := context.WithCancel(context.Background())
sub, err := client.Sub(ctx, 1, -1)
require.NoError(t, err)
go func() {
for i := 0; i < n; i++ {
require.Equal(t, i+1, <-sub)
}
}()
_, err = client.Sub(ctx, 2, -1)
require.NoError(t, err)
}
+60 -24
View File
@@ -11,6 +11,7 @@ import (
"sync/atomic"
"github.com/gorilla/websocket"
"golang.org/x/xerrors"
)
const wsCancel = "xrpc.cancel"
@@ -33,8 +34,10 @@ type frame struct {
}
type outChanReg struct {
id uint64
ch reflect.Value
reqID int64
chID uint64
ch reflect.Value
}
type wsConn struct {
@@ -134,51 +137,80 @@ func (c *wsConn) sendRequest(req request) {
// (forwards channel messages to client)
func (c *wsConn) handleOutChans() {
regV := reflect.ValueOf(c.registerCh)
exitV := reflect.ValueOf(c.exiting)
cases := []reflect.SelectCase{
{ // registration chan always 0
Dir: reflect.SelectRecv,
Chan: regV,
},
{ // exit chan always 1
Dir: reflect.SelectRecv,
Chan: exitV,
},
}
internal := len(cases)
var caseToID []uint64
for {
chosen, val, ok := reflect.Select(cases)
if chosen == 0 { // control channel
switch chosen {
case 0: // registration channel
if !ok {
// control channel closed - signals closed connection
// This shouldn't happen, instead the exiting channel should get closed
log.Warn("control channel closed")
return
}
registration := val.Interface().(outChanReg)
caseToID = append(caseToID, registration.chID)
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: registration.ch,
})
c.nextWriter(func(w io.Writer) {
resp := &response{
Jsonrpc: "2.0",
ID: registration.reqID,
Result: registration.chID,
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Error(err)
return
}
})
continue
case 1: // exiting channel
if !ok {
// exiting channel closed - signals closed connection
//
// We're not closing any channels as we're on receiving end.
// Also, context cancellation below should take care of any running
// requests
return
}
registration := val.Interface().(outChanReg)
caseToID = append(caseToID, registration.id)
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: registration.ch,
})
log.Warn("exiting channel received a message")
continue
}
if !ok {
// Output channel closed, cleanup, and tell remote that this happened
n := len(caseToID)
n := len(cases) - 1
if n > 0 {
cases[chosen] = cases[n]
caseToID[chosen-1] = caseToID[n-1]
caseToID[chosen-internal] = caseToID[n-internal]
}
id := caseToID[chosen-1]
id := caseToID[chosen-internal]
cases = cases[:n]
caseToID = caseToID[:n-1]
caseToID = caseToID[:n-internal]
c.sendRequest(request{
Jsonrpc: "2.0",
@@ -194,24 +226,29 @@ func (c *wsConn) handleOutChans() {
Jsonrpc: "2.0",
ID: nil, // notification
Method: chValue,
Params: []param{{v: reflect.ValueOf(caseToID[chosen-1])}, {v: val}},
Params: []param{{v: reflect.ValueOf(caseToID[chosen-internal])}, {v: val}},
})
}
}
// handleChanOut registers output channel for forwarding to client
func (c *wsConn) handleChanOut(ch reflect.Value) interface{} {
func (c *wsConn) handleChanOut(ch reflect.Value, req int64) error {
c.spawnOutChanHandlerOnce.Do(func() {
go c.handleOutChans()
})
id := atomic.AddUint64(&c.chanCtr, 1)
c.registerCh <- outChanReg{
id: id,
ch: ch,
}
select {
case c.registerCh <- outChanReg{
reqID: req,
return id
chID: id,
ch: ch,
}:
return nil
case <-c.exiting:
return xerrors.New("connection closing")
}
}
// //
@@ -389,7 +426,6 @@ func (c *wsConn) handleWsConn(ctx context.Context) {
c.chanHandlers = map[uint64]func(m []byte, ok bool){}
c.registerCh = make(chan outChanReg)
defer close(c.registerCh)
defer close(c.exiting)
// ////
+13 -7
View File
@@ -1,12 +1,18 @@
package lotuslog
import logging "github.com/ipfs/go-log/v2"
import (
"os"
logging "github.com/ipfs/go-log/v2"
)
func SetupLogLevels() {
logging.SetLogLevel("*", "INFO")
logging.SetLogLevel("dht", "ERROR")
logging.SetLogLevel("swarm2", "WARN")
logging.SetLogLevel("bitswap", "WARN")
logging.SetLogLevel("pubsub", "WARN")
logging.SetLogLevel("connmgr", "WARN")
if _, set := os.LookupEnv("GOLOG_LOG_LEVEL"); !set {
logging.SetLogLevel("*", "INFO")
logging.SetLogLevel("dht", "ERROR")
logging.SetLogLevel("swarm2", "WARN")
logging.SetLogLevel("bitswap", "WARN")
logging.SetLogLevel("pubsub", "WARN")
logging.SetLogLevel("connmgr", "WARN")
}
}
+227
View File
@@ -0,0 +1,227 @@
package statemachine
import (
"fmt"
"io"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
var _ = xerrors.Errorf
func (t *TestState) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{162}); err != nil {
return err
}
// t.A (uint64) (uint64)
if len("A") > cbg.MaxLength {
return xerrors.Errorf("Value in field \"A\" was too long")
}
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("A")))); err != nil {
return err
}
if _, err := w.Write([]byte("A")); err != nil {
return err
}
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.A))); err != nil {
return err
}
// t.B (uint64) (uint64)
if len("B") > cbg.MaxLength {
return xerrors.Errorf("Value in field \"B\" was too long")
}
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("B")))); err != nil {
return err
}
if _, err := w.Write([]byte("B")); err != nil {
return err
}
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.B))); err != nil {
return err
}
return nil
}
func (t *TestState) UnmarshalCBOR(r io.Reader) error {
br := cbg.GetPeeker(r)
maj, extra, err := cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajMap {
return fmt.Errorf("cbor input should be of type map")
}
if extra > cbg.MaxLength {
return fmt.Errorf("TestState: map struct too large (%d)", extra)
}
var name string
n := extra
for i := uint64(0); i < n; i++ {
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
name = string(sval)
}
switch name {
// t.A (uint64) (uint64)
case "A":
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.A = uint64(extra)
// t.B (uint64) (uint64)
case "B":
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.B = uint64(extra)
default:
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
}
}
return nil
}
func (t *TestEvent) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{162}); err != nil {
return err
}
// t.A (string) (string)
if len("A") > cbg.MaxLength {
return xerrors.Errorf("Value in field \"A\" was too long")
}
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("A")))); err != nil {
return err
}
if _, err := w.Write([]byte("A")); err != nil {
return err
}
if len(t.A) > cbg.MaxLength {
return xerrors.Errorf("Value in field t.A was too long")
}
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len(t.A)))); err != nil {
return err
}
if _, err := w.Write([]byte(t.A)); err != nil {
return err
}
// t.Val (uint64) (uint64)
if len("Val") > cbg.MaxLength {
return xerrors.Errorf("Value in field \"Val\" was too long")
}
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajTextString, uint64(len("Val")))); err != nil {
return err
}
if _, err := w.Write([]byte("Val")); err != nil {
return err
}
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Val))); err != nil {
return err
}
return nil
}
func (t *TestEvent) UnmarshalCBOR(r io.Reader) error {
br := cbg.GetPeeker(r)
maj, extra, err := cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajMap {
return fmt.Errorf("cbor input should be of type map")
}
if extra > cbg.MaxLength {
return fmt.Errorf("TestEvent: map struct too large (%d)", extra)
}
var name string
n := extra
for i := uint64(0); i < n; i++ {
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
name = string(sval)
}
switch name {
// t.A (string) (string)
case "A":
{
sval, err := cbg.ReadString(br)
if err != nil {
return err
}
t.A = string(sval)
}
// t.Val (uint64) (uint64)
case "Val":
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.Val = uint64(extra)
default:
return fmt.Errorf("unknown struct field %d: '%s'", i, name)
}
}
return nil
}
+16
View File
@@ -0,0 +1,16 @@
package statemachine
import "context"
type Context struct {
ctx context.Context
send func(evt interface{}) error
}
func (ctx *Context) Context() context.Context {
return ctx.ctx
}
func (ctx *Context) Send(evt interface{}) error {
return ctx.send(evt)
}
+116
View File
@@ -0,0 +1,116 @@
package statemachine
import (
"context"
"reflect"
"sync"
"github.com/filecoin-project/go-statestore"
"github.com/ipfs/go-datastore"
"golang.org/x/xerrors"
)
type StateHandler interface {
// returns
Plan(events []Event, user interface{}) (interface{}, error)
}
// StateGroup manages a group of state machines sharing the same logic
type StateGroup struct {
sts *statestore.StateStore
hnd StateHandler
stateType reflect.Type
lk sync.Mutex
sms map[datastore.Key]*StateMachine
}
// stateType: T - (MyStateStruct{})
func New(ds datastore.Datastore, hnd StateHandler, stateType interface{}) *StateGroup {
return &StateGroup{
sts: statestore.New(ds),
hnd: hnd,
stateType: reflect.TypeOf(stateType),
sms: map[datastore.Key]*StateMachine{},
}
}
// Send sends an event to machine identified by `id`.
// `evt` is going to be passed into StateHandler.Planner, in the events[].User param
//
// If a state machine with the specified id doesn't exits, it's created, and it's
// state is set to zero-value of stateType provided in group constructor
func (s *StateGroup) Send(id interface{}, evt interface{}) (err error) {
s.lk.Lock()
defer s.lk.Unlock()
sm, exist := s.sms[statestore.ToKey(id)]
if !exist {
sm, err = s.loadOrCreate(id)
if err != nil {
return xerrors.Errorf("loadOrCreate state: %w", err)
}
s.sms[statestore.ToKey(id)] = sm
}
return sm.send(Event{User: evt})
}
func (s *StateGroup) loadOrCreate(name interface{}) (*StateMachine, error) {
exists, err := s.sts.Has(name)
if err != nil {
return nil, xerrors.Errorf("failed to check if state for %v exists: %w", name, err)
}
if !exists {
userState := reflect.New(s.stateType).Interface()
err = s.sts.Begin(name, userState)
if err != nil {
return nil, xerrors.Errorf("saving initial state: %w", err)
}
}
res := &StateMachine{
planner: s.hnd.Plan,
eventsIn: make(chan Event),
name: name,
st: s.sts.Get(name),
stateType: s.stateType,
stageDone: make(chan struct{}),
closing: make(chan struct{}),
closed: make(chan struct{}),
}
go res.run()
return res, nil
}
// Stop stops all state machines in this group
func (s *StateGroup) Stop(ctx context.Context) error {
s.lk.Lock()
defer s.lk.Unlock()
for _, sm := range s.sms {
if err := sm.stop(ctx); err != nil {
return err
}
}
return nil
}
// List outputs states of all state machines in this group
// out: *[]StateT
func (s *StateGroup) List(out interface{}) error {
return s.sts.List(out)
}
// Get gets state for a single state machine
func (s *StateGroup) Get(id interface{}) *statestore.StoredState {
return s.sts.Get(id)
}
+127
View File
@@ -0,0 +1,127 @@
package statemachine
import (
"context"
"reflect"
"sync/atomic"
"github.com/filecoin-project/go-statestore"
logging "github.com/ipfs/go-log"
)
var log = logging.Logger("evtsm")
type Event struct {
User interface{}
}
// TODO: This probably should be returning an int indicating partial event processing
// (or something like errPartial(nEvents))
// returns func(ctx Context, st <T>) (func(*<T>), error), where <T> is the typeOf(User) param
type Planner func(events []Event, user interface{}) (interface{}, error)
type StateMachine struct {
planner Planner
eventsIn chan Event
name interface{}
st *statestore.StoredState
stateType reflect.Type
stageDone chan struct{}
closing chan struct{}
closed chan struct{}
busy int32
}
func (fsm *StateMachine) run() {
defer close(fsm.closed)
var pendingEvents []Event
for {
// NOTE: This requires at least one event to be sent to trigger a stage
// This means that after restarting the state machine users of this
// code must send a 'restart' event
select {
case evt := <-fsm.eventsIn:
pendingEvents = append(pendingEvents, evt)
case <-fsm.stageDone:
if len(pendingEvents) == 0 {
continue
}
case <-fsm.closing:
return
}
if atomic.CompareAndSwapInt32(&fsm.busy, 0, 1) {
var nextStep interface{}
var ustate interface{}
err := fsm.mutateUser(func(user interface{}) (err error) {
nextStep, err = fsm.planner(pendingEvents, user)
ustate = user
return err
})
if err != nil {
log.Errorf("Executing event planner failed: %+v", err)
return
}
pendingEvents = nil
if nextStep == nil {
continue
}
ctx := Context{
ctx: context.TODO(),
send: func(evt interface{}) error {
return fsm.send(Event{User: evt})
},
}
go func() {
res := reflect.ValueOf(nextStep).Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(ustate).Elem()})
if res[0].Interface() != nil {
log.Errorf("executing step: %+v", res[0].Interface().(error)) // TODO: propagate top level
return
}
atomic.StoreInt32(&fsm.busy, 0)
fsm.stageDone <- struct{}{}
}()
}
}
}
func (fsm *StateMachine) mutateUser(cb func(user interface{}) error) error {
mutt := reflect.FuncOf([]reflect.Type{reflect.PtrTo(fsm.stateType)}, []reflect.Type{reflect.TypeOf(new(error)).Elem()}, false)
mutf := reflect.MakeFunc(mutt, func(args []reflect.Value) (results []reflect.Value) {
err := cb(args[0].Interface())
return []reflect.Value{reflect.ValueOf(&err).Elem()}
})
return fsm.st.Mutate(mutf.Interface())
}
func (fsm *StateMachine) send(evt Event) error {
fsm.eventsIn <- evt // TODO: ctx, at least
return nil
}
func (fsm *StateMachine) stop(ctx context.Context) error {
close(fsm.closing)
select {
case <-fsm.closed:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
+105
View File
@@ -0,0 +1,105 @@
package statemachine
import (
"context"
"testing"
"github.com/ipfs/go-datastore"
logging "github.com/ipfs/go-log"
"gotest.tools/assert"
)
func init() {
logging.SetLogLevel("*", "INFO")
}
type testHandler struct {
t *testing.T
proceed chan struct{}
done chan struct{}
}
func (t *testHandler) Plan(events []Event, state interface{}) (interface{}, error) {
return t.plan(events, state.(*TestState))
}
func (t *testHandler) plan(events []Event, state *TestState) (func(Context, TestState) error, error) {
for _, event := range events {
e := event.User.(*TestEvent)
switch e.A {
case "restart":
case "start":
state.A = 1
case "b":
state.A = 2
state.B = e.Val
}
}
switch state.A {
case 1:
return t.step0, nil
case 2:
return t.step1, nil
default:
t.t.Fatal(state.A)
}
panic("how?")
}
func (t *testHandler) step0(ctx Context, st TestState) error {
ctx.Send(&TestEvent{A: "b", Val: 55})
<-t.proceed
return nil
}
func (t *testHandler) step1(ctx Context, st TestState) error {
assert.Equal(t.t, uint64(2), st.A)
close(t.done)
return nil
}
func TestBasic(t *testing.T) {
for i := 0; i < 1000; i++ { // run a few times to expose any races
ds := datastore.NewMapDatastore()
th := &testHandler{t: t, done: make(chan struct{}), proceed: make(chan struct{})}
close(th.proceed)
smm := New(ds, th, TestState{})
if err := smm.Send(uint64(2), &TestEvent{A: "start"}); err != nil {
t.Fatalf("%+v", err)
}
<-th.done
}
}
func TestPersist(t *testing.T) {
for i := 0; i < 1000; i++ { // run a few times to expose any races
ds := datastore.NewMapDatastore()
th := &testHandler{t: t, done: make(chan struct{}), proceed: make(chan struct{})}
smm := New(ds, th, TestState{})
if err := smm.Send(uint64(2), &TestEvent{A: "start"}); err != nil {
t.Fatalf("%+v", err)
}
if err := smm.Stop(context.Background()); err != nil {
t.Fatal(err)
return
}
smm = New(ds, th, TestState{})
if err := smm.Send(uint64(2), &TestEvent{A: "restart"}); err != nil {
t.Fatalf("%+v", err)
}
close(th.proceed)
<-th.done
}
}
var _ StateHandler = &testHandler{}
+11
View File
@@ -0,0 +1,11 @@
package statemachine
type TestState struct {
A uint64
B uint64
}
type TestEvent struct {
A string
Val uint64
}
+36 -6
View File
@@ -2,6 +2,7 @@ package miner
import (
"context"
"fmt"
"sync"
"time"
@@ -11,6 +12,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/types"
lru "github.com/hashicorp/golang-lru"
logging "github.com/ipfs/go-log/v2"
"go.opencensus.io/trace"
@@ -22,6 +24,11 @@ var log = logging.Logger("miner")
type waitFunc func(ctx context.Context, baseTime uint64) error
func NewMiner(api api.FullNode, epp gen.ElectionPoStProver) *Miner {
arc, err := lru.NewARC(10000)
if err != nil {
panic(err)
}
return &Miner{
api: api,
epp: epp,
@@ -32,6 +39,7 @@ func NewMiner(api api.FullNode, epp gen.ElectionPoStProver) *Miner {
return nil
},
minedBlockHeights: arc,
}
}
@@ -48,6 +56,8 @@ type Miner struct {
waitFunc waitFunc
lastWork *MiningBase
minedBlockHeights *lru.ARCCache
}
func (m *Miner) Addresses() ([]address.Address, error) {
@@ -200,13 +210,33 @@ eventLoop:
mWon[b.Header.Miner] = struct{}{}
}
for _, b := range blks {
// TODO: this code was written to handle creating blocks for multiple miners.
// However, we don't use that, and we probably never will. So even though this code will
// never see different miners, i'm going to handle the caching as if it was going to.
// We can clean it up later when we remove all the multiple miner logic.
blkKey := fmt.Sprintf("%s-%d", b.Header.Miner, b.Header.Height)
if _, ok := m.minedBlockHeights.Get(blkKey); ok {
log.Warnw("Created a block at the same height as another block we've created", "height", b.Header.Height, "miner", b.Header.Miner, "parents", b.Header.Parents)
continue
}
m.minedBlockHeights.Add(blkKey, true)
if err := m.api.SyncSubmitBlock(ctx, b); err != nil {
log.Errorf("failed to submit newly mined block: %s", err)
}
}
} else {
nextRound := time.Unix(int64(base.ts.MinTimestamp()+uint64(build.BlockDelay*base.nullRounds)), 0)
time.Sleep(time.Until(nextRound))
select {
case <-time.After(time.Until(nextRound)):
case <-m.stop:
stopping := m.stopping
m.stop = nil
m.stopping = nil
close(stopping)
return
}
}
}
}
@@ -356,13 +386,13 @@ func (m *Miner) computeTicket(ctx context.Context, addr address.Address, base *M
}
func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *types.Ticket, proof *types.EPostProof, pending []*types.SignedMessage) (*types.BlockMsg, error) {
msgs, err := selectMessages(context.TODO(), m.api.StateGetActor, base, pending)
msgs, err := SelectMessages(context.TODO(), m.api.StateGetActor, base.ts, pending)
if err != nil {
return nil, xerrors.Errorf("message filtering failed: %w", err)
}
if len(msgs) > build.BlockMessageLimit {
log.Error("selectMessages returned too many messages: ", len(msgs))
log.Error("SelectMessages returned too many messages: ", len(msgs))
msgs = msgs[:build.BlockMessageLimit]
}
@@ -374,7 +404,7 @@ func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *type
return m.api.MinerCreateBlock(context.TODO(), addr, base.ts, ticket, proof, msgs, nheight, uint64(uts))
}
type actorLookup func(context.Context, address.Address, *types.TipSet) (*types.Actor, error)
type ActorLookup func(context.Context, address.Address, *types.TipSet) (*types.Actor, error)
func countFrom(msgs []*types.SignedMessage, from address.Address) (out int) {
for _, msg := range msgs {
@@ -385,7 +415,7 @@ func countFrom(msgs []*types.SignedMessage, from address.Address) (out int) {
return out
}
func selectMessages(ctx context.Context, al actorLookup, base *MiningBase, msgs []*types.SignedMessage) ([]*types.SignedMessage, error) {
func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs []*types.SignedMessage) ([]*types.SignedMessage, error) {
out := make([]*types.SignedMessage, 0, build.BlockMessageLimit)
inclNonces := make(map[address.Address]uint64)
inclBalances := make(map[address.Address]types.BigInt)
@@ -401,7 +431,7 @@ func selectMessages(ctx context.Context, al actorLookup, base *MiningBase, msgs
from := msg.Message.From
if _, ok := inclNonces[from]; !ok {
act, err := al(ctx, from, base.ts)
act, err := al(ctx, from, ts)
if err != nil {
log.Warnf("failed to check message sender balance, skipping message: %+v", err)
continue
+1 -1
View File
@@ -80,7 +80,7 @@ func TestMessageFiltering(t *testing.T) {
},
}
outmsgs, err := selectMessages(ctx, af, &MiningBase{}, wrapMsgs(msgs))
outmsgs, err := SelectMessages(ctx, af, nil, wrapMsgs(msgs))
if err != nil {
t.Fatal(err)
}
+10 -3
View File
@@ -6,14 +6,21 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/gen"
lru "github.com/hashicorp/golang-lru"
)
func NewTestMiner(nextCh <-chan struct{}, addr address.Address) func(api.FullNode, gen.ElectionPoStProver) *Miner {
return func(api api.FullNode, epp gen.ElectionPoStProver) *Miner {
arc, err := lru.NewARC(10000)
if err != nil {
panic(err)
}
m := &Miner{
api: api,
waitFunc: chanWaiter(nextCh),
epp: epp,
api: api,
waitFunc: chanWaiter(nextCh),
epp: epp,
minedBlockHeights: arc,
}
if err := m.Register(addr); err != nil {
+2 -1
View File
@@ -50,6 +50,7 @@ import (
"github.com/filecoin-project/lotus/paych"
"github.com/filecoin-project/lotus/peermgr"
"github.com/filecoin-project/lotus/storage"
"github.com/filecoin-project/lotus/storage/sealing"
"github.com/filecoin-project/lotus/storage/sectorblocks"
)
@@ -245,7 +246,7 @@ func Online() Option {
ApplyIf(func(s *Settings) bool { return s.nodeType == repo.StorageMiner },
Override(new(sectorbuilder.Interface), modules.SectorBuilder),
Override(new(*sectorblocks.SectorBlocks), sectorblocks.NewSectorBlocks),
Override(new(storage.TicketFn), modules.SealTicketGen),
Override(new(sealing.TicketFn), modules.SealTicketGen),
Override(new(*storage.Miner), modules.StorageMiner),
Override(new(dtypes.StagingBlockstore), modules.StagingBlockstore),
+38
View File
@@ -3,6 +3,7 @@ package full
import (
"context"
"fmt"
"io"
"strconv"
"strings"
@@ -18,10 +19,12 @@ import (
"github.com/ipfs/go-path"
"github.com/ipfs/go-path/resolver"
mh "github.com/multiformats/go-multihash"
"github.com/prometheus/common/log"
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
@@ -83,6 +86,10 @@ func (a *ChainAPI) ChainGetBlockMessages(ctx context.Context, msg cid.Cid) (*api
}, nil
}
func (a *ChainAPI) ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*store.HeadChange, error) {
return a.Chain.GetPath(ctx, from, to)
}
func (a *ChainAPI) ChainGetParentMessages(ctx context.Context, bcid cid.Cid) ([]api.Message, error) {
b, err := a.Chain.GetBlock(bcid)
if err != nil {
@@ -308,3 +315,34 @@ func (a *ChainAPI) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Mess
return cm.VMMessage(), nil
}
func (a *ChainAPI) ChainExport(ctx context.Context, ts *types.TipSet) (<-chan []byte, error) {
r, w := io.Pipe()
out := make(chan []byte)
go func() {
defer w.Close()
if err := a.Chain.Export(ctx, ts, w); err != nil {
log.Errorf("chain export call failed: %s", err)
return
}
}()
go func() {
defer close(out)
for {
buf := make([]byte, 4096)
n, err := r.Read(buf)
if err != nil {
log.Errorf("chain export pipe read failed: %s", err)
return
}
select {
case out <- buf[:n]:
case <-ctx.Done():
log.Warnf("export writer failed: %s", ctx.Err())
}
}
}()
return out, nil
}
+5 -1
View File
@@ -108,7 +108,7 @@ func (a *StateAPI) StatePledgeCollateral(ctx context.Context, ts *types.TipSet)
return types.BigFromBytes(ret.Return), nil
}
func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.MethodCall, error) {
return a.StateManager.Call(ctx, msg, ts)
}
@@ -400,3 +400,7 @@ func (a *StateAPI) StateListMessages(ctx context.Context, match *types.Message,
return out, nil
}
func (a *StateAPI) StateCompute(ctx context.Context, height uint64, msgs []*types.Message, ts *types.TipSet) (cid.Cid, error) {
return stmgr.ComputeState(ctx, a.StateManager, height, msgs, ts)
}
+12 -1
View File
@@ -160,6 +160,16 @@ func (sm *StorageMinerAPI) SectorsStatus(ctx context.Context, sid uint64) (api.S
deals[i] = piece.DealID
}
log := make([]api.SectorLog, len(info.Log))
for i, l := range info.Log {
log[i] = api.SectorLog{
Kind: l.Kind,
Timestamp: l.Timestamp,
Trace: l.Trace,
Message: l.Message,
}
}
return api.SectorInfo{
SectorID: sid,
State: info.State,
@@ -172,6 +182,7 @@ func (sm *StorageMinerAPI) SectorsStatus(ctx context.Context, sid uint64) (api.S
Retries: info.Nonce,
LastErr: info.LastErr,
Log: log,
}, nil
}
@@ -206,7 +217,7 @@ func (sm *StorageMinerAPI) SectorsRefs(context.Context) (map[string][]api.Sealed
}
func (sm *StorageMinerAPI) SectorsUpdate(ctx context.Context, id uint64, state api.SectorState) error {
return sm.Miner.UpdateSectorState(ctx, id, storage.NonceIncrement, state)
return sm.Miner.ForceSectorState(ctx, id, state)
}
func (sm *StorageMinerAPI) WorkerQueue(ctx context.Context, cfg sectorbuilder.WorkerCfg) (<-chan sectorbuilder.WorkerTask, error) {
+13 -13
View File
@@ -5,7 +5,16 @@ import (
"math"
"reflect"
"github.com/filecoin-project/go-address"
dtgraphsync "github.com/filecoin-project/go-data-transfer/impl/graphsync"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
retrievalimpl "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl"
"github.com/filecoin-project/go-fil-markets/storagemarket"
deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
paramfetch "github.com/filecoin-project/go-paramfetch"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/filecoin-project/go-statestore"
"github.com/ipfs/go-bitswap"
"github.com/ipfs/go-bitswap/network"
"github.com/ipfs/go-blockservice"
@@ -23,25 +32,16 @@ import (
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
dtgraphsync "github.com/filecoin-project/go-data-transfer/impl/graphsync"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
retrievalimpl "github.com/filecoin-project/go-fil-markets/retrievalmarket/impl"
"github.com/filecoin-project/go-fil-markets/storagemarket"
deals "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/filecoin-project/go-statestore"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/markets/retrievaladapter"
"github.com/filecoin-project/lotus/miner"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/modules/helpers"
"github.com/filecoin-project/lotus/node/repo"
"github.com/filecoin-project/lotus/markets/retrievaladapter"
"github.com/filecoin-project/lotus/storage"
"github.com/filecoin-project/lotus/storage/sealing"
"github.com/filecoin-project/lotus/storage/sectorblocks"
)
@@ -98,7 +98,7 @@ func SectorBuilderConfig(storagePath string, threads uint, noprecommit, nocommit
}
}
func StorageMiner(mctx helpers.MetricsCtx, lc fx.Lifecycle, api api.FullNode, h host.Host, ds dtypes.MetadataDS, sb sectorbuilder.Interface, tktFn storage.TicketFn) (*storage.Miner, error) {
func StorageMiner(mctx helpers.MetricsCtx, lc fx.Lifecycle, api api.FullNode, h host.Host, ds dtypes.MetadataDS, sb sectorbuilder.Interface, tktFn sealing.TicketFn) (*storage.Miner, error) {
maddr, err := minerAddrFromDS(ds)
if err != nil {
return nil, err
@@ -241,7 +241,7 @@ func SectorBuilder(cfg *sectorbuilder.Config, ds dtypes.MetadataDS) (*sectorbuil
return sb, nil
}
func SealTicketGen(api api.FullNode) storage.TicketFn {
func SealTicketGen(api api.FullNode) sealing.TicketFn {
return func(ctx context.Context) (*sectorbuilder.SealTicket, error) {
ts, err := api.ChainHead(ctx)
if err != nil {
+6
View File
@@ -393,6 +393,12 @@ func TestAPIRPC(t *testing.T) {
}
func TestAPIDealFlow(t *testing.T) {
logging.SetLogLevel("miner", "ERROR")
logging.SetLogLevel("chainstore", "ERROR")
logging.SetLogLevel("chain", "ERROR")
logging.SetLogLevel("sub", "ERROR")
logging.SetLogLevel("storageminer", "ERROR")
test.TestDealFlow(t, mockSbBuilder, 10*time.Millisecond)
}
+9 -7
View File
@@ -42,7 +42,7 @@ type PeerMgr struct {
maxFilPeers int
minFilPeers int
expanding bool
expanding chan struct{}
h host.Host
dht *dht.IpfsDHT
@@ -56,7 +56,8 @@ func NewPeerMgr(h host.Host, dht *dht.IpfsDHT, bootstrap dtypes.BootstrapPeers)
dht: dht,
bootstrappers: bootstrap,
peers: make(map[peer.ID]time.Duration),
peers: make(map[peer.ID]time.Duration),
expanding: make(chan struct{}, 1),
maxFilPeers: MaxFilPeers,
minFilPeers: MinFilPeers,
@@ -125,18 +126,19 @@ func (pmgr *PeerMgr) getPeerCount() int {
}
func (pmgr *PeerMgr) expandPeers() {
if pmgr.expanding {
select {
case pmgr.expanding <- struct{}{}:
default:
return
}
pmgr.expanding = true
go func() {
defer func() {
pmgr.expanding = false
}()
ctx, cancel := context.WithTimeout(context.TODO(), time.Second*30)
defer cancel()
pmgr.doExpand(ctx)
<-pmgr.expanding
}()
}
+8 -1
View File
@@ -117,7 +117,10 @@ func (s *fpostScheduler) runPost(ctx context.Context, eps uint64, ts *types.TipS
return nil, xerrors.Errorf("getting sorted sector info: %w", err)
}
log.Infow("running fPoSt", "chain-random", rand, "eps", eps, "height", ts.Height())
log.Infow("running fPoSt",
"chain-random", rand,
"eps", eps,
"height", ts.Height())
faults, err := s.checkFaults(ctx, ssi)
if err != nil {
@@ -129,6 +132,10 @@ func (s *fpostScheduler) runPost(ctx context.Context, eps uint64, ts *types.TipS
var seed [32]byte
copy(seed[:], rand)
log.Infow("generating fPoSt",
"sectors", len(ssi.Values()),
"faults", len(faults))
scandidates, proof, err := s.sb.GenerateFallbackPoSt(ssi, seed, faults)
if err != nil {
return nil, xerrors.Errorf("running post failed: %w", err)
+6
View File
@@ -70,6 +70,9 @@ func (s *fpostScheduler) run(ctx context.Context) {
var lowest, highest *types.TipSet = s.cur, nil
for _, change := range changes {
if change.Val == nil {
log.Errorf("change.Val was nil")
}
switch change.Type {
case store.HCRevert:
lowest = change.Val
@@ -111,6 +114,9 @@ func (s *fpostScheduler) revert(ctx context.Context, newLowest *types.TipSet) er
}
func (s *fpostScheduler) update(ctx context.Context, new *types.TipSet) error {
if new == nil {
return xerrors.Errorf("no new tipset in fpostScheduler.update")
}
newEPS, start, err := s.shouldFallbackPost(ctx, new)
if err != nil {
return err
+30 -45
View File
@@ -5,50 +5,42 @@ import (
"errors"
"time"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p-core/host"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/filecoin-project/go-statestore"
"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/events"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/storage/sealing"
)
var log = logging.Logger("storageminer")
const SectorStorePrefix = "/sectors"
type Miner struct {
api storageMinerApi
events *events.Events
h host.Host
api storageMinerApi
h host.Host
sb sectorbuilder.Interface
ds datastore.Batching
tktFn sealing.TicketFn
maddr address.Address
worker address.Address
// Sealing
sb sectorbuilder.Interface
sectors *statestore.StateStore
tktFn TicketFn
sectorIncoming chan *SectorInfo
sectorUpdated chan sectorUpdate
stop chan struct{}
stopped chan struct{}
sealing *sealing.Sealing
}
type storageMinerApi interface {
// Call a read only method on actors (no interaction with the chain required)
StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error)
StateCall(context.Context, *types.Message, *types.TipSet) (*api.MethodCall, error)
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error)
StateMinerSectors(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error)
@@ -57,6 +49,7 @@ type storageMinerApi interface {
StateWaitMsg(context.Context, cid.Cid) (*api.MsgWait, error) // TODO: removeme eventually
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error)
StateMarketStorageDeal(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error)
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error)
@@ -65,28 +58,25 @@ type storageMinerApi interface {
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
WalletBalance(context.Context, address.Address) (types.BigInt, error)
WalletHas(context.Context, address.Address) (bool, error)
}
func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datastore.Batching, sb sectorbuilder.Interface, tktFn TicketFn) (*Miner, error) {
return &Miner{
api: api,
maddr: addr,
func NewMiner(api storageMinerApi, addr address.Address, h host.Host, ds datastore.Batching, sb sectorbuilder.Interface, tktFn sealing.TicketFn) (*Miner, error) {
m := &Miner{
api: api,
h: h,
sb: sb,
ds: ds,
tktFn: tktFn,
sectors: statestore.New(namespace.Wrap(ds, datastore.NewKey(SectorStorePrefix))),
maddr: addr,
}
sectorIncoming: make(chan *SectorInfo),
sectorUpdated: make(chan sectorUpdate),
stop: make(chan struct{}),
stopped: make(chan struct{}),
}, nil
return m, nil
}
func (m *Miner) Run(ctx context.Context) error {
@@ -94,32 +84,27 @@ func (m *Miner) Run(ctx context.Context) error {
return xerrors.Errorf("miner preflight checks failed: %w", err)
}
m.events = events.NewEvents(ctx, m.api)
fps := &fpostScheduler{
api: m.api,
sb: m.sb,
api: m.api,
sb: m.sb,
actor: m.maddr,
worker: m.worker,
}
go fps.run(ctx)
if err := m.sectorStateLoop(ctx); err != nil {
log.Errorf("%+v", err)
return xerrors.Errorf("failed to startup sector state loop: %w", err)
}
evts := events.NewEvents(ctx, m.api)
m.sealing = sealing.New(m.api, evts, m.maddr, m.worker, m.ds, m.sb, m.tktFn)
go m.sealing.Run(ctx)
return nil
}
func (m *Miner) Stop(ctx context.Context) error {
close(m.stop)
select {
case <-m.stopped:
return nil
case <-ctx.Done():
return ctx.Err()
}
defer m.sealing.Stop(ctx)
return nil
}
func (m *Miner) runPreflightChecks(ctx context.Context) error {
+4
View File
@@ -207,6 +207,10 @@ func (sb *SBMock) GetPath(string, string) (string, error) {
panic("nyi")
}
func (sb *SBMock) CanCommit(sectorID uint64) (bool, error) {
return true, nil
}
func (sb *SBMock) WorkerStats() sectorbuilder.WorkerStats {
panic("nyi")
}
+35
View File
@@ -0,0 +1,35 @@
package storage
import (
"context"
"io"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/storage/sealing"
)
// TODO: refactor this to be direct somehow
func (m *Miner) AllocatePiece(size uint64) (sectorID uint64, offset uint64, err error) {
return m.sealing.AllocatePiece(size)
}
func (m *Miner) SealPiece(ctx context.Context, size uint64, r io.Reader, sectorID uint64, dealID uint64) error {
return m.sealing.SealPiece(ctx, size, r, sectorID, dealID)
}
func (m *Miner) ListSectors() ([]sealing.SectorInfo, error) {
return m.sealing.ListSectors()
}
func (m *Miner) GetSectorInfo(sid uint64) (sealing.SectorInfo, error) {
return m.sealing.GetSectorInfo(sid)
}
func (m *Miner) PledgeSector() error {
return m.sealing.PledgeSector()
}
func (m *Miner) ForceSectorState(ctx context.Context, id uint64, state api.SectorState) error {
return m.sealing.ForceSectorState(ctx, id, state)
}
File diff suppressed because it is too large Load Diff
+105
View File
@@ -0,0 +1,105 @@
package sealing
import (
"context"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
)
// TODO: For now we handle this by halting state execution, when we get jsonrpc reconnecting
// We should implement some wait-for-api logic
type ErrApi struct{ error }
type ErrInvalidDeals struct{ error }
type ErrExpiredDeals struct{ error }
type ErrBadCommD struct{ error }
type ErrExpiredTicket struct{ error }
// checkPieces validates that:
// - Each piece han a corresponding on chain deal
// - Piece commitments match with on chain deals
// - Piece sizes match
// - Deals aren't expired
func checkPieces(ctx context.Context, si SectorInfo, api sealingApi) error {
head, err := api.ChainHead(ctx)
if err != nil {
return &ErrApi{xerrors.Errorf("getting chain head: %w", err)}
}
for i, piece := range si.Pieces {
deal, err := api.StateMarketStorageDeal(ctx, piece.DealID, nil)
if err != nil {
return &ErrApi{xerrors.Errorf("getting deal %d for piece %d: %w", piece.DealID, i, err)}
}
if string(deal.PieceRef) != string(piece.CommP) {
return &ErrInvalidDeals{xerrors.Errorf("piece %d (or %d) of sector %d refers deal %d with wrong CommP: %x != %x", i, len(si.Pieces), si.SectorID, piece.DealID, piece.CommP, deal.PieceRef)}
}
if piece.Size != deal.PieceSize {
return &ErrInvalidDeals{xerrors.Errorf("piece %d (or %d) of sector %d refers deal %d with different size: %d != %d", i, len(si.Pieces), si.SectorID, piece.DealID, piece.Size, deal.PieceSize)}
}
if head.Height() >= deal.ProposalExpiration {
return &ErrExpiredDeals{xerrors.Errorf("piece %d (or %d) of sector %d refers expired deal %d - expires %d, head %d", i, len(si.Pieces), si.SectorID, piece.DealID, deal.ProposalExpiration, head.Height())}
}
}
return nil
}
// checkSeal checks that data commitment generated in the sealing process
// matches pieces, and that the seal ticket isn't expired
func checkSeal(ctx context.Context, maddr address.Address, si SectorInfo, api sealingApi) (err error) {
head, err := api.ChainHead(ctx)
if err != nil {
return &ErrApi{xerrors.Errorf("getting chain head: %w", err)}
}
ssize, err := api.StateMinerSectorSize(ctx, maddr, head)
if err != nil {
return &ErrApi{err}
}
ccparams, err := actors.SerializeParams(&actors.ComputeDataCommitmentParams{
DealIDs: si.deals(),
SectorSize: ssize,
})
if err != nil {
return xerrors.Errorf("computing params for ComputeDataCommitment: %w", err)
}
ccmt := &types.Message{
To: actors.StorageMarketAddress,
From: maddr,
Value: types.NewInt(0),
GasPrice: types.NewInt(0),
GasLimit: types.NewInt(9999999999),
Method: actors.SMAMethods.ComputeDataCommitment,
Params: ccparams,
}
r, err := api.StateCall(ctx, ccmt, nil)
if err != nil {
return &ErrApi{xerrors.Errorf("calling ComputeDataCommitment: %w", err)}
}
if r.ExitCode != 0 {
return &ErrBadCommD{xerrors.Errorf("receipt for ComputeDataCommitment had exit code %d", r.ExitCode)}
}
if string(r.Return) != string(si.CommD) {
return &ErrBadCommD{xerrors.Errorf("on chain CommD differs from sector: %x != %x", r.Return, si.CommD)}
}
if int64(head.Height())-int64(si.Ticket.BlockHeight+build.SealRandomnessLookback) > build.SealRandomnessLookbackLimit {
return &ErrExpiredTicket{xerrors.Errorf("ticket expired: seal height: %d, head: %d", si.Ticket.BlockHeight+build.SealRandomnessLookback, head.Height())}
}
return nil
}
+281
View File
@@ -0,0 +1,281 @@
package sealing
import (
"context"
"fmt"
"reflect"
"time"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/lib/statemachine"
)
func (m *Sealing) Plan(events []statemachine.Event, user interface{}) (interface{}, error) {
next, err := m.plan(events, user.(*SectorInfo))
if err != nil || next == nil {
return nil, err
}
return func(ctx statemachine.Context, si SectorInfo) error {
err := next(ctx, si)
if err != nil {
log.Errorf("unhandled sector error (%d): %+v", si.SectorID, err)
return nil
}
return nil
}, nil
}
var fsmPlanners = []func(events []statemachine.Event, state *SectorInfo) error{
api.UndefinedSectorState: planOne(on(SectorStart{}, api.Packing)),
api.Packing: planOne(on(SectorPacked{}, api.Unsealed)),
api.Unsealed: planOne(
on(SectorSealed{}, api.PreCommitting),
on(SectorSealFailed{}, api.SealFailed),
on(SectorPackingFailed{}, api.PackingFailed),
),
api.PreCommitting: planOne(
on(SectorSealFailed{}, api.SealFailed),
on(SectorPreCommitted{}, api.WaitSeed),
on(SectorPreCommitFailed{}, api.PreCommitFailed),
),
api.WaitSeed: planOne(
on(SectorSeedReady{}, api.Committing),
on(SectorPreCommitFailed{}, api.PreCommitFailed),
),
api.Committing: planCommitting,
api.CommitWait: planOne(
on(SectorProving{}, api.Proving),
on(SectorCommitFailed{}, api.CommitFailed),
),
api.Proving: planOne(
on(SectorFaultReported{}, api.FaultReported),
on(SectorFaulty{}, api.Faulty),
),
api.SealFailed: planOne(
on(SectorRetrySeal{}, api.Unsealed),
),
api.PreCommitFailed: planOne(
on(SectorRetryPreCommit{}, api.PreCommitting),
on(SectorRetryWaitSeed{}, api.WaitSeed),
on(SectorSealFailed{}, api.SealFailed),
),
api.Faulty: planOne(
on(SectorFaultReported{}, api.FaultReported),
),
api.FaultedFinal: final,
}
func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(statemachine.Context, SectorInfo) error, error) {
/////
// First process all events
for _, event := range events {
l := Log{
Timestamp: uint64(time.Now().Unix()),
Message: fmt.Sprintf("%+v", event),
Kind: fmt.Sprintf("event;%T", event.User),
}
if err, iserr := event.User.(xerrors.Formatter); iserr {
l.Trace = fmt.Sprintf("%+v", err)
}
state.Log = append(state.Log, l)
}
p := fsmPlanners[state.State]
if p == nil {
return nil, xerrors.Errorf("planner for state %s not found", api.SectorStates[state.State])
}
if err := p(events, state); err != nil {
return nil, xerrors.Errorf("running planner for state %s failed: %w", api.SectorStates[state.State], err)
}
/////
// Now decide what to do next
/*
* Empty
| |
| v
*<- Packing <- incoming
| |
| v
*<- Unsealed <--> SealFailed
| |
| v
* PreCommitting <--> PreCommitFailed
| | ^
| v |
*<- WaitSeed ----------/
| |||
| vvv v--> SealCommitFailed
*<- Committing
| | ^--> CommitFailed
| v ^
*<- CommitWait ---/
| |
| v
*<- Proving
|
v
FailedUnrecoverable
UndefinedSectorState <- ¯\_()_/¯
| ^
*---------------------/
*/
switch state.State {
// Happy path
case api.Packing:
return m.handlePacking, nil
case api.Unsealed:
return m.handleUnsealed, nil
case api.PreCommitting:
return m.handlePreCommitting, nil
case api.WaitSeed:
return m.handleWaitSeed, nil
case api.Committing:
return m.handleCommitting, nil
case api.CommitWait:
return m.handleCommitWait, nil
case api.Proving:
// TODO: track sector health / expiration
log.Infof("Proving sector %d", state.SectorID)
// Handled failure modes
case api.SealFailed:
return m.handleSealFailed, nil
case api.PreCommitFailed:
return m.handlePreCommitFailed, nil
case api.SealCommitFailed:
log.Warnf("sector %d entered unimplemented state 'SealCommitFailed'", state.SectorID)
case api.CommitFailed:
log.Warnf("sector %d entered unimplemented state 'CommitFailed'", state.SectorID)
// Faults
case api.Faulty:
return m.handleFaulty, nil
case api.FaultReported:
return m.handleFaultReported, nil
// Fatal errors
case api.UndefinedSectorState:
log.Error("sector update with undefined state!")
case api.FailedUnrecoverable:
log.Errorf("sector %d failed unrecoverably", state.SectorID)
default:
log.Errorf("unexpected sector update state: %d", state.State)
}
return nil, nil
}
func planCommitting(events []statemachine.Event, state *SectorInfo) error {
for _, event := range events {
switch e := event.User.(type) {
case globalMutator:
if e.applyGlobal(state) {
return nil
}
case SectorCommitted: // the normal case
e.apply(state)
state.State = api.CommitWait
case SectorSeedReady: // seed changed :/
if e.seed.Equals(&state.Seed) {
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 = api.Committing
return nil
case SectorComputeProofFailed:
state.State = api.SealCommitFailed
case SectorSealFailed:
state.State = api.CommitFailed
default:
return xerrors.Errorf("planCommitting got event of unknown type %T, events: %+v", event.User, events)
}
}
return nil
}
func (m *Sealing) restartSectors(ctx context.Context) error {
trackedSectors, err := m.ListSectors()
if err != nil {
log.Errorf("loading sector list: %+v", err)
}
for _, sector := range trackedSectors {
if err := m.sectors.Send(sector.SectorID, SectorRestart{}); err != nil {
log.Errorf("restarting sector %d: %+v", sector.SectorID, err)
}
}
// TODO: Grab on-chain sector set and diff with trackedSectors
return nil
}
func (m *Sealing) ForceSectorState(ctx context.Context, id uint64, state api.SectorState) error {
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", api.SectorStates[state.State], events)
}
func on(mut mutator, next api.SectorState) func() (mutator, api.SectorState) {
return func() (mutator, api.SectorState) {
return mut, next
}
}
func planOne(ts ...func() (mut mutator, next api.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
}
}
return xerrors.Errorf("planner for state %s only has a plan for a single event only, got %+v", api.SectorStates[state.State], events)
}
if gm, ok := events[0].User.(globalMutator); ok {
gm.applyGlobal(state)
return nil
}
for _, t := range ts {
mut, next := t()
if reflect.TypeOf(events[0].User) != reflect.TypeOf(mut) {
continue
}
if err, iserr := events[0].User.(error); iserr {
log.Warnf("sector %d got error event %T: %+v", state.SectorID, events[0].User, err)
}
events[0].User.(mutator).apply(state)
state.State = next
return nil
}
return xerrors.Errorf("planner for state %s received unexpected event %T (%+v)", api.SectorStates[state.State], events[0].User, events[0])
}
}
+149
View File
@@ -0,0 +1,149 @@
package sealing
import (
"github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/api"
)
type mutator interface {
apply(state *SectorInfo)
}
// globalMutator is an event which can apply in every state
type globalMutator interface {
// applyGlobal applies the event to the state. If if returns true,
// event processing should be interrupted
applyGlobal(state *SectorInfo) bool
}
// Global events
type SectorRestart struct{}
func (evt SectorRestart) applyGlobal(*SectorInfo) bool { return false }
type SectorFatalError struct{ error }
func (evt SectorFatalError) applyGlobal(state *SectorInfo) bool {
log.Errorf("Fatal error on sector %d: %+v", state.SectorID, evt.error)
// TODO: Do we want to mark the state as unrecoverable?
// I feel like this should be a softer error, where the user would
// be able to send a retry event of some kind
return true
}
type SectorForceState struct {
state api.SectorState
}
func (evt SectorForceState) applyGlobal(state *SectorInfo) bool {
state.State = evt.state
return true
}
// Normal path
type SectorStart struct {
id uint64
pieces []Piece
}
func (evt SectorStart) apply(state *SectorInfo) {
state.SectorID = evt.id
state.Pieces = evt.pieces
}
type SectorPacked struct{ pieces []Piece }
func (evt SectorPacked) apply(state *SectorInfo) {
state.Pieces = append(state.Pieces, evt.pieces...)
}
type SectorPackingFailed struct{ error }
func (evt SectorPackingFailed) apply(*SectorInfo) {}
type SectorSealed struct {
commR []byte
commD []byte
ticket SealTicket
}
func (evt SectorSealed) apply(state *SectorInfo) {
state.CommD = evt.commD
state.CommR = evt.commR
state.Ticket = evt.ticket
}
type SectorSealFailed struct{ error }
func (evt SectorSealFailed) apply(*SectorInfo) {}
type SectorPreCommitFailed struct{ error }
func (evt SectorPreCommitFailed) apply(*SectorInfo) {}
type SectorPreCommitted struct {
message cid.Cid
}
func (evt SectorPreCommitted) apply(state *SectorInfo) {
state.PreCommitMessage = &evt.message
}
type SectorSeedReady struct {
seed SealSeed
}
func (evt SectorSeedReady) apply(state *SectorInfo) {
state.Seed = evt.seed
}
type SectorComputeProofFailed struct{ error }
type SectorCommitFailed struct{ error }
func (evt SectorCommitFailed) apply(*SectorInfo) {}
type SectorCommitted struct {
message cid.Cid
proof []byte
}
func (evt SectorCommitted) apply(state *SectorInfo) {
state.Proof = evt.proof
state.CommitMessage = &evt.message
}
type SectorProving struct{}
func (evt SectorProving) apply(*SectorInfo) {}
// Failed state recovery
type SectorRetrySeal struct{}
func (evt SectorRetrySeal) apply(state *SectorInfo) {}
type SectorRetryPreCommit struct{}
func (evt SectorRetryPreCommit) apply(state *SectorInfo) {}
type SectorRetryWaitSeed struct{}
func (evt SectorRetryWaitSeed) apply(state *SectorInfo) {}
// Faults
type SectorFaulty struct{}
func (evt SectorFaulty) apply(state *SectorInfo) {}
type SectorFaultReported struct{ reportMsg cid.Cid }
func (evt SectorFaultReported) apply(state *SectorInfo) {
state.FaultReportMsg = &evt.reportMsg
}
type SectorFaultedFinal struct{}
+85
View File
@@ -0,0 +1,85 @@
package sealing
import (
"testing"
logging "github.com/ipfs/go-log/v2"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/lib/statemachine"
)
func init() {
_ = logging.SetLogLevel("*", "INFO")
}
func (t *test) planSingle(evt interface{}) {
_, err := t.s.plan([]statemachine.Event{{evt}}, t.state)
require.NoError(t.t, err)
}
type test struct {
s *Sealing
t *testing.T
state *SectorInfo
next func(statemachine.Context, SectorInfo) error
}
func TestHappyPath(t *testing.T) {
m := test{
s: &Sealing{},
t: t,
state: &SectorInfo{State: api.Packing},
}
m.planSingle(SectorPacked{})
require.Equal(m.t, m.state.State, api.Unsealed)
m.planSingle(SectorSealed{})
require.Equal(m.t, m.state.State, api.PreCommitting)
m.planSingle(SectorPreCommitted{})
require.Equal(m.t, m.state.State, api.WaitSeed)
m.planSingle(SectorSeedReady{})
require.Equal(m.t, m.state.State, api.Committing)
m.planSingle(SectorCommitted{})
require.Equal(m.t, m.state.State, api.CommitWait)
m.planSingle(SectorProving{})
require.Equal(m.t, m.state.State, api.Proving)
}
func TestSeedRevert(t *testing.T) {
m := test{
s: &Sealing{},
t: t,
state: &SectorInfo{State: api.Packing},
}
m.planSingle(SectorPacked{})
require.Equal(m.t, m.state.State, api.Unsealed)
m.planSingle(SectorSealed{})
require.Equal(m.t, m.state.State, api.PreCommitting)
m.planSingle(SectorPreCommitted{})
require.Equal(m.t, m.state.State, api.WaitSeed)
m.planSingle(SectorSeedReady{})
require.Equal(m.t, m.state.State, api.Committing)
_, err := m.s.plan([]statemachine.Event{{SectorSeedReady{seed: SealSeed{BlockHeight: 5}}}, {SectorCommitted{}}}, m.state)
require.NoError(t, err)
require.Equal(m.t, m.state.State, api.Committing)
// not changing the seed this time
_, err = m.s.plan([]statemachine.Event{{SectorSeedReady{seed: SealSeed{BlockHeight: 5}}}, {SectorCommitted{}}}, m.state)
require.Equal(m.t, m.state.State, api.CommitWait)
m.planSingle(SectorProving{})
require.Equal(m.t, m.state.State, api.Proving)
}
@@ -1,4 +1,4 @@
package storage
package sealing
import (
"bytes"
@@ -14,7 +14,7 @@ import (
"github.com/filecoin-project/lotus/chain/types"
)
func (m *Miner) pledgeSector(ctx context.Context, sectorID uint64, existingPieceSizes []uint64, sizes ...uint64) ([]Piece, error) {
func (m *Sealing) pledgeSector(ctx context.Context, sectorID uint64, existingPieceSizes []uint64, sizes ...uint64) ([]Piece, error) {
if len(sizes) == 0 {
return nil, nil
}
@@ -98,7 +98,7 @@ func (m *Miner) pledgeSector(ctx context.Context, sectorID uint64, existingPiece
return out, nil
}
func (m *Miner) PledgeSector() error {
func (m *Sealing) PledgeSector() error {
go func() {
ctx := context.TODO() // we can't use the context from command which invokes
// this, as we run everything here async, and it's cancelled when the
+135
View File
@@ -0,0 +1,135 @@
package sealing
import (
"context"
"io"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-sectorbuilder"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
logging "github.com/ipfs/go-log/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/padreader"
"github.com/filecoin-project/lotus/lib/statemachine"
)
const SectorStorePrefix = "/sectors"
var log = logging.Logger("sectors")
type TicketFn func(context.Context) (*sectorbuilder.SealTicket, error)
type sealingApi interface { // TODO: trim down
// Call a read only method on actors (no interaction with the chain required)
StateCall(context.Context, *types.Message, *types.TipSet) (*api.MethodCall, error)
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
StateMinerElectionPeriodStart(ctx context.Context, actor address.Address, ts *types.TipSet) (uint64, error)
StateMinerSectors(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error)
StateMinerProvingSet(context.Context, address.Address, *types.TipSet) ([]*api.ChainSectorInfo, error)
StateMinerSectorSize(context.Context, address.Address, *types.TipSet) (uint64, error)
StateWaitMsg(context.Context, cid.Cid) (*api.MsgWait, error) // TODO: removeme eventually
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
StateGetReceipt(context.Context, cid.Cid, *types.TipSet) (*types.MessageReceipt, error)
StateMarketStorageDeal(context.Context, uint64, *types.TipSet) (*actors.OnChainDeal, error)
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error)
ChainHead(context.Context) (*types.TipSet, error)
ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
ChainReadObj(context.Context, cid.Cid) ([]byte, error)
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
WalletBalance(context.Context, address.Address) (types.BigInt, error)
WalletHas(context.Context, address.Address) (bool, error)
}
type Sealing struct {
api sealingApi
events *events.Events
maddr address.Address
worker address.Address
sb sectorbuilder.Interface
sectors *statemachine.StateGroup
tktFn TicketFn
}
func New(api sealingApi, events *events.Events, maddr address.Address, worker address.Address, ds datastore.Batching, sb sectorbuilder.Interface, tktFn TicketFn) *Sealing {
s := &Sealing{
api: api,
events: events,
maddr: maddr,
worker: worker,
sb: sb,
tktFn: tktFn,
}
s.sectors = statemachine.New(namespace.Wrap(ds, datastore.NewKey(SectorStorePrefix)), s, SectorInfo{})
return s
}
func (m *Sealing) Run(ctx context.Context) error {
if err := m.restartSectors(ctx); err != nil {
log.Errorf("%+v", err)
return xerrors.Errorf("failed load sector states: %w", err)
}
return nil
}
func (m *Sealing) Stop(ctx context.Context) error {
return m.sectors.Stop(ctx)
}
func (m *Sealing) AllocatePiece(size uint64) (sectorID uint64, offset uint64, err error) {
if padreader.PaddedSize(size) != size {
return 0, 0, xerrors.Errorf("cannot allocate unpadded piece")
}
sid, err := m.sb.AcquireSectorId() // TODO: Put more than one thing in a sector
if err != nil {
return 0, 0, xerrors.Errorf("acquiring sector ID: %w", err)
}
// offset hard-coded to 0 since we only put one thing in a sector for now
return sid, 0, nil
}
func (m *Sealing) SealPiece(ctx context.Context, size uint64, r io.Reader, sectorID uint64, dealID uint64) error {
log.Infof("Seal piece for deal %d", dealID)
ppi, err := m.sb.AddPiece(size, sectorID, r, []uint64{})
if err != nil {
return xerrors.Errorf("adding piece to sector: %w", err)
}
return m.newSector(ctx, sectorID, dealID, ppi)
}
func (m *Sealing) newSector(ctx context.Context, sid uint64, dealID uint64, ppi sectorbuilder.PublicPieceInfo) error {
return m.sectors.Send(sid, SectorStart{
id: sid,
pieces: []Piece{
{
DealID: dealID,
Size: ppi.Size,
CommP: ppi.CommP[:],
},
},
})
}
+281
View File
@@ -0,0 +1,281 @@
package sealing
import (
"context"
sectorbuilder "github.com/filecoin-project/go-sectorbuilder"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/statemachine"
)
func (m *Sealing) handlePacking(ctx statemachine.Context, sector SectorInfo) error {
log.Infow("performing filling up rest of the sector...", "sector", sector.SectorID)
var allocated uint64
for _, piece := range sector.Pieces {
allocated += piece.Size
}
ubytes := sectorbuilder.UserBytesForSectorSize(m.sb.SectorSize())
if allocated > ubytes {
return xerrors.Errorf("too much data in sector: %d > %d", allocated, ubytes)
}
fillerSizes, err := fillersFromRem(ubytes - allocated)
if err != nil {
return err
}
if len(fillerSizes) > 0 {
log.Warnf("Creating %d filler pieces for sector %d", len(fillerSizes), sector.SectorID)
}
pieces, err := m.pledgeSector(ctx.Context(), sector.SectorID, sector.existingPieces(), fillerSizes...)
if err != nil {
return xerrors.Errorf("filling up the sector (%v): %w", fillerSizes, err)
}
return ctx.Send(SectorPacked{pieces: pieces})
}
func (m *Sealing) handleUnsealed(ctx statemachine.Context, sector SectorInfo) error {
if err := checkPieces(ctx.Context(), sector, m.api); err != nil { // Sanity check state
switch err.(type) {
case *ErrApi:
log.Errorf("handleUnsealed: api error, not proceeding: %+v", err)
return nil
case *ErrInvalidDeals:
return ctx.Send(SectorPackingFailed{xerrors.Errorf("invalid deals in sector: %w", err)})
case *ErrExpiredDeals: // Probably not much we can do here, maybe re-pack the sector?
return ctx.Send(SectorPackingFailed{xerrors.Errorf("expired deals in sector: %w", err)})
default:
return xerrors.Errorf("checkPieces sanity check error: %w", err)
}
}
log.Infow("performing sector replication...", "sector", sector.SectorID)
ticket, err := m.tktFn(ctx.Context())
if err != nil {
return ctx.Send(SectorSealFailed{xerrors.Errorf("getting ticket failed: %w", err)})
}
rspco, err := m.sb.SealPreCommit(ctx.Context(), sector.SectorID, *ticket, sector.pieceInfos())
if err != nil {
return ctx.Send(SectorSealFailed{xerrors.Errorf("seal pre commit failed: %w", err)})
}
return ctx.Send(SectorSealed{
commD: rspco.CommD[:],
commR: rspco.CommR[:],
ticket: SealTicket{
BlockHeight: ticket.BlockHeight,
TicketBytes: ticket.TicketBytes[:],
},
})
}
func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInfo) error {
if err := checkSeal(ctx.Context(), m.maddr, sector, m.api); err != nil {
switch err.(type) {
case *ErrApi:
log.Errorf("handlePreCommitting: api error, not proceeding: %+v", err)
return nil
case *ErrBadCommD: // TODO: Should this just back to packing? (not really needed since handleUnsealed will do that too)
return ctx.Send(SectorSealFailed{xerrors.Errorf("bad CommD error: %w", err)})
case *ErrExpiredTicket:
return ctx.Send(SectorSealFailed{xerrors.Errorf("bad CommD error: %w", err)})
default:
return xerrors.Errorf("checkSeal sanity check error: %w", err)
}
}
params := &actors.SectorPreCommitInfo{
SectorNumber: sector.SectorID,
CommR: sector.CommR,
SealEpoch: sector.Ticket.BlockHeight,
DealIDs: sector.deals(),
}
enc, aerr := actors.SerializeParams(params)
if aerr != nil {
return ctx.Send(SectorPreCommitFailed{xerrors.Errorf("could not serialize commit sector parameters: %w", aerr)})
}
msg := &types.Message{
To: m.maddr,
From: m.worker,
Method: actors.MAMethods.PreCommitSector,
Params: enc,
Value: types.NewInt(0), // TODO: need to ensure sufficient collateral
GasLimit: types.NewInt(1000000 /* i dont know help */),
GasPrice: types.NewInt(1),
}
log.Info("submitting precommit for sector: ", sector.SectorID)
smsg, err := m.api.MpoolPushMessage(ctx.Context(), msg)
if err != nil {
return ctx.Send(SectorPreCommitFailed{xerrors.Errorf("pushing message to mpool: %w", err)})
}
return ctx.Send(SectorPreCommitted{message: smsg.Cid()})
}
func (m *Sealing) handleWaitSeed(ctx statemachine.Context, sector SectorInfo) error {
// would be ideal to just use the events.Called handler, but it wouldnt be able to handle individual message timeouts
log.Info("Sector precommitted: ", sector.SectorID)
mw, err := m.api.StateWaitMsg(ctx.Context(), *sector.PreCommitMessage)
if err != nil {
return ctx.Send(SectorPreCommitFailed{err})
}
if mw.Receipt.ExitCode != 0 {
log.Error("sector precommit failed: ", mw.Receipt.ExitCode)
err := xerrors.Errorf("sector precommit failed: %d", mw.Receipt.ExitCode)
return ctx.Send(SectorPreCommitFailed{err})
}
log.Info("precommit message landed on chain: ", sector.SectorID)
randHeight := mw.TipSet.Height() + build.InteractivePoRepDelay - 1 // -1 because of how the messages are applied
log.Infof("precommit for sector %d made it on chain, will start proof computation at height %d", sector.SectorID, randHeight)
err = m.events.ChainAt(func(ectx context.Context, ts *types.TipSet, curH uint64) error {
rand, err := m.api.ChainGetRandomness(ectx, ts.Key(), int64(randHeight))
if err != nil {
err = xerrors.Errorf("failed to get randomness for computing seal proof: %w", err)
ctx.Send(SectorFatalError{error: err})
return err
}
ctx.Send(SectorSeedReady{seed: SealSeed{
BlockHeight: randHeight,
TicketBytes: rand,
}})
return nil
}, func(ctx context.Context, ts *types.TipSet) error {
log.Warn("revert in interactive commit sector step")
// TODO: need to cancel running process and restart...
return nil
}, build.InteractivePoRepConfidence, mw.TipSet.Height()+build.InteractivePoRepDelay)
if err != nil {
log.Warn("waitForPreCommitMessage ChainAt errored: ", err)
}
return nil
}
func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) error {
log.Info("scheduling seal proof computation...")
proof, err := m.sb.SealCommit(ctx.Context(), sector.SectorID, sector.Ticket.SB(), sector.Seed.SB(), sector.pieceInfos(), sector.rspco())
if err != nil {
return ctx.Send(SectorComputeProofFailed{xerrors.Errorf("computing seal proof failed: %w", err)})
}
// TODO: Consider splitting states and persist proof for faster recovery
params := &actors.SectorProveCommitInfo{
Proof: proof,
SectorID: sector.SectorID,
DealIDs: sector.deals(),
}
enc, aerr := actors.SerializeParams(params)
if aerr != nil {
return ctx.Send(SectorCommitFailed{xerrors.Errorf("could not serialize commit sector parameters: %w", aerr)})
}
msg := &types.Message{
To: m.maddr,
From: m.worker,
Method: actors.MAMethods.ProveCommitSector,
Params: enc,
Value: types.NewInt(0), // TODO: need to ensure sufficient collateral
GasLimit: types.NewInt(1000000 /* i dont know help */),
GasPrice: types.NewInt(1),
}
// TODO: check seed / ticket are up to date
smsg, err := m.api.MpoolPushMessage(ctx.Context(), msg)
if err != nil {
return ctx.Send(SectorCommitFailed{xerrors.Errorf("pushing message to mpool: %w", err)})
}
return ctx.Send(SectorCommitted{
proof: proof,
message: smsg.Cid(),
})
}
func (m *Sealing) handleCommitWait(ctx statemachine.Context, sector SectorInfo) error {
if sector.CommitMessage == nil {
log.Errorf("sector %d entered commit wait state without a message cid", sector.SectorID)
return ctx.Send(SectorCommitFailed{xerrors.Errorf("entered commit wait with no commit cid")})
}
mw, err := m.api.StateWaitMsg(ctx.Context(), *sector.CommitMessage)
if err != nil {
return ctx.Send(SectorCommitFailed{xerrors.Errorf("failed to wait for porep inclusion: %w", err)})
}
if mw.Receipt.ExitCode != 0 {
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.Ticket.TicketBytes, sector.Seed.TicketBytes, sector.Seed.BlockHeight, sector.Proof)})
}
return ctx.Send(SectorProving{})
}
func (m *Sealing) handleFaulty(ctx statemachine.Context, sector SectorInfo) error {
// TODO: check if the fault has already been reported, and that this sector is even valid
// TODO: coalesce faulty sector reporting
bf := types.NewBitField()
bf.Set(sector.SectorID)
enc, aerr := actors.SerializeParams(&actors.DeclareFaultsParams{bf})
if aerr != nil {
return xerrors.Errorf("failed to serialize declare fault params: %w", aerr)
}
msg := &types.Message{
To: m.maddr,
From: m.worker,
Method: actors.MAMethods.DeclareFaults,
Params: enc,
Value: types.NewInt(0), // TODO: need to ensure sufficient collateral
GasLimit: types.NewInt(1000000 /* i dont know help */),
GasPrice: types.NewInt(1),
}
smsg, err := m.api.MpoolPushMessage(ctx.Context(), msg)
if err != nil {
return xerrors.Errorf("failed to push declare faults message to network: %w", err)
}
return ctx.Send(SectorFaultReported{reportMsg: smsg.Cid()})
}
func (m *Sealing) handleFaultReported(ctx statemachine.Context, sector SectorInfo) error {
if sector.FaultReportMsg == nil {
return xerrors.Errorf("entered fault reported state without a FaultReportMsg cid")
}
mw, err := m.api.StateWaitMsg(ctx.Context(), *sector.FaultReportMsg)
if err != nil {
return xerrors.Errorf("failed to wait for fault declaration: %w", err)
}
if mw.Receipt.ExitCode != 0 {
log.Errorf("UNHANDLED: declaring sector fault failed (exit=%d, msg=%s) (id: %d)", mw.Receipt.ExitCode, *sector.FaultReportMsg, sector.SectorID)
return xerrors.Errorf("UNHANDLED: submitting fault declaration failed (exit %d)", mw.Receipt.ExitCode)
}
return ctx.Send(SectorFaultedFinal{})
}
+119
View File
@@ -0,0 +1,119 @@
package sealing
import (
"bytes"
"fmt"
"time"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/lib/statemachine"
)
const minRetryTime = 1 * time.Minute
func failedCooldown(ctx statemachine.Context, sector SectorInfo) error {
retryStart := time.Unix(int64(sector.Log[len(sector.Log)-1].Timestamp), 0).Add(minRetryTime)
if len(sector.Log) > 0 && !time.Now().After(retryStart) {
log.Infof("%s(%d), waiting %s before retrying", api.SectorStates[sector.State], sector.SectorID, time.Until(retryStart))
select {
case <-time.After(time.Until(retryStart)):
case <-ctx.Context().Done():
return ctx.Context().Err()
}
}
return nil
}
func (m *Sealing) checkPreCommitted(ctx statemachine.Context, sector SectorInfo) (*actors.PreCommittedSector, bool) {
act, err := m.api.StateGetActor(ctx.Context(), m.maddr, nil)
if err != nil {
log.Errorf("handleSealFailed(%d): temp error: %+v", sector.SectorID, err)
return nil, true
}
st, err := m.api.ChainReadObj(ctx.Context(), act.Head)
if err != nil {
log.Errorf("handleSealFailed(%d): temp error: %+v", sector.SectorID, err)
return nil, true
}
var state actors.StorageMinerActorState
if err := state.UnmarshalCBOR(bytes.NewReader(st)); err != nil {
log.Errorf("handleSealFailed(%d): temp error: unmarshaling miner state: %+v", sector.SectorID, err)
return nil, true
}
pci, found := state.PreCommittedSectors[fmt.Sprint(sector.SectorID)]
if found {
// TODO: If not expired yet, we can just try reusing sealticket
log.Warnf("sector %d found in miner preseal array", sector.SectorID)
return pci, true
}
return nil, false
}
func (m *Sealing) handleSealFailed(ctx statemachine.Context, sector SectorInfo) error {
if _, is := m.checkPreCommitted(ctx, sector); is {
// TODO: Remove this after we can re-precommit
return nil // noop, for now
}
if err := failedCooldown(ctx, sector); err != nil {
return err
}
return ctx.Send(SectorRetrySeal{})
}
func (m *Sealing) handlePreCommitFailed(ctx statemachine.Context, sector SectorInfo) error {
if err := checkSeal(ctx.Context(), m.maddr, sector, m.api); err != nil {
switch err.(type) {
case *ErrApi:
log.Errorf("handlePreCommitFailed: api error, not proceeding: %+v", err)
return nil
case *ErrBadCommD: // TODO: Should this just back to packing? (not really needed since handleUnsealed will do that too)
return ctx.Send(SectorSealFailed{xerrors.Errorf("bad CommD error: %w", err)})
case *ErrExpiredTicket:
return ctx.Send(SectorSealFailed{xerrors.Errorf("ticket expired error: %w", err)})
default:
return xerrors.Errorf("checkSeal sanity check error: %w", err)
}
}
if pci, is := m.checkPreCommitted(ctx, sector); is && pci != nil {
if sector.PreCommitMessage != nil {
log.Warn("sector %d is precommitted on chain, but we don't have precommit message", sector.SectorID)
return nil // TODO: SeedWait needs this currently
}
if string(pci.Info.CommR) != string(sector.CommR) {
log.Warn("sector %d is precommitted on chain, with different CommR: %x != %x", sector.SectorID, pci.Info.CommR, sector.CommR)
return nil // TODO: remove when the actor allows re-precommit
}
// TODO: we could compare more things, but I don't think we really need to
// CommR tells us that CommD (and CommPs), and the ticket are all matching
if err := failedCooldown(ctx, sector); err != nil {
return err
}
return ctx.Send(SectorRetryWaitSeed{})
}
if sector.PreCommitMessage != nil {
log.Warn("retrying precommit even though the message failed to apply")
}
if err := failedCooldown(ctx, sector); err != nil {
return err
}
return ctx.Send(SectorRetryPreCommit{})
}

Some files were not shown because too many files have changed in this diff Show More