Compare commits

...
204 Commits
Author SHA1 Message Date
Aayush Rajasekaran 1ebad94da9 Merge pull request #4117 from filecoin-project/asr/changelog
Lotus version 0.8.1
2020-09-30 21:40:08 -04:00
Aayush Rajasekaran 636810daa5 Lotus version 0.8.1 2020-09-30 21:30:52 -04:00
Łukasz Magiera 954d806367 Merge pull request #4116 from filecoin-project/feat/sched-par-pcc2
sched: Allow some single-thread tasks to run in parallel with PC2/C2
2020-10-01 01:35:05 +02:00
Aayush Rajasekaran 7699f195f1 Merge pull request #4108 from filecoin-project/feat/nice-sectorslist
Improve miner sectors list UX
2020-09-30 19:07:23 -04:00
Łukasz Magiera 6981f776f4 Lower PC2 memory requirements 2020-10-01 00:54:53 +02:00
Łukasz Magiera 1b7cdb9341 Fix storage manager tests 2020-10-01 00:54:34 +02:00
Łukasz Magiera 5e08d56630 sched: Allow some single-thread tasks to run in parallel with PC2/C2 2020-10-01 00:28:44 +02:00
Łukasz Magiera feecee310d Merge pull request #4089 from filecoin-project/feat/update-markets-0.7.0
Markets 0.7.0 with updated data stores
2020-09-30 21:49:14 +02:00
hannahhoward be884e27be feat(markets): update markets 0.7.0 2020-09-30 10:26:50 -07:00
Łukasz Magiera 5bffea6f54 Merge pull request #4064 from filecoin-project/tvx
tvx: a test vector extraction and execution tool
2020-09-30 17:22:42 +02:00
Raúl Kripalani 1c4f8e83d7 tvx/extract-many: add batch id, change generated filename. 2020-09-30 16:11:44 +01:00
Raúl Kripalani ff8663faa0 update test-vectors submodule. 2020-09-30 14:58:22 +01:00
Raúl Kripalani f58881e966 minor fixes. 2020-09-30 14:57:24 +01:00
Łukasz Magiera d2f279c3de Print heman-readable transferred bytes in client list-transfers 2020-09-30 13:51:52 +02:00
Łukasz Magiera 4d4bab12eb Improve miner sectors list UX 2020-09-30 13:34:05 +02:00
Raúl Kripalani eb6191d0ff tvx: precursor selection modes; canonical message fetching; basefee. 2020-09-30 11:02:10 +01:00
Łukasz Magiera 54dfeb1fd9 Merge pull request #4043 from zgfzgf/test-post
add printf in TestWindowPost
2020-09-30 10:53:16 +02:00
Łukasz Magiera c2c64caefe Merge pull request #4020 from zgfzgf/post-trace
add trace wdpost
2020-09-30 10:46:05 +02:00
Łukasz Magiera 90be8d801b Merge branch 'master' into post-trace 2020-09-30 10:03:54 +02:00
Łukasz Magiera 430897c9ac Merge pull request #3445 from filecoin-project/asr/precommit-failed-fix
Fix some failed precommit handling
2020-09-30 09:35:41 +02:00
Aayush Rajasekaran c77ab47cb5 Merge pull request #4103 from filecoin-project/asr/config
Add an option to set config
2020-09-30 03:33:52 -04:00
Łukasz Magiera bc85e3ce50 Merge pull request #4107 from shaodan/worker-no-swap
Add --no-swap flag for worker
2020-09-30 09:25:57 +02:00
Łukasz Magiera 76b1ec1de0 Merge pull request #4065 from filecoin-project/asr/sync-validate
Add lotus shed util to validate a tipset
2020-09-30 09:24:47 +02:00
Aayush Rajasekaran 6abccc4d5e Add an option to set config 2020-09-30 03:19:04 -04:00
Łukasz Magiera 7a3a2f8db9 Merge pull request #3924 from filecoin-project/feat/dont-recompute-PoST-redux
Dont recompute post on revert <<redux>>
2020-09-30 09:18:46 +02:00
Łukasz Magiera 4e8ccb2158 Merge pull request #4092 from filecoin-project/fix/finalize-uns-panic
sectorstorage: Fix potential panic in FinalizeSector
2020-09-30 09:18:01 +02:00
Dan Shao 1affd498c1 Add --no-swap flag for worker 2020-09-30 14:23:35 +08:00
Aayush Rajasekaran e250429f56 Merge pull request #4106 from filecoin-project/asr/validate-address
Add an endpoint to validate whether a string is a well-formed address
2020-09-30 01:58:29 -04:00
Aayush Rajasekaran bc4cbdc895 Update docs 2020-09-30 01:45:03 -04:00
Aayush Rajasekaran a388bcfad6 Add an endpoint to validate whether a string is a well-formed address 2020-09-30 01:43:10 -04:00
Łukasz Magiera e39c5dc372 Merge pull request #4104 from filecoin-project/asr/wallet-fix
Fix wallet list
2020-09-30 07:41:10 +02:00
Aayush Rajasekaran c45c8f34a1 Parametrise whether sync validators should use cache 2020-09-30 01:39:06 -04:00
Aayush Rajasekaran 73d193bd9c Update docs 2020-09-30 01:29:04 -04:00
Aayush Rajasekaran a4e7117429 Add lotus shed util to validate a tipset 2020-09-30 01:29:04 -04:00
Aayush Rajasekaran 9fe32b7777 Fix wallet list 2020-09-30 01:25:47 -04:00
Łukasz Magiera 6025bceaa1 Merge pull request #3431 from filecoin-project/feat/docs-reorg
Docs review and re-organization
2020-09-29 22:29:58 +02:00
Łukasz Magiera 91d04ede0f Merge pull request #4051 from filecoin-project/chore/final-dealstat-version
Chore/final dealstat version
2020-09-29 19:39:16 +02:00
Łukasz Magiera 237c8c4deb Merge pull request #3868 from austinabell/async/stransition
Make state transition in validation async
2020-09-29 16:50:52 +02:00
Łukasz Magiera 0ba911279a Merge pull request #3888 from filecoin-project/feat/bench-cache
Implement caching syscalls for import-bench
2020-09-29 16:38:05 +02:00
Łukasz Magiera baef3c8dd2 sectorstorage: Fix potential panic in FinalizeSector 2020-09-29 15:22:46 +02:00
Łukasz Magiera 867ca2f25d Merge pull request #3434 from filecoin-project/feat/shed-import-object-cmd
add a command to import an ipld object into the chainstore
2020-09-29 15:02:31 +02:00
Jakub Sztandera 247a5e2c49 Go mod tidy
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:50:08 +02:00
Jakub Sztandera 7e8c6e5070 Remove statediff, fix lint, go mod tidy
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:47 +02:00
Jakub Sztandera 0d914ac1d4 Switch to badger
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:47 +02:00
Jakub Sztandera 76db65b1af Update pebble
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:47 +02:00
Jakub Sztandera ff8c0af8c8 Add only-import option
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:47 +02:00
Jakub Sztandera 55c6b88537 Add toggle for badger, flag out gas tracing
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:46 +02:00
Jakub Sztandera 0771c23fb0 Use pebble
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:46 +02:00
Jakub Sztandera 1c6214b76d Usage go-bitfield with buffer pool
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:45 +02:00
Jakub Sztandera b7f18b4601 Disable callers
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:18 +02:00
Jakub Sztandera 242a77b391 go mod tidy
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:18 +02:00
Jakub Sztandera 35cf69ae64 Disable bloomcache
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:18 +02:00
Jakub Sztandera ebc8489ff1 Add global-profile option
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:18 +02:00
Jakub Sztandera f21c5cbbe2 Add start-at
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:18 +02:00
Jakub Sztandera 01386a206c Update options
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:18 +02:00
Jakub Sztandera 3858309368 Add http to import bench
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:17 +02:00
Jakub Sztandera 782717948a Add logs
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:17 +02:00
Jakub Sztandera 108fe7823c Add command to trigger gc
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:17 +02:00
Jakub Sztandera 12a0dd3d0a <3 to linter
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:17 +02:00
Jakub Sztandera 1f4d1dcc58 Do not sync
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:17 +02:00
Jakub Sztandera 53ab17cf50 Add no import to import-bench
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:17 +02:00
Jakub Sztandera 79ba4598d6 Implement cache
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:17 +02:00
Jakub Sztandera 96193c2044 Implement bench-cache
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-29 14:49:11 +02:00
Hector Sanjuan 09e5cc90a4 Add README to documentation/en with explanations 2020-09-29 14:45:55 +02:00
Łukasz Magiera edf1875e88 Merge pull request #4003 from filecoin-project/option-to-hide-sectors
Added an option to show or not show sector info for `lotus-miner info`
2020-09-29 14:26:23 +02:00
Łukasz Magiera 1479142047 Merge pull request #3970 from filecoin-project/feat/mpool-to-wallet
refactor: move nonce generation out of mpool
2020-09-29 13:24:35 +02:00
Dirk McCormick d1c10a61dd fix: message signer - always compare with mpool nonce 2020-09-29 12:20:09 +02:00
Łukasz Magiera 8205149675 Merge pull request #4088 from filecoin-project/feat/walletlist-ux
wallet list cli: Print balances/nonces
2020-09-29 11:33:29 +02:00
Łukasz Magiera eaece306b6 wallet list cli: Print balances/nonces 2020-09-29 11:21:08 +02:00
Raúl Kripalani 044674487e fix double mutex. 2020-09-28 23:14:37 +01:00
Raúl Kripalani 0070d2716c Merge pull request #4078 from filecoin-project/tvx-circsupply 2020-09-28 23:12:43 +01:00
Raúl Kripalani 9b403e26e5 fix lint. 2020-09-28 23:07:45 +01:00
Raúl Kripalani 4b3b35c9de conformance: record and feed circulating supply. 2020-09-28 23:04:52 +01:00
jennijuju 64f24fd276 Added an option to hide sector info for lotus-miner info 2020-09-28 15:42:50 -04:00
Łukasz Magiera 4b61f1756e Merge pull request #4070 from filecoin-project/feat/import-ux
snapshot import progress bar, http support
2020-09-28 20:25:53 +02:00
Jakub Sztandera 2633198e6c Merge pull request #4056 from zgfzgf/tipset-equals
optimize Tipset equals
2020-09-28 20:15:54 +02:00
Łukasz Magiera b84cc46c3d Merge pull request #4074 from filecoin-project/refactor/chain-store-ts-parallel
fetch tipset blocks in parallel
2020-09-28 18:43:43 +02:00
Raúl Kripalani 96f882860f add extract-many command. 2020-09-28 17:02:56 +01:00
Dirk McCormick cfe6f595b0 fix: unsafe append in LoadTipSet 2020-09-28 16:54:18 +02:00
Dirk McCormick 6b16d48bad refactor: fetch tipset blocks in parallel 2020-09-28 15:56:44 +02:00
Raúl Kripalani 9f6862a456 fix lint. 2020-09-28 13:22:56 +01:00
Raúl Kripalani 4c9717187f tvx/extract: small refactor. 2020-09-28 13:19:45 +01:00
Raúl Kripalani a0dffb44d3 tvx/extract: perform sanity check on receipt. 2020-09-28 13:11:34 +01:00
Raúl Kripalani 8f3be78667 tvx/extract: allow passing in block to speed things up. 2020-09-28 13:00:07 +01:00
Raúl Kripalani dfdcbd184d add --repo flag. 2020-09-28 12:35:01 +01:00
Raúl Kripalani f05a40feed use lotus CLI package; document API endpoint setting in usage. 2020-09-28 12:27:42 +01:00
Łukasz Magiera f809529699 daemon cmd: Support imports straight from http 2020-09-28 12:30:12 +02:00
Łukasz Magiera a3145bae07 daemon cmd: Add progress bar to chain import 2020-09-28 12:17:54 +02:00
Łukasz Magiera b14afba859 Merge pull request #4058 from zgfzgf/miner-debug
miner debug where injectNulls != 0
2020-09-28 11:52:42 +02:00
Łukasz Magiera c33b3dfa55 Merge pull request #4022 from filecoin-project/feat/dont-recompute-PoST-abstract
Use abstract types for Dont recompute post on revert
2020-09-28 11:44:38 +02:00
Łukasz Magiera c448d85cf5 Merge pull request #4050 from filecoin-project/fix/stats-network-power
Add back network power to stats
2020-09-28 11:33:03 +02:00
Łukasz Magiera bc495a5c8c Merge pull request #4049 from filecoin-project/fix/miner-power-cli
fix GetPower with no miner address
2020-09-28 11:30:54 +02:00
Łukasz Magiera 18dac6d415 Merge pull request #4067 from IPFSMain-Official/master
add ipfsmain bootstrapper
2020-09-28 09:35:23 +02:00
Łukasz Magiera 5a72662d26 Merge branch 'master' into master 2020-09-28 09:22:21 +02:00
Łukasz Magiera b479988044 Merge pull request #4061 from filecoin-project/dev
Post-upgrade staging branch
2020-09-28 09:20:47 +02:00
刘林欣 a2d24b5b33 add ipfsmain bootstrapper 2020-09-28 13:53:29 +08:00
Raúl Kripalani 9a355c4bc5 fix lint errors. 2020-09-27 21:11:32 +01:00
Raúl Kripalani fe869c9c22 address review comments; lint. 2020-09-27 21:06:07 +01:00
Raúl Kripalani f5f23f7291 driver: option for VM flushing. 2020-09-27 20:55:09 +01:00
Raúl Kripalani a712c109d8 tvx/extract: print confirmation. 2020-09-27 20:30:32 +01:00
Raúl Kripalani e5c56da321 move conformance tvx tool to lotus. 2020-09-27 20:10:05 +01:00
Peter Rabbitson be5dc2c57f Walk back 10 epochs for stat generation 2020-09-27 20:45:45 +02:00
Peter Rabbitson e4c1f090af Disable exclusion of miner-associated addresses 2020-09-27 20:44:50 +02:00
zgfzgf 7a14455ac8 miner debug where injectNulls != 0 2020-09-27 15:01:42 +08:00
zgfzgf 04876c663e modify tipset Equals 2020-09-27 10:17:06 +08:00
zgfzgf 70071e273d optimize tipset Equals func 2020-09-27 09:58:37 +08:00
Peter Rabbitson fb3bcc4ce5 Add startup warning 2020-09-26 21:49:05 +02:00
Peter Rabbitson 1483f1e59a Add filtering of addresses associated with miners 2020-09-26 21:45:34 +02:00
Peter Rabbitson 10cdbadd82 Arrange json as the frontend expects it 2020-09-26 21:44:40 +02:00
Peter Rabbitson 8955b8d8a7 Centralize filtering, output wallet addresses 2020-09-26 21:44:36 +02:00
Travis Person 1c5cb50da3 Add back network power to stats 2020-09-26 17:09:16 +00:00
whyrusleeping bddd6dd8a8 fix GetPower with no miner address 2020-09-26 11:06:16 -05:00
zgfzgf 650725d71e add printf 2020-09-26 17:30:58 +08:00
Łukasz Magiera c243378cab Merge pull request #4032 from filecoin-project/fix/bench-2k
Move policy change to seal bench
2020-09-26 10:27:03 +02:00
Łukasz Magiera e15065b142 Merge pull request #4038 from filecoin-project/feat/state-compute-json
add json output to state compute
2020-09-26 10:20:30 +02:00
Łukasz Magiera 71fa198eee Merge pull request #4004 from ArseniiPetrovich/patch-1
Add glif node to bootstrap peers
2020-09-26 10:19:02 +02:00
Łukasz Magiera ca1715616f Merge branch 'dev' into patch-1 2020-09-26 10:18:47 +02:00
Łukasz Magiera b12bade989 Merge pull request #4007 from maobisheng/master
Add new booststrappers
2020-09-26 10:18:04 +02:00
Łukasz Magiera 28566b4d64 Merge branch 'dev' into master 2020-09-26 10:17:45 +02:00
Łukasz Magiera dfd1473384 Merge pull request #4041 from ipfs-force-community/master
Add one more node located in China
2020-09-26 10:16:01 +02:00
Łukasz Magiera 3fe0aa5026 Merge pull request #4030 from filecoin-project/feat/export-logging
add logging to chain export
2020-09-26 10:14:32 +02:00
Łukasz Magiera 6a502090f4 Merge pull request #4028 from filecoin-project/asr/address-update
Add an envvar to set address network version
2020-09-26 10:14:10 +02:00
Łukasz Magiera 3d40fb294e Merge pull request #3998 from filecoin-project/deps/update-ffi-blst-fix
update ffi to code with blst fixes
2020-09-26 10:12:34 +02:00
Steven Li 81a30cbf06 Add one more node located in China 2020-09-26 16:01:10 +08:00
Łukasz Magiera 2c1d96bcaa Merge pull request #4040 from filecoin-project/asr/changelog
Lotus version 0.8.0
2020-09-26 09:16:49 +02:00
Aayush Rajasekaran 45eadc1b3a Lotus version 0.8.0 2020-09-26 03:13:20 -04:00
Łukasz Magiera b453b8da99 Merge pull request #4039 from filecoin-project/feat/v0.8-actors
Ignition upgrades, for release v0.8.0
2020-09-26 09:13:07 +02:00
whyrusleeping 567261e2c7 set upgrade heights for testground builds 2020-09-26 00:03:03 -07:00
Aayush Rajasekaran 12e97dbea7 Fix docs and linter 2020-09-26 02:59:24 -04:00
Łukasz Magiera ef28ebb14a Ignition upgrades, much excite. 2020-09-25 23:55:37 -07:00
whyrusleeping 8545c08f30 add json output to state compute 2020-09-25 23:47:59 -07:00
Jakub Sztandera eaf3424938 Merge pull request #4033 from filecoin-project/asr/add-supported-proof
Fix AddSupportedProofTypes
2020-09-26 00:13:53 +02:00
Steven Allen a5f13a5b31 Test supported proof types. 2020-09-25 14:36:36 -07:00
Aayush Rajasekaran d8431ff611 Fix AddSupportedProofTypes 2020-09-25 17:33:25 -04:00
Jakub Sztandera 4eec4a0141 Move policy change to seal bench
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-09-25 23:28:12 +02:00
Steven Allen 6db6a90b1e Merge pull request #4026 from filecoin-project/steb/fix-status-inactive
return an error when we fail to find a sector when checking sector expiration
2020-09-25 13:28:27 -07:00
Travis Person a13d10e42d add logging to chain export 2020-09-25 20:20:18 +00:00
Aayush Rajasekaran 60e43ccbb1 Add an envvar to set address network version 2020-09-25 15:45:27 -04:00
Steven Allen ddcbcdea48 test sector status on expiring sectors 2020-09-25 12:16:35 -07:00
Łukasz Magiera 3491e7b707 Merge pull request #4013 from filecoin-project/feat/update-markets-0.6.3
Update markets to 0.6.3
2020-09-25 20:42:04 +02:00
Steven Allen bdc782617f return an error when we fail to find a sector when checking sector expiration
Returning nil, nil is a footgun.

fix: https://github.com/filecoin-project/lotus/issues/3984
2020-09-25 11:28:38 -07:00
Dirk McCormick 80a7ed8116 refactor: use abstract types instead of specs-actors 2020-09-25 15:54:27 +02:00
zgfzgf 13b38815c2 add trace wdpost 2020-09-25 20:31:07 +08:00
MaoBisheng-IPFSUNION 9dfce400a7 delate 2020-09-25 17:36:57 +08:00
hannahhoward 4ff38aa856 feat(markets): update to 0.6.3 2020-09-25 02:35:34 -07:00
Łukasz Magiera e5873d5dea Merge pull request #4009 from filecoin-project/asr/fractional-asks
Correct helptext
2020-09-25 09:00:02 +02:00
Aayush Rajasekaran 8fbbebbaf1 Correct helptext around set ask 2020-09-25 00:11:10 -04:00
MaoBisheng-IPFSUNION 89bfe84f36 add new booststrappers
we hope to add two booststrappers for the filecoin network to improve the network synchronization
2020-09-25 10:18:12 +08:00
Arsenii Petrovich 585b8cc51d Add glif node to bootstrap peers 2020-09-25 03:54:29 +03:00
whyrusleeping 306c098d30 also update our vendored blst repo 2020-09-24 11:32:38 -07:00
whyrusleeping 6db37b72a8 update ffi to code with blst fixes 2020-09-24 11:05:21 -07:00
Łukasz Magiera 80ee3be4d0 Merge pull request #3870 from filecoin-project/asr/msig-set-locked
Add CLIs for new multisig LockBalance method
2020-09-24 19:42:47 +02:00
Hector Sanjuan e48c525053 Re-add jaeger-tracing 2020-09-24 19:41:14 +02:00
Łukasz Magiera cf9dfdb972 Multisig: fix build 2020-09-24 19:26:32 +02:00
Łukasz Magiera 6ca5308c82 Merge pull request #3996 from filecoin-project/conformance-ntwork-upgrade-adjustments
small adjustments following network upgradability changes
2020-09-24 19:24:45 +02:00
Raúl Kripalani 68663060dc add state.StateTree#Version() accessor. 2020-09-24 17:58:49 +01:00
Raúl Kripalani 87b48c94a6 conformance: supply network version to driver. 2020-09-24 17:58:34 +01:00
Jakub Sztandera 45d8cb08ba Merge pull request #3995 from filecoin-project/feat/more-stateroot-stats
add some more big pictures stats to stateroot stat
2020-09-24 18:57:05 +02:00
Łukasz Magiera 130ae3ccb3 Multisig: Fix from flag descriptions 2020-09-24 18:46:21 +02:00
whyrusleeping 4eaa05db52 add some more big pictures stats to stateroot stat 2020-09-24 09:40:49 -07:00
Łukasz Magiera a19023fada Merge remote-tracking branch 'origin/master' into asr/msig-set-locked 2020-09-24 18:38:02 +02:00
Łukasz Magiera 85caa48814 Merge pull request #3991 from filecoin-project/feat/nicer-syncwait
Make sync wait nicer
2020-09-24 17:03:21 +02:00
Łukasz Magiera cdda66a154 Merge pull request #3942 from filecoin-project/3941-deadline-info
3941: Added cmd `lotus-miner proving deadline <deadline index>`
2020-09-24 16:25:45 +02:00
jennijuju f8f335df55 3941: Added cmd `lotus-miner proving deadline <deadline index>"
- To show the current proving period deadline information of given deadline index.
- It outputs the following:
        - number of partitions in this deadline
        - partitions numbers has submitted PoSt since the current proving period started
        - if the deadline is the current proving deadline
        - for each patition, shows the amount of the sectors in this partition, and their numbers. Also, shows the number of fault sectors and corresponding sector numbers.
2020-09-24 16:23:12 +02:00
Łukasz Magiera 28b46ad08c Merge branch 'zgfzgf-miner-break' into master 2020-09-24 16:03:47 +02:00
Łukasz Magiera 43323b2188 Use continue instead of goto 2020-09-24 16:03:24 +02:00
Łukasz Magiera 17a84c9a2c Merge pull request #3975 from filecoin-project/steb/abstract-actor-policy
Add some actors policy setters for testing
2020-09-24 15:51:41 +02:00
Dirk McCormick 7eb9bec13f feat: dont recompute post on submit redux 2020-09-24 14:38:33 +02:00
Łukasz Magiera 2867b31d2b Merge pull request #3208 from austinabell/extratsload
Update beacon entry load error message
2020-09-24 14:19:00 +02:00
Dirk McCormick 3c524ac0e0 refactor: move nonce generation from mpool to wallet 2020-09-24 14:09:42 +02:00
Łukasz Magiera 1a70dd4fb4 Merge pull request #3977 from filecoin-project/steb/remove-cbor-abstraction
Remove a misleading miner actor abstraction
2020-09-24 14:02:59 +02:00
Łukasz Magiera 15eddf0c96 Make sync wait nicer 2020-09-24 13:39:49 +02:00
Łukasz Magiera bbcad52ce0 Merge pull request #3980 from filecoin-project/feat/batch-blockstore-copy
batch blockstore copies after block validation
2020-09-24 13:07:16 +02:00
Łukasz Magiera ded3a30f55 fix lint 2020-09-24 11:56:54 +02:00
whyrusleeping 38e256cece add some tracing to the vm's blockstore copy 2020-09-23 20:19:20 -07:00
whyrusleeping b4e03d1759 batch blockstore copies after block validation 2020-09-23 18:53:28 -07:00
Steven Allen 32a699d6a3 Add some actors policy setters for testing
Addresses:
* https://github.com/filecoin-project/lotus/pull/3781/files/a307e4593a005888395db34e5af4691ee50d765d#r491966115
* https://github.com/filecoin-project/lotus/pull/3781/files/a307e4593a005888395db34e5af4691ee50d765d#r491966634

Note: This puts everything into a policy package to avoid a dependency cycle
between the build package, the miner package, and the types package. This is
also why I introduced a GetPreCommitChallengeDelay function and removed the
variable.
2020-09-23 15:00:52 -07:00
Steven Allen 46f5b62a76 Remove a misleading miner actor abstraction
We shouldn't implement CBOR functions on the "abstract" miner info. Otherwise,
we could end up trying to actually _use_ this "abstract" info when decoding
state (which won't work across version).

Also remove the use of these CBOR functions, and instead explicitly use miner0
types. We'll have to abstract over versions eventually, but we'll probably need
some form of abstract miner builder (or maybe even adding some "add sector",
etc. functions to the current miner abstraction?
2020-09-23 14:49:53 -07:00
zgfzgf a4ade158f4 change minerStop to minerLoop 2020-09-23 20:24:19 +08:00
zgfzgf 001ba17d37 break error 2020-09-22 15:21:35 +08:00
Hector Sanjuan ad3db0e83c Move unclassified docs back to the docs root 2020-09-21 19:05:49 +02:00
Hector Sanjuan a5e7fd8f5c Re-add docs-check step to circle 2020-09-21 18:43:57 +02:00
Hector Sanjuan 9e2df907f8 Merge remote-tracking branch 'origin/master' into feat/docs-reorg 2020-09-18 15:23:42 +02:00
Hector Sanjuan dfd28ab0c4 Fix links in READMEs. 2020-09-17 20:56:43 +02:00
Hector Sanjuan 784399738a Keep api-methods.md even if not part of the docs 2020-09-17 20:50:37 +02:00
Hector Sanjuan bc9232544c Remove faqs too 2020-09-17 20:48:09 +02:00
Hector Sanjuan 591e32af48 Remove circle_ci docs-check. 2020-09-17 20:46:14 +02:00
Hector Sanjuan a4fd356fcb Delete most docs. Update about page with links to docs.filecoin.io 2020-09-17 20:35:19 +02:00
Hector Sanjuan 6849d18b03 Merge branch 'master' into feat/docs-reorg 2020-09-17 19:28:54 +02:00
austinabell ca956ca57e Merge branch 'master' of github.com:filecoin-project/lotus into async/stransition 2020-09-16 11:23:55 -04:00
Aayush Rajasekaran 2ca1d111c5 Multisig: Add a CLI command to cancel locking some balance 2020-09-15 19:52:58 -04:00
Aayush Rajasekaran b20f558abb Multisig: Add a CLI command to approve locking some balance 2020-09-15 19:51:18 -04:00
Aayush Rajasekaran c2148ba106 Multisig: Add a CLI command to propose locking some balance 2020-09-15 19:27:36 -04:00
austinabell e53aee26a3 Make state transition in validation async 2020-09-15 18:36:06 -04:00
Hector Sanjuan bd0c6a4ccc Fix filename 2020-09-03 16:35:55 +02:00
Hector Sanjuan af38c902f8 Fix architecture entry 2020-09-03 16:35:18 +02:00
Hector Sanjuan a153e1d586 Fix #2334: Specify seal options to disable with co-located storage worker 2020-09-03 16:35:18 +02:00
Hector Sanjuan fe52c47570 Docs review and re-organization
This:

* Re-organizes the docs into sections that align with what docs.filecoin.io becoming:
  * An installation section
  * A "getting started" section (lotus client focused)
  * A "storing" section (lotus client focused)
  * A "mining" section (miner focused)
  * A "build" section (developer focused)
  * An legacy "architecture" section is left in the last place.

A few high-value documentation pages have been reviewed and updated with the latest recommendations:

* Installation section and lotus setup
* Miner setup
* etc.
...

Other pages have been correctly merged into the new relevant sections. Some pages have not been touched. The filesystem layout of the documentation has been changed into folders corresponding to the sections (as requested by @cw). Some pages that were not linked at all and/or where hidden, have been moved to "unclassified".

This should make the porting of the Lotus documentation to docs.filecoin.io much easier, while ensuring it is more up to date than it was before.

For the moment, this breaks most links as link-aliasing is not supported in lotus-docs.
2020-09-03 16:35:16 +02:00
austinabell 6b63fa379f format 2020-09-02 16:45:50 -04:00
austinabell 485f13de12 Remove load check 2020-09-02 16:45:23 -04:00
austinabell 7f3ba0e6eb Merge branch 'master' of github.com:filecoin-project/lotus into extratsload 2020-09-02 16:44:06 -04:00
Aayush Rajasekaran f58e8bc9a3 Fix some failed precommit handling 2020-09-01 02:18:02 -04:00
whyrusleeping cac848c106 add a command to import an ipld object into the chainstore 2020-08-31 14:24:23 -07:00
austinabell e4daadbfcf Remove extra tipset load from checking beacon entries 2020-08-20 17:54:59 -04:00
150 changed files with 7147 additions and 3903 deletions
+1
View File
@@ -13,3 +13,4 @@
### Conformance testing.
conformance/ @raulk
extern/test-vectors @raulk
cmd/tvx @raulk
+123
View File
@@ -1,5 +1,128 @@
# Lotus changelog
# 0.8.1 / 2020-09-30
This optional release of Lotus introduces a new version of markets which switches to CBOR-map encodings, and allows datastore migrations. The release also introduces several improvements to the mining process, a few performance optimizations, and a battery of UX additions and enhancements.
## Changes
#### Dependencies
- Markets 0.7.0 with updated data stores (https://github.com/filecoin-project/lotus/pull/4089)
- Update ffi to code with blst fixes (https://github.com/filecoin-project/lotus/pull/3998)
#### Core Lotus
- Fix GetPower with no miner address (https://github.com/filecoin-project/lotus/pull/4049)
- Refactor: Move nonce generation out of mpool (https://github.com/filecoin-project/lotus/pull/3970)
#### Performance
- Implement caching syscalls for import-bench (https://github.com/filecoin-project/lotus/pull/3888)
- Fetch tipset blocks in parallel (https://github.com/filecoin-project/lotus/pull/4074)
- Optimize Tipset equals() (https://github.com/filecoin-project/lotus/pull/4056)
- Make state transition in validation async (https://github.com/filecoin-project/lotus/pull/3868)
#### Mining
- Add trace window post (https://github.com/filecoin-project/lotus/pull/4020)
- Use abstract types for Dont recompute post on revert (https://github.com/filecoin-project/lotus/pull/4022)
- Fix injectNulls logic in test miner (https://github.com/filecoin-project/lotus/pull/4058)
- Fix potential panic in FinalizeSector (https://github.com/filecoin-project/lotus/pull/4092)
- Don't recompute post on revert (https://github.com/filecoin-project/lotus/pull/3924)
- Fix some failed precommit handling (https://github.com/filecoin-project/lotus/pull/3445)
- Add --no-swap flag for worker (https://github.com/filecoin-project/lotus/pull/4107)
- Allow some single-thread tasks to run in parallel with PC2/C2 (https://github.com/filecoin-project/lotus/pull/4116)
#### UX
- Add an envvar to set address network version (https://github.com/filecoin-project/lotus/pull/4028)
- Add logging to chain export (https://github.com/filecoin-project/lotus/pull/4030)
- Add JSON output to state compute (https://github.com/filecoin-project/lotus/pull/4038)
- Wallet list CLI: Print balances/nonces (https://github.com/filecoin-project/lotus/pull/4088)
- Added an option to show or not show sector info for `lotus-miner info` (https://github.com/filecoin-project/lotus/pull/4003)
- Add a command to import an ipld object into the chainstore (https://github.com/filecoin-project/lotus/pull/3434)
- Improve the lotus-shed dealtracker (https://github.com/filecoin-project/lotus/pull/4051)
- Docs review and re-organization (https://github.com/filecoin-project/lotus/pull/3431)
- Fix wallet list (https://github.com/filecoin-project/lotus/pull/4104)
- Add an endpoint to validate whether a string is a well-formed address (https://github.com/filecoin-project/lotus/pull/4106)
- Add an option to set config path (https://github.com/filecoin-project/lotus/pull/4103)
- Add printf in TestWindowPost (https://github.com/filecoin-project/lotus/pull/4043)
- Improve miner sectors list UX (https://github.com/filecoin-project/lotus/pull/4108)
#### Tooling
- Move policy change to seal bench (https://github.com/filecoin-project/lotus/pull/4032)
- Add back network power to stats (https://github.com/filecoin-project/lotus/pull/4050)
- Conformance: Record and feed circulating supply (https://github.com/filecoin-project/lotus/pull/4078)
- Snapshot import progress bar, add HTTP support (https://github.com/filecoin-project/lotus/pull/4070)
- Add lotus shed util to validate a tipset (https://github.com/filecoin-project/lotus/pull/4065)
- tvx: a test vector extraction and execution tool (https://github.com/filecoin-project/lotus/pull/4064)
#### Bootstrap
- Add new bootstrappers (https://github.com/filecoin-project/lotus/pull/4007)
- Add Glif node to bootstrap peers (https://github.com/filecoin-project/lotus/pull/4004)
- Add one more node located in China (https://github.com/filecoin-project/lotus/pull/4041)
- Add ipfsmain bootstrapper (https://github.com/filecoin-project/lotus/pull/4067)
# 0.8.0 / 2020-09-26
This consensus-breaking release of Lotus introduces an upgrade to the network. The changes that break consensus are:
- Upgrading to specs-actors v0.9.11, which reduces WindowPoSt faults per [FIP 0002](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0002.md) to reduce cost for honest miners with occasional faults (see https://github.com/filecoin-project/specs-actors/pull/1181)
- Revisions to some cryptoeconomics and network params
This release also updates go-fil-markets to fix an incompatibility issue between v0.7.2 and earlier versions.
## Changes
#### Dependencies
- Update spec actors to 0.9.11 (https://github.com/filecoin-project/lotus/pull/4039)
- Update markets to 0.6.3 (https://github.com/filecoin-project/lotus/pull/4013)
#### Core Lotus
- Network upgrade (https://github.com/filecoin-project/lotus/pull/4039)
- Fix AddSupportedProofTypes (https://github.com/filecoin-project/lotus/pull/4033)
- Return an error when we fail to find a sector when checking sector expiration (https://github.com/filecoin-project/lotus/pull/4026)
- Batch blockstore copies after block validation (https://github.com/filecoin-project/lotus/pull/3980)
- Remove a misleading miner actor abstraction (https://github.com/filecoin-project/lotus/pull/3977)
- Fix out-of-bounds when loading all sector infos (https://github.com/filecoin-project/lotus/pull/3976)
- Fix break condition in the miner (https://github.com/filecoin-project/lotus/pull/3953)
#### UX
- Correct helptext around miners setting ask (https://github.com/filecoin-project/lotus/pull/4009)
- Make sync wait nicer (https://github.com/filecoin-project/lotus/pull/3991)
#### Tooling and validation
- Small adjustments following network upgradability changes (https://github.com/filecoin-project/lotus/pull/3996)
- Add some more big pictures stats to stateroot stat (https://github.com/filecoin-project/lotus/pull/3995)
- Add some actors policy setters for testing (https://github.com/filecoin-project/lotus/pull/3975)
## Contributors
The following contributors had 5 or more commits go into this release.
We are grateful for every contribution!
| Contributor | Commits | Lines ± |
|--------------------|---------|---------------|
| arajasek | 66 | +3140/-1261 |
| Stebalien | 64 | +3797/-3434 |
| magik6k | 48 | +1892/-976 |
| raulk | 40 | +2412/-1549 |
| vyzo | 22 | +287/-196 |
| alanshaw | 15 | +761/-146 |
| whyrusleeping | 15 | +736/-52 |
| hannahhoward | 14 | +1237/837- |
| anton | 6 | +32/-8 |
| travisperson | 5 | +502/-6 |
| Frank | 5 | +78/-39 |
| Jennifer | 5 | +148/-41 |
# 0.7.2 / 2020-09-23
This optional release of Lotus introduces a major refactor around how a Lotus node interacts with code from the specs-actors repo. We now use interfaces to read the state of actors, which is required to be able to reason about different versions of actors code at the same time.
+2 -2
View File
@@ -1,5 +1,5 @@
<p align="center">
<a href="https://lotu.sh/" title="Lotus Docs">
<a href="https://docs.filecoin.io/" title="Filecoin Docs">
<img src="documentation/images/lotus_logo_h.png" alt="Project Lotus Logo" width="244" />
</a>
</p>
@@ -18,7 +18,7 @@ Lotus is an implementation of the Filecoin Distributed Storage Network. For more
## Building & Documentation
For instructions on how to build lotus from source, please visit [Lotus build and setup instruction](https://docs.filecoin.io/get-started/lotus/installation/#minimal-requirements) or read the source [here](https://github.com/filecoin-project/lotus/tree/master/documentation).
For instructions on how to build, install and setup lotus, please visit [https://docs.filecoin.io/get-started/lotus](https://docs.filecoin.io/get-started/lotus/).
## Reporting a Vulnerability
+8 -1
View File
@@ -176,6 +176,9 @@ type FullNode interface {
// the reason.
SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error)
// SyncValidateTipset indicates whether the provided tipset is valid or not
SyncValidateTipset(ctx context.Context, tsk types.TipSetKey) (bool, error)
// MethodGroup: Mpool
// The Mpool methods are for interacting with the message pool. The message pool
// manages all incoming and outgoing 'messages' going over the network.
@@ -244,6 +247,8 @@ type FullNode interface {
WalletImport(context.Context, *types.KeyInfo) (address.Address, error)
// WalletDelete deletes an address from the wallet.
WalletDelete(context.Context, address.Address) error
// WalletValidateAddress validates whether a given string can be decoded as a well-formed address
WalletValidateAddress(context.Context, string) (address.Address, error)
// Other
@@ -275,7 +280,7 @@ type FullNode interface {
// of status updates.
ClientRetrieveWithEvents(ctx context.Context, order RetrievalOrder, ref *FileRef) (<-chan marketevents.RetrievalEvent, error)
// ClientQueryAsk returns a signed StorageAsk from the specified miner.
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error)
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.StorageAsk, error)
// ClientCalcCommP calculates the CommP for a specified file
ClientCalcCommP(ctx context.Context, inpath string) (*CommPRet, error)
// ClientGenCar generates a CAR file for the specified file.
@@ -725,6 +730,8 @@ type ActiveSync struct {
type SyncState struct {
ActiveSyncs []ActiveSync
VMApplied uint64
}
type SyncStateStage int
+24 -14
View File
@@ -112,6 +112,7 @@ type FullNodeStruct struct {
SyncMarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
SyncUnmarkBad func(ctx context.Context, bcid cid.Cid) error `perm:"admin"`
SyncCheckBad func(ctx context.Context, bcid cid.Cid) (string, error) `perm:"read"`
SyncValidateTipset func(ctx context.Context, tsk types.TipSetKey) (bool, error) `perm:"read"`
MpoolGetConfig func(context.Context) (*types.MpoolConfig, error) `perm:"read"`
MpoolSetConfig func(context.Context, *types.MpoolConfig) error `perm:"write"`
@@ -129,18 +130,19 @@ type FullNodeStruct struct {
MinerGetBaseInfo func(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"`
MinerCreateBlock func(context.Context, *api.BlockTemplate) (*types.BlockMsg, error) `perm:"write"`
WalletNew func(context.Context, crypto.SigType) (address.Address, error) `perm:"write"`
WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"`
WalletList func(context.Context) ([]address.Address, error) `perm:"write"`
WalletBalance func(context.Context, address.Address) (types.BigInt, error) `perm:"read"`
WalletSign func(context.Context, address.Address, []byte) (*crypto.Signature, error) `perm:"sign"`
WalletSignMessage func(context.Context, address.Address, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
WalletVerify func(context.Context, address.Address, []byte, *crypto.Signature) (bool, error) `perm:"read"`
WalletDefaultAddress func(context.Context) (address.Address, error) `perm:"write"`
WalletSetDefault func(context.Context, address.Address) error `perm:"admin"`
WalletExport func(context.Context, address.Address) (*types.KeyInfo, error) `perm:"admin"`
WalletImport func(context.Context, *types.KeyInfo) (address.Address, error) `perm:"admin"`
WalletDelete func(context.Context, address.Address) error `perm:"write"`
WalletNew func(context.Context, crypto.SigType) (address.Address, error) `perm:"write"`
WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"`
WalletList func(context.Context) ([]address.Address, error) `perm:"write"`
WalletBalance func(context.Context, address.Address) (types.BigInt, error) `perm:"read"`
WalletSign func(context.Context, address.Address, []byte) (*crypto.Signature, error) `perm:"sign"`
WalletSignMessage func(context.Context, address.Address, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
WalletVerify func(context.Context, address.Address, []byte, *crypto.Signature) (bool, error) `perm:"read"`
WalletDefaultAddress func(context.Context) (address.Address, error) `perm:"write"`
WalletSetDefault func(context.Context, address.Address) error `perm:"admin"`
WalletExport func(context.Context, address.Address) (*types.KeyInfo, error) `perm:"admin"`
WalletImport func(context.Context, *types.KeyInfo) (address.Address, error) `perm:"admin"`
WalletDelete func(context.Context, address.Address) error `perm:"write"`
WalletValidateAddress func(context.Context, string) (address.Address, error) `perm:"read"`
ClientImport func(ctx context.Context, ref api.FileRef) (*api.ImportRes, error) `perm:"admin"`
ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"`
@@ -154,7 +156,7 @@ type FullNodeStruct struct {
ClientGetDealUpdates func(ctx context.Context) (<-chan api.DealInfo, error) `perm:"read"`
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error `perm:"admin"`
ClientRetrieveWithEvents func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) (<-chan marketevents.RetrievalEvent, error) `perm:"admin"`
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.StorageAsk, error) `perm:"read"`
ClientCalcCommP func(ctx context.Context, inpath string) (*api.CommPRet, error) `perm:"read"`
ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"`
ClientDealSize func(ctx context.Context, root cid.Cid) (api.DataSize, error) `perm:"read"`
@@ -484,7 +486,7 @@ func (c *FullNodeStruct) ClientRetrieveWithEvents(ctx context.Context, order api
return c.Internal.ClientRetrieveWithEvents(ctx, order, ref)
}
func (c *FullNodeStruct) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) {
func (c *FullNodeStruct) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.StorageAsk, error) {
return c.Internal.ClientQueryAsk(ctx, p, miner)
}
func (c *FullNodeStruct) ClientCalcCommP(ctx context.Context, inpath string) (*api.CommPRet, error) {
@@ -631,6 +633,10 @@ func (c *FullNodeStruct) WalletDelete(ctx context.Context, addr address.Address)
return c.Internal.WalletDelete(ctx, addr)
}
func (c *FullNodeStruct) WalletValidateAddress(ctx context.Context, str string) (address.Address, error) {
return c.Internal.WalletValidateAddress(ctx, str)
}
func (c *FullNodeStruct) MpoolGetNonce(ctx context.Context, addr address.Address) (uint64, error) {
return c.Internal.MpoolGetNonce(ctx, addr)
}
@@ -735,6 +741,10 @@ func (c *FullNodeStruct) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string
return c.Internal.SyncCheckBad(ctx, bcid)
}
func (c *FullNodeStruct) SyncValidateTipset(ctx context.Context, tsk types.TipSetKey) (bool, error) {
return c.Internal.SyncValidateTipset(ctx, tsk)
}
func (c *FullNodeStruct) StateNetworkName(ctx context.Context) (dtypes.NetworkName, error) {
return c.Internal.StateNetworkName(ctx)
}
+16
View File
@@ -94,6 +94,22 @@ func TestCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) {
require.Less(t, 50000, int(exp.OnTime))
}
dlInfo, err := client.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK)
require.NoError(t, err)
// Sector should expire.
for {
// Wait for the sector to expire.
status, err := miner.SectorsStatus(ctx, CC, true)
require.NoError(t, err)
if status.OnTime == 0 && status.Early == 0 {
break
}
t.Log("waiting for sector to expire")
// wait one deadline per loop.
time.Sleep(time.Duration(dlInfo.WPoStChallengeWindow) * blocktime)
}
fmt.Println("shutting down mining")
atomic.AddInt64(&mine, -1)
<-done
+9 -12
View File
@@ -153,18 +153,16 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
require.NoError(t, err)
fmt.Printf("Running one proving period\n")
fmt.Printf("End for head.Height > %d\n", di.PeriodStart+di.WPoStProvingPeriod+2)
for {
head, err := client.ChainHead(ctx)
require.NoError(t, err)
if head.Height() > di.PeriodStart+(di.WPoStProvingPeriod)+2 {
fmt.Printf("Now head.Height = %d\n", head.Height())
break
}
if head.Height()%100 == 0 {
fmt.Printf("@%d\n", head.Height())
}
build.Clock.Sleep(blocktime)
}
@@ -186,7 +184,6 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
require.Greater(t, len(parts), 0)
secs := parts[0].AllSectors
require.NoError(t, err)
n, err := secs.Count()
require.NoError(t, err)
require.Equal(t, uint64(2), n)
@@ -210,7 +207,6 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
require.Greater(t, len(parts), 0)
secs := parts[0].AllSectors
require.NoError(t, err)
n, err := secs.Count()
require.NoError(t, err)
require.Equal(t, uint64(2), n)
@@ -236,18 +232,17 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
require.NoError(t, err)
fmt.Printf("Go through another PP, wait for sectors to become faulty\n")
fmt.Printf("End for head.Height > %d\n", di.PeriodStart+di.WPoStProvingPeriod+2)
for {
head, err := client.ChainHead(ctx)
require.NoError(t, err)
if head.Height() > di.PeriodStart+(di.WPoStProvingPeriod)+2 {
fmt.Printf("Now head.Height = %d\n", head.Height())
break
}
if head.Height()%100 == 0 {
fmt.Printf("@%d\n", head.Height())
}
build.Clock.Sleep(blocktime)
}
@@ -267,17 +262,17 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
di, err = client.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK)
require.NoError(t, err)
fmt.Printf("End for head.Height > %d\n", di.PeriodStart+di.WPoStProvingPeriod+2)
for {
head, err := client.ChainHead(ctx)
require.NoError(t, err)
if head.Height() > di.PeriodStart+di.WPoStProvingPeriod+2 {
fmt.Printf("Now head.Height = %d\n", head.Height())
break
}
if head.Height()%100 == 0 {
fmt.Printf("@%d\n", head.Height())
}
build.Clock.Sleep(blocktime)
}
@@ -300,12 +295,14 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
require.NoError(t, err)
waitUntil := head.Height() + 10
fmt.Printf("End for head.Height > %d\n", waitUntil)
for {
head, err := client.ChainHead(ctx)
require.NoError(t, err)
if head.Height() > waitUntil {
fmt.Printf("Now head.Height = %d\n", head.Height())
break
}
}
+6
View File
@@ -4,3 +4,9 @@
/dns4/bootstrap-4.testnet.fildev.network/tcp/1347/p2p/12D3KooWPkL9LrKRQgHtq7kn9ecNhGU9QaziG8R5tX8v9v7t3h34
/dns4/bootstrap-3.testnet.fildev.network/tcp/1347/p2p/12D3KooWKYSsbpgZ3HAjax5M1BXCwXLa6gVkUARciz7uN3FNtr7T
/dns4/bootstrap-5.testnet.fildev.network/tcp/1347/p2p/12D3KooWQYzqnLASJAabyMpPb1GcWZvNSe7JDcRuhdRqonFoiK9W
/dns4/lotus-bootstrap.forceup.cn/tcp/41778/p2p/12D3KooWFQsv3nRMUevZNWWsY1Wu6NUzUbawnWU5NcRhgKuJA37C
/dns4/bootstrap-0.starpool.in/tcp/12757/p2p/12D3KooWGHpBMeZbestVEWkfdnC9u7p6uFHXL1n7m1ZBqsEmiUzz
/dns4/bootstrap-1.starpool.in/tcp/12757/p2p/12D3KooWQZrGH1PxSNZPum99M1zNvjNFM33d1AAu5DcvdHptuU7u
/dns4/node.glif.io/tcp/1235/p2p/12D3KooWBF8cpp65hp2u9LK5mh19x67ftAam84z9LsfaquTDSBpt
/dns4/bootstrap-0.ipfsmain.cn/tcp/34721/p2p/12D3KooWQnwEGNqcM2nAcPtRR9rAX8Hrg4k9kJLCHoTR5chJfz6d
/dns4/bootstrap-1.ipfsmain.cn/tcp/34723/p2p/12D3KooWMKxMkD5DMpSWsW7dBddKxKT7L2GgbNuckz9otxvkvByP
+7 -9
View File
@@ -4,27 +4,25 @@ package build
import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
"github.com/filecoin-project/lotus/chain/actors/policy"
)
const UpgradeBreezeHeight = -1
const BreezeGasTampingDuration = 0
const UpgradeSmokeHeight = -1
const UpgradeIgnitionHeight = -2
const UpgradeLiftoffHeight = -3
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
0: DrandMainnet,
}
func init() {
power0.ConsensusMinerMinPower = big.NewInt(2048)
miner0.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
}
verifreg0.MinVerifiedDealSize = big.NewInt(256)
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
BuildType |= Build2k
}
+6
View File
@@ -3,6 +3,8 @@ package build
import (
"sort"
"github.com/filecoin-project/go-address"
"github.com/libp2p/go-libp2p-core/protocol"
"github.com/filecoin-project/go-state-types/abi"
@@ -44,3 +46,7 @@ func UseNewestNetwork() bool {
}
return false
}
func SetAddressNetwork(n address.Network) {
address.CurrentNetwork = n
}
+16 -2
View File
@@ -4,6 +4,9 @@ package build
import (
"math/big"
"os"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/network"
@@ -22,8 +25,8 @@ const UnixfsLinksPerLevel = 1024
// Consensus / Network
const AllowableClockDriftSecs = uint64(1)
const NewestNetworkVersion = network.Version2
const ActorUpgradeNetworkVersion = network.Version3
const NewestNetworkVersion = network.Version3
const ActorUpgradeNetworkVersion = network.Version4
// Epochs
const ForkLengthThreshold = Finality
@@ -60,9 +63,16 @@ const TicketRandomnessLookback = abi.ChainEpoch(1)
const WinningPoStSectorSetLookback = abi.ChainEpoch(10)
// /////
// Address
const AddressMainnetEnvVar = "_mainnet_"
// /////
// Devnet settings
var Devnet = true
const FilBase = uint64(2_000_000_000)
const FilAllocStorageMining = uint64(1_100_000_000)
@@ -75,6 +85,10 @@ var InitialRewardBalance *big.Int
func init() {
InitialRewardBalance = big.NewInt(int64(FilAllocStorageMining))
InitialRewardBalance = InitialRewardBalance.Mul(InitialRewardBalance, big.NewInt(int64(FilecoinPrecision)))
if os.Getenv("LOTUS_ADDRESS_TYPE") == AddressMainnetEnvVar {
SetAddressNetwork(address.Mainnet)
}
}
// Sync
+5 -1
View File
@@ -74,7 +74,9 @@ var (
UpgradeBreezeHeight abi.ChainEpoch = -1
BreezeGasTampingDuration abi.ChainEpoch = 0
UpgradeSmokeHeight abi.ChainEpoch = -1
UpgradeSmokeHeight abi.ChainEpoch = -1
UpgradeIgnitionHeight abi.ChainEpoch = -2
UpgradeLiftoffHeight abi.ChainEpoch = -3
DrandSchedule = map[abi.ChainEpoch]DrandEnum{
0: DrandMainnet,
@@ -82,4 +84,6 @@ var (
NewestNetworkVersion = network.Version2
ActorUpgradeNetworkVersion = network.Version3
Devnet = true
)
+17 -10
View File
@@ -6,10 +6,9 @@ package build
import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
"github.com/filecoin-project/lotus/chain/actors/policy"
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
)
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
@@ -22,14 +21,22 @@ const BreezeGasTampingDuration = 120
const UpgradeSmokeHeight = 51000
const UpgradeIgnitionHeight = 94000
// This signals our tentative epoch for mainnet launch. Can make it later, but not earlier.
// Miners, clients, developers, custodians all need time to prepare.
// We still have upgrades and state changes to do, but can happen after signaling timing here.
const UpgradeLiftoffHeight = 148888
func init() {
power0.ConsensusMinerMinPower = big.NewInt(10 << 40)
miner0.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg32GiBV1: {},
abi.RegisteredSealProof_StackedDrg64GiBV1: {},
}
policy.SetConsensusMinerMinPower(abi.NewStoragePower(10 << 40))
policy.SetSupportedProofTypes(
abi.RegisteredSealProof_StackedDrg32GiBV1,
abi.RegisteredSealProof_StackedDrg64GiBV1,
)
Devnet = false
}
const BlockDelaySecs = uint64(builtin.EpochDurationSeconds)
const BlockDelaySecs = uint64(builtin0.EpochDurationSeconds)
const PropagationDelaySecs = uint64(6)
+1 -1
View File
@@ -29,7 +29,7 @@ func buildType() string {
}
// BuildVersion is the local build version, set by build system
const BuildVersion = "0.7.2"
const BuildVersion = "0.8.1"
func UserVersion() string {
return BuildVersion + buildType() + CurrentCommit
+1 -1
View File
@@ -21,7 +21,7 @@ const (
// Converts a network version into a specs-actors version.
func VersionForNetwork(version network.Version) Version {
switch version {
case network.Version0, network.Version1, network.Version2:
case network.Version0, network.Version1, network.Version2, network.Version3:
return Version0
default:
panic(fmt.Sprintf("unsupported network version %d", version))
-313
View File
@@ -1,313 +0,0 @@
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
package miner
import (
"fmt"
"io"
abi "github.com/filecoin-project/go-state-types/abi"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
var _ = xerrors.Errorf
var lengthBufSectorOnChainInfo = []byte{139}
func (t *SectorOnChainInfo) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write(lengthBufSectorOnChainInfo); err != nil {
return err
}
scratch := make([]byte, 9)
// t.SectorNumber (abi.SectorNumber) (uint64)
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.SectorNumber)); err != nil {
return err
}
// t.SealProof (abi.RegisteredSealProof) (int64)
if t.SealProof >= 0 {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.SealProof)); err != nil {
return err
}
} else {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajNegativeInt, uint64(-t.SealProof-1)); err != nil {
return err
}
}
// t.SealedCID (cid.Cid) (struct)
if err := cbg.WriteCidBuf(scratch, w, t.SealedCID); err != nil {
return xerrors.Errorf("failed to write cid field t.SealedCID: %w", err)
}
// t.DealIDs ([]abi.DealID) (slice)
if len(t.DealIDs) > cbg.MaxLength {
return xerrors.Errorf("Slice value in field t.DealIDs was too long")
}
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajArray, uint64(len(t.DealIDs))); err != nil {
return err
}
for _, v := range t.DealIDs {
if err := cbg.CborWriteHeader(w, cbg.MajUnsignedInt, uint64(v)); err != nil {
return err
}
}
// t.Activation (abi.ChainEpoch) (int64)
if t.Activation >= 0 {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Activation)); err != nil {
return err
}
} else {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajNegativeInt, uint64(-t.Activation-1)); err != nil {
return err
}
}
// t.Expiration (abi.ChainEpoch) (int64)
if t.Expiration >= 0 {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Expiration)); err != nil {
return err
}
} else {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajNegativeInt, uint64(-t.Expiration-1)); err != nil {
return err
}
}
// t.DealWeight (big.Int) (struct)
if err := t.DealWeight.MarshalCBOR(w); err != nil {
return err
}
// t.VerifiedDealWeight (big.Int) (struct)
if err := t.VerifiedDealWeight.MarshalCBOR(w); err != nil {
return err
}
// t.InitialPledge (big.Int) (struct)
if err := t.InitialPledge.MarshalCBOR(w); err != nil {
return err
}
// t.ExpectedDayReward (big.Int) (struct)
if err := t.ExpectedDayReward.MarshalCBOR(w); err != nil {
return err
}
// t.ExpectedStoragePledge (big.Int) (struct)
if err := t.ExpectedStoragePledge.MarshalCBOR(w); err != nil {
return err
}
return nil
}
func (t *SectorOnChainInfo) UnmarshalCBOR(r io.Reader) error {
*t = SectorOnChainInfo{}
br := cbg.GetPeeker(r)
scratch := make([]byte, 8)
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
if err != nil {
return err
}
if maj != cbg.MajArray {
return fmt.Errorf("cbor input should be of type array")
}
if extra != 11 {
return fmt.Errorf("cbor input had wrong number of fields")
}
// t.SectorNumber (abi.SectorNumber) (uint64)
{
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.SectorNumber = abi.SectorNumber(extra)
}
// t.SealProof (abi.RegisteredSealProof) (int64)
{
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
var extraI int64
if err != nil {
return err
}
switch maj {
case cbg.MajUnsignedInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 positive overflow")
}
case cbg.MajNegativeInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 negative oveflow")
}
extraI = -1 - extraI
default:
return fmt.Errorf("wrong type for int64 field: %d", maj)
}
t.SealProof = abi.RegisteredSealProof(extraI)
}
// t.SealedCID (cid.Cid) (struct)
{
c, err := cbg.ReadCid(br)
if err != nil {
return xerrors.Errorf("failed to read cid field t.SealedCID: %w", err)
}
t.SealedCID = c
}
// t.DealIDs ([]abi.DealID) (slice)
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
if err != nil {
return err
}
if extra > cbg.MaxLength {
return fmt.Errorf("t.DealIDs: array too large (%d)", extra)
}
if maj != cbg.MajArray {
return fmt.Errorf("expected cbor array")
}
if extra > 0 {
t.DealIDs = make([]abi.DealID, extra)
}
for i := 0; i < int(extra); i++ {
maj, val, err := cbg.CborReadHeaderBuf(br, scratch)
if err != nil {
return xerrors.Errorf("failed to read uint64 for t.DealIDs slice: %w", err)
}
if maj != cbg.MajUnsignedInt {
return xerrors.Errorf("value read for array t.DealIDs was not a uint, instead got %d", maj)
}
t.DealIDs[i] = abi.DealID(val)
}
// t.Activation (abi.ChainEpoch) (int64)
{
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
var extraI int64
if err != nil {
return err
}
switch maj {
case cbg.MajUnsignedInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 positive overflow")
}
case cbg.MajNegativeInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 negative oveflow")
}
extraI = -1 - extraI
default:
return fmt.Errorf("wrong type for int64 field: %d", maj)
}
t.Activation = abi.ChainEpoch(extraI)
}
// t.Expiration (abi.ChainEpoch) (int64)
{
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
var extraI int64
if err != nil {
return err
}
switch maj {
case cbg.MajUnsignedInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 positive overflow")
}
case cbg.MajNegativeInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 negative oveflow")
}
extraI = -1 - extraI
default:
return fmt.Errorf("wrong type for int64 field: %d", maj)
}
t.Expiration = abi.ChainEpoch(extraI)
}
// t.DealWeight (big.Int) (struct)
{
if err := t.DealWeight.UnmarshalCBOR(br); err != nil {
return xerrors.Errorf("unmarshaling t.DealWeight: %w", err)
}
}
// t.VerifiedDealWeight (big.Int) (struct)
{
if err := t.VerifiedDealWeight.UnmarshalCBOR(br); err != nil {
return xerrors.Errorf("unmarshaling t.VerifiedDealWeight: %w", err)
}
}
// t.InitialPledge (big.Int) (struct)
{
if err := t.InitialPledge.UnmarshalCBOR(br); err != nil {
return xerrors.Errorf("unmarshaling t.InitialPledge: %w", err)
}
}
// t.ExpectedDayReward (big.Int) (struct)
{
if err := t.ExpectedDayReward.UnmarshalCBOR(br); err != nil {
return xerrors.Errorf("unmarshaling t.ExpectedDayReward: %w", err)
}
}
// t.ExpectedStoragePledge (big.Int) (struct)
{
if err := t.ExpectedStoragePledge.UnmarshalCBOR(br); err != nil {
return xerrors.Errorf("unmarshaling t.ExpectedStoragePledge: %w", err)
}
}
return nil
}
+4 -1
View File
@@ -19,8 +19,11 @@ import (
)
// Unchanged between v0 and v1 actors
var PreCommitChallengeDelay = miner0.PreCommitChallengeDelay
var WPoStProvingPeriod = miner0.WPoStProvingPeriod
var WPoStPeriodDeadlines = miner0.WPoStPeriodDeadlines
var WPoStChallengeWindow = miner0.WPoStChallengeWindow
var WPoStChallengeLookback = miner0.WPoStChallengeLookback
var FaultDeclarationCutoff = miner0.FaultDeclarationCutoff
const MinSectorExpiration = miner0.MinSectorExpiration
+3 -4
View File
@@ -6,6 +6,7 @@ import (
"github.com/libp2p/go-libp2p-core/peer"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield"
@@ -96,9 +97,7 @@ func (s *state0) NumLiveSectors() (uint64, error) {
// GetSectorExpiration returns the effective expiration of the given sector.
//
// If the sector isn't found or has already been terminated, this method returns
// nil and no error. If the sector does not expire early, the Early expiration
// field is 0.
// If the sector does not expire early, the Early expiration field is 0.
func (s *state0) GetSectorExpiration(num abi.SectorNumber) (*SectorExpiration, error) {
dls, err := s.State.LoadDeadlines(s.store)
if err != nil {
@@ -161,7 +160,7 @@ func (s *state0) GetSectorExpiration(num abi.SectorNumber) (*SectorExpiration, e
return nil, err
}
if out.Early == 0 && out.OnTime == 0 {
return nil, nil
return nil, xerrors.Errorf("failed to find sector %d", num)
}
return &out, nil
}
+54
View File
@@ -0,0 +1,54 @@
package policy
import (
"github.com/filecoin-project/go-state-types/abi"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
)
// SetSupportedProofTypes sets supported proof types, across all actor versions.
// This should only be used for testing.
func SetSupportedProofTypes(types ...abi.RegisteredSealProof) {
newTypes := make(map[abi.RegisteredSealProof]struct{}, len(types))
for _, t := range types {
newTypes[t] = struct{}{}
}
// Set for all miner versions.
miner0.SupportedProofTypes = newTypes
}
// AddSupportedProofTypes sets supported proof types, across all actor versions.
// This should only be used for testing.
func AddSupportedProofTypes(types ...abi.RegisteredSealProof) {
for _, t := range types {
// Set for all miner versions.
miner0.SupportedProofTypes[t] = struct{}{}
}
}
// SetPreCommitChallengeDelay sets the pre-commit challenge delay across all
// actors versions. Use for testing.
func SetPreCommitChallengeDelay(delay abi.ChainEpoch) {
// Set for all miner versions.
miner0.PreCommitChallengeDelay = delay
}
// TODO: this function shouldn't really exist. Instead, the API should expose the precommit delay.
func GetPreCommitChallengeDelay() abi.ChainEpoch {
return miner0.PreCommitChallengeDelay
}
// SetConsensusMinerMinPower sets the minimum power of an individual miner must
// meet for leader election, across all actor versions. This should only be used
// for testing.
func SetConsensusMinerMinPower(p abi.StoragePower) {
power0.ConsensusMinerMinPower = p
}
// SetMinVerifiedDealSize sets the minimum size of a verified deal. This should
// only be used for testing.
func SetMinVerifiedDealSize(size abi.StoragePower) {
verifreg0.MinVerifiedDealSize = size
}
+36
View File
@@ -0,0 +1,36 @@
package policy
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/abi"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
)
func TestSupportedProofTypes(t *testing.T) {
var oldTypes []abi.RegisteredSealProof
for t := range miner0.SupportedProofTypes {
oldTypes = append(oldTypes, t)
}
t.Cleanup(func() {
SetSupportedProofTypes(oldTypes...)
})
SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
require.EqualValues(t,
miner0.SupportedProofTypes,
map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
},
)
AddSupportedProofTypes(abi.RegisteredSealProof_StackedDrg8MiBV1)
require.EqualValues(t,
miner0.SupportedProofTypes,
map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
abi.RegisteredSealProof_StackedDrg8MiBV1: {},
},
)
}
+3 -3
View File
@@ -581,7 +581,7 @@ func createEmptyMinerState(ctx context.Context, t *testing.T, store adt.Store, o
func createSectorsAMT(ctx context.Context, t *testing.T, store adt.Store, sectors []miner.SectorOnChainInfo) cid.Cid {
root := adt.MakeEmptyArray(store)
for _, sector := range sectors {
sector := sector
sector := (miner0.SectorOnChainInfo)(sector)
err := root.Set(uint64(sector.SectorNumber), &sector)
require.NoError(t, err)
}
@@ -614,8 +614,8 @@ const (
)
// returns a unique SectorPreCommitInfo with each invocation with SectorNumber set to `sectorNo`.
func newSectorPreCommitInfo(sectorNo abi.SectorNumber, sealed cid.Cid, expiration abi.ChainEpoch) *miner.SectorPreCommitInfo {
return &miner.SectorPreCommitInfo{
func newSectorPreCommitInfo(sectorNo abi.SectorNumber, sealed cid.Cid, expiration abi.ChainEpoch) *miner0.SectorPreCommitInfo {
return &miner0.SectorPreCommitInfo{
SealProof: abi.RegisteredSealProof_StackedDrg32GiBV1,
SectorNumber: sectorNo,
SealedCID: sealed,
+3 -4
View File
@@ -14,7 +14,6 @@ import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid"
@@ -28,6 +27,7 @@ import (
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/beacon"
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
"github.com/filecoin-project/lotus/chain/stmgr"
@@ -121,9 +121,8 @@ var DefaultRemainderAccountActor = genesis.Actor{
}
func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
miner0.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
}
// TODO: we really shouldn't modify a global variable here.
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
mr := repo.NewMemory(nil)
lr, err := mr.Lock(repo.StorageMiner)
+4 -9
View File
@@ -4,21 +4,16 @@ import (
"testing"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
"github.com/filecoin-project/lotus/chain/actors/policy"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
)
func init() {
miner0.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
}
power0.ConsensusMinerMinPower = big.NewInt(2048)
verifreg0.MinVerifiedDealSize = big.NewInt(256)
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
}
func testGeneration(t testing.TB, n int, msgs int, sectors int) {
-94
View File
@@ -75,8 +75,6 @@ var (
ErrRBFTooLowPremium = errors.New("replace by fee has too low GasPremium")
ErrTooManyPendingMessages = errors.New("too many pending messages for actor")
ErrNonceGap = errors.New("unfulfilled nonce gap")
ErrTryAgain = errors.New("state inconsistency while pushing message; please try again")
)
const (
@@ -795,98 +793,6 @@ func (mp *MessagePool) getStateBalance(addr address.Address, ts *types.TipSet) (
return act.Balance, nil
}
func (mp *MessagePool) PushWithNonce(ctx context.Context, addr address.Address, cb func(address.Address, uint64) (*types.SignedMessage, error)) (*types.SignedMessage, error) {
// serialize push access to reduce lock contention
mp.addSema <- struct{}{}
defer func() {
<-mp.addSema
}()
mp.curTsLk.Lock()
mp.lk.Lock()
curTs := mp.curTs
fromKey := addr
if fromKey.Protocol() == address.ID {
var err error
fromKey, err = mp.api.StateAccountKey(ctx, fromKey, mp.curTs)
if err != nil {
mp.lk.Unlock()
mp.curTsLk.Unlock()
return nil, xerrors.Errorf("resolving sender key: %w", err)
}
}
nonce, err := mp.getNonceLocked(fromKey, mp.curTs)
if err != nil {
mp.lk.Unlock()
mp.curTsLk.Unlock()
return nil, xerrors.Errorf("get nonce locked failed: %w", err)
}
// release the locks for signing
mp.lk.Unlock()
mp.curTsLk.Unlock()
msg, err := cb(fromKey, nonce)
if err != nil {
return nil, err
}
err = mp.checkMessage(msg)
if err != nil {
return nil, err
}
msgb, err := msg.Serialize()
if err != nil {
return nil, err
}
// reacquire the locks and check state for consistency
mp.curTsLk.Lock()
defer mp.curTsLk.Unlock()
if mp.curTs != curTs {
return nil, ErrTryAgain
}
mp.lk.Lock()
defer mp.lk.Unlock()
nonce2, err := mp.getNonceLocked(fromKey, mp.curTs)
if err != nil {
return nil, xerrors.Errorf("get nonce locked failed: %w", err)
}
if nonce2 != nonce {
return nil, ErrTryAgain
}
publish, err := mp.verifyMsgBeforeAdd(msg, curTs, true)
if err != nil {
return nil, err
}
if err := mp.checkBalance(msg, curTs); err != nil {
return nil, err
}
if err := mp.addLocked(msg, false); err != nil {
return nil, xerrors.Errorf("add locked failed: %w", err)
}
if err := mp.addLocal(msg, msgb); err != nil {
log.Errorf("addLocal failed: %+v", err)
}
if publish {
err = mp.api.PubSubPublish(build.MessagesTopic(mp.netName), msgb)
}
return msg, err
}
func (mp *MessagePool) Remove(from address.Address, nonce uint64, applied bool) {
mp.lk.Lock()
defer mp.lk.Unlock()
+124
View File
@@ -0,0 +1,124 @@
package messagesigner
import (
"bytes"
"context"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
logging "github.com/ipfs/go-log/v2"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
)
const dsKeyActorNonce = "ActorNonce"
var log = logging.Logger("messagesigner")
type mpoolAPI interface {
GetNonce(address.Address) (uint64, error)
}
// MessageSigner keeps track of nonces per address, and increments the nonce
// when signing a message
type MessageSigner struct {
wallet *wallet.Wallet
mpool mpoolAPI
ds datastore.Batching
}
func NewMessageSigner(wallet *wallet.Wallet, mpool *messagepool.MessagePool, ds dtypes.MetadataDS) *MessageSigner {
return newMessageSigner(wallet, mpool, ds)
}
func newMessageSigner(wallet *wallet.Wallet, mpool mpoolAPI, ds dtypes.MetadataDS) *MessageSigner {
ds = namespace.Wrap(ds, datastore.NewKey("/message-signer/"))
return &MessageSigner{
wallet: wallet,
mpool: mpool,
ds: ds,
}
}
// SignMessage increments the nonce for the message From address, and signs
// the message
func (ms *MessageSigner) SignMessage(ctx context.Context, msg *types.Message) (*types.SignedMessage, error) {
nonce, err := ms.nextNonce(msg.From)
if err != nil {
return nil, xerrors.Errorf("failed to create nonce: %w", err)
}
msg.Nonce = nonce
sig, err := ms.wallet.Sign(ctx, msg.From, msg.Cid().Bytes())
if err != nil {
return nil, xerrors.Errorf("failed to sign message: %w", err)
}
return &types.SignedMessage{
Message: *msg,
Signature: *sig,
}, nil
}
// nextNonce increments the nonce.
// If there is no nonce in the datastore, gets the nonce from the message pool.
func (ms *MessageSigner) nextNonce(addr address.Address) (uint64, error) {
// Nonces used to be created by the mempool and we need to support nodes
// that have mempool nonces, so first check the mempool for a nonce for
// this address. Note that the mempool returns the actor state's nonce
// by default.
nonce, err := ms.mpool.GetNonce(addr)
if err != nil {
return 0, xerrors.Errorf("failed to get nonce from mempool: %w", err)
}
// Get the nonce for this address from the datastore
addrNonceKey := datastore.KeyWithNamespaces([]string{dsKeyActorNonce, addr.String()})
dsNonceBytes, err := ms.ds.Get(addrNonceKey)
switch {
case xerrors.Is(err, datastore.ErrNotFound):
// If a nonce for this address hasn't yet been created in the
// datastore, just use the nonce from the mempool
case err != nil:
return 0, xerrors.Errorf("failed to get nonce from datastore: %w", err)
default:
// There is a nonce in the datastore, so unmarshall and increment it
maj, val, err := cbg.CborReadHeader(bytes.NewReader(dsNonceBytes))
if err != nil {
return 0, xerrors.Errorf("failed to parse nonce from datastore: %w", err)
}
if maj != cbg.MajUnsignedInt {
return 0, xerrors.Errorf("bad cbor type parsing nonce from datastore")
}
dsNonce := val + 1
// The message pool nonce should be <= than the datastore nonce
if nonce <= dsNonce {
nonce = dsNonce
} else {
log.Warnf("mempool nonce was larger than datastore nonce (%d > %d)", nonce, dsNonce)
}
}
// Write the nonce for this address to the datastore
buf := bytes.Buffer{}
_, err = buf.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, nonce))
if err != nil {
return 0, xerrors.Errorf("failed to marshall nonce: %w", err)
}
err = ms.ds.Put(addrNonceKey, buf.Bytes())
if err != nil {
return 0, xerrors.Errorf("failed to write nonce to datastore: %w", err)
}
return nonce, nil
}
+158
View File
@@ -0,0 +1,158 @@
package messagesigner
import (
"context"
"sync"
"testing"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/stretchr/testify/require"
ds_sync "github.com/ipfs/go-datastore/sync"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-datastore"
)
type mockMpool struct {
lk sync.RWMutex
nonces map[address.Address]uint64
}
func newMockMpool() *mockMpool {
return &mockMpool{nonces: make(map[address.Address]uint64)}
}
func (mp *mockMpool) setNonce(addr address.Address, nonce uint64) {
mp.lk.Lock()
defer mp.lk.Unlock()
mp.nonces[addr] = nonce
}
func (mp *mockMpool) GetNonce(addr address.Address) (uint64, error) {
mp.lk.RLock()
defer mp.lk.RUnlock()
return mp.nonces[addr], nil
}
func TestMessageSignerSignMessage(t *testing.T) {
ctx := context.Background()
w, _ := wallet.NewWallet(wallet.NewMemKeyStore())
from1, err := w.GenerateKey(crypto.SigTypeSecp256k1)
require.NoError(t, err)
from2, err := w.GenerateKey(crypto.SigTypeSecp256k1)
require.NoError(t, err)
to1, err := w.GenerateKey(crypto.SigTypeSecp256k1)
require.NoError(t, err)
to2, err := w.GenerateKey(crypto.SigTypeSecp256k1)
require.NoError(t, err)
type msgSpec struct {
msg *types.Message
mpoolNonce [1]uint64
expNonce uint64
}
tests := []struct {
name string
msgs []msgSpec
}{{
// No nonce yet in datastore
name: "no nonce yet",
msgs: []msgSpec{{
msg: &types.Message{
To: to1,
From: from1,
},
expNonce: 0,
}},
}, {
// Get nonce value of zero from mpool
name: "mpool nonce zero",
msgs: []msgSpec{{
msg: &types.Message{
To: to1,
From: from1,
},
mpoolNonce: [1]uint64{0},
expNonce: 0,
}},
}, {
// Get non-zero nonce value from mpool
name: "mpool nonce set",
msgs: []msgSpec{{
msg: &types.Message{
To: to1,
From: from1,
},
mpoolNonce: [1]uint64{5},
expNonce: 5,
}, {
msg: &types.Message{
To: to1,
From: from1,
},
// Should adjust datastore nonce because mpool nonce is higher
mpoolNonce: [1]uint64{10},
expNonce: 10,
}},
}, {
// Nonce should increment independently for each address
name: "nonce increments per address",
msgs: []msgSpec{{
msg: &types.Message{
To: to1,
From: from1,
},
expNonce: 0,
}, {
msg: &types.Message{
To: to1,
From: from1,
},
expNonce: 1,
}, {
msg: &types.Message{
To: to2,
From: from2,
},
mpoolNonce: [1]uint64{5},
expNonce: 5,
}, {
msg: &types.Message{
To: to2,
From: from2,
},
expNonce: 6,
}, {
msg: &types.Message{
To: to1,
From: from1,
},
expNonce: 2,
}},
}}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
mpool := newMockMpool()
ds := ds_sync.MutexWrap(datastore.NewMapDatastore())
ms := newMessageSigner(w, mpool, ds)
for _, m := range tt.msgs {
if len(m.mpoolNonce) == 1 {
mpool.setNonce(m.msg.From, m.mpoolNonce[0])
}
smsg, err := ms.SignMessage(ctx, m.msg)
require.NoError(t, err)
require.Equal(t, m.expNonce, smsg.Message.Nonce)
}
})
}
}
+5
View File
@@ -395,6 +395,11 @@ func (st *StateTree) ForEach(f func(address.Address, *types.Actor) error) error
})
}
// Version returns the version of the StateTree data structure in use.
func (st *StateTree) Version() builtin.Version {
return st.version
}
func Diff(oldTree, newTree *StateTree) (map[string]types.Actor, error) {
out := map[string]types.Actor{}
+343 -38
View File
@@ -1,44 +1,59 @@
package stmgr
import (
"bytes"
"context"
"encoding/binary"
"math"
multisig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
"github.com/filecoin-project/lotus/chain/actors/builtin/multisig"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/specs-actors/actors/migration/nv3"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
init0 "github.com/filecoin-project/specs-actors/actors/builtin/init"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
adt0 "github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
cbor "github.com/ipfs/go-ipld-cbor"
"golang.org/x/xerrors"
)
var ForksAtHeight = map[abi.ChainEpoch]func(context.Context, *StateManager, types.StateTree, *types.TipSet) error{
build.UpgradeBreezeHeight: UpgradeFaucetBurnRecovery,
var ForksAtHeight = map[abi.ChainEpoch]func(context.Context, *StateManager, ExecCallback, cid.Cid, *types.TipSet) (cid.Cid, error){
build.UpgradeBreezeHeight: UpgradeFaucetBurnRecovery,
build.UpgradeIgnitionHeight: UpgradeIgnition,
build.UpgradeLiftoffHeight: UpgradeLiftoff,
}
func (sm *StateManager) handleStateForks(ctx context.Context, st types.StateTree, height abi.ChainEpoch, ts *types.TipSet) (err error) {
func (sm *StateManager) handleStateForks(ctx context.Context, root cid.Cid, height abi.ChainEpoch, cb ExecCallback, ts *types.TipSet) (cid.Cid, error) {
retCid := root
var err error
f, ok := ForksAtHeight[height]
if ok {
err := f(ctx, sm, st, ts)
retCid, err = f(ctx, sm, cb, root, ts)
if err != nil {
return err
return cid.Undef, err
}
}
return nil
return retCid, nil
}
type forEachTree interface {
ForEach(func(address.Address, *types.Actor) error) error
}
func doTransfer(tree types.StateTree, from, to address.Address, amt abi.TokenAmount) error {
func doTransfer(cb ExecCallback, tree types.StateTree, from, to address.Address, amt abi.TokenAmount) error {
fromAct, err := tree.GetActor(from)
if err != nil {
return xerrors.Errorf("failed to get 'from' actor for transfer: %w", err)
@@ -64,10 +79,43 @@ func doTransfer(tree types.StateTree, from, to address.Address, amt abi.TokenAmo
return xerrors.Errorf("failed to persist to actor: %w", err)
}
if cb != nil {
// record the transfer in execution traces
fakeMsg := &types.Message{
From: from,
To: to,
Value: amt,
Nonce: math.MaxUint64,
}
fakeRct := &types.MessageReceipt{
ExitCode: 0,
Return: nil,
GasUsed: 0,
}
if err := cb(fakeMsg.Cid(), fakeMsg, &vm.ApplyRet{
MessageReceipt: *fakeRct,
ActorErr: nil,
ExecutionTrace: types.ExecutionTrace{
Msg: fakeMsg,
MsgRct: fakeRct,
Error: "",
Duration: 0,
GasCharges: nil,
Subcalls: nil,
},
Duration: 0,
GasCosts: vm.ZeroGasOutputs(),
}); err != nil {
return xerrors.Errorf("recording transfer: %w", err)
}
}
return nil
}
func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, tree types.StateTree, ts *types.TipSet) error {
func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCallback, root cid.Cid, ts *types.TipSet) (cid.Cid, error) {
// Some initial parameters
FundsForMiners := types.FromFil(1_000_000)
LookbackEpoch := abi.ChainEpoch(32000)
@@ -94,22 +142,22 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, tree types
// Grab lookback state for account checks
lbts, err := sm.ChainStore().GetTipsetByHeight(ctx, LookbackEpoch, ts, false)
if err != nil {
return xerrors.Errorf("failed to get tipset at lookback height: %w", err)
return cid.Undef, xerrors.Errorf("failed to get tipset at lookback height: %w", err)
}
lbtree, err := sm.ParentState(lbts)
if err != nil {
return xerrors.Errorf("loading state tree failed: %w", err)
return cid.Undef, xerrors.Errorf("loading state tree failed: %w", err)
}
ReserveAddress, err := address.NewFromString("t090")
if err != nil {
return xerrors.Errorf("failed to parse reserve address: %w", err)
return cid.Undef, xerrors.Errorf("failed to parse reserve address: %w", err)
}
fetree, ok := tree.(forEachTree)
if !ok {
return xerrors.Errorf("fork transition state tree doesnt support ForEach (%T)", tree)
tree, err := sm.StateTree(root)
if err != nil {
return cid.Undef, xerrors.Errorf("getting state tree: %w", err)
}
type transfer struct {
@@ -121,7 +169,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, tree types
var transfers []transfer
// Take all excess funds away, put them into the reserve account
err = fetree.ForEach(func(addr address.Address, act *types.Actor) error {
err = tree.ForEach(func(addr address.Address, act *types.Actor) error {
switch act.Code {
case builtin0.AccountActorCodeID, builtin0.MultisigActorCodeID, builtin0.PaymentChannelActorCodeID:
sysAcc, err := isSystemAccount(addr)
@@ -163,13 +211,13 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, tree types
return nil
})
if err != nil {
return xerrors.Errorf("foreach over state tree failed: %w", err)
return cid.Undef, xerrors.Errorf("foreach over state tree failed: %w", err)
}
// Execute transfers from previous step
for _, t := range transfers {
if err := doTransfer(tree, t.From, t.To, t.Amt); err != nil {
return xerrors.Errorf("transfer %s %s->%s failed: %w", t.Amt, t.From, t.To, err)
if err := doTransfer(cb, tree, t.From, t.To, t.Amt); err != nil {
return cid.Undef, xerrors.Errorf("transfer %s %s->%s failed: %w", t.Amt, t.From, t.To, err)
}
}
@@ -177,19 +225,19 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, tree types
var ps power0.State
powAct, err := tree.GetActor(builtin0.StoragePowerActorAddr)
if err != nil {
return xerrors.Errorf("failed to load power actor: %w", err)
return cid.Undef, xerrors.Errorf("failed to load power actor: %w", err)
}
cst := cbor.NewCborStore(sm.ChainStore().Blockstore())
if err := cst.Get(ctx, powAct.Head, &ps); err != nil {
return xerrors.Errorf("failed to get power actor state: %w", err)
return cid.Undef, xerrors.Errorf("failed to get power actor state: %w", err)
}
totalPower := ps.TotalBytesCommitted
var transfersBack []transfer
// Now, we return some funds to places where they are needed
err = fetree.ForEach(func(addr address.Address, act *types.Actor) error {
err = tree.ForEach(func(addr address.Address, act *types.Actor) error {
lbact, err := lbtree.GetActor(addr)
if err != nil {
if !xerrors.Is(err, types.ErrActorNotFound) {
@@ -267,53 +315,310 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, tree types
return nil
})
if err != nil {
return xerrors.Errorf("foreach over state tree failed: %w", err)
return cid.Undef, xerrors.Errorf("foreach over state tree failed: %w", err)
}
for _, t := range transfersBack {
if err := doTransfer(tree, t.From, t.To, t.Amt); err != nil {
return xerrors.Errorf("transfer %s %s->%s failed: %w", t.Amt, t.From, t.To, err)
if err := doTransfer(cb, tree, t.From, t.To, t.Amt); err != nil {
return cid.Undef, xerrors.Errorf("transfer %s %s->%s failed: %w", t.Amt, t.From, t.To, err)
}
}
// transfer all burnt funds back to the reserve account
burntAct, err := tree.GetActor(builtin0.BurntFundsActorAddr)
if err != nil {
return xerrors.Errorf("failed to load burnt funds actor: %w", err)
return cid.Undef, xerrors.Errorf("failed to load burnt funds actor: %w", err)
}
if err := doTransfer(tree, builtin0.BurntFundsActorAddr, ReserveAddress, burntAct.Balance); err != nil {
return xerrors.Errorf("failed to unburn funds: %w", err)
if err := doTransfer(cb, tree, builtin0.BurntFundsActorAddr, ReserveAddress, burntAct.Balance); err != nil {
return cid.Undef, xerrors.Errorf("failed to unburn funds: %w", err)
}
// Top up the reimbursement service
reimbAddr, err := address.NewFromString("t0111")
if err != nil {
return xerrors.Errorf("failed to parse reimbursement service address")
return cid.Undef, xerrors.Errorf("failed to parse reimbursement service address")
}
reimb, err := tree.GetActor(reimbAddr)
if err != nil {
return xerrors.Errorf("failed to load reimbursement account actor: %w", err)
return cid.Undef, xerrors.Errorf("failed to load reimbursement account actor: %w", err)
}
difference := types.BigSub(DesiredReimbursementBalance, reimb.Balance)
if err := doTransfer(tree, ReserveAddress, reimbAddr, difference); err != nil {
return xerrors.Errorf("failed to top up reimbursement account: %w", err)
if err := doTransfer(cb, tree, ReserveAddress, reimbAddr, difference); err != nil {
return cid.Undef, xerrors.Errorf("failed to top up reimbursement account: %w", err)
}
// Now, a final sanity check to make sure the balances all check out
total := abi.NewTokenAmount(0)
err = fetree.ForEach(func(addr address.Address, act *types.Actor) error {
err = tree.ForEach(func(addr address.Address, act *types.Actor) error {
total = types.BigAdd(total, act.Balance)
return nil
})
if err != nil {
return xerrors.Errorf("checking final state balance failed: %w", err)
return cid.Undef, xerrors.Errorf("checking final state balance failed: %w", err)
}
exp := types.FromFil(build.FilBase)
if !exp.Equals(total) {
return xerrors.Errorf("resultant state tree account balance was not correct: %s", total)
return cid.Undef, xerrors.Errorf("resultant state tree account balance was not correct: %s", total)
}
return tree.Flush(ctx)
}
func UpgradeIgnition(ctx context.Context, sm *StateManager, cb ExecCallback, root cid.Cid, ts *types.TipSet) (cid.Cid, error) {
store := sm.cs.Store(ctx)
nst, err := nv3.MigrateStateTree(ctx, store, root, build.UpgradeIgnitionHeight)
if err != nil {
return cid.Undef, xerrors.Errorf("migrating actors state: %w", err)
}
tree, err := sm.StateTree(nst)
if err != nil {
return cid.Undef, xerrors.Errorf("getting state tree: %w", err)
}
err = setNetworkName(ctx, store, tree, "ignition")
if err != nil {
return cid.Undef, xerrors.Errorf("setting network name: %w", err)
}
split1, err := address.NewFromString("t0115")
if err != nil {
return cid.Undef, xerrors.Errorf("first split address: %w", err)
}
split2, err := address.NewFromString("t0116")
if err != nil {
return cid.Undef, xerrors.Errorf("second split address: %w", err)
}
err = resetGenesisMsigs(ctx, sm, store, tree)
if err != nil {
return cid.Undef, xerrors.Errorf("resetting genesis msig start epochs: %w", err)
}
err = splitGenesisMultisig(ctx, cb, split1, store, tree, 50)
if err != nil {
return cid.Undef, xerrors.Errorf("splitting first msig: %w", err)
}
err = splitGenesisMultisig(ctx, cb, split2, store, tree, 50)
if err != nil {
return cid.Undef, xerrors.Errorf("splitting second msig: %w", err)
}
err = nv3.CheckStateTree(ctx, store, nst, build.UpgradeIgnitionHeight, builtin0.TotalFilecoin)
if err != nil {
return cid.Undef, xerrors.Errorf("sanity check after ignition upgrade failed: %w", err)
}
return tree.Flush(ctx)
}
func UpgradeLiftoff(ctx context.Context, sm *StateManager, cb ExecCallback, root cid.Cid, ts *types.TipSet) (cid.Cid, error) {
tree, err := sm.StateTree(root)
if err != nil {
return cid.Undef, xerrors.Errorf("getting state tree: %w", err)
}
err = setNetworkName(ctx, sm.cs.Store(ctx), tree, "mainnet")
if err != nil {
return cid.Undef, xerrors.Errorf("setting network name: %w", err)
}
return tree.Flush(ctx)
}
func setNetworkName(ctx context.Context, store adt0.Store, tree *state.StateTree, name string) error {
ia, err := tree.GetActor(builtin0.InitActorAddr)
if err != nil {
return xerrors.Errorf("getting init actor: %w", err)
}
var initState init0.State
if err := store.Get(ctx, ia.Head, &initState); err != nil {
return xerrors.Errorf("reading init state: %w", err)
}
initState.NetworkName = name
ia.Head, err = store.Put(ctx, &initState)
if err != nil {
return xerrors.Errorf("writing new init state: %w", err)
}
if err := tree.SetActor(builtin0.InitActorAddr, ia); err != nil {
return xerrors.Errorf("setting init actor: %w", err)
}
return nil
}
func splitGenesisMultisig(ctx context.Context, cb ExecCallback, addr address.Address, store adt0.Store, tree *state.StateTree, portions uint64) error {
if portions < 1 {
return xerrors.Errorf("cannot split into 0 portions")
}
mact, err := tree.GetActor(addr)
if err != nil {
return xerrors.Errorf("getting msig actor: %w", err)
}
mst, err := multisig.Load(store, mact)
if err != nil {
return xerrors.Errorf("getting msig state: %w", err)
}
signers, err := mst.Signers()
if err != nil {
return xerrors.Errorf("getting msig signers: %w", err)
}
thresh, err := mst.Threshold()
if err != nil {
return xerrors.Errorf("getting msig threshold: %w", err)
}
ibal, err := mst.InitialBalance()
if err != nil {
return xerrors.Errorf("getting msig initial balance: %w", err)
}
se, err := mst.StartEpoch()
if err != nil {
return xerrors.Errorf("getting msig start epoch: %w", err)
}
ud, err := mst.UnlockDuration()
if err != nil {
return xerrors.Errorf("getting msig unlock duration: %w", err)
}
pending, err := adt0.MakeEmptyMap(store).Root()
if err != nil {
return xerrors.Errorf("failed to create empty map: %w", err)
}
newIbal := big.Div(ibal, types.NewInt(portions))
newState := &multisig0.State{
Signers: signers,
NumApprovalsThreshold: thresh,
NextTxnID: 0,
InitialBalance: newIbal,
StartEpoch: se,
UnlockDuration: ud,
PendingTxns: pending,
}
scid, err := store.Put(ctx, newState)
if err != nil {
return xerrors.Errorf("storing new state: %w", err)
}
newActor := types.Actor{
Code: builtin0.MultisigActorCodeID,
Head: scid,
Nonce: 0,
Balance: big.Zero(),
}
i := uint64(0)
for i < portions {
keyAddr, err := makeKeyAddr(addr, i)
if err != nil {
return xerrors.Errorf("creating key address: %w", err)
}
idAddr, err := tree.RegisterNewAddress(keyAddr)
if err != nil {
return xerrors.Errorf("registering new address: %w", err)
}
err = tree.SetActor(idAddr, &newActor)
if err != nil {
return xerrors.Errorf("setting new msig actor state: %w", err)
}
if err := doTransfer(cb, tree, addr, idAddr, newIbal); err != nil {
return xerrors.Errorf("transferring split msig balance: %w", err)
}
i++
}
return nil
}
func makeKeyAddr(splitAddr address.Address, count uint64) (address.Address, error) {
var b bytes.Buffer
if err := splitAddr.MarshalCBOR(&b); err != nil {
return address.Undef, xerrors.Errorf("marshalling split address: %w", err)
}
if err := binary.Write(&b, binary.BigEndian, count); err != nil {
return address.Undef, xerrors.Errorf("writing count into a buffer: %w", err)
}
if err := binary.Write(&b, binary.BigEndian, []byte("Ignition upgrade")); err != nil {
return address.Undef, xerrors.Errorf("writing fork name into a buffer: %w", err)
}
addr, err := address.NewActorAddress(b.Bytes())
if err != nil {
return address.Undef, xerrors.Errorf("create actor address: %w", err)
}
return addr, nil
}
func resetGenesisMsigs(ctx context.Context, sm *StateManager, store adt0.Store, tree *state.StateTree) error {
gb, err := sm.cs.GetGenesis()
if err != nil {
return xerrors.Errorf("getting genesis block: %w", err)
}
gts, err := types.NewTipSet([]*types.BlockHeader{gb})
if err != nil {
return xerrors.Errorf("getting genesis tipset: %w", err)
}
cst := cbor.NewCborStore(sm.cs.Blockstore())
genesisTree, err := state.LoadStateTree(cst, gts.ParentState())
if err != nil {
return xerrors.Errorf("loading state tree: %w", err)
}
err = genesisTree.ForEach(func(addr address.Address, genesisActor *types.Actor) error {
if genesisActor.Code == builtin0.MultisigActorCodeID {
currActor, err := tree.GetActor(addr)
if err != nil {
return xerrors.Errorf("loading actor: %w", err)
}
var currState multisig0.State
if err := store.Get(ctx, currActor.Head, &currState); err != nil {
return xerrors.Errorf("reading multisig state: %w", err)
}
currState.StartEpoch = build.UpgradeLiftoffHeight
currActor.Head, err = store.Put(ctx, &currState)
if err != nil {
return xerrors.Errorf("writing new multisig state: %w", err)
}
if err := tree.SetActor(addr, currActor); err != nil {
return xerrors.Errorf("setting multisig actor: %w", err)
}
}
return nil
})
if err != nil {
return xerrors.Errorf("iterating over genesis actors: %w", err)
}
return nil
+16 -15
View File
@@ -8,18 +8,15 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
"github.com/filecoin-project/specs-actors/actors/runtime"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/aerrors"
lotusinit "github.com/filecoin-project/lotus/chain/actors/builtin/init"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/stmgr"
. "github.com/filecoin-project/lotus/chain/stmgr"
@@ -28,17 +25,16 @@ import (
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log"
cbg "github.com/whyrusleeping/cbor-gen"
)
func init() {
miner0.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
}
power0.ConsensusMinerMinPower = big.NewInt(2048)
verifreg0.MinVerifiedDealSize = big.NewInt(256)
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
}
const testForkHeight = 40
@@ -119,33 +115,38 @@ func TestForkHeightTriggers(t *testing.T) {
t.Fatal(err)
}
stmgr.ForksAtHeight[testForkHeight] = func(ctx context.Context, sm *StateManager, st types.StateTree, ts *types.TipSet) error {
stmgr.ForksAtHeight[testForkHeight] = func(ctx context.Context, sm *StateManager, cb ExecCallback, root cid.Cid, ts *types.TipSet) (cid.Cid, error) {
cst := cbor.NewCborStore(sm.ChainStore().Blockstore())
st, err := sm.StateTree(root)
if err != nil {
return cid.Undef, xerrors.Errorf("getting state tree: %w", err)
}
act, err := st.GetActor(taddr)
if err != nil {
return err
return cid.Undef, err
}
var tas testActorState
if err := cst.Get(ctx, act.Head, &tas); err != nil {
return xerrors.Errorf("in fork handler, failed to run get: %w", err)
return cid.Undef, xerrors.Errorf("in fork handler, failed to run get: %w", err)
}
tas.HasUpgraded = 55
ns, err := cst.Put(ctx, &tas)
if err != nil {
return err
return cid.Undef, err
}
act.Head = ns
if err := st.SetActor(taddr, act); err != nil {
return err
return cid.Undef, err
}
return nil
return st.Flush(ctx)
}
inv.Register(builtin.PaymentChannelActorCodeID, &testActor{}, &testActorState{})
+159 -35
View File
@@ -41,12 +41,13 @@ var log = logging.Logger("statemgr")
type StateManager struct {
cs *store.ChainStore
stCache map[string][]cid.Cid
compWait map[string]chan struct{}
stlk sync.Mutex
genesisMsigLk sync.Mutex
newVM func(context.Context, *vm.VMOpts) (*vm.VM, error)
genInfo *genesisInfo
stCache map[string][]cid.Cid
compWait map[string]chan struct{}
stlk sync.Mutex
genesisMsigLk sync.Mutex
newVM func(context.Context, *vm.VMOpts) (*vm.VM, error)
preIgnitionGenInfos *genesisInfo
postIgnitionGenInfos *genesisInfo
}
func NewStateManager(cs *store.ChainStore) *StateManager {
@@ -123,9 +124,8 @@ func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st c
return st, rec, nil
}
func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) {
var trace []*api.InvocResult
st, _, err := sm.computeTipSetState(ctx, ts, func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
func traceFunc(trace *[]*api.InvocResult) func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
return func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
ir := &api.InvocResult{
Msg: msg,
MsgRct: &ret.MessageReceipt,
@@ -135,9 +135,14 @@ func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (c
if ret.ActorErr != nil {
ir.Error = ret.ActorErr.Error()
}
trace = append(trace, ir)
*trace = append(*trace, ir)
return nil
})
}
}
func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) {
var trace []*api.InvocResult
st, _, err := sm.computeTipSetState(ctx, ts, traceFunc(&trace))
if err != nil {
return cid.Undef, nil, err
}
@@ -149,20 +154,24 @@ type ExecCallback func(cid.Cid, *types.Message, *vm.ApplyRet) error
func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEpoch, pstate cid.Cid, bms []store.BlockMessages, epoch abi.ChainEpoch, r vm.Rand, cb ExecCallback, baseFee abi.TokenAmount, ts *types.TipSet) (cid.Cid, cid.Cid, error) {
vmopt := &vm.VMOpts{
StateBase: pstate,
Epoch: epoch,
Rand: r,
Bstore: sm.cs.Blockstore(),
Syscalls: sm.cs.VMSys(),
CircSupplyCalc: sm.GetCirculatingSupply,
NtwkVersion: sm.GetNtwkVersion,
BaseFee: baseFee,
makeVmWithBaseState := func(base cid.Cid) (*vm.VM, error) {
vmopt := &vm.VMOpts{
StateBase: base,
Epoch: epoch,
Rand: r,
Bstore: sm.cs.Blockstore(),
Syscalls: sm.cs.VMSys(),
CircSupplyCalc: sm.GetCirculatingSupply,
NtwkVersion: sm.GetNtwkVersion,
BaseFee: baseFee,
}
return sm.newVM(ctx, vmopt)
}
vmi, err := sm.newVM(ctx, vmopt)
vmi, err := makeVmWithBaseState(pstate)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err)
return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err)
}
runCron := func() error {
@@ -202,19 +211,32 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEp
for i := parentEpoch; i < epoch; i++ {
// handle state forks
// XXX: The state tree
err = sm.handleStateForks(ctx, vmi.StateTree(), i, ts)
newState, err := sm.handleStateForks(ctx, pstate, i, cb, ts)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("error handling state forks: %w", err)
}
if pstate != newState {
vmi, err = makeVmWithBaseState(newState)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err)
}
}
if i > parentEpoch {
// run cron for null rounds if any
if err := runCron(); err != nil {
return cid.Cid{}, cid.Cid{}, err
}
newState, err = vmi.Flush(ctx)
if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("flushing vm: %w", err)
}
}
vmi.SetBlockHeight(i + 1)
pstate = newState
}
var receipts []cbg.CBORMarshaler
@@ -904,7 +926,7 @@ func (sm *StateManager) setupGenesisActors(ctx context.Context) error {
gi.genesisMsigs = append(gi.genesisMsigs, ns)
}
sm.genInfo = &gi
sm.preIgnitionGenInfos = &gi
return nil
}
@@ -912,7 +934,7 @@ func (sm *StateManager) setupGenesisActors(ctx context.Context) error {
// sets up information about the actors in the genesis state
// For testnet we use a hardcoded set of multisig states, instead of what's actually in the genesis multisigs
// We also do not consider ANY account actors (including the faucet)
func (sm *StateManager) setupGenesisActorsTestnet(ctx context.Context) error {
func (sm *StateManager) setupPreIgnitionGenesisActorsTestnet(ctx context.Context) error {
gi := genesisInfo{}
@@ -981,7 +1003,87 @@ func (sm *StateManager) setupGenesisActorsTestnet(ctx context.Context) error {
gi.genesisMsigs = append(gi.genesisMsigs, ns)
}
sm.genInfo = &gi
sm.preIgnitionGenInfos = &gi
return nil
}
// sets up information about the actors in the genesis state, post the ignition fork
func (sm *StateManager) setupPostIgnitionGenesisActors(ctx context.Context) error {
gi := genesisInfo{}
gb, err := sm.cs.GetGenesis()
if err != nil {
return xerrors.Errorf("getting genesis block: %w", err)
}
gts, err := types.NewTipSet([]*types.BlockHeader{gb})
if err != nil {
return xerrors.Errorf("getting genesis tipset: %w", err)
}
st, _, err := sm.TipSetState(ctx, gts)
if err != nil {
return xerrors.Errorf("getting genesis tipset state: %w", err)
}
cst := cbor.NewCborStore(sm.cs.Blockstore())
sTree, err := state.LoadStateTree(cst, st)
if err != nil {
return xerrors.Errorf("loading state tree: %w", err)
}
// Unnecessary, should be removed
gi.genesisMarketFunds, err = getFilMarketLocked(ctx, sTree)
if err != nil {
return xerrors.Errorf("setting up genesis market funds: %w", err)
}
// Unnecessary, should be removed
gi.genesisPledge, err = getFilPowerLocked(ctx, sTree)
if err != nil {
return xerrors.Errorf("setting up genesis pledge: %w", err)
}
totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount)
// 6 months
sixMonths := abi.ChainEpoch(183 * builtin0.EpochsInDay)
totalsByEpoch[sixMonths] = big.NewInt(49_929_341)
totalsByEpoch[sixMonths] = big.Add(totalsByEpoch[sixMonths], big.NewInt(32_787_700))
// 1 year
oneYear := abi.ChainEpoch(365 * builtin0.EpochsInDay)
totalsByEpoch[oneYear] = big.NewInt(22_421_712)
// 2 years
twoYears := abi.ChainEpoch(2 * 365 * builtin0.EpochsInDay)
totalsByEpoch[twoYears] = big.NewInt(7_223_364)
// 3 years
threeYears := abi.ChainEpoch(3 * 365 * builtin0.EpochsInDay)
totalsByEpoch[threeYears] = big.NewInt(87_637_883)
// 6 years
sixYears := abi.ChainEpoch(6 * 365 * builtin0.EpochsInDay)
totalsByEpoch[sixYears] = big.NewInt(100_000_000)
totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000))
gi.genesisMsigs = make([]msig0.State, 0, len(totalsByEpoch))
for k, v := range totalsByEpoch {
ns := msig0.State{
// In the pre-ignition logic, we incorrectly set this value in Fil, not attoFil, an off-by-10^18 error
InitialBalance: big.Mul(v, big.NewInt(int64(build.FilecoinPrecision))),
UnlockDuration: k,
PendingTxns: cid.Undef,
// In the pre-ignition logic, the start epoch was 0. This changes in the fork logic of the Ignition upgrade itself.
StartEpoch: build.UpgradeLiftoffHeight,
}
gi.genesisMsigs = append(gi.genesisMsigs, ns)
}
sm.postIgnitionGenInfos = &gi
return nil
}
@@ -991,13 +1093,23 @@ func (sm *StateManager) setupGenesisActorsTestnet(ctx context.Context) error {
// - For Accounts, it counts max(currentBalance - genesisBalance, 0).
func (sm *StateManager) GetFilVested(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) {
vf := big.Zero()
for _, v := range sm.genInfo.genesisMsigs {
au := big.Sub(v.InitialBalance, v.AmountLocked(height))
vf = big.Add(vf, au)
if height <= build.UpgradeIgnitionHeight {
for _, v := range sm.preIgnitionGenInfos.genesisMsigs {
au := big.Sub(v.InitialBalance, v.AmountLocked(height))
vf = big.Add(vf, au)
}
} else {
for _, v := range sm.postIgnitionGenInfos.genesisMsigs {
// In the pre-ignition logic, we simply called AmountLocked(height), assuming startEpoch was 0.
// The start epoch changed in the Ignition upgrade.
au := big.Sub(v.InitialBalance, v.AmountLocked(height-v.StartEpoch))
vf = big.Add(vf, au)
}
}
// there should not be any such accounts in testnet (and also none in mainnet?)
for _, v := range sm.genInfo.genesisActors {
// continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch
for _, v := range sm.preIgnitionGenInfos.genesisActors {
act, err := st.GetActor(v.addr)
if err != nil {
return big.Zero(), xerrors.Errorf("failed to get actor: %w", err)
@@ -1009,8 +1121,10 @@ func (sm *StateManager) GetFilVested(ctx context.Context, height abi.ChainEpoch,
}
}
vf = big.Add(vf, sm.genInfo.genesisPledge)
vf = big.Add(vf, sm.genInfo.genesisMarketFunds)
// continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch
vf = big.Add(vf, sm.preIgnitionGenInfos.genesisPledge)
// continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch
vf = big.Add(vf, sm.preIgnitionGenInfos.genesisMarketFunds)
return vf, nil
}
@@ -1084,10 +1198,16 @@ func GetFilBurnt(ctx context.Context, st *state.StateTree) (abi.TokenAmount, err
func (sm *StateManager) GetCirculatingSupplyDetailed(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (api.CirculatingSupply, error) {
sm.genesisMsigLk.Lock()
defer sm.genesisMsigLk.Unlock()
if sm.genInfo == nil {
err := sm.setupGenesisActorsTestnet(ctx)
if sm.preIgnitionGenInfos == nil {
err := sm.setupPreIgnitionGenesisActorsTestnet(ctx)
if err != nil {
return api.CirculatingSupply{}, xerrors.Errorf("failed to setup genesis information: %w", err)
return api.CirculatingSupply{}, xerrors.Errorf("failed to setup pre-ignition genesis information: %w", err)
}
}
if sm.postIgnitionGenInfos == nil {
err := sm.setupPostIgnitionGenesisActors(ctx)
if err != nil {
return api.CirculatingSupply{}, xerrors.Errorf("failed to setup post-ignition genesis information: %w", err)
}
}
@@ -1152,6 +1272,10 @@ func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoc
return network.Version1
}
if height <= build.UpgradeIgnitionHeight {
return network.Version2
}
return build.NewestNetworkVersion
}
+15 -14
View File
@@ -102,6 +102,7 @@ func GetPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr addres
}
var mpow power.Claim
var minpow bool
if maddr != address.Undef {
var found bool
mpow, found, err = pas.MinerPower(maddr)
@@ -109,11 +110,11 @@ func GetPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr addres
// TODO: return an error when not found?
return power.Claim{}, power.Claim{}, false, err
}
}
minpow, err := pas.MinerNominalPowerMeetsConsensusMinimum(maddr)
if err != nil {
return power.Claim{}, power.Claim{}, false, err
minpow, err = pas.MinerNominalPowerMeetsConsensusMinimum(maddr)
if err != nil {
return power.Claim{}, power.Claim{}, false, err
}
}
return mpow, tpow, minpow, nil
@@ -368,6 +369,16 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch,
return cid.Undef, nil, err
}
for i := ts.Height(); i < height; i++ {
// handle state forks
base, err = sm.handleStateForks(ctx, base, i, traceFunc(&trace), ts)
if err != nil {
return cid.Undef, nil, xerrors.Errorf("error handling state forks: %w", err)
}
// TODO: should we also run cron here?
}
r := store.NewChainRand(sm.cs, ts.Cids())
vmopt := &vm.VMOpts{
StateBase: base,
@@ -384,16 +395,6 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch,
return cid.Undef, nil, err
}
for i := ts.Height(); i < height; i++ {
// handle state forks
err = sm.handleStateForks(ctx, vmi.StateTree(), i, ts)
if err != nil {
return cid.Undef, nil, xerrors.Errorf("error handling state forks: %w", err)
}
// TODO: should we also run cron here?
}
for i, msg := range msgs {
// TODO: Use the signed message length for secp messages
ret, err := vmi.ApplyMessage(ctx, msg)
+46 -8
View File
@@ -10,14 +10,18 @@ import (
"strconv"
"sync"
"golang.org/x/sync/errgroup"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/minio/blake2b-simd"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/journal"
bstore "github.com/filecoin-project/lotus/lib/blockstore"
@@ -282,6 +286,16 @@ func (cs *ChainStore) MarkBlockAsValidated(ctx context.Context, blkid cid.Cid) e
return nil
}
func (cs *ChainStore) UnmarkBlockAsValidated(ctx context.Context, blkid cid.Cid) error {
key := blockValidationCacheKeyPrefix.Instance(blkid.String())
if err := cs.ds.Delete(key); err != nil {
return xerrors.Errorf("removing from valid block cache: %w", err)
}
return nil
}
func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error {
ts, err := types.NewTipSet([]*types.BlockHeader{b})
if err != nil {
@@ -465,14 +479,25 @@ func (cs *ChainStore) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error) {
return v.(*types.TipSet), nil
}
var blks []*types.BlockHeader
for _, c := range tsk.Cids() {
b, err := cs.GetBlock(c)
if err != nil {
return nil, xerrors.Errorf("get block %s: %w", c, err)
}
// Fetch tipset block headers from blockstore in parallel
var eg errgroup.Group
cids := tsk.Cids()
blks := make([]*types.BlockHeader, len(cids))
for i, c := range cids {
i, c := i, c
eg.Go(func() error {
b, err := cs.GetBlock(c)
if err != nil {
return xerrors.Errorf("get block %s: %w", c, err)
}
blks = append(blks, b)
blks[i] = b
return nil
})
}
err := eg.Wait()
if err != nil {
return nil, err
}
ts, err := types.NewTipSet(blks)
@@ -1183,6 +1208,7 @@ func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRo
}
blocksToWalk := ts.Cids()
currentMinHeight := ts.Height()
walkChain := func(blk cid.Cid) error {
if !seen.Visit(blk) {
@@ -1203,6 +1229,13 @@ func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRo
return xerrors.Errorf("unmarshaling block header (cid=%s): %w", blk, err)
}
if currentMinHeight > b.Height {
currentMinHeight = b.Height
if currentMinHeight%builtin.EpochsInDay == 0 {
log.Infow("export", "height", currentMinHeight)
}
}
var cids []cid.Cid
if !skipOldMsgs || b.Height > ts.Height()-inclRecentRoots {
mcids, err := recurseLinks(cs.bs, walked, b.Messages, []cid.Cid{b.Messages})
@@ -1251,6 +1284,9 @@ func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRo
return nil
}
log.Infow("export started")
exportStart := build.Clock.Now()
for len(blocksToWalk) > 0 {
next := blocksToWalk[0]
blocksToWalk = blocksToWalk[1:]
@@ -1259,6 +1295,8 @@ func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRo
}
}
log.Infow("export finished", "duration", build.Clock.Now().Sub(exportStart).Seconds())
return nil
}
@@ -1301,7 +1339,7 @@ func (cs *ChainStore) GetLatestBeaconEntry(ts *types.TipSet) (*types.BeaconEntry
}, nil
}
return nil, xerrors.Errorf("found NO beacon entries in the 20 blocks prior to given tipset")
return nil, xerrors.Errorf("found NO beacon entries in the 20 latest tipsets")
}
type chainRand struct {
+4 -9
View File
@@ -8,12 +8,9 @@ import (
datastore "github.com/ipfs/go-datastore"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
@@ -22,11 +19,9 @@ import (
)
func init() {
miner0.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
}
power0.ConsensusMinerMinPower = big.NewInt(2048)
verifreg0.MinVerifiedDealSize = big.NewInt(256)
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
}
func BenchmarkGetRandomness(b *testing.B) {
+61 -41
View File
@@ -17,6 +17,7 @@ import (
"github.com/Gurpartap/async"
"github.com/hashicorp/go-multierror"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log/v2"
@@ -374,21 +375,28 @@ func (syncer *Syncer) InformNewBlock(from peer.ID, blk *types.FullBlock) bool {
return syncer.InformNewHead(from, fts)
}
func copyBlockstore(from, to bstore.Blockstore) error {
cids, err := from.AllKeysChan(context.TODO())
func copyBlockstore(ctx context.Context, from, to bstore.Blockstore) error {
ctx, span := trace.StartSpan(ctx, "copyBlockstore")
defer span.End()
cids, err := from.AllKeysChan(ctx)
if err != nil {
return err
}
// TODO: should probably expose better methods on the blockstore for this operation
var blks []blocks.Block
for c := range cids {
b, err := from.Get(c)
if err != nil {
return err
}
if err := to.Put(b); err != nil {
return err
}
blks = append(blks, b)
}
if err := to.PutMany(blks); err != nil {
return err
}
return nil
@@ -589,7 +597,7 @@ func isPermanent(err error) bool {
return !errors.Is(err, ErrTemporal)
}
func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet) error {
func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet, useCache bool) error {
ctx, span := trace.StartSpan(ctx, "validateTipSet")
defer span.End()
@@ -605,7 +613,7 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet)
b := b // rebind to a scoped variable
futures = append(futures, async.Err(func() error {
if err := syncer.ValidateBlock(ctx, b); err != nil {
if err := syncer.ValidateBlock(ctx, b, useCache); err != nil {
if isPermanent(err) {
syncer.bad.Add(b.Cid(), NewBadBlockReason([]cid.Cid{b.Cid()}, err.Error()))
}
@@ -672,7 +680,7 @@ func blockSanityChecks(h *types.BlockHeader) error {
}
// ValidateBlock should match up with 'Semantical Validation' in validation.md in the spec
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (err error) {
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock, useCache bool) (err error) {
defer func() {
// b.Cid() could panic for empty blocks that are used in tests.
if rerr := recover(); rerr != nil {
@@ -681,13 +689,15 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
}
}()
isValidated, err := syncer.store.IsBlockValidated(ctx, b.Cid())
if err != nil {
return xerrors.Errorf("check block validation cache %s: %w", b.Cid(), err)
}
if useCache {
isValidated, err := syncer.store.IsBlockValidated(ctx, b.Cid())
if err != nil {
return xerrors.Errorf("check block validation cache %s: %w", b.Cid(), err)
}
if isValidated {
return nil
if isValidated {
return nil
}
}
validationStart := build.Clock.Now()
@@ -774,31 +784,35 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
b.Header.ParentWeight, pweight)
}
// Stuff that needs stateroot / worker address
stateroot, precp, err := syncer.sm.TipSetState(ctx, baseTs)
if err != nil {
return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err)
}
if stateroot != h.ParentStateRoot {
msgs, err := syncer.store.MessagesForTipset(baseTs)
stateRootCheck := async.Err(func() error {
stateroot, precp, err := syncer.sm.TipSetState(ctx, baseTs)
if err != nil {
log.Error("failed to load messages for tipset during tipset state mismatch error: ", err)
} else {
log.Warn("Messages for tipset with mismatching state:")
for i, m := range msgs {
mm := m.VMMessage()
log.Warnf("Message[%d]: from=%s to=%s method=%d params=%x", i, mm.From, mm.To, mm.Method, mm.Params)
}
return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err)
}
return xerrors.Errorf("parent state root did not match computed state (%s != %s)", stateroot, h.ParentStateRoot)
}
if stateroot != h.ParentStateRoot {
msgs, err := syncer.store.MessagesForTipset(baseTs)
if err != nil {
log.Error("failed to load messages for tipset during tipset state mismatch error: ", err)
} else {
log.Warn("Messages for tipset with mismatching state:")
for i, m := range msgs {
mm := m.VMMessage()
log.Warnf("Message[%d]: from=%s to=%s method=%d params=%x", i, mm.From, mm.To, mm.Method, mm.Params)
}
}
if precp != h.ParentMessageReceipts {
return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts)
}
return xerrors.Errorf("parent state root did not match computed state (%s != %s)", stateroot, h.ParentStateRoot)
}
if precp != h.ParentMessageReceipts {
return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts)
}
return nil
})
// Stuff that needs worker address
waddr, err := stmgr.GetMinerWorkerRaw(ctx, syncer.sm, lbst, h.Miner)
if err != nil {
return xerrors.Errorf("GetMinerWorkerRaw failed: %w", err)
@@ -919,6 +933,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
winnerCheck,
msgsCheck,
baseFeeCheck,
stateRootCheck,
}
var merr error
@@ -946,8 +961,10 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
return mulErr
}
if err := syncer.store.MarkBlockAsValidated(ctx, b.Cid()); err != nil {
return xerrors.Errorf("caching block validation %s: %w", b.Cid(), err)
if useCache {
if err := syncer.store.MarkBlockAsValidated(ctx, b.Cid()); err != nil {
return xerrors.Errorf("caching block validation %s: %w", b.Cid(), err)
}
}
return nil
@@ -1449,7 +1466,7 @@ func (syncer *Syncer) syncMessagesAndCheckState(ctx context.Context, headers []*
return syncer.iterFullTipsets(ctx, headers, func(ctx context.Context, fts *store.FullTipSet) error {
log.Debugw("validating tipset", "height", fts.TipSet().Height(), "size", len(fts.TipSet().Cids()))
if err := syncer.ValidateTipSet(ctx, fts); err != nil {
if err := syncer.ValidateTipSet(ctx, fts, true); err != nil {
log.Errorf("failed to validate tipset: %+v", err)
return xerrors.Errorf("message processing failed: %w", err)
}
@@ -1515,11 +1532,11 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
return err
}
if err := persistMessages(bs, bstip); err != nil {
if err := persistMessages(ctx, bs, bstip); err != nil {
return err
}
if err := copyBlockstore(bs, syncer.store.Blockstore()); err != nil {
if err := copyBlockstore(ctx, bs, syncer.store.Blockstore()); err != nil {
return xerrors.Errorf("message processing failed: %w", err)
}
}
@@ -1596,7 +1613,10 @@ func (syncer *Syncer) fetchMessages(ctx context.Context, headers []*types.TipSet
return batch, nil
}
func persistMessages(bs bstore.Blockstore, bst *exchange.CompactedMessages) error {
func persistMessages(ctx context.Context, bs bstore.Blockstore, bst *exchange.CompactedMessages) error {
_, span := trace.StartSpan(ctx, "persistMessages")
defer span.End()
for _, m := range bst.Bls {
//log.Infof("putting BLS message: %s", m.Cid())
if _, err := store.PutMessage(bs, m); err != nil {
@@ -1728,7 +1748,7 @@ func (syncer *Syncer) getLatestBeaconEntry(_ context.Context, ts *types.TipSet)
cur = next
}
return nil, xerrors.Errorf("found NO beacon entries in the 20 blocks prior to given tipset")
return nil, xerrors.Errorf("found NO beacon entries in the 20 latest tipsets")
}
func (syncer *Syncer) IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool {
+6 -11
View File
@@ -19,13 +19,10 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/gen/slashfilter"
"github.com/filecoin-project/lotus/chain/store"
@@ -43,11 +40,9 @@ func init() {
if err != nil {
panic(err)
}
miner0.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
}
power0.ConsensusMinerMinPower = big.NewInt(2048)
verifreg0.MinVerifiedDealSize = big.NewInt(256)
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
}
const source = 0
@@ -737,7 +732,7 @@ func TestSyncInputs(t *testing.T) {
err := s.ValidateBlock(context.TODO(), &types.FullBlock{
Header: &types.BlockHeader{},
})
}, false)
if err == nil {
t.Fatal("should error on empty block")
}
@@ -746,7 +741,7 @@ func TestSyncInputs(t *testing.T) {
h.ElectionProof = nil
err = s.ValidateBlock(context.TODO(), &types.FullBlock{Header: h})
err = s.ValidateBlock(context.TODO(), &types.FullBlock{Header: h}, false)
if err == nil {
t.Fatal("should error on block with nil election proof")
}
+7 -3
View File
@@ -167,12 +167,16 @@ func (ts *TipSet) Equals(ots *TipSet) bool {
return false
}
if len(ts.blks) != len(ots.blks) {
if ts.height != ots.height {
return false
}
for i, b := range ts.blks {
if b.Cid() != ots.blks[i].Cid() {
if len(ts.cids) != len(ots.cids) {
return false
}
for i, cid := range ts.cids {
if cid != ots.cids[i] {
return false
}
}
+40
View File
@@ -1,9 +1,12 @@
package chain
import (
"crypto/rand"
"encoding/json"
"testing"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
)
@@ -35,3 +38,40 @@ func TestSignedMessageJsonRoundtrip(t *testing.T) {
t.Fatal(err)
}
}
func TestAddressType(t *testing.T) {
build.SetAddressNetwork(address.Testnet)
addr, err := makeRandomAddress()
if err != nil {
t.Fatal(err)
}
if string(addr[0]) != address.TestnetPrefix {
t.Fatalf("address should start with %s", address.TestnetPrefix)
}
build.SetAddressNetwork(address.Mainnet)
addr, err = makeRandomAddress()
if err != nil {
t.Fatal(err)
}
if string(addr[0]) != address.MainnetPrefix {
t.Fatalf("address should start with %s", address.MainnetPrefix)
}
}
func makeRandomAddress() (string, error) {
bytes := make([]byte, 32)
_, err := rand.Read(bytes)
if err != nil {
return "", err
}
addr, err := address.NewActorAddress(bytes)
if err != nil {
return "", err
}
return addr.String(), nil
}
+32 -26
View File
@@ -5,7 +5,6 @@ import (
"context"
"encoding/binary"
"fmt"
gruntime "runtime"
"time"
"github.com/filecoin-project/go-address"
@@ -460,8 +459,10 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
}
func (rt *Runtime) finilizeGasTracing() {
if rt.lastGasCharge != nil {
rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime)
if enableTracing {
if rt.lastGasCharge != nil {
rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime)
}
}
}
@@ -490,34 +491,39 @@ func (rt *Runtime) chargeGasFunc(skip int) func(GasCharge) {
}
var enableTracing = false
func (rt *Runtime) chargeGasInternal(gas GasCharge, skip int) aerrors.ActorError {
toUse := gas.Total()
var callers [10]uintptr
cout := gruntime.Callers(2+skip, callers[:])
if enableTracing {
var callers [10]uintptr
now := build.Clock.Now()
if rt.lastGasCharge != nil {
rt.lastGasCharge.TimeTaken = now.Sub(rt.lastGasChargeTime)
cout := 0 //gruntime.Callers(2+skip, callers[:])
now := build.Clock.Now()
if rt.lastGasCharge != nil {
rt.lastGasCharge.TimeTaken = now.Sub(rt.lastGasChargeTime)
}
gasTrace := types.GasTrace{
Name: gas.Name,
Extra: gas.Extra,
TotalGas: toUse,
ComputeGas: gas.ComputeGas,
StorageGas: gas.StorageGas,
TotalVirtualGas: gas.VirtualCompute*GasComputeMulti + gas.VirtualStorage*GasStorageMulti,
VirtualComputeGas: gas.VirtualCompute,
VirtualStorageGas: gas.VirtualStorage,
Callers: callers[:cout],
}
rt.executionTrace.GasCharges = append(rt.executionTrace.GasCharges, &gasTrace)
rt.lastGasChargeTime = now
rt.lastGasCharge = &gasTrace
}
gasTrace := types.GasTrace{
Name: gas.Name,
Extra: gas.Extra,
TotalGas: toUse,
ComputeGas: gas.ComputeGas,
StorageGas: gas.StorageGas,
TotalVirtualGas: gas.VirtualCompute*GasComputeMulti + gas.VirtualStorage*GasStorageMulti,
VirtualComputeGas: gas.VirtualCompute,
VirtualStorageGas: gas.VirtualStorage,
Callers: callers[:cout],
}
rt.executionTrace.GasCharges = append(rt.executionTrace.GasCharges, &gasTrace)
rt.lastGasChargeTime = now
rt.lastGasCharge = &gasTrace
// overflow safe
if rt.gasUsed > rt.gasAvailable-toUse {
rt.gasUsed = rt.gasAvailable
+39 -7
View File
@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"reflect"
"sync/atomic"
"time"
block "github.com/ipfs/go-block-format"
@@ -40,6 +41,12 @@ var log = logging.Logger("vm")
var actorLog = logging.Logger("actors")
var gasOnActorExec = newGasCharge("OnActorExec", 0, 0)
// stat counters
var (
StatSends uint64
StatApplied uint64
)
// ResolveToKeyAddr returns the public key type of address (`BLS`/`SECP256K1`) of an account actor identified by `addr`.
func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Address) (address.Address, error) {
if addr.Protocol() == address.BLS || addr.Protocol() == address.SECP256K1 {
@@ -204,6 +211,8 @@ type ApplyRet struct {
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
gasCharge *GasCharge, start time.Time) ([]byte, aerrors.ActorError, *Runtime) {
defer atomic.AddUint64(&StatSends, 1)
st := vm.cstate
origin := msg.From
@@ -218,14 +227,21 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
}
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
rt.lastGasChargeTime = start
if enableTracing {
rt.lastGasChargeTime = start
if parent != nil {
rt.lastGasChargeTime = parent.lastGasChargeTime
rt.lastGasCharge = parent.lastGasCharge
defer func() {
parent.lastGasChargeTime = rt.lastGasChargeTime
parent.lastGasCharge = rt.lastGasCharge
}()
}
}
if parent != nil {
rt.lastGasChargeTime = parent.lastGasChargeTime
rt.lastGasCharge = parent.lastGasCharge
defer func() {
parent.gasUsed = rt.gasUsed
parent.lastGasChargeTime = rt.lastGasChargeTime
parent.lastGasCharge = rt.lastGasCharge
}()
}
if gasCharge != nil {
@@ -312,6 +328,7 @@ func checkMessage(msg *types.Message) error {
func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) {
start := build.Clock.Now()
defer atomic.AddUint64(&StatApplied, 1)
ret, actorErr, rt := vm.send(ctx, msg, nil, nil, start)
rt.finilizeGasTracing()
return &ApplyRet{
@@ -331,6 +348,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
start := build.Clock.Now()
ctx, span := trace.StartSpan(ctx, "vm.ApplyMessage")
defer span.End()
defer atomic.AddUint64(&StatApplied, 1)
msg := cmsg.VMMessage()
if span.IsRecordingEvents() {
span.AddAttributes(
@@ -546,7 +564,7 @@ func (vm *VM) Flush(ctx context.Context) (cid.Cid, error) {
return cid.Undef, xerrors.Errorf("flushing vm: %w", err)
}
if err := Copy(from, to, root); err != nil {
if err := Copy(ctx, from, to, root); err != nil {
return cid.Undef, xerrors.Errorf("copying tree: %w", err)
}
@@ -600,9 +618,18 @@ func linksForObj(blk block.Block, cb func(cid.Cid)) error {
}
}
func Copy(from, to blockstore.Blockstore, root cid.Cid) error {
func Copy(ctx context.Context, from, to blockstore.Blockstore, root cid.Cid) error {
ctx, span := trace.StartSpan(ctx, "vm.Copy") // nolint
defer span.End()
var numBlocks int
var totalCopySize int
var batch []block.Block
batchCp := func(blk block.Block) error {
numBlocks++
totalCopySize += len(blk.RawData())
batch = append(batch, blk)
if len(batch) > 100 {
if err := to.PutMany(batch); err != nil {
@@ -623,6 +650,11 @@ func Copy(from, to blockstore.Blockstore, root cid.Cid) error {
}
}
span.AddAttributes(
trace.Int64Attribute("numBlocks", int64(numBlocks)),
trace.Int64Attribute("copySize", int64(totalCopySize)),
)
return nil
}
+6 -6
View File
@@ -569,7 +569,7 @@ func interactiveDeal(cctx *cli.Context) error {
continue
}
ask = *a.Ask
ask = *a
// TODO: run more validation
state = "confirm"
@@ -951,15 +951,15 @@ var clientQueryAskCmd = &cli.Command{
}
fmt.Printf("Ask: %s\n", maddr)
fmt.Printf("Price per GiB: %s\n", types.FIL(ask.Ask.Price))
fmt.Printf("Verified Price per GiB: %s\n", types.FIL(ask.Ask.VerifiedPrice))
fmt.Printf("Max Piece size: %s\n", types.SizeStr(types.NewInt(uint64(ask.Ask.MaxPieceSize))))
fmt.Printf("Price per GiB: %s\n", types.FIL(ask.Price))
fmt.Printf("Verified Price per GiB: %s\n", types.FIL(ask.VerifiedPrice))
fmt.Printf("Max Piece size: %s\n", types.SizeStr(types.NewInt(uint64(ask.MaxPieceSize))))
size := cctx.Int64("size")
if size == 0 {
return nil
}
perEpoch := types.BigDiv(types.BigMul(ask.Ask.Price, types.NewInt(uint64(size))), types.NewInt(1<<30))
perEpoch := types.BigDiv(types.BigMul(ask.Price, types.NewInt(uint64(size))), types.NewInt(1<<30))
fmt.Printf("Price per Block: %s\n", types.FIL(perEpoch))
duration := cctx.Int64("duration")
@@ -1423,7 +1423,7 @@ func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTr
otherPartyColumn: otherParty,
"Root Cid": rootCid,
"Initiated?": initiated,
"Transferred": channel.Transferred,
"Transferred": units.BytesSize(float64(channel.Transferred)),
"Voucher": voucher,
"Message": channel.Message,
}
+284
View File
@@ -9,6 +9,10 @@ import (
"strconv"
"text/tabwriter"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-address"
@@ -16,6 +20,7 @@ import (
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/specs-actors/actors/builtin"
init0 "github.com/filecoin-project/specs-actors/actors/builtin/init"
msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
@@ -40,6 +45,9 @@ var multisigCmd = &cli.Command{
msigSwapProposeCmd,
msigSwapApproveCmd,
msigSwapCancelCmd,
msigLockProposeCmd,
msigLockApproveCmd,
msigLockCancelCmd,
msigVestedCmd,
},
}
@@ -954,6 +962,282 @@ var msigSwapCancelCmd = &cli.Command{
},
}
var msigLockProposeCmd = &cli.Command{
Name: "lock-propose",
Usage: "Propose to lock up some balance",
ArgsUsage: "[multisigAddress startEpoch unlockDuration amount]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the propose message from",
},
},
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 4 {
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, start epoch, unlock duration, and amount"))
}
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
start, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64)
if err != nil {
return err
}
duration, err := strconv.ParseUint(cctx.Args().Get(2), 10, 64)
if err != nil {
return err
}
amount, err := types.ParseFIL(cctx.Args().Get(3))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
params, actErr := actors.SerializeParams(&msig0.LockBalanceParams{
StartEpoch: abi.ChainEpoch(start),
UnlockDuration: abi.ChainEpoch(duration),
Amount: abi.NewTokenAmount(amount.Int64()),
})
if actErr != nil {
return actErr
}
msgCid, err := api.MsigPropose(ctx, msig, msig, big.Zero(), from, uint64(builtin.MethodsMultisig.LockBalance), params)
if err != nil {
return err
}
fmt.Println("sent lock proposal in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
if err != nil {
return err
}
if wait.Receipt.ExitCode != 0 {
return fmt.Errorf("lock proposal returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigLockApproveCmd = &cli.Command{
Name: "lock-approve",
Usage: "Approve a message to lock up some balance",
ArgsUsage: "[multisigAddress proposerAddress txId startEpoch unlockDuration amount]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the approve message from",
},
},
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 6 {
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, tx id, start epoch, unlock duration, and amount"))
}
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
msig, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
prop, err := address.NewFromString(cctx.Args().Get(1))
if err != nil {
return err
}
txid, err := strconv.ParseUint(cctx.Args().Get(2), 10, 64)
if err != nil {
return err
}
start, err := strconv.ParseUint(cctx.Args().Get(3), 10, 64)
if err != nil {
return err
}
duration, err := strconv.ParseUint(cctx.Args().Get(4), 10, 64)
if err != nil {
return err
}
amount, err := types.ParseFIL(cctx.Args().Get(5))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
params, actErr := actors.SerializeParams(&msig0.LockBalanceParams{
StartEpoch: abi.ChainEpoch(start),
UnlockDuration: abi.ChainEpoch(duration),
Amount: abi.NewTokenAmount(amount.Int64()),
})
if actErr != nil {
return actErr
}
msgCid, err := api.MsigApprove(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(builtin.MethodsMultisig.LockBalance), params)
if err != nil {
return err
}
fmt.Println("sent lock approval in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
if err != nil {
return err
}
if wait.Receipt.ExitCode != 0 {
return fmt.Errorf("lock approval returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigLockCancelCmd = &cli.Command{
Name: "lock-cancel",
Usage: "Cancel a message to lock up some balance",
ArgsUsage: "[multisigAddress txId startEpoch unlockDuration amount]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "account to send the cancel message from",
},
},
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 6 {
return ShowHelp(cctx, fmt.Errorf("must pass multisig address, tx id, start epoch, unlock duration, and amount"))
}
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := ReqContext(cctx)
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
}
start, err := strconv.ParseUint(cctx.Args().Get(2), 10, 64)
if err != nil {
return err
}
duration, err := strconv.ParseUint(cctx.Args().Get(3), 10, 64)
if err != nil {
return err
}
amount, err := types.ParseFIL(cctx.Args().Get(4))
if err != nil {
return err
}
var from address.Address
if cctx.IsSet("from") {
f, err := address.NewFromString(cctx.String("from"))
if err != nil {
return err
}
from = f
} else {
defaddr, err := api.WalletDefaultAddress(ctx)
if err != nil {
return err
}
from = defaddr
}
params, actErr := actors.SerializeParams(&msig0.LockBalanceParams{
StartEpoch: abi.ChainEpoch(start),
UnlockDuration: abi.ChainEpoch(duration),
Amount: abi.NewTokenAmount(amount.Int64()),
})
if actErr != nil {
return actErr
}
msgCid, err := api.MsigCancel(ctx, msig, txid, msig, big.Zero(), from, uint64(builtin.MethodsMultisig.LockBalance), params)
if err != nil {
return err
}
fmt.Println("sent lock cancellation in message: ", msgCid)
wait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence)
if err != nil {
return err
}
if wait.Receipt.ExitCode != 0 {
return fmt.Errorf("lock cancellation returned exit %d", wait.Receipt.ExitCode)
}
return nil
},
}
var msigVestedCmd = &cli.Command{
Name: "vested",
Usage: "Gets the amount vested in an msig between two epochs",
+20 -28
View File
@@ -12,39 +12,31 @@ import (
"testing"
"time"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/go-state-types/big"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
power0 "github.com/filecoin-project/specs-actors/actors/builtin/power"
verifreg0 "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
"github.com/multiformats/go-multiaddr"
"github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/api/apibstore"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api/test"
"github.com/filecoin-project/lotus/chain/wallet"
builder "github.com/filecoin-project/lotus/node/test"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/multiformats/go-multiaddr"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/builtin/paych"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/api/apibstore"
"github.com/filecoin-project/lotus/api/test"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/events"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
builder "github.com/filecoin-project/lotus/node/test"
)
func init() {
power0.ConsensusMinerMinPower = big.NewInt(2048)
miner0.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
}
verifreg0.MinVerifiedDealSize = big.NewInt(256)
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048))
policy.SetMinVerifiedDealSize(abi.NewStoragePower(256))
}
// TestPaymentChannels does a basic test to exercise the payment channel CLI
+29 -8
View File
@@ -19,6 +19,7 @@ import (
"github.com/multiformats/go-multiaddr"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/multiformats/go-multihash"
"github.com/urfave/cli/v2"
@@ -33,7 +34,9 @@ import (
"github.com/filecoin-project/specs-actors/actors/builtin/exported"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/apibstore"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/types"
)
@@ -818,6 +821,10 @@ var stateComputeStateCmd = &cli.Command{
Name: "html",
Usage: "generate html report",
},
&cli.BoolFlag{
Name: "json",
Usage: "generate json output",
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
@@ -834,14 +841,14 @@ var stateComputeStateCmd = &cli.Command{
}
h := abi.ChainEpoch(cctx.Uint64("vm-height"))
if h == 0 {
if ts == nil {
head, err := api.ChainHead(ctx)
if err != nil {
return err
}
ts = head
if ts == nil {
head, err := api.ChainHead(ctx)
if err != nil {
return err
}
ts = head
}
if h == 0 {
h = ts.Height()
}
@@ -862,14 +869,28 @@ var stateComputeStateCmd = &cli.Command{
return err
}
if cctx.Bool("json") {
out, err := json.Marshal(stout)
if err != nil {
return err
}
fmt.Println(string(out))
return nil
}
if cctx.Bool("html") {
st, err := state.LoadStateTree(cbor.NewCborStore(apibstore.NewAPIBlockstore(api)), stout.Root)
if err != nil {
return xerrors.Errorf("loading state tree: %w", err)
}
codeCache := map[address.Address]cid.Cid{}
getCode := func(addr address.Address) (cid.Cid, error) {
if c, found := codeCache[addr]; found {
return c, nil
}
c, err := api.StateGetActor(ctx, addr, ts.Key())
c, err := st.GetActor(addr)
if err != nil {
return cid.Cid{}, err
}
+31 -2
View File
@@ -225,6 +225,16 @@ var syncCheckpointCmd = &cli.Command{
}
func SyncWait(ctx context.Context, napi api.FullNode) error {
tick := time.Second / 4
lastLines := 0
ticker := time.NewTicker(tick)
defer ticker.Stop()
samples := 8
i := 0
var app, lastApp uint64
for {
state, err := napi.SyncState(ctx)
if err != nil {
@@ -266,7 +276,24 @@ func SyncWait(ctx context.Context, napi api.FullNode) error {
heightDiff = 0
}
fmt.Printf("\r\x1b[2KWorker %d: Base Height: %d\tTarget Height: %d\t Height diff: %d\tTarget: %s\tState: %s\tHeight: %d", working, baseHeight, theight, heightDiff, target, ss.Stage, ss.Height)
for i := 0; i < lastLines; i++ {
fmt.Print("\r\x1b[2K\x1b[A")
}
fmt.Printf("Worker: %d; Base: %d; Target: %d (diff: %d)\n", working, baseHeight, theight, heightDiff)
fmt.Printf("State: %s; Current Epoch: %d; Todo: %d\n", ss.Stage, ss.Height, theight-ss.Height)
lastLines = 2
if i%samples == 0 {
lastApp = app
app = state.VMApplied
}
if i > 0 {
fmt.Printf("Validated %d messages (%d per second)\n", state.VMApplied, (app-lastApp)*uint64(time.Second/tick)/uint64(samples))
lastLines++
}
_ = target // todo: maybe print? (creates a bunch of line wrapping issues with most tipsets)
if time.Now().Unix()-int64(head.MinTimestamp()) < int64(build.BlockDelaySecs) {
fmt.Println("\nDone!")
@@ -277,7 +304,9 @@ func SyncWait(ctx context.Context, napi api.FullNode) error {
case <-ctx.Done():
fmt.Println("\nExit by user")
return nil
case <-build.Clock.After(1 * time.Second):
case <-ticker.C:
}
i++
}
}
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"time"
"github.com/hako/durafmt"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/go-state-types/abi"
@@ -36,11 +37,11 @@ func parseTipSet(ctx context.Context, api api.FullNode, vals []string) (*types.T
func EpochTime(curr, e abi.ChainEpoch) string {
switch {
case curr > e:
return fmt.Sprintf("%d (%s ago)", e, time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e)))
return fmt.Sprintf("%d (%s ago)", e, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e))).LimitFirstN(2))
case curr == e:
return fmt.Sprintf("%d (now)", e)
case curr < e:
return fmt.Sprintf("%d (in %s)", e, time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr)))
return fmt.Sprintf("%d (in %s)", e, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr))).LimitFirstN(2))
}
panic("math broke")
+59 -6
View File
@@ -9,13 +9,16 @@ import (
"os"
"strings"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/crypto"
types "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/crypto"
types "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/lib/tablewriter"
)
var walletCmd = &cli.Command{
@@ -66,6 +69,13 @@ var walletNew = &cli.Command{
var walletList = &cli.Command{
Name: "list",
Usage: "List wallet address",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "addr-only",
Usage: "Only print addresses",
Aliases: []string{"a"},
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
@@ -79,9 +89,52 @@ var walletList = &cli.Command{
return err
}
// Assume an error means no default key is set
def, _ := api.WalletDefaultAddress(ctx)
tw := tablewriter.New(
tablewriter.Col("Address"),
tablewriter.Col("Balance"),
tablewriter.Col("Nonce"),
tablewriter.Col("Default"),
tablewriter.NewLineCol("Error"))
for _, addr := range addrs {
fmt.Println(addr.String())
if cctx.Bool("addr-only") {
fmt.Println(addr.String())
} else {
a, err := api.StateGetActor(ctx, addr, types.EmptyTSK)
if err != nil {
if !strings.Contains(err.Error(), "actor not found") {
tw.Write(map[string]interface{}{
"Address": addr,
"Error": err,
})
continue
}
a = &types.Actor{
Balance: big.Zero(),
}
}
row := map[string]interface{}{
"Address": addr,
"Balance": types.FIL(a.Balance),
"Nonce": a.Nonce,
}
if addr == def {
row["Default"] = "X"
}
tw.Write(row)
}
}
if !cctx.Bool("addr-only") {
return tw.Flush(os.Stdout)
}
return nil
},
}
+98
View File
@@ -0,0 +1,98 @@
package main
import (
"bufio"
"context"
"errors"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
"github.com/filecoin-project/specs-actors/actors/runtime/proof"
"github.com/ipfs/go-datastore"
"github.com/minio/blake2b-simd"
cbg "github.com/whyrusleeping/cbor-gen"
)
type cachingVerifier struct {
ds datastore.Datastore
backend ffiwrapper.Verifier
}
const bufsize = 128
func (cv cachingVerifier) withCache(execute func() (bool, error), param cbg.CBORMarshaler) (bool, error) {
hasher := blake2b.New256()
wr := bufio.NewWriterSize(hasher, bufsize)
err := param.MarshalCBOR(wr)
if err != nil {
log.Errorf("could not marshal call info: %+v", err)
return execute()
}
err = wr.Flush()
if err != nil {
log.Errorf("could not flush: %+v", err)
return execute()
}
hash := hasher.Sum(nil)
key := datastore.NewKey(string(hash))
fromDs, err := cv.ds.Get(key)
if err == nil {
switch fromDs[0] {
case 's':
return true, nil
case 'f':
return false, nil
case 'e':
return false, errors.New(string(fromDs[1:]))
default:
log.Errorf("bad cached result in cache %s(%x)", fromDs[0], fromDs[0])
return execute()
}
} else if errors.Is(err, datastore.ErrNotFound) {
// recalc
ok, err := execute()
var save []byte
if err != nil {
if ok {
log.Errorf("success with an error: %+v", err)
} else {
save = append([]byte{'e'}, []byte(err.Error())...)
}
} else if ok {
save = []byte{'s'}
} else {
save = []byte{'f'}
}
if len(save) != 0 {
errSave := cv.ds.Put(key, save)
if errSave != nil {
log.Errorf("error saving result: %+v", errSave)
}
}
return ok, err
} else {
log.Errorf("could not get data from cache: %+v", err)
return execute()
}
}
func (cv *cachingVerifier) VerifySeal(svi proof.SealVerifyInfo) (bool, error) {
return cv.withCache(func() (bool, error) {
return cv.backend.VerifySeal(svi)
}, &svi)
}
func (cv *cachingVerifier) VerifyWinningPoSt(ctx context.Context, info proof.WinningPoStVerifyInfo) (bool, error) {
return cv.backend.VerifyWinningPoSt(ctx, info)
}
func (cv *cachingVerifier) VerifyWindowPoSt(ctx context.Context, info proof.WindowPoStVerifyInfo) (bool, error) {
return cv.withCache(func() (bool, error) {
return cv.backend.VerifyWindowPoSt(ctx, info)
}, &info)
}
func (cv *cachingVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, proofType abi.RegisteredPoStProof, a abi.ActorID, rnd abi.PoStRandomness, u uint64) ([]uint64, error) {
return cv.backend.GenerateWinningPoStSectorChallenge(ctx, proofType, a, rnd, u)
}
var _ ffiwrapper.Verifier = (*cachingVerifier)(nil)
+165 -43
View File
@@ -16,6 +16,8 @@ import (
"sort"
"time"
"github.com/cockroachdb/pebble"
"github.com/cockroachdb/pebble/bloom"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
@@ -24,12 +26,16 @@ import (
"github.com/filecoin-project/lotus/lib/blockstore"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/ipld/go-car"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
bdg "github.com/dgraph-io/badger/v2"
"github.com/ipfs/go-datastore"
badger "github.com/ipfs/go-ds-badger2"
pebbleds "github.com/ipfs/go-ds-pebble"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
)
@@ -60,6 +66,29 @@ var importBenchCmd = &cli.Command{
Name: "repodir",
Usage: "set the repo directory for the lotus bench run (defaults to /tmp)",
},
&cli.StringFlag{
Name: "syscall-cache",
Usage: "read and write syscall results from datastore",
},
&cli.BoolFlag{
Name: "export-traces",
Usage: "should we export execution traces",
Value: true,
},
&cli.BoolFlag{
Name: "no-import",
Usage: "should we import the chain? if set to true chain has to be previously imported",
},
&cli.BoolFlag{
Name: "global-profile",
Value: true,
},
&cli.Int64Flag{
Name: "start-at",
},
&cli.BoolFlag{
Name: "only-import",
},
},
Action: func(cctx *cli.Context) error {
vm.BatchSealVerifyParallelism = cctx.Int("batch-seal-verify-threads")
@@ -74,6 +103,10 @@ var importBenchCmd = &cli.Command{
}
defer cfi.Close() //nolint:errcheck // read only file
go func() {
http.ListenAndServe("localhost:6060", nil) //nolint:errcheck
}()
var tdir string
if rdir := cctx.String("repodir"); rdir != "" {
tdir = rdir
@@ -85,33 +118,105 @@ var importBenchCmd = &cli.Command{
tdir = tmp
}
bds, err := badger.NewDatastore(tdir, nil)
bdgOpt := badger.DefaultOptions
bdgOpt.GcInterval = 0
bdgOpt.Options = bdg.DefaultOptions("")
bdgOpt.Options.SyncWrites = false
bdgOpt.Options.Truncate = true
bdgOpt.Options.DetectConflicts = false
var bds datastore.Batching
if false {
cache := 512
bds, err = pebbleds.NewDatastore(tdir, &pebble.Options{
// Pebble has a single combined cache area and the write
// buffers are taken from this too. Assign all available
// memory allowance for cache.
Cache: pebble.NewCache(int64(cache * 1024 * 1024)),
// The size of memory table(as well as the write buffer).
// Note, there may have more than two memory tables in the system.
// MemTableStopWritesThreshold can be configured to avoid the memory abuse.
MemTableSize: cache * 1024 * 1024 / 4,
// The default compaction concurrency(1 thread),
// Here use all available CPUs for faster compaction.
MaxConcurrentCompactions: runtime.NumCPU(),
// Per-level options. Options for at least one level must be specified. The
// options for the last level are used for all subsequent levels.
Levels: []pebble.LevelOptions{
{TargetFileSize: 16 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10), Compression: pebble.NoCompression},
},
Logger: log,
})
} else {
bds, err = badger.NewDatastore(tdir, &bdgOpt)
}
if err != nil {
return err
}
defer bds.Close() //nolint:errcheck
bs := blockstore.NewBlockstore(bds)
cbs, err := blockstore.CachedBlockstore(context.TODO(), bs, blockstore.DefaultCacheOpts())
cacheOpts := blockstore.DefaultCacheOpts()
cacheOpts.HasBloomFilterSize = 0
cbs, err := blockstore.CachedBlockstore(context.TODO(), bs, cacheOpts)
if err != nil {
return err
}
bs = cbs
ds := datastore.NewMapDatastore()
cs := store.NewChainStore(bs, ds, vm.Syscalls(ffiwrapper.ProofVerifier))
var verifier ffiwrapper.Verifier = ffiwrapper.ProofVerifier
if cctx.IsSet("syscall-cache") {
scds, err := badger.NewDatastore(cctx.String("syscall-cache"), &bdgOpt)
if err != nil {
return xerrors.Errorf("opening syscall-cache datastore: %w", err)
}
defer scds.Close() //nolint:errcheck
verifier = &cachingVerifier{
ds: scds,
backend: verifier,
}
}
if cctx.Bool("only-gc") {
return nil
}
cs := store.NewChainStore(bs, ds, vm.Syscalls(verifier))
stm := stmgr.NewStateManager(cs)
prof, err := os.Create("import-bench.prof")
if err != nil {
return err
}
defer prof.Close() //nolint:errcheck
if cctx.Bool("global-profile") {
prof, err := os.Create("import-bench.prof")
if err != nil {
return err
}
defer prof.Close() //nolint:errcheck
if err := pprof.StartCPUProfile(prof); err != nil {
return err
if err := pprof.StartCPUProfile(prof); err != nil {
return err
}
}
head, err := cs.Import(cfi)
if err != nil {
return err
var head *types.TipSet
if !cctx.Bool("no-import") {
head, err = cs.Import(cfi)
if err != nil {
return err
}
} else {
cr, err := car.NewCarReader(cfi)
if err != nil {
return err
}
head, err = cs.LoadTipSet(types.NewTipSetKey(cr.Header.Roots...))
if err != nil {
return err
}
}
if cctx.Bool("only-import") {
return nil
}
gb, err := cs.GetTipsetByHeight(context.TODO(), 0, head, true)
@@ -124,6 +229,20 @@ var importBenchCmd = &cli.Command{
return err
}
startEpoch := abi.ChainEpoch(1)
if cctx.IsSet("start-at") {
startEpoch = abi.ChainEpoch(cctx.Int64("start-at"))
start, err := cs.GetTipsetByHeight(context.TODO(), abi.ChainEpoch(cctx.Int64("start-at")), head, true)
if err != nil {
return err
}
err = cs.SetHead(start)
if err != nil {
return err
}
}
if h := cctx.Int64("height"); h != 0 {
tsh, err := cs.GetTipsetByHeight(context.TODO(), abi.ChainEpoch(h), head, true)
if err != nil {
@@ -134,7 +253,7 @@ var importBenchCmd = &cli.Command{
ts := head
tschain := []*types.TipSet{ts}
for ts.Height() != 0 {
for ts.Height() > startEpoch {
next, err := cs.LoadTipSet(ts.Parents())
if err != nil {
return err
@@ -144,45 +263,48 @@ var importBenchCmd = &cli.Command{
ts = next
}
ibj, err := os.Create("import-bench.json")
if err != nil {
return err
var enc *json.Encoder
if cctx.Bool("export-traces") {
ibj, err := os.Create("import-bench.json")
if err != nil {
return err
}
defer ibj.Close() //nolint:errcheck
enc = json.NewEncoder(ibj)
}
defer ibj.Close() //nolint:errcheck
enc := json.NewEncoder(ibj)
var lastTse *TipSetExec
lastState := tschain[len(tschain)-1].ParentState()
for i := len(tschain) - 2; i >= 0; i-- {
for i := len(tschain) - 1; i >= 1; i-- {
cur := tschain[i]
start := time.Now()
log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids())
if cur.ParentState() != lastState {
lastTrace := lastTse.Trace
st, trace, err := stm.ExecutionTrace(context.TODO(), cur)
if err != nil {
return err
}
tse := &TipSetExec{
TipSet: cur.Key(),
Trace: trace,
Duration: time.Since(start),
}
if enc != nil {
stripCallers(tse.Trace)
if err := enc.Encode(tse); err != nil {
return xerrors.Errorf("failed to write out tipsetexec: %w", err)
}
}
if tschain[i-1].ParentState() != st {
stripCallers(tse.Trace)
lastTrace := tse.Trace
d, err := json.MarshalIndent(lastTrace, "", " ")
if err != nil {
panic(err)
}
fmt.Println("TRACE")
fmt.Println(string(d))
return xerrors.Errorf("tipset chain had state mismatch at height %d (%s != %s)", cur.Height(), cur.ParentState(), lastState)
}
start := time.Now()
st, trace, err := stm.ExecutionTrace(context.TODO(), cur)
if err != nil {
return err
}
stripCallers(trace)
lastTse = &TipSetExec{
TipSet: cur.Key(),
Trace: trace,
Duration: time.Since(start),
}
lastState = st
if err := enc.Encode(lastTse); err != nil {
return xerrors.Errorf("failed to write out tipsetexec: %w", err)
//fmt.Println(statediff.Diff(context.Background(), bs, tschain[i-1].ParentState(), st, statediff.ExpandActors))
return xerrors.Errorf("tipset chain had state mismatch at height %d (%s != %s)", cur.Height(), cur.ParentState(), st)
}
}
+3 -3
View File
@@ -27,11 +27,11 @@ import (
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper/basicfs"
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/specs-storage/storage"
lapi "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/genesis"
)
@@ -76,8 +76,6 @@ func main() {
log.Info("Starting lotus-bench")
miner.SupportedProofTypes[abi.RegisteredSealProof_StackedDrg2KiBV1] = struct{}{}
app := &cli.App{
Name: "lotus-bench",
Usage: "Benchmark performance of lotus on your hardware",
@@ -147,6 +145,8 @@ var sealBenchCmd = &cli.Command{
},
},
Action: func(c *cli.Context) error {
policy.AddSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
if c.Bool("no-gpu") {
err := os.Setenv("BELLMAN_NO_GPU", "1")
if err != nil {
+7
View File
@@ -109,6 +109,11 @@ var runCmd = &cli.Command{
Name: "no-local-storage",
Usage: "don't use storageminer repo for sector storage",
},
&cli.BoolFlag{
Name: "no-swap",
Usage: "don't use swap",
Value: false,
},
&cli.BoolFlag{
Name: "addpiece",
Usage: "enable addpiece",
@@ -346,6 +351,7 @@ var runCmd = &cli.Command{
LocalWorker: sectorstorage.NewLocalWorker(sectorstorage.WorkerConfig{
SealProof: spt,
TaskTypes: taskTypes,
NoSwap: cctx.Bool("no-swap"),
}, remote, localStore, nodeApi),
localStore: localStore,
ls: lr,
@@ -465,6 +471,7 @@ func watchMinerConn(ctx context.Context, cctx *cli.Context, nodeApi api.StorageM
"run",
fmt.Sprintf("--listen=%s", cctx.String("listen")),
fmt.Sprintf("--no-local-storage=%t", cctx.Bool("no-local-storage")),
fmt.Sprintf("--no-swap=%t", cctx.Bool("no-swap")),
fmt.Sprintf("--addpiece=%t", cctx.Bool("addpiece")),
fmt.Sprintf("--precommit1=%t", cctx.Bool("precommit1")),
fmt.Sprintf("--unseal=%t", cctx.Bool("unseal")),
+138
View File
@@ -3,9 +3,16 @@ package main
import (
"context"
"fmt"
"strconv"
"github.com/docker/go-units"
lotusbuiltin "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
"github.com/filecoin-project/lotus/chain/actors/builtin/reward"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log/v2"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
@@ -44,6 +51,7 @@ var auditsCmd = &cli.Command{
Subcommands: []*cli.Command{
chainBalanceCmd,
chainBalanceStateCmd,
chainPledgeCmd,
},
}
@@ -248,3 +256,133 @@ var chainBalanceStateCmd = &cli.Command{
return nil
},
}
var chainPledgeCmd = &cli.Command{
Name: "stateroot-pledge",
Description: "Calculate sector pledge numbers",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
},
},
ArgsUsage: "[stateroot epoch]",
Action: func(cctx *cli.Context) error {
logging.SetLogLevel("badger", "ERROR")
ctx := context.TODO()
if !cctx.Args().Present() {
return fmt.Errorf("must pass state root")
}
sroot, err := cid.Decode(cctx.Args().First())
if err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}
epoch, err := strconv.ParseInt(cctx.Args().Get(1), 10, 64)
if err != nil {
return xerrors.Errorf("parsing epoch arg: %w", err)
}
fsrepo, err := repo.NewFS(cctx.String("repo"))
if err != nil {
return err
}
lkrepo, err := fsrepo.Lock(repo.FullNode)
if err != nil {
return err
}
defer lkrepo.Close() //nolint:errcheck
ds, err := lkrepo.Datastore("/chain")
if err != nil {
return err
}
mds, err := lkrepo.Datastore("/metadata")
if err != nil {
return err
}
bs := blockstore.NewBlockstore(ds)
cs := store.NewChainStore(bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier))
cst := cbor.NewCborStore(bs)
store := adt.WrapStore(ctx, cst)
sm := stmgr.NewStateManager(cs)
state, err := state.LoadStateTree(cst, sroot)
if err != nil {
return err
}
var (
powerSmoothed lotusbuiltin.FilterEstimate
pledgeCollateral abi.TokenAmount
)
if act, err := state.GetActor(power.Address); err != nil {
return xerrors.Errorf("loading miner actor: %w", err)
} else if s, err := power.Load(store, act); err != nil {
return xerrors.Errorf("loading power actor state: %w", err)
} else if p, err := s.TotalPowerSmoothed(); err != nil {
return xerrors.Errorf("failed to determine total power: %w", err)
} else if c, err := s.TotalLocked(); err != nil {
return xerrors.Errorf("failed to determine pledge collateral: %w", err)
} else {
powerSmoothed = p
pledgeCollateral = c
}
circ, err := sm.GetCirculatingSupplyDetailed(ctx, abi.ChainEpoch(epoch), state)
if err != nil {
return err
}
fmt.Println("(real) circulating supply: ", types.FIL(circ.FilCirculating))
if circ.FilCirculating.LessThan(big.Zero()) {
circ.FilCirculating = big.Zero()
}
rewardActor, err := state.GetActor(reward.Address)
if err != nil {
return xerrors.Errorf("loading miner actor: %w", err)
}
rewardState, err := reward.Load(store, rewardActor)
if err != nil {
return xerrors.Errorf("loading reward actor state: %w", err)
}
fmt.Println("FilVested", types.FIL(circ.FilVested))
fmt.Println("FilMined", types.FIL(circ.FilMined))
fmt.Println("FilBurnt", types.FIL(circ.FilBurnt))
fmt.Println("FilLocked", types.FIL(circ.FilLocked))
fmt.Println("FilCirculating", types.FIL(circ.FilCirculating))
for _, sectorWeight := range []abi.StoragePower{
types.NewInt(32 << 30),
types.NewInt(64 << 30),
types.NewInt(32 << 30 * 10),
types.NewInt(64 << 30 * 10),
} {
initialPledge, err := rewardState.InitialPledgeForPower(
sectorWeight,
pledgeCollateral,
&powerSmoothed,
circ.FilCirculating,
)
if err != nil {
return xerrors.Errorf("calculating initial pledge: %w", err)
}
fmt.Println("IP ", units.HumanSize(float64(sectorWeight.Uint64())), types.FIL(initialPledge))
}
return nil
},
}
+164 -104
View File
@@ -5,10 +5,10 @@ import (
"encoding/json"
"net"
"net/http"
"os"
"strings"
"sync"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/ipfs/go-cid"
@@ -19,58 +19,54 @@ type dealStatsServer struct {
api api.FullNode
}
var filteredClients map[address.Address]bool
// Requested by @jbenet
// How many epochs back to look at for dealstats
var epochLookback = abi.ChainEpoch(10)
// these lists grow continuously with the network
// TODO: need to switch this to an LRU of sorts, to ensure refreshes
var knownFiltered = new(sync.Map)
var resolvedWallets = new(sync.Map)
func init() {
fc := []string{"t0112", "t0113", "t0114", "t010089"}
filtered, set := os.LookupEnv("FILTERED_CLIENTS")
if set {
fc = strings.Split(filtered, ":")
}
filteredClients = make(map[address.Address]bool)
for _, a := range fc {
addr, err := address.NewFromString(a)
for _, a := range []string{
"t0100", // client for genesis miner
"t0101", // client for genesis miner
"t0102", // client for genesis miner
"t0112", // client for genesis miner
"t0113", // client for genesis miner
"t0114", // client for genesis miner
"t1nslxql4pck5pq7hddlzym3orxlx35wkepzjkm3i", // SR1 dealbot wallet
"t1stghxhdp2w53dym2nz2jtbpk6ccd4l2lxgmezlq", // SR1 dealbot wallet
"t1mcr5xkgv4jdl3rnz77outn6xbmygb55vdejgbfi", // SR1 dealbot wallet
"t1qiqdbbmrdalbntnuapriirduvxu5ltsc5mhy7si", // SR1 dealbot wallet
} {
a, err := address.NewFromString(a)
if err != nil {
panic(err)
}
filteredClients[addr] = true
knownFiltered.Store(a, true)
}
}
type dealCountResp struct {
Total int64 `json:"total"`
Epoch int64 `json:"epoch"`
Epoch int64 `json:"epoch"`
Endpoint string `json:"endpoint"`
Payload int64 `json:"payload"`
}
func (dss *dealStatsServer) handleStorageDealCount(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
head, err := dss.api.ChainHead(ctx)
if err != nil {
log.Warnf("failed to get chain head: %s", err)
epoch, deals := dss.filteredDealList()
if epoch == 0 {
w.WriteHeader(500)
return
}
deals, err := dss.api.StateMarketDeals(ctx, head.Key())
if err != nil {
log.Warnf("failed to get market deals: %s", err)
w.WriteHeader(500)
return
}
var count int64
for _, d := range deals {
if !filteredClients[d.Proposal.Client] {
count++
}
}
if err := json.NewEncoder(w).Encode(&dealCountResp{
Total: count,
Epoch: int64(head.Height()),
Endpoint: "COUNT_DEALS",
Payload: int64(len(deals)),
Epoch: epoch,
}); err != nil {
log.Warnf("failed to write back deal count response: %s", err)
return
@@ -78,39 +74,28 @@ func (dss *dealStatsServer) handleStorageDealCount(w http.ResponseWriter, r *htt
}
type dealAverageResp struct {
AverageSize int64 `json:"average_size"`
Epoch int64 `json:"epoch"`
Epoch int64 `json:"epoch"`
Endpoint string `json:"endpoint"`
Payload int64 `json:"payload"`
}
func (dss *dealStatsServer) handleStorageDealAverageSize(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
head, err := dss.api.ChainHead(ctx)
if err != nil {
log.Warnf("failed to get chain head: %s", err)
epoch, deals := dss.filteredDealList()
if epoch == 0 {
w.WriteHeader(500)
return
}
deals, err := dss.api.StateMarketDeals(ctx, head.Key())
if err != nil {
log.Warnf("failed to get market deals: %s", err)
w.WriteHeader(500)
return
}
var count int64
var totalBytes int64
for _, d := range deals {
if !filteredClients[d.Proposal.Client] {
count++
totalBytes += int64(d.Proposal.PieceSize.Unpadded())
}
totalBytes += int64(d.deal.Proposal.PieceSize.Unpadded())
}
if err := json.NewEncoder(w).Encode(&dealAverageResp{
AverageSize: totalBytes / count,
Epoch: int64(head.Height()),
Endpoint: "AVERAGE_DEAL_SIZE",
Payload: totalBytes / int64(len(deals)),
Epoch: epoch,
}); err != nil {
log.Warnf("failed to write back deal average response: %s", err)
return
@@ -118,37 +103,27 @@ func (dss *dealStatsServer) handleStorageDealAverageSize(w http.ResponseWriter,
}
type dealTotalResp struct {
TotalBytes int64 `json:"total_size"`
Epoch int64 `json:"epoch"`
Epoch int64 `json:"epoch"`
Endpoint string `json:"endpoint"`
Payload int64 `json:"payload"`
}
func (dss *dealStatsServer) handleStorageDealTotalReal(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
head, err := dss.api.ChainHead(ctx)
if err != nil {
log.Warnf("failed to get chain head: %s", err)
w.WriteHeader(500)
return
}
deals, err := dss.api.StateMarketDeals(ctx, head.Key())
if err != nil {
log.Warnf("failed to get market deals: %s", err)
epoch, deals := dss.filteredDealList()
if epoch == 0 {
w.WriteHeader(500)
return
}
var totalBytes int64
for _, d := range deals {
if !filteredClients[d.Proposal.Client] {
totalBytes += int64(d.Proposal.PieceSize.Unpadded())
}
totalBytes += int64(d.deal.Proposal.PieceSize.Unpadded())
}
if err := json.NewEncoder(w).Encode(&dealTotalResp{
TotalBytes: totalBytes,
Epoch: int64(head.Height()),
Endpoint: "DEAL_BYTES",
Payload: totalBytes,
Epoch: epoch,
}); err != nil {
log.Warnf("failed to write back deal average response: %s", err)
return
@@ -157,6 +132,12 @@ func (dss *dealStatsServer) handleStorageDealTotalReal(w http.ResponseWriter, r
}
type clientStatsOutput struct {
Epoch int64 `json:"epoch"`
Endpoint string `json:"endpoint"`
Payload []*clientStats `json:"payload"`
}
type clientStats struct {
Client address.Address `json:"client"`
DataSize int64 `json:"data_size"`
NumCids int `json:"num_cids"`
@@ -168,51 +149,41 @@ type clientStatsOutput struct {
}
func (dss *dealStatsServer) handleStorageClientStats(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
head, err := dss.api.ChainHead(ctx)
if err != nil {
log.Warnf("failed to get chain head: %s", err)
epoch, deals := dss.filteredDealList()
if epoch == 0 {
w.WriteHeader(500)
return
}
deals, err := dss.api.StateMarketDeals(ctx, head.Key())
if err != nil {
log.Warnf("failed to get market deals: %s", err)
w.WriteHeader(500)
return
}
stats := make(map[address.Address]*clientStatsOutput)
stats := make(map[address.Address]*clientStats)
for _, d := range deals {
if filteredClients[d.Proposal.Client] {
continue
}
st, ok := stats[d.Proposal.Client]
st, ok := stats[d.deal.Proposal.Client]
if !ok {
st = &clientStatsOutput{
Client: d.Proposal.Client,
st = &clientStats{
Client: d.resolvedWallet,
cids: make(map[cid.Cid]bool),
providers: make(map[address.Address]bool),
}
stats[d.Proposal.Client] = st
stats[d.deal.Proposal.Client] = st
}
st.DataSize += int64(d.Proposal.PieceSize.Unpadded())
st.cids[d.Proposal.PieceCID] = true
st.providers[d.Proposal.Provider] = true
st.DataSize += int64(d.deal.Proposal.PieceSize.Unpadded())
st.cids[d.deal.Proposal.PieceCID] = true
st.providers[d.deal.Proposal.Provider] = true
st.NumDeals++
}
out := make([]*clientStatsOutput, 0, len(stats))
for _, cso := range stats {
cso.NumCids = len(cso.cids)
cso.NumMiners = len(cso.providers)
out = append(out, cso)
out := clientStatsOutput{
Epoch: epoch,
Endpoint: "CLIENT_DEAL_STATS",
Payload: make([]*clientStats, 0, len(stats)),
}
for _, cs := range stats {
cs.NumCids = len(cs.cids)
cs.NumMiners = len(cs.providers)
out.Payload = append(out.Payload, cs)
}
if err := json.NewEncoder(w).Encode(out); err != nil {
@@ -221,6 +192,93 @@ func (dss *dealStatsServer) handleStorageClientStats(w http.ResponseWriter, r *h
}
}
type dealInfo struct {
deal api.MarketDeal
resolvedWallet address.Address
}
// filteredDealList returns the current epoch and a list of filtered deals
// on error returns an epoch of 0
func (dss *dealStatsServer) filteredDealList() (int64, map[string]dealInfo) {
ctx := context.Background()
head, err := dss.api.ChainHead(ctx)
if err != nil {
log.Warnf("failed to get chain head: %s", err)
return 0, nil
}
head, err = dss.api.ChainGetTipSetByHeight(ctx, head.Height()-epochLookback, head.Key())
if err != nil {
log.Warnf("failed to walk back %s epochs: %s", epochLookback, err)
return 0, nil
}
// Disabled as per @pooja's request
//
// // Exclude any address associated with a miner
// miners, err := dss.api.StateListMiners(ctx, head.Key())
// if err != nil {
// log.Warnf("failed to get miner list: %s", err)
// return 0, nil
// }
// for _, m := range miners {
// info, err := dss.api.StateMinerInfo(ctx, m, head.Key())
// if err != nil {
// log.Warnf("failed to get info for known miner '%s': %s", m, err)
// continue
// }
// knownFiltered.Store(info.Owner, true)
// knownFiltered.Store(info.Worker, true)
// for _, a := range info.ControlAddresses {
// knownFiltered.Store(a, true)
// }
// }
deals, err := dss.api.StateMarketDeals(ctx, head.Key())
if err != nil {
log.Warnf("failed to get market deals: %s", err)
return 0, nil
}
ret := make(map[string]dealInfo, len(deals))
for dealKey, d := range deals {
// Counting no-longer-active deals as per Pooja's request
// // https://github.com/filecoin-project/specs-actors/blob/v0.9.9/actors/builtin/market/deal.go#L81-L85
// if d.State.SectorStartEpoch < 0 {
// continue
// }
if _, isFiltered := knownFiltered.Load(d.Proposal.Client); isFiltered {
continue
}
if _, wasSeen := resolvedWallets.Load(d.Proposal.Client); !wasSeen {
w, err := dss.api.StateAccountKey(ctx, d.Proposal.Client, head.Key())
if err != nil {
log.Warnf("failed to resolve id '%s' to wallet address: %s", d.Proposal.Client, err)
continue
} else {
resolvedWallets.Store(d.Proposal.Client, w)
}
}
w, _ := resolvedWallets.Load(d.Proposal.Client)
if _, isFiltered := knownFiltered.Load(w); isFiltered {
continue
}
ret[dealKey] = dealInfo{
deal: d,
resolvedWallet: w.(address.Address),
}
}
return int64(head.Height()), ret
}
var serveDealStatsCmd = &cli.Command{
Name: "serve-deal-stats",
Flags: []cli.Flag{},
@@ -260,6 +318,8 @@ var serveDealStatsCmd = &cli.Command{
panic(err)
}
log.Warnf("deal-stat server listening on %s\n== NOTE: QUERIES ARE EXPENSIVE - YOU MUST FRONT-CACHE THIS SERVICE\n", list.Addr().String())
return s.Serve(list)
},
}
+57
View File
@@ -1,10 +1,13 @@
package main
import (
"encoding/hex"
"fmt"
"io"
"os"
block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"github.com/ipld/go-car"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
@@ -81,3 +84,57 @@ var importCarCmd = &cli.Command{
}
},
}
var importObjectCmd = &cli.Command{
Name: "import-obj",
Usage: "import a raw ipld object into your datastore",
Action: func(cctx *cli.Context) error {
r, err := repo.NewFS(cctx.String("repo"))
if err != nil {
return xerrors.Errorf("opening fs repo: %w", err)
}
exists, err := r.Exists()
if err != nil {
return err
}
if !exists {
return xerrors.Errorf("lotus repo doesn't exist")
}
lr, err := r.Lock(repo.FullNode)
if err != nil {
return err
}
defer lr.Close() //nolint:errcheck
ds, err := lr.Datastore("/chain")
if err != nil {
return err
}
bs := blockstore.NewBlockstore(ds)
c, err := cid.Decode(cctx.Args().Get(0))
if err != nil {
return err
}
data, err := hex.DecodeString(cctx.Args().Get(1))
if err != nil {
return err
}
blk, err := block.NewBlockWithCid(data, c)
if err != nil {
return err
}
if err := bs.Put(blk); err != nil {
return err
}
return nil
},
}
+2
View File
@@ -25,6 +25,7 @@ func main() {
staterootCmd,
auditsCmd,
importCarCmd,
importObjectCmd,
commpToCidCmd,
fetchParamCmd,
proofsCmd,
@@ -37,6 +38,7 @@ func main() {
exportChainCmd,
consensusCmd,
serveDealStatsCmd,
syncCmd,
}
app := &cli.App{
+14
View File
@@ -182,6 +182,11 @@ var staterootStatCmd = &cli.Command{
return infos[i].Stat.Size > infos[j].Stat.Size
})
var totalActorsSize uint64
for _, info := range infos {
totalActorsSize += info.Stat.Size
}
outcap := 10
if cctx.Args().Len() > outcap {
outcap = cctx.Args().Len()
@@ -190,6 +195,15 @@ var staterootStatCmd = &cli.Command{
outcap = len(infos)
}
totalStat, err := api.ChainStatObj(ctx, ts.ParentState(), cid.Undef)
if err != nil {
return err
}
fmt.Println("Total state tree size: ", totalStat.Size)
fmt.Println("Sum of actor state size: ", totalActorsSize)
fmt.Println("State tree structure size: ", totalStat.Size-totalActorsSize)
fmt.Print("Addr\tType\tSize\n")
for _, inf := range infos[:outcap] {
cmh, err := multihash.Decode(inf.Actor.Code.Hash())
+64
View File
@@ -0,0 +1,64 @@
package main
import (
"fmt"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/urfave/cli/v2"
)
var syncCmd = &cli.Command{
Name: "sync",
Usage: "tools for diagnosing sync issues",
Flags: []cli.Flag{},
Subcommands: []*cli.Command{
syncValidateCmd,
},
}
var syncValidateCmd = &cli.Command{
Name: "validate",
Usage: "checks whether a provided tipset is valid",
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
if cctx.Args().Len() < 1 {
fmt.Println("usage: <blockCid1> <blockCid2>...")
fmt.Println("At least one block cid must be provided")
return nil
}
args := cctx.Args().Slice()
var tscids []cid.Cid
for _, s := range args {
c, err := cid.Decode(s)
if err != nil {
return fmt.Errorf("block cid was invalid: %s", err)
}
tscids = append(tscids, c)
}
tsk := types.NewTipSetKey(tscids...)
valid, err := api.SyncValidateTipset(ctx, tsk)
if err != nil {
fmt.Println("Tipset is invalid: ", err)
}
if valid {
fmt.Println("Tipset is valid")
}
return nil
},
}
+12 -4
View File
@@ -33,6 +33,12 @@ var infoCmd = &cli.Command{
Subcommands: []*cli.Command{
infoAllCmd,
},
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "hide-sectors-info",
Usage: "hide sectors info",
},
},
Action: infoCmdAct,
}
@@ -199,10 +205,12 @@ func infoCmdAct(cctx *cli.Context) error {
fmt.Printf("Expected Seal Duration: %s\n\n", sealdur)
fmt.Println("Sectors:")
err = sectorsInfo(ctx, nodeApi)
if err != nil {
return err
if !cctx.Bool("hide-sectors-info") {
fmt.Println("Sectors:")
err = sectorsInfo(ctx, nodeApi)
if err != nil {
return err
}
}
// TODO: grab actr state / info
+2 -2
View File
@@ -156,12 +156,12 @@ var setAskCmd = &cli.Command{
Flags: []cli.Flag{
&cli.Uint64Flag{
Name: "price",
Usage: "Set the price of the ask for unverified deals (specified as FIL / GiB / Epoch) to `PRICE`",
Usage: "Set the price of the ask for unverified deals (specified as attoFIL / GiB / Epoch) to `PRICE`",
Required: true,
},
&cli.Uint64Flag{
Name: "verified-price",
Usage: "Set the price of the ask for verified deals (specified as FIL / GiB / Epoch) to `PRICE`",
Usage: "Set the price of the ask for verified deals (specified as attoFIL / GiB / Epoch) to `PRICE`",
Required: true,
},
&cli.StringFlag{
+92
View File
@@ -3,6 +3,7 @@ package main
import (
"fmt"
"os"
"strconv"
"text/tabwriter"
"github.com/fatih/color"
@@ -22,6 +23,7 @@ var provingCmd = &cli.Command{
Subcommands: []*cli.Command{
provingInfoCmd,
provingDeadlinesCmd,
provingDeadlineInfoCmd,
provingFaultsCmd,
},
}
@@ -279,3 +281,93 @@ var provingDeadlinesCmd = &cli.Command{
return tw.Flush()
},
}
var provingDeadlineInfoCmd = &cli.Command{
Name: "deadline",
Usage: "View the current proving period deadline information by its index ",
ArgsUsage: "<deadlineIdx>",
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 1 {
return xerrors.Errorf("must pass deadline index")
}
dlIdx, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64)
if err != nil {
return xerrors.Errorf("could not parse deadline index: %w", err)
}
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
api, acloser, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer acloser()
ctx := lcli.ReqContext(cctx)
maddr, err := getActorAddress(ctx, nodeApi, cctx.String("actor"))
if err != nil {
return err
}
deadlines, err := api.StateMinerDeadlines(ctx, maddr, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("getting deadlines: %w", err)
}
di, err := api.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("getting deadlines: %w", err)
}
partitions, err := api.StateMinerPartitions(ctx, maddr, dlIdx, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("getting partitions for deadline %d: %w", dlIdx, err)
}
provenPartitions, err := deadlines[dlIdx].PostSubmissions.Count()
if err != nil {
return err
}
fmt.Printf("Deadline Index: %d\n", dlIdx)
fmt.Printf("Partitions: %d\n", len(partitions))
fmt.Printf("Proven Partitions: %d\n", provenPartitions)
fmt.Printf("Current: %t\n\n", di.Index == dlIdx)
for pIdx, partition := range partitions {
sectorCount, err := partition.AllSectors.Count()
if err != nil {
return err
}
sectorNumbers, err := partition.AllSectors.All(sectorCount)
if err != nil {
return err
}
faultsCount, err := partition.FaultySectors.Count()
if err != nil {
return err
}
fn, err := partition.FaultySectors.All(faultsCount)
if err != nil {
return err
}
fmt.Printf("Partition Index: %d\n", pIdx)
fmt.Printf("Sectors: %d\n", sectorCount)
fmt.Printf("Sector Numbers: %v\n", sectorNumbers)
fmt.Printf("Faults: %d\n", faultsCount)
fmt.Printf("Faulty Sectors: %d\n", fn)
}
return nil
},
}
+92 -19
View File
@@ -5,19 +5,22 @@ import (
"os"
"sort"
"strconv"
"text/tabwriter"
"time"
"github.com/docker/go-units"
"github.com/fatih/color"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/tablewriter"
lcli "github.com/filecoin-project/lotus/cli"
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
@@ -144,8 +147,19 @@ var sectorsListCmd = &cli.Command{
Name: "show-removed",
Usage: "show removed sectors",
},
&cli.BoolFlag{
Name: "color",
Aliases: []string{"c"},
Value: true,
},
&cli.BoolFlag{
Name: "fast",
Usage: "don't show on-chain info for better performance",
},
},
Action: func(cctx *cli.Context) error {
color.NoColor = !cctx.Bool("color")
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
@@ -170,7 +184,12 @@ var sectorsListCmd = &cli.Command{
return err
}
activeSet, err := fullApi.StateMinerActiveSectors(ctx, maddr, types.EmptyTSK)
head, err := fullApi.ChainHead(ctx)
if err != nil {
return err
}
activeSet, err := fullApi.StateMinerActiveSectors(ctx, maddr, head.Key())
if err != nil {
return err
}
@@ -179,7 +198,7 @@ var sectorsListCmd = &cli.Command{
activeIDs[info.SectorNumber] = struct{}{}
}
sset, err := fullApi.StateMinerSectors(ctx, maddr, nil, types.EmptyTSK)
sset, err := fullApi.StateMinerSectors(ctx, maddr, nil, head.Key())
if err != nil {
return err
}
@@ -192,12 +211,26 @@ var sectorsListCmd = &cli.Command{
return list[i] < list[j]
})
w := tabwriter.NewWriter(os.Stdout, 8, 4, 1, ' ', 0)
tw := tablewriter.New(
tablewriter.Col("ID"),
tablewriter.Col("State"),
tablewriter.Col("OnChain"),
tablewriter.Col("Active"),
tablewriter.Col("Expiration"),
tablewriter.Col("Deals"),
tablewriter.Col("DealWeight"),
tablewriter.NewLineCol("Error"),
tablewriter.NewLineCol("EarlyExpiration"))
fast := cctx.Bool("fast")
for _, s := range list {
st, err := nodeApi.SectorsStatus(ctx, s, false)
st, err := nodeApi.SectorsStatus(ctx, s, !fast)
if err != nil {
fmt.Fprintf(w, "%d:\tError: %s\n", s, err)
tw.Write(map[string]interface{}{
"ID": s,
"Error": err,
})
continue
}
@@ -205,20 +238,60 @@ var sectorsListCmd = &cli.Command{
_, inSSet := commitedIDs[s]
_, inASet := activeIDs[s]
_, _ = fmt.Fprintf(w, "%d: %s\tsSet: %s\tactive: %s\ttktH: %d\tseedH: %d\tdeals: %v\t toUpgrade:%t\n",
s,
st.State,
yesno(inSSet),
yesno(inASet),
st.Ticket.Epoch,
st.Seed.Epoch,
st.Deals,
st.ToUpgrade,
)
dw := .0
if st.Expiration-st.Activation > 0 {
dw = float64(big.Div(st.DealWeight, big.NewInt(int64(st.Expiration-st.Activation))).Uint64())
}
var deals int
for _, deal := range st.Deals {
if deal != 0 {
deals++
}
}
exp := st.Expiration
if st.OnTime > 0 && st.OnTime < exp {
exp = st.OnTime // Can be different when the sector was CC upgraded
}
m := map[string]interface{}{
"ID": s,
"State": color.New(stateOrder[sealing.SectorState(st.State)].col).Sprint(st.State),
"OnChain": yesno(inSSet),
"Active": yesno(inASet),
}
if deals > 0 {
m["Deals"] = color.GreenString("%d", deals)
} else {
m["Deals"] = color.BlueString("CC")
if st.ToUpgrade {
m["Deals"] = color.CyanString("CC(upgrade)")
}
}
if !fast {
if !inSSet {
m["Expiration"] = "n/a"
} else {
m["Expiration"] = lcli.EpochTime(head.Height(), exp)
if !fast && deals > 0 {
m["DealWeight"] = units.BytesSize(dw)
}
if st.Early > 0 {
m["EarlyExpiration"] = color.YellowString(lcli.EpochTime(head.Height(), st.Early))
}
}
}
tw.Write(m)
}
}
return w.Flush()
return tw.Flush(os.Stdout)
},
}
@@ -447,7 +520,7 @@ var sectorsUpdateCmd = &cli.Command{
func yesno(b bool) string {
if b {
return "YES"
return color.GreenString("YES")
}
return "NO"
return color.RedString("NO")
}
+64 -14
View File
@@ -3,11 +3,14 @@
package main
import (
"bufio"
"context"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"runtime/pprof"
"strings"
@@ -23,6 +26,7 @@ import (
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
"golang.org/x/xerrors"
"gopkg.in/cheggaaa/pb.v1"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
@@ -100,11 +104,11 @@ var DaemonCmd = &cli.Command{
},
&cli.StringFlag{
Name: "import-chain",
Usage: "on first run, load chain from given file and validate",
Usage: "on first run, load chain from given file or url and validate",
},
&cli.StringFlag{
Name: "import-snapshot",
Usage: "import chain state from a given chain export file",
Usage: "import chain state from a given chain export file or url",
},
&cli.BoolFlag{
Name: "halt-after-import",
@@ -123,6 +127,10 @@ var DaemonCmd = &cli.Command{
Usage: "manage open file limit",
Value: true,
},
&cli.StringFlag{
Name: "config",
Usage: "specify path of config file to use",
},
},
Action: func(cctx *cli.Context) error {
err := runmetrics.Enable(runmetrics.RunMetricOptions{
@@ -176,6 +184,10 @@ var DaemonCmd = &cli.Command{
return xerrors.Errorf("opening fs repo: %w", err)
}
if cctx.String("config") != "" {
r.SetConfigPath(cctx.String("config"))
}
if err := r.Init(repo.FullNode); err != nil && err != repo.ErrRepoExists {
return xerrors.Errorf("repo init error: %w", err)
}
@@ -206,11 +218,6 @@ var DaemonCmd = &cli.Command{
issnapshot = true
}
chainfile, err := homedir.Expand(chainfile)
if err != nil {
return err
}
if err := ImportChain(r, chainfile, issnapshot); err != nil {
return err
}
@@ -326,12 +333,42 @@ func importKey(ctx context.Context, api api.FullNode, f string) error {
return nil
}
func ImportChain(r repo.Repo, fname string, snapshot bool) error {
fi, err := os.Open(fname)
if err != nil {
return err
func ImportChain(r repo.Repo, fname string, snapshot bool) (err error) {
var rd io.Reader
var l int64
if strings.HasPrefix(fname, "http://") || strings.HasPrefix(fname, "https://") {
resp, err := http.Get(fname) //nolint:gosec
if err != nil {
return err
}
defer resp.Body.Close() //nolint:errcheck
if resp.StatusCode != http.StatusOK {
return xerrors.Errorf("non-200 response: %d", resp.StatusCode)
}
rd = resp.Body
l = resp.ContentLength
} else {
fname, err = homedir.Expand(fname)
if err != nil {
return err
}
fi, err := os.Open(fname)
if err != nil {
return err
}
defer fi.Close() //nolint:errcheck
st, err := os.Stat(fname)
if err != nil {
return err
}
rd = fi
l = st.Size()
}
defer fi.Close() //nolint:errcheck
lr, err := r.Lock(repo.FullNode)
if err != nil {
@@ -353,8 +390,21 @@ func ImportChain(r repo.Repo, fname string, snapshot bool) error {
cst := store.NewChainStore(bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier))
log.Info("importing chain from file...")
ts, err := cst.Import(fi)
log.Infof("importing chain from %s...", fname)
bufr := bufio.NewReaderSize(rd, 1<<20)
bar := pb.New64(l)
br := bar.NewProxyReader(bufr)
bar.ShowTimeLeft = true
bar.ShowPercent = true
bar.ShowSpeed = true
bar.Units = pb.U_BYTES
bar.Start()
ts, err := cst.Import(br)
bar.Finish()
if err != nil {
return xerrors.Errorf("importing chain failed: %w", err)
}
+44
View File
@@ -0,0 +1,44 @@
package main
import (
"reflect"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
)
var ActorMethodTable = make(map[string][]string, 64)
var Actors = map[cid.Cid]interface{}{
builtin.InitActorCodeID: builtin.MethodsInit,
builtin.CronActorCodeID: builtin.MethodsCron,
builtin.AccountActorCodeID: builtin.MethodsAccount,
builtin.StoragePowerActorCodeID: builtin.MethodsPower,
builtin.StorageMinerActorCodeID: builtin.MethodsMiner,
builtin.StorageMarketActorCodeID: builtin.MethodsMarket,
builtin.PaymentChannelActorCodeID: builtin.MethodsPaych,
builtin.MultisigActorCodeID: builtin.MethodsMultisig,
builtin.RewardActorCodeID: builtin.MethodsReward,
builtin.VerifiedRegistryActorCodeID: builtin.MethodsVerifiedRegistry,
}
func init() {
for code, methods := range Actors {
cmh, err := multihash.Decode(code.Hash()) // identity hash.
if err != nil {
panic(err)
}
var (
aname = string(cmh.Digest)
rt = reflect.TypeOf(methods)
nf = rt.NumField()
)
ActorMethodTable[aname] = append(ActorMethodTable[aname], "Send")
for i := 0; i < nf; i++ {
ActorMethodTable[aname] = append(ActorMethodTable[aname], rt.Field(i).Name)
}
}
}
+92
View File
@@ -0,0 +1,92 @@
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
"github.com/fatih/color"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/lotus/conformance"
"github.com/filecoin-project/test-vectors/schema"
)
var execFlags struct {
file string
}
var execCmd = &cli.Command{
Name: "exec",
Description: "execute one or many test vectors against Lotus; supplied as a single JSON file, or a ndjson stdin stream",
Action: runExecLotus,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "file",
Usage: "input file; if not supplied, the vector will be read from stdin",
TakesFile: true,
Destination: &execFlags.file,
},
},
}
func runExecLotus(_ *cli.Context) error {
if file := execFlags.file; file != "" {
// we have a single test vector supplied as a file.
file, err := os.Open(file)
if err != nil {
return fmt.Errorf("failed to open test vector: %w", err)
}
var (
dec = json.NewDecoder(file)
tv schema.TestVector
)
if err = dec.Decode(&tv); err != nil {
return fmt.Errorf("failed to decode test vector: %w", err)
}
return executeTestVector(tv)
}
for dec := json.NewDecoder(os.Stdin); ; {
var tv schema.TestVector
switch err := dec.Decode(&tv); err {
case nil:
if err = executeTestVector(tv); err != nil {
return err
}
case io.EOF:
// we're done.
return nil
default:
// something bad happened.
return err
}
}
}
func executeTestVector(tv schema.TestVector) error {
log.Println("executing test vector:", tv.Meta.ID)
r := new(conformance.LogReporter)
switch class := tv.Class; class {
case "message":
conformance.ExecuteMessageVector(r, &tv)
case "tipset":
conformance.ExecuteTipsetVector(r, &tv)
default:
return fmt.Errorf("test vector class %s not supported", class)
}
if r.Failed() {
log.Println(color.HiRedString("❌ test vector failed"))
} else {
log.Println(color.GreenString("✅ test vector succeeded"))
}
return nil
}
+503
View File
@@ -0,0 +1,503 @@
package main
import (
"bytes"
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"github.com/fatih/color"
"github.com/filecoin-project/lotus/api"
init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init"
"github.com/filecoin-project/lotus/chain/actors/builtin/reward"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/conformance"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/test-vectors/schema"
"github.com/ipfs/go-cid"
"github.com/urfave/cli/v2"
)
const (
PrecursorSelectAll = "all"
PrecursorSelectSender = "sender"
)
type extractOpts struct {
id string
block string
class string
cid string
file string
retain string
precursor string
}
var extractFlags extractOpts
var extractCmd = &cli.Command{
Name: "extract",
Description: "generate a test vector by extracting it from a live chain",
Action: runExtract,
Flags: []cli.Flag{
&repoFlag,
&cli.StringFlag{
Name: "class",
Usage: "class of vector to extract; other required flags depend on the; values: 'message'",
Value: "message",
Destination: &extractFlags.class,
},
&cli.StringFlag{
Name: "id",
Usage: "identifier to name this test vector with",
Value: "(undefined)",
Destination: &extractFlags.id,
},
&cli.StringFlag{
Name: "block",
Usage: "optionally, the block CID the message was included in, to avoid expensive chain scanning",
Destination: &extractFlags.block,
},
&cli.StringFlag{
Name: "cid",
Usage: "message CID to generate test vector from",
Required: true,
Destination: &extractFlags.cid,
},
&cli.StringFlag{
Name: "out",
Aliases: []string{"o"},
Usage: "file to write test vector to",
Destination: &extractFlags.file,
},
&cli.StringFlag{
Name: "state-retain",
Usage: "state retention policy; values: 'accessed-cids', 'accessed-actors'",
Value: "accessed-cids",
Destination: &extractFlags.retain,
},
&cli.StringFlag{
Name: "precursor-select",
Usage: "precursors to apply; values: 'all', 'sender'; 'all' selects all preceding" +
"messages in the canonicalised tipset, 'sender' selects only preceding messages from the same" +
"sender. Usually, 'sender' is a good tradeoff and gives you sufficient accuracy. If the receipt sanity" +
"check fails due to gas reasons, switch to 'all', as previous messages in the tipset may have" +
"affected state in a disruptive way",
Value: "sender",
Destination: &extractFlags.precursor,
},
},
}
func runExtract(c *cli.Context) error {
// LOTUS_DISABLE_VM_BUF disables what's called "VM state tree buffering",
// which stashes write operations in a BufferedBlockstore
// (https://github.com/filecoin-project/lotus/blob/b7a4dbb07fd8332b4492313a617e3458f8003b2a/lib/bufbstore/buf_bstore.go#L21)
// such that they're not written until the VM is actually flushed.
//
// For some reason, the standard behaviour was not working for me (raulk),
// and disabling it (such that the state transformations are written immediately
// to the blockstore) worked.
_ = os.Setenv("LOTUS_DISABLE_VM_BUF", "iknowitsabadidea")
ctx := context.Background()
// Make the API client.
fapi, closer, err := lcli.GetFullNodeAPI(c)
if err != nil {
return err
}
defer closer()
return doExtract(ctx, fapi, extractFlags)
}
func doExtract(ctx context.Context, fapi api.FullNode, opts extractOpts) error {
mcid, err := cid.Decode(opts.cid)
if err != nil {
return err
}
msg, execTs, incTs, err := resolveFromChain(ctx, fapi, mcid, opts.block)
if err != nil {
return fmt.Errorf("failed to resolve message and tipsets from chain: %w", err)
}
// get the circulating supply before the message was executed.
circSupplyDetail, err := fapi.StateCirculatingSupply(ctx, incTs.Key())
if err != nil {
return fmt.Errorf("failed while fetching circulating supply: %w", err)
}
circSupply := circSupplyDetail.FilCirculating
log.Printf("message was executed in tipset: %s", execTs.Key())
log.Printf("message was included in tipset: %s", incTs.Key())
log.Printf("circulating supply at inclusion tipset: %d", circSupply)
log.Printf("finding precursor messages using mode: %s", opts.precursor)
// Fetch messages in canonical order from inclusion tipset.
msgs, err := fapi.ChainGetParentMessages(ctx, execTs.Blocks()[0].Cid())
if err != nil {
return fmt.Errorf("failed to fetch messages in canonical order from inclusion tipset: %w", err)
}
related, found, err := findMsgAndPrecursors(opts.precursor, msg, msgs)
if err != nil {
return fmt.Errorf("failed while finding message and precursors: %w", err)
}
if !found {
return fmt.Errorf("message not found; precursors found: %d", len(related))
}
var (
precursors = related[:len(related)-1]
precursorsCids []cid.Cid
)
for _, p := range precursors {
precursorsCids = append(precursorsCids, p.Cid())
}
log.Println(color.GreenString("found message; precursors (count: %d): %v", len(precursors), precursorsCids))
var (
// create a read-through store that uses ChainGetObject to fetch unknown CIDs.
pst = NewProxyingStores(ctx, fapi)
g = NewSurgeon(ctx, fapi, pst)
)
driver := conformance.NewDriver(ctx, schema.Selector{}, conformance.DriverOpts{
DisableVMFlush: true,
})
// this is the root of the state tree we start with.
root := incTs.ParentState()
log.Printf("base state tree root CID: %s", root)
basefee := incTs.Blocks()[0].ParentBaseFee
log.Printf("basefee: %s", basefee)
// on top of that state tree, we apply all precursors.
log.Printf("number of precursors to apply: %d", len(precursors))
for i, m := range precursors {
log.Printf("applying precursor %d, cid: %s", i, m.Cid())
_, root, err = driver.ExecuteMessage(pst.Blockstore, conformance.ExecuteMessageParams{
Preroot: root,
Epoch: execTs.Height(),
Message: m,
CircSupply: &circSupplyDetail.FilCirculating,
BaseFee: &basefee,
})
if err != nil {
return fmt.Errorf("failed to execute precursor message: %w", err)
}
}
var (
preroot cid.Cid
postroot cid.Cid
applyret *vm.ApplyRet
carWriter func(w io.Writer) error
retention = opts.retain
)
log.Printf("using state retention strategy: %s", retention)
switch retention {
case "accessed-cids":
tbs, ok := pst.Blockstore.(TracingBlockstore)
if !ok {
return fmt.Errorf("requested 'accessed-cids' state retention, but no tracing blockstore was present")
}
tbs.StartTracing()
preroot = root
applyret, postroot, err = driver.ExecuteMessage(pst.Blockstore, conformance.ExecuteMessageParams{
Preroot: preroot,
Epoch: execTs.Height(),
Message: msg,
CircSupply: &circSupplyDetail.FilCirculating,
BaseFee: &basefee,
})
if err != nil {
return fmt.Errorf("failed to execute message: %w", err)
}
accessed := tbs.FinishTracing()
carWriter = func(w io.Writer) error {
return g.WriteCARIncluding(w, accessed, preroot, postroot)
}
case "accessed-actors":
log.Printf("calculating accessed actors")
// get actors accessed by message.
retain, err := g.GetAccessedActors(ctx, fapi, mcid)
if err != nil {
return fmt.Errorf("failed to calculate accessed actors: %w", err)
}
// also append the reward actor and the burnt funds actor.
retain = append(retain, reward.Address, builtin.BurntFundsActorAddr, init_.Address)
log.Printf("calculated accessed actors: %v", retain)
// get the masked state tree from the root,
preroot, err = g.GetMaskedStateTree(root, retain)
if err != nil {
return err
}
applyret, postroot, err = driver.ExecuteMessage(pst.Blockstore, conformance.ExecuteMessageParams{
Preroot: preroot,
Epoch: execTs.Height(),
Message: msg,
CircSupply: &circSupplyDetail.FilCirculating,
BaseFee: &basefee,
})
if err != nil {
return fmt.Errorf("failed to execute message: %w", err)
}
carWriter = func(w io.Writer) error {
return g.WriteCAR(w, preroot, postroot)
}
default:
return fmt.Errorf("unknown state retention option: %s", retention)
}
log.Printf("message applied; preroot: %s, postroot: %s", preroot, postroot)
log.Println("performing sanity check on receipt")
// TODO sometimes this returns a nil receipt and no error ¯\_(ツ)_/¯
// ex: https://filfox.info/en/message/bafy2bzacebpxw3yiaxzy2bako62akig46x3imji7fewszen6fryiz6nymu2b2
// This code is lenient and skips receipt comparison in case of a nil receipt.
rec, err := fapi.StateGetReceipt(ctx, mcid, execTs.Key())
if err != nil {
return fmt.Errorf("failed to find receipt on chain: %w", err)
}
log.Printf("found receipt: %+v", rec)
// generate the schema receipt; if we got
var receipt *schema.Receipt
if rec != nil {
receipt = &schema.Receipt{
ExitCode: int64(rec.ExitCode),
ReturnValue: rec.Return,
GasUsed: rec.GasUsed,
}
reporter := new(conformance.LogReporter)
conformance.AssertMsgResult(reporter, receipt, applyret, "as locally executed")
if reporter.Failed() {
log.Println(color.RedString("receipt sanity check failed; aborting"))
return fmt.Errorf("vector generation aborted")
}
log.Println(color.GreenString("receipt sanity check succeeded"))
} else {
receipt = &schema.Receipt{
ExitCode: int64(applyret.ExitCode),
ReturnValue: applyret.Return,
GasUsed: applyret.GasUsed,
}
log.Println(color.YellowString("skipping receipts comparison; we got back a nil receipt from lotus"))
}
log.Println("generating vector")
msgBytes, err := msg.Serialize()
if err != nil {
return err
}
var (
out = new(bytes.Buffer)
gw = gzip.NewWriter(out)
)
if err := carWriter(gw); err != nil {
return err
}
if err = gw.Flush(); err != nil {
return err
}
if err = gw.Close(); err != nil {
return err
}
version, err := fapi.Version(ctx)
if err != nil {
return err
}
ntwkName, err := fapi.StateNetworkName(ctx)
if err != nil {
return err
}
// Write out the test vector.
vector := schema.TestVector{
Class: schema.ClassMessage,
Meta: &schema.Metadata{
ID: opts.id,
// TODO need to replace schema.GenerationData with a more flexible
// data structure that makes no assumption about the traceability
// data that's being recorded; a flexible map[string]string
// would do.
Gen: []schema.GenerationData{
{Source: fmt.Sprintf("network:%s", ntwkName)},
{Source: fmt.Sprintf("message:%s", msg.Cid().String())},
{Source: fmt.Sprintf("inclusion_tipset:%s", incTs.Key().String())},
{Source: fmt.Sprintf("execution_tipset:%s", execTs.Key().String())},
{Source: "github.com/filecoin-project/lotus", Version: version.String()}},
},
CAR: out.Bytes(),
Pre: &schema.Preconditions{
Epoch: int64(execTs.Height()),
CircSupply: circSupply.Int,
BaseFee: basefee.Int,
StateTree: &schema.StateTree{
RootCID: preroot,
},
},
ApplyMessages: []schema.Message{{Bytes: msgBytes}},
Post: &schema.Postconditions{
StateTree: &schema.StateTree{
RootCID: postroot,
},
Receipts: []*schema.Receipt{
{
ExitCode: int64(applyret.ExitCode),
ReturnValue: applyret.Return,
GasUsed: applyret.GasUsed,
},
},
},
}
output := io.WriteCloser(os.Stdout)
if file := opts.file; file != "" {
dir := filepath.Dir(file)
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("unable to create directory %s: %w", dir, err)
}
output, err = os.Create(file)
if err != nil {
return err
}
defer output.Close() //nolint:errcheck
defer log.Printf("wrote test vector to file: %s", file)
}
enc := json.NewEncoder(output)
enc.SetIndent("", " ")
if err := enc.Encode(&vector); err != nil {
return err
}
return nil
}
// resolveFromChain queries the chain for the provided message, using the block CID to
// speed up the query, if provided
func resolveFromChain(ctx context.Context, api api.FullNode, mcid cid.Cid, block string) (msg *types.Message, execTs *types.TipSet, incTs *types.TipSet, err error) {
// Extract the full message.
msg, err = api.ChainGetMessage(ctx, mcid)
if err != nil {
return nil, nil, nil, err
}
log.Printf("found message with CID %s: %+v", mcid, msg)
if block == "" {
log.Printf("locating message in blockchain")
// Locate the message.
msgInfo, err := api.StateSearchMsg(ctx, mcid)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to locate message: %w", err)
}
log.Printf("located message at tipset %s (height: %d) with exit code: %s", msgInfo.TipSet, msgInfo.Height, msgInfo.Receipt.ExitCode)
execTs, incTs, err = fetchThisAndPrevTipset(ctx, api, msgInfo.TipSet)
return msg, execTs, incTs, err
}
bcid, err := cid.Decode(block)
if err != nil {
return nil, nil, nil, err
}
log.Printf("message inclusion block CID was provided; scanning around it: %s", bcid)
blk, err := api.ChainGetBlock(ctx, bcid)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to get block: %w", err)
}
// types.EmptyTSK hints to use the HEAD.
execTs, err = api.ChainGetTipSetByHeight(ctx, blk.Height+1, types.EmptyTSK)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to get message execution tipset: %w", err)
}
// walk back from the execTs instead of HEAD, to save time.
incTs, err = api.ChainGetTipSetByHeight(ctx, blk.Height, execTs.Key())
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to get message inclusion tipset: %w", err)
}
return msg, execTs, incTs, nil
}
// fetchThisAndPrevTipset returns the full tipset identified by the key, as well
// as the previous tipset. In the context of vector generation, the target
// tipset is the one where a message was executed, and the previous tipset is
// the one where the message was included.
func fetchThisAndPrevTipset(ctx context.Context, api api.FullNode, target types.TipSetKey) (targetTs *types.TipSet, prevTs *types.TipSet, err error) {
// get the tipset on which this message was "executed" on.
// https://github.com/filecoin-project/lotus/issues/2847
targetTs, err = api.ChainGetTipSet(ctx, target)
if err != nil {
return nil, nil, err
}
// get the previous tipset, on which this message was mined,
// i.e. included on-chain.
prevTs, err = api.ChainGetTipSet(ctx, targetTs.Parents())
if err != nil {
return nil, nil, err
}
return targetTs, prevTs, nil
}
// findMsgAndPrecursors ranges through the canonical messages slice, locating
// the target message and returning precursors in accordance to the supplied
// mode.
func findMsgAndPrecursors(mode string, target *types.Message, msgs []api.Message) (related []*types.Message, found bool, err error) {
// Range through canonicalised messages, selecting only the precursors based
// on selection mode.
for _, other := range msgs {
switch {
case mode == PrecursorSelectAll:
fallthrough
case mode == PrecursorSelectSender && other.Message.From == target.From:
related = append(related, other.Message)
}
// this message is the target; we're done.
if other.Cid == target.Cid() {
return related, true, nil
}
}
// this could happen because a block contained related messages, but not
// the target (that is, messages with a lower nonce, but ultimately not the
// target).
return related, false, nil
}
+232
View File
@@ -0,0 +1,232 @@
package main
import (
"context"
"encoding/csv"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/hashicorp/go-multierror"
"github.com/urfave/cli/v2"
lcli "github.com/filecoin-project/lotus/cli"
)
var extractManyFlags struct {
in string
outdir string
batchId string
}
var extractManyCmd = &cli.Command{
Name: "extract-many",
Description: `generate many test vectors by repeatedly calling tvx extract, using a csv file as input.
The CSV file must have a format just like the following:
message_cid,receiver_code,method_num,exit_code,height,block_cid,seq
bafy2bzacedvuvgpsnwq7i7kltfap6hnp7fdmzf6lr4w34zycjrthb3v7k6zi6,fil/1/account,0,0,67972,bafy2bzacebthpxzlk7zhlkz3jfzl4qw7mdoswcxlf3rkof3b4mbxfj3qzfk7w,1
bafy2bzacedwicofymn4imgny2hhbmcm4o5bikwnv3qqgohyx73fbtopiqlro6,fil/1/account,0,0,67860,bafy2bzacebj7beoxyzll522o6o76mt7von4psn3tlvunokhv4zhpwmfpipgti,2
...
The first row MUST be a header row. At the bare minimum, those seven fields
must appear, in the order specified. Extra fields are accepted, but always
after these compulsory seven.
`,
Action: runExtractMany,
Flags: []cli.Flag{
&repoFlag,
&cli.StringFlag{
Name: "batch-id",
Usage: "batch id; a four-digit left-zero-padded sequential number (e.g. 0041)",
Required: true,
Destination: &extractManyFlags.batchId,
},
&cli.StringFlag{
Name: "in",
Usage: "path to input file (csv)",
Destination: &extractManyFlags.in,
},
&cli.StringFlag{
Name: "outdir",
Usage: "output directory",
Destination: &extractManyFlags.outdir,
},
},
}
func runExtractMany(c *cli.Context) error {
// LOTUS_DISABLE_VM_BUF disables what's called "VM state tree buffering",
// which stashes write operations in a BufferedBlockstore
// (https://github.com/filecoin-project/lotus/blob/b7a4dbb07fd8332b4492313a617e3458f8003b2a/lib/bufbstore/buf_bstore.go#L21)
// such that they're not written until the VM is actually flushed.
//
// For some reason, the standard behaviour was not working for me (raulk),
// and disabling it (such that the state transformations are written immediately
// to the blockstore) worked.
_ = os.Setenv("LOTUS_DISABLE_VM_BUF", "iknowitsabadidea")
ctx := context.Background()
// Make the API client.
fapi, closer, err := lcli.GetFullNodeAPI(c)
if err != nil {
return err
}
defer closer()
var (
in = extractManyFlags.in
outdir = extractManyFlags.outdir
)
if in == "" {
return fmt.Errorf("input file not provided")
}
if outdir == "" {
return fmt.Errorf("output dir not provided")
}
// Open the CSV file for reading.
f, err := os.Open(in)
if err != nil {
return fmt.Errorf("could not open file %s: %w", in, err)
}
// Ensure the output directory exists.
if err := os.MkdirAll(outdir, 0755); err != nil {
return fmt.Errorf("could not create output dir %s: %w", outdir, err)
}
// Create a CSV reader and validate the header row.
reader := csv.NewReader(f)
if header, err := reader.Read(); err != nil {
return fmt.Errorf("failed to read header from csv: %w", err)
} else if l := len(header); l < 7 {
return fmt.Errorf("insufficient number of fields: %d", l)
} else if f := header[0]; f != "message_cid" {
return fmt.Errorf("csv sanity check failed: expected first field in header to be 'message_cid'; was: %s", f)
} else {
log.Println(color.GreenString("csv sanity check succeeded; header contains fields: %v", header))
}
var (
generated []string
merr = new(multierror.Error)
retry []extractOpts // to retry with 'canonical' precursor selection mode
)
// Read each row and extract the requested message.
for {
row, err := reader.Read()
if err == io.EOF {
break
} else if err != nil {
return fmt.Errorf("failed to read row: %w", err)
}
var (
cid = row[0]
actorcode = row[1]
methodnumstr = row[2]
exitcodestr = row[3]
_ = row[4]
block = row[5]
seq = row[6]
exit int
methodnum int
methodname string
)
// Parse the exit code.
if exit, err = strconv.Atoi(exitcodestr); err != nil {
return fmt.Errorf("invalid exitcode number: %d", exit)
}
// Parse the method number.
if methodnum, err = strconv.Atoi(methodnumstr); err != nil {
return fmt.Errorf("invalid method number: %s", methodnumstr)
}
// Lookup the method in actor method table.
if m, ok := ActorMethodTable[actorcode]; !ok {
return fmt.Errorf("unrecognized actor: %s", actorcode)
} else if methodnum >= len(m) {
return fmt.Errorf("unrecognized method number for actor %s: %d", actorcode, methodnum)
} else {
methodname = m[methodnum]
}
// exitcode string representations are of kind ErrType(0); strip out
// the number portion.
exitcodename := strings.Split(exitcode.ExitCode(exit).String(), "(")[0]
// replace the slashes in the actor code name with underscores.
actorcodename := strings.ReplaceAll(actorcode, "/", "_")
// Compute the ID of the vector.
id := fmt.Sprintf("ext-%s-%s-%s-%s-%s", extractManyFlags.batchId, actorcodename, methodname, exitcodename, seq)
// Vector filename, using a base of outdir.
file := filepath.Join(outdir, actorcodename, methodname, exitcodename, id) + ".json"
log.Println(color.YellowString("processing message cid with 'sender' precursor mode: %s", id))
opts := extractOpts{
id: id,
block: block,
class: "message",
cid: cid,
file: file,
retain: "accessed-cids",
precursor: PrecursorSelectSender,
}
if err := doExtract(ctx, fapi, opts); err != nil {
log.Println(color.RedString("failed to extract vector for message %s: %s; queuing for 'canonical' precursor selection", cid, err))
retry = append(retry, opts)
continue
}
log.Println(color.MagentaString("generated file: %s", file))
generated = append(generated, file)
}
log.Printf("extractions to try with canonical precursor selection mode: %d", len(retry))
for _, r := range retry {
log.Printf("retrying %s: %s", r.cid, r.id)
r.precursor = PrecursorSelectAll
if err := doExtract(ctx, fapi, r); err != nil {
merr = multierror.Append(merr, fmt.Errorf("failed to extract vector for message %s: %w", r.cid, err))
continue
}
log.Println(color.MagentaString("generated file: %s", r.file))
generated = append(generated, r.file)
}
if len(generated) == 0 {
log.Println("no files generated")
} else {
log.Println("files generated:")
for _, g := range generated {
log.Println(g)
}
}
if merr.ErrorOrNil() != nil {
log.Println(color.YellowString("done processing with errors: %v", merr))
} else {
log.Println(color.GreenString("done processing with no errors"))
}
return merr.ErrorOrNil()
}
+71
View File
@@ -0,0 +1,71 @@
package main
import (
"log"
"os"
"sort"
"github.com/urfave/cli/v2"
)
// DefaultLotusRepoPath is where the fallback path where to look for a Lotus
// client repo. It is expanded with mitchellh/go-homedir, so it'll work with all
// OSes despite the Unix twiddle notation.
const DefaultLotusRepoPath = "~/.lotus"
var repoFlag = cli.StringFlag{
Name: "repo",
EnvVars: []string{"LOTUS_PATH"},
Value: DefaultLotusRepoPath,
TakesFile: true,
}
func main() {
app := &cli.App{
Name: "tvx",
Description: `tvx is a tool for extracting and executing test vectors. It has three subcommands.
tvx extract extracts a test vector from a live network. It requires access to
a Filecoin client that exposes the standard JSON-RPC API endpoint. Only
message class test vectors are supported at this time.
tvx exec executes test vectors against Lotus. Either you can supply one in a
file, or many as an ndjson stdin stream.
tvx extract-many performs a batch extraction of many messages, supplied in a
CSV file. Refer to the help of that subcommand for more info.
SETTING THE JSON-RPC API ENDPOINT
You can set the JSON-RPC API endpoint through one of the following methods.
1. Directly set the API endpoint on the FULLNODE_API_INFO env variable.
The format is [token]:multiaddr, where token is optional for commands not
accessing privileged operations.
2. If you're running tvx against a local Lotus client, you can set the REPO
env variable to have the API endpoint and token extracted from the repo.
Alternatively, you can pass the --repo CLI flag.
3. Rely on the default fallback, which inspects ~/.lotus and extracts the
API endpoint string if the location is a Lotus repo.
tvx will apply these methods in the same order of precedence they're listed.
`,
Usage: "tvx is a tool for extracting and executing test vectors",
Commands: []*cli.Command{
extractCmd,
execCmd,
extractManyCmd,
},
}
sort.Sort(cli.CommandsByName(app.Commands))
for _, c := range app.Commands {
sort.Sort(cli.FlagsByName(c.Flags))
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
+293
View File
@@ -0,0 +1,293 @@
package main
import (
"context"
"fmt"
"io"
"log"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/ipfs/go-cid"
format "github.com/ipfs/go-ipld-format"
"github.com/ipld/go-car"
cbg "github.com/whyrusleeping/cbor-gen"
"github.com/filecoin-project/lotus/api"
init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
)
// StateSurgeon is an object used to fetch and manipulate state.
type StateSurgeon struct {
ctx context.Context
api api.FullNode
stores *Stores
}
// NewSurgeon returns a state surgeon, an object used to fetch and manipulate
// state.
func NewSurgeon(ctx context.Context, api api.FullNode, stores *Stores) *StateSurgeon {
return &StateSurgeon{
ctx: ctx,
api: api,
stores: stores,
}
}
// GetMaskedStateTree trims the state tree at the supplied tipset to contain
// only the state of the actors in the retain set. It also "dives" into some
// singleton system actors, like the init actor, to trim the state so as to
// compute a minimal state tree. In the future, thid method will dive into
// other system actors like the power actor and the market actor.
func (sg *StateSurgeon) GetMaskedStateTree(previousRoot cid.Cid, retain []address.Address) (cid.Cid, error) {
// TODO: this will need to be parameterized on network version.
st, err := state.LoadStateTree(sg.stores.CBORStore, previousRoot)
if err != nil {
return cid.Undef, err
}
initActor, initState, err := sg.loadInitActor(st)
if err != nil {
return cid.Undef, err
}
err = sg.retainInitEntries(initState, retain)
if err != nil {
return cid.Undef, err
}
err = sg.saveInitActor(initActor, initState, st)
if err != nil {
return cid.Undef, err
}
// resolve all addresses to ID addresses.
resolved, err := sg.resolveAddresses(retain, initState)
if err != nil {
return cid.Undef, err
}
st, err = sg.transplantActors(st, resolved)
if err != nil {
return cid.Undef, err
}
root, err := st.Flush(sg.ctx)
if err != nil {
return cid.Undef, err
}
return root, nil
}
// GetAccessedActors identifies the actors that were accessed during the
// execution of a message.
func (sg *StateSurgeon) GetAccessedActors(ctx context.Context, a api.FullNode, mid cid.Cid) ([]address.Address, error) {
log.Printf("calculating accessed actors during execution of message: %s", mid)
msgInfo, err := a.StateSearchMsg(ctx, mid)
if err != nil {
return nil, err
}
if msgInfo == nil {
return nil, fmt.Errorf("message info is nil")
}
msgObj, err := a.ChainGetMessage(ctx, mid)
if err != nil {
return nil, err
}
ts, err := a.ChainGetTipSet(ctx, msgInfo.TipSet)
if err != nil {
return nil, err
}
trace, err := a.StateCall(ctx, msgObj, ts.Parents())
if err != nil {
return nil, fmt.Errorf("could not replay msg: %w", err)
}
accessed := make(map[address.Address]struct{})
var recur func(trace *types.ExecutionTrace)
recur = func(trace *types.ExecutionTrace) {
accessed[trace.Msg.To] = struct{}{}
accessed[trace.Msg.From] = struct{}{}
for i := range trace.Subcalls {
recur(&trace.Subcalls[i])
}
}
recur(&trace.ExecutionTrace)
ret := make([]address.Address, 0, len(accessed))
for k := range accessed {
ret = append(ret, k)
}
return ret, nil
}
// WriteCAR recursively writes the tree referenced by the root as a CAR into the
// supplied io.Writer.
func (sg *StateSurgeon) WriteCAR(w io.Writer, roots ...cid.Cid) error {
carWalkFn := func(nd format.Node) (out []*format.Link, err error) {
for _, link := range nd.Links() {
if link.Cid.Prefix().Codec == cid.FilCommitmentSealed || link.Cid.Prefix().Codec == cid.FilCommitmentUnsealed {
continue
}
out = append(out, link)
}
return out, nil
}
return car.WriteCarWithWalker(sg.ctx, sg.stores.DAGService, roots, w, carWalkFn)
}
// WriteCARIncluding writes a CAR including only the CIDs that are listed in
// the include set. This leads to an intentially sparse tree with dangling links.
func (sg *StateSurgeon) WriteCARIncluding(w io.Writer, include map[cid.Cid]struct{}, roots ...cid.Cid) error {
carWalkFn := func(nd format.Node) (out []*format.Link, err error) {
for _, link := range nd.Links() {
if _, ok := include[link.Cid]; !ok {
continue
}
if link.Cid.Prefix().Codec == cid.FilCommitmentSealed || link.Cid.Prefix().Codec == cid.FilCommitmentUnsealed {
continue
}
out = append(out, link)
}
return out, nil
}
return car.WriteCarWithWalker(sg.ctx, sg.stores.DAGService, roots, w, carWalkFn)
}
// transplantActors plucks the state from the supplied actors at the given
// tipset, and places it into the supplied state map.
func (sg *StateSurgeon) transplantActors(src *state.StateTree, pluck []address.Address) (*state.StateTree, error) {
log.Printf("transplanting actor states: %v", pluck)
dst, err := state.NewStateTree(sg.stores.CBORStore, src.Version())
if err != nil {
return nil, err
}
for _, a := range pluck {
actor, err := src.GetActor(a)
if err != nil {
return nil, fmt.Errorf("get actor %s failed: %w", a, err)
}
err = dst.SetActor(a, actor)
if err != nil {
return nil, err
}
// recursive copy of the actor state.
err = vm.Copy(context.TODO(), sg.stores.Blockstore, sg.stores.Blockstore, actor.Head)
if err != nil {
return nil, err
}
actorState, err := sg.api.ChainReadObj(sg.ctx, actor.Head)
if err != nil {
return nil, err
}
cid, err := sg.stores.CBORStore.Put(sg.ctx, &cbg.Deferred{Raw: actorState})
if err != nil {
return nil, err
}
if cid != actor.Head {
panic("mismatched cids")
}
}
return dst, nil
}
// saveInitActor saves the state of the init actor to the provided state map.
func (sg *StateSurgeon) saveInitActor(initActor *types.Actor, initState init_.State, st *state.StateTree) error {
log.Printf("saving init actor into state tree")
// Store the state of the init actor.
cid, err := sg.stores.CBORStore.Put(sg.ctx, initState)
if err != nil {
return err
}
actor := *initActor
actor.Head = cid
err = st.SetActor(init_.Address, &actor)
if err != nil {
return err
}
cid, _ = st.Flush(sg.ctx)
log.Printf("saved init actor into state tree; new root: %s", cid)
return nil
}
// retainInitEntries takes an old init actor state, and retains only the
// entries in the retain set, returning a new init actor state.
func (sg *StateSurgeon) retainInitEntries(state init_.State, retain []address.Address) error {
log.Printf("retaining init actor entries for addresses: %v", retain)
m := make(map[address.Address]struct{}, len(retain))
for _, a := range retain {
m[a] = struct{}{}
}
var remove []address.Address
_ = state.ForEachActor(func(id abi.ActorID, address address.Address) error {
if _, ok := m[address]; !ok {
remove = append(remove, address)
}
return nil
})
err := state.Remove(remove...)
log.Printf("new init actor state: %+v", state)
return err
}
// resolveAddresses resolved the requested addresses from the provided
// InitActor state, returning a slice of length len(orig), where each index
// contains the resolved address.
func (sg *StateSurgeon) resolveAddresses(orig []address.Address, ist init_.State) (ret []address.Address, err error) {
log.Printf("resolving addresses: %v", orig)
ret = make([]address.Address, len(orig))
for i, addr := range orig {
resolved, found, err := ist.ResolveAddress(addr)
if err != nil {
return nil, err
}
if !found {
return nil, fmt.Errorf("address not found: %s", addr)
}
ret[i] = resolved
}
log.Printf("resolved addresses: %v", ret)
return ret, nil
}
// loadInitActor loads the init actor state from a given tipset.
func (sg *StateSurgeon) loadInitActor(st *state.StateTree) (*types.Actor, init_.State, error) {
actor, err := st.GetActor(init_.Address)
if err != nil {
return nil, nil, err
}
initState, err := init_.Load(sg.stores.ADTStore, actor)
if err != nil {
return nil, nil, err
}
log.Printf("loaded init actor state: %+v", initState)
return actor, initState, nil
}
+142
View File
@@ -0,0 +1,142 @@
package main
import (
"context"
"log"
"sync"
"github.com/fatih/color"
dssync "github.com/ipfs/go-datastore/sync"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/lib/blockstore"
"github.com/filecoin-project/lotus/chain/actors/adt"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore"
exchange "github.com/ipfs/go-ipfs-exchange-interface"
offline "github.com/ipfs/go-ipfs-exchange-offline"
cbor "github.com/ipfs/go-ipld-cbor"
format "github.com/ipfs/go-ipld-format"
"github.com/ipfs/go-merkledag"
)
// Stores is a collection of the different stores and services that are needed
// to deal with the data layer of Filecoin, conveniently interlinked with one
// another.
type Stores struct {
CBORStore cbor.IpldStore
ADTStore adt.Store
Datastore ds.Batching
Blockstore blockstore.Blockstore
BlockService blockservice.BlockService
Exchange exchange.Interface
DAGService format.DAGService
}
// NewProxyingStores is a set of Stores backed by a proxying Blockstore that
// proxies Get requests for unknown CIDs to a Filecoin node, via the
// ChainReadObj RPC.
func NewProxyingStores(ctx context.Context, api api.FullNode) *Stores {
ds := dssync.MutexWrap(ds.NewMapDatastore())
bs := &proxyingBlockstore{
ctx: ctx,
api: api,
Blockstore: blockstore.NewBlockstore(ds),
}
return NewStores(ctx, ds, bs)
}
// NewStores creates a non-proxying set of Stores.
func NewStores(ctx context.Context, ds ds.Batching, bs blockstore.Blockstore) *Stores {
var (
cborstore = cbor.NewCborStore(bs)
offl = offline.Exchange(bs)
blkserv = blockservice.New(bs, offl)
dserv = merkledag.NewDAGService(blkserv)
)
return &Stores{
CBORStore: cborstore,
ADTStore: adt.WrapStore(ctx, cborstore),
Datastore: ds,
Blockstore: bs,
Exchange: offl,
BlockService: blkserv,
DAGService: dserv,
}
}
// TracingBlockstore is a Blockstore trait that records CIDs that were accessed
// through Get.
type TracingBlockstore interface {
// StartTracing starts tracing CIDs accessed through the this Blockstore.
StartTracing()
// FinishTracing finishes tracing accessed CIDs, and returns a map of the
// CIDs that were traced.
FinishTracing() map[cid.Cid]struct{}
}
// proxyingBlockstore is a Blockstore wrapper that fetches unknown CIDs from
// a Filecoin node via JSON-RPC.
type proxyingBlockstore struct {
ctx context.Context
api api.FullNode
lk sync.RWMutex
tracing bool
traced map[cid.Cid]struct{}
blockstore.Blockstore
}
var _ TracingBlockstore = (*proxyingBlockstore)(nil)
func (pb *proxyingBlockstore) StartTracing() {
pb.lk.Lock()
pb.tracing = true
pb.traced = map[cid.Cid]struct{}{}
pb.lk.Unlock()
}
func (pb *proxyingBlockstore) FinishTracing() map[cid.Cid]struct{} {
pb.lk.Lock()
ret := pb.traced
pb.tracing = false
pb.traced = map[cid.Cid]struct{}{}
pb.lk.Unlock()
return ret
}
func (pb *proxyingBlockstore) Get(cid cid.Cid) (blocks.Block, error) {
pb.lk.RLock()
if pb.tracing {
pb.traced[cid] = struct{}{}
}
pb.lk.RUnlock()
if block, err := pb.Blockstore.Get(cid); err == nil {
return block, err
}
log.Println(color.CyanString("fetching cid via rpc: %v", cid))
item, err := pb.api.ChainReadObj(pb.ctx, cid)
if err != nil {
return nil, err
}
block, err := blocks.NewBlockWithCid(item, cid)
if err != nil {
return nil, err
}
err = pb.Blockstore.Put(block)
if err != nil {
return nil, err
}
return block, nil
}
+133
View File
@@ -0,0 +1,133 @@
package conformance
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/filecoin-project/test-vectors/schema"
)
const (
// EnvSkipConformance, if 1, skips the conformance test suite.
EnvSkipConformance = "SKIP_CONFORMANCE"
// EnvCorpusRootDir is the name of the environment variable where the path
// to an alternative corpus location can be provided.
//
// The default is defaultCorpusRoot.
EnvCorpusRootDir = "CORPUS_DIR"
// defaultCorpusRoot is the directory where the test vector corpus is hosted.
// It is mounted on the Lotus repo as a git submodule.
//
// When running this test, the corpus root can be overridden through the
// -conformance.corpus CLI flag to run an alternate corpus.
defaultCorpusRoot = "../extern/test-vectors/corpus"
)
// ignore is a set of paths relative to root to skip.
var ignore = map[string]struct{}{
".git": {},
"schema.json": {},
}
// TestConformance is the entrypoint test that runs all test vectors found
// in the corpus root directory.
//
// It locates all json files via a recursive walk, skipping over the ignore set,
// as well as files beginning with _. It parses each file as a test vector, and
// runs it via the Driver.
func TestConformance(t *testing.T) {
if skip := strings.TrimSpace(os.Getenv(EnvSkipConformance)); skip == "1" {
t.SkipNow()
}
// corpusRoot is the effective corpus root path, taken from the `-conformance.corpus` CLI flag,
// falling back to defaultCorpusRoot if not provided.
corpusRoot := defaultCorpusRoot
if dir := strings.TrimSpace(os.Getenv(EnvCorpusRootDir)); dir != "" {
corpusRoot = dir
}
var vectors []string
err := filepath.Walk(corpusRoot+"/", func(path string, info os.FileInfo, err error) error {
if err != nil {
t.Fatal(err)
}
filename := filepath.Base(path)
rel, err := filepath.Rel(corpusRoot, path)
if err != nil {
t.Fatal(err)
}
if _, ok := ignore[rel]; ok {
// skip over using the right error.
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
if info.IsDir() {
// dive into directories.
return nil
}
if filepath.Ext(path) != ".json" {
// skip if not .json.
return nil
}
if ignored := strings.HasPrefix(filename, "_"); ignored {
// ignore files starting with _.
t.Logf("ignoring: %s", rel)
return nil
}
vectors = append(vectors, rel)
return nil
})
if err != nil {
t.Fatal(err)
}
if len(vectors) == 0 {
t.Fatalf("no test vectors found")
}
// Run a test for each vector.
for _, v := range vectors {
path := filepath.Join(corpusRoot, v)
raw, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("failed to read test raw file: %s", path)
}
var vector schema.TestVector
err = json.Unmarshal(raw, &vector)
if err != nil {
t.Errorf("failed to parse test vector %s: %s; skipping", path, err)
continue
}
t.Run(v, func(t *testing.T) {
for _, h := range vector.Hints {
if h == schema.HintIncorrect {
t.Logf("skipping vector marked as incorrect: %s", vector.Meta.ID)
t.SkipNow()
}
}
// dispatch the execution depending on the vector class.
switch vector.Class {
case "message":
ExecuteMessageVector(t, &vector)
case "tipset":
ExecuteTipsetVector(t, &vector)
default:
t.Fatalf("test vector class not supported: %s", vector.Class)
}
})
}
}
+75 -17
View File
@@ -2,9 +2,9 @@ package conformance
import (
"context"
"os"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
@@ -14,6 +14,7 @@ import (
"github.com/filecoin-project/lotus/lib/blockstore"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/test-vectors/schema"
@@ -24,18 +25,36 @@ import (
)
var (
// BaseFee to use in the VM.
// TODO make parametrisable through vector.
BaseFee = abi.NewTokenAmount(100)
// DefaultCirculatingSupply is the fallback circulating supply returned by
// the driver's CircSupplyCalculator function, used if the vector specifies
// no circulating supply.
DefaultCirculatingSupply = types.TotalFilecoinInt
// DefaultBaseFee to use in the VM, if one is not supplied in the vector.
DefaultBaseFee = abi.NewTokenAmount(100)
)
type Driver struct {
ctx context.Context
selector schema.Selector
vmFlush bool
}
func NewDriver(ctx context.Context, selector schema.Selector) *Driver {
return &Driver{ctx: ctx, selector: selector}
type DriverOpts struct {
// DisableVMFlush, when true, avoids calling VM.Flush(), forces a blockstore
// recursive copy, from the temporary buffer blockstore, to the real
// system's blockstore. Disabling VM flushing is useful when extracting test
// vectors and trimming state, as we don't want to force an accidental
// deep copy of the state tree.
//
// Disabling VM flushing almost always should go hand-in-hand with
// LOTUS_DISABLE_VM_BUF=iknowitsabadidea. That way, state tree writes are
// immediately committed to the blockstore.
DisableVMFlush bool
}
func NewDriver(ctx context.Context, selector schema.Selector, opts DriverOpts) *Driver {
return &Driver{ctx: ctx, selector: selector, vmFlush: !opts.DisableVMFlush}
}
type ExecuteTipsetResult struct {
@@ -120,16 +139,47 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, preroot
return ret, nil
}
type ExecuteMessageParams struct {
Preroot cid.Cid
Epoch abi.ChainEpoch
Message *types.Message
CircSupply *abi.TokenAmount
BaseFee *abi.TokenAmount
}
// ExecuteMessage executes a conformance test vector message in a temporary VM.
func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, preroot cid.Cid, epoch abi.ChainEpoch, msg *types.Message) (*vm.ApplyRet, cid.Cid, error) {
func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageParams) (*vm.ApplyRet, cid.Cid, error) {
if !d.vmFlush {
// do not flush the VM, just the state tree; this should be used with
// LOTUS_DISABLE_VM_BUF enabled, so writes will anyway be visible.
_ = os.Setenv("LOTUS_DISABLE_VM_BUF", "iknowitsabadidea")
}
basefee := DefaultBaseFee
if params.BaseFee != nil {
basefee = *params.BaseFee
}
circSupply := DefaultCirculatingSupply
if params.CircSupply != nil {
circSupply = *params.CircSupply
}
// dummy state manager; only to reference the GetNetworkVersion method,
// which does not depend on state.
sm := new(stmgr.StateManager)
vmOpts := &vm.VMOpts{
StateBase: preroot,
Epoch: epoch,
Rand: &testRand{}, // TODO always succeeds; need more flexibility.
Bstore: bs,
Syscalls: mkFakedSigSyscalls(vm.Syscalls(ffiwrapper.ProofVerifier)), // TODO always succeeds; need more flexibility.
CircSupplyCalc: nil,
BaseFee: BaseFee,
StateBase: params.Preroot,
Epoch: params.Epoch,
Rand: &testRand{}, // TODO always succeeds; need more flexibility.
Bstore: bs,
Syscalls: mkFakedSigSyscalls(vm.Syscalls(ffiwrapper.ProofVerifier)), // TODO always succeeds; need more flexibility.
CircSupplyCalc: func(_ context.Context, _ abi.ChainEpoch, _ *state.StateTree) (abi.TokenAmount, error) {
return circSupply, nil
},
BaseFee: basefee,
NtwkVersion: sm.GetNtwkVersion,
}
lvm, err := vm.NewVM(context.TODO(), vmOpts)
@@ -146,12 +196,20 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, preroot cid.Cid, epoch
lvm.SetInvoker(invoker)
ret, err := lvm.ApplyMessage(d.ctx, toChainMsg(msg))
ret, err := lvm.ApplyMessage(d.ctx, toChainMsg(params.Message))
if err != nil {
return nil, cid.Undef, err
}
root, err := lvm.Flush(d.ctx)
var root cid.Cid
if d.vmFlush {
// flush the VM, committing the state tree changes and forcing a
// recursive copoy from the temporary blcokstore to the real blockstore.
root, err = lvm.Flush(d.ctx)
} else {
root, err = lvm.StateTree().(*state.StateTree).Flush(d.ctx)
}
return ret, root, err
}
+62
View File
@@ -0,0 +1,62 @@
package conformance
import (
"log"
"os"
"sync/atomic"
"testing"
"github.com/fatih/color"
)
// Reporter is a contains a subset of the testing.T methods, so that the
// Execute* functions in this package can be used inside or outside of
// go test runs.
type Reporter interface {
Helper()
Log(args ...interface{})
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
Logf(format string, args ...interface{})
FailNow()
Failed() bool
}
var _ Reporter = (*testing.T)(nil)
// LogReporter wires the Reporter methods to the log package. It is appropriate
// to use when calling the Execute* functions from a standalone CLI program.
type LogReporter struct {
failed int32
}
var _ Reporter = (*LogReporter)(nil)
func (*LogReporter) Helper() {}
func (*LogReporter) Log(args ...interface{}) {
log.Println(args...)
}
func (*LogReporter) Logf(format string, args ...interface{}) {
log.Printf(format, args...)
}
func (*LogReporter) FailNow() {
os.Exit(1)
}
func (l *LogReporter) Failed() bool {
return atomic.LoadInt32(&l.failed) == 1
}
func (l *LogReporter) Errorf(format string, args ...interface{}) {
atomic.StoreInt32(&l.failed, 1)
log.Println(color.HiRedString("❌ "+format, args...))
}
func (l *LogReporter) Fatalf(format string, args ...interface{}) {
atomic.StoreInt32(&l.failed, 1)
log.Fatal(color.HiRedString("❌ "+format, args...))
}
+272
View File
@@ -0,0 +1,272 @@
package conformance
import (
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strconv"
"github.com/fatih/color"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/test-vectors/schema"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore"
offline "github.com/ipfs/go-ipfs-exchange-offline"
format "github.com/ipfs/go-ipld-format"
"github.com/ipfs/go-merkledag"
"github.com/ipld/go-car"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/lib/blockstore"
)
// ExecuteMessageVector executes a message-class test vector.
func ExecuteMessageVector(r Reporter, vector *schema.TestVector) {
var (
ctx = context.Background()
epoch = vector.Pre.Epoch
root = vector.Pre.StateTree.RootCID
)
// Load the CAR into a new temporary Blockstore.
bs, err := LoadVectorCAR(vector.CAR)
if err != nil {
r.Fatalf("failed to load the vector CAR: %w", err)
}
// Create a new Driver.
driver := NewDriver(ctx, vector.Selector, DriverOpts{DisableVMFlush: true})
var circSupply *abi.TokenAmount
if cs := vector.Pre.CircSupply; cs != nil {
ta := big.NewFromGo(cs)
circSupply = &ta
}
var basefee *abi.TokenAmount
if bf := vector.Pre.BaseFee; bf != nil {
ta := big.NewFromGo(bf)
basefee = &ta
}
// Apply every message.
for i, m := range vector.ApplyMessages {
msg, err := types.DecodeMessage(m.Bytes)
if err != nil {
r.Fatalf("failed to deserialize message: %s", err)
}
// add an epoch if one's set.
if m.Epoch != nil {
epoch = *m.Epoch
}
// Execute the message.
var ret *vm.ApplyRet
ret, root, err = driver.ExecuteMessage(bs, ExecuteMessageParams{
Preroot: root,
Epoch: abi.ChainEpoch(epoch),
Message: msg,
CircSupply: circSupply,
BaseFee: basefee,
})
if err != nil {
r.Fatalf("fatal failure when executing message: %s", err)
}
// Assert that the receipt matches what the test vector expects.
AssertMsgResult(r, vector.Post.Receipts[i], ret, strconv.Itoa(i))
}
// Once all messages are applied, assert that the final state root matches
// the expected postcondition root.
if expected, actual := vector.Post.StateTree.RootCID, root; expected != actual {
r.Errorf("wrong post root cid; expected %v, but got %v", expected, actual)
dumpThreeWayStateDiff(r, vector, bs, root)
r.FailNow()
}
}
// ExecuteTipsetVector executes a tipset-class test vector.
func ExecuteTipsetVector(r Reporter, vector *schema.TestVector) {
var (
ctx = context.Background()
prevEpoch = vector.Pre.Epoch
root = vector.Pre.StateTree.RootCID
tmpds = ds.NewMapDatastore()
)
// Load the vector CAR into a new temporary Blockstore.
bs, err := LoadVectorCAR(vector.CAR)
if err != nil {
r.Fatalf("failed to load the vector CAR: %w", err)
}
// Create a new Driver.
driver := NewDriver(ctx, vector.Selector, DriverOpts{})
// Apply every tipset.
var receiptsIdx int
for i, ts := range vector.ApplyTipsets {
ts := ts // capture
ret, err := driver.ExecuteTipset(bs, tmpds, root, abi.ChainEpoch(prevEpoch), &ts)
if err != nil {
r.Fatalf("failed to apply tipset %d message: %s", i, err)
}
for j, v := range ret.AppliedResults {
AssertMsgResult(r, vector.Post.Receipts[receiptsIdx], v, fmt.Sprintf("%d of tipset %d", j, i))
receiptsIdx++
}
// Compare the receipts root.
if expected, actual := vector.Post.ReceiptsRoots[i], ret.ReceiptsRoot; expected != actual {
r.Errorf("post receipts root doesn't match; expected: %s, was: %s", expected, actual)
}
prevEpoch = ts.Epoch
root = ret.PostStateRoot
}
// Once all messages are applied, assert that the final state root matches
// the expected postcondition root.
if expected, actual := vector.Post.StateTree.RootCID, root; expected != actual {
r.Errorf("wrong post root cid; expected %v, but got %v", expected, actual)
dumpThreeWayStateDiff(r, vector, bs, root)
r.FailNow()
}
}
// AssertMsgResult compares a message result. It takes the expected receipt
// encoded in the vector, the actual receipt returned by Lotus, and a message
// label to log in the assertion failure message to facilitate debugging.
func AssertMsgResult(r Reporter, expected *schema.Receipt, actual *vm.ApplyRet, label string) {
r.Helper()
if expected, actual := exitcode.ExitCode(expected.ExitCode), actual.ExitCode; expected != actual {
r.Errorf("exit code of msg %s did not match; expected: %s, got: %s", label, expected, actual)
}
if expected, actual := expected.GasUsed, actual.GasUsed; expected != actual {
r.Errorf("gas used of msg %s did not match; expected: %d, got: %d", label, expected, actual)
}
if expected, actual := []byte(expected.ReturnValue), actual.Return; !bytes.Equal(expected, actual) {
r.Errorf("return value of msg %s did not match; expected: %s, got: %s", label, base64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(actual))
}
}
func dumpThreeWayStateDiff(r Reporter, vector *schema.TestVector, bs blockstore.Blockstore, actual cid.Cid) {
// check if statediff exists; if not, skip.
if err := exec.Command("statediff", "--help").Run(); err != nil {
r.Log("could not dump 3-way state tree diff upon test failure: statediff command not found")
r.Log("install statediff with:")
r.Log("$ git clone https://github.com/filecoin-project/statediff.git")
r.Log("$ cd statediff")
r.Log("$ go generate ./...")
r.Log("$ go install ./cmd/statediff")
return
}
tmpCar, err := writeStateToTempCAR(bs,
vector.Pre.StateTree.RootCID,
vector.Post.StateTree.RootCID,
actual,
)
if err != nil {
r.Fatalf("failed to write temporary state CAR: %s", err)
}
defer os.RemoveAll(tmpCar) //nolint:errcheck
color.NoColor = false // enable colouring.
var (
a = color.New(color.FgMagenta, color.Bold).Sprint("(A) expected final state")
b = color.New(color.FgYellow, color.Bold).Sprint("(B) actual final state")
c = color.New(color.FgCyan, color.Bold).Sprint("(C) initial state")
d1 = color.New(color.FgGreen, color.Bold).Sprint("[Δ1]")
d2 = color.New(color.FgGreen, color.Bold).Sprint("[Δ2]")
d3 = color.New(color.FgGreen, color.Bold).Sprint("[Δ3]")
)
printDiff := func(left, right cid.Cid) {
cmd := exec.Command("statediff", "car", "--file", tmpCar, left.String(), right.String())
b, err := cmd.CombinedOutput()
if err != nil {
r.Fatalf("statediff failed: %s", err)
}
r.Log(string(b))
}
bold := color.New(color.Bold).SprintfFunc()
// run state diffs.
r.Log(bold("=== dumping 3-way diffs between %s, %s, %s ===", a, b, c))
r.Log(bold("--- %s left: %s; right: %s ---", d1, a, b))
printDiff(vector.Post.StateTree.RootCID, actual)
r.Log(bold("--- %s left: %s; right: %s ---", d2, c, b))
printDiff(vector.Pre.StateTree.RootCID, actual)
r.Log(bold("--- %s left: %s; right: %s ---", d3, c, a))
printDiff(vector.Pre.StateTree.RootCID, vector.Post.StateTree.RootCID)
}
// writeStateToTempCAR writes the provided roots to a temporary CAR that'll be
// cleaned up via t.Cleanup(). It returns the full path of the temp file.
func writeStateToTempCAR(bs blockstore.Blockstore, roots ...cid.Cid) (string, error) {
tmp, err := ioutil.TempFile("", "lotus-tests-*.car")
if err != nil {
return "", fmt.Errorf("failed to create temp file to dump CAR for diffing: %w", err)
}
carWalkFn := func(nd format.Node) (out []*format.Link, err error) {
for _, link := range nd.Links() {
if link.Cid.Prefix().Codec == cid.FilCommitmentSealed || link.Cid.Prefix().Codec == cid.FilCommitmentUnsealed {
continue
}
out = append(out, link)
}
return out, nil
}
var (
offl = offline.Exchange(bs)
blkserv = blockservice.New(bs, offl)
dserv = merkledag.NewDAGService(blkserv)
)
err = car.WriteCarWithWalker(context.Background(), dserv, roots, tmp, carWalkFn)
if err != nil {
return "", fmt.Errorf("failed to dump CAR for diffing: %w", err)
}
_ = tmp.Close()
return tmp.Name(), nil
}
func LoadVectorCAR(vectorCAR schema.Base64EncodedBytes) (blockstore.Blockstore, error) {
bs := blockstore.NewTemporary()
// Read the base64-encoded CAR from the vector, and inflate the gzip.
buf := bytes.NewReader(vectorCAR)
r, err := gzip.NewReader(buf)
if err != nil {
return nil, fmt.Errorf("failed to inflate gzipped CAR: %s", err)
}
defer r.Close() // nolint
// Load the CAR embedded in the test vector into the Blockstore.
_, err = car.LoadCar(bs, r)
if err != nil {
return nil, fmt.Errorf("failed to load state tree car from test vector: %s", err)
}
return bs, nil
}
-376
View File
@@ -1,376 +0,0 @@
package conformance
import (
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"testing"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore"
offline "github.com/ipfs/go-ipfs-exchange-offline"
format "github.com/ipfs/go-ipld-format"
"github.com/ipfs/go-merkledag"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/lib/blockstore"
"github.com/filecoin-project/test-vectors/schema"
"github.com/fatih/color"
"github.com/ipld/go-car"
)
const (
// EnvSkipConformance, if 1, skips the conformance test suite.
EnvSkipConformance = "SKIP_CONFORMANCE"
// EnvCorpusRootDir is the name of the environment variable where the path
// to an alternative corpus location can be provided.
//
// The default is defaultCorpusRoot.
EnvCorpusRootDir = "CORPUS_DIR"
// defaultCorpusRoot is the directory where the test vector corpus is hosted.
// It is mounted on the Lotus repo as a git submodule.
//
// When running this test, the corpus root can be overridden through the
// -conformance.corpus CLI flag to run an alternate corpus.
defaultCorpusRoot = "../extern/test-vectors/corpus"
)
// ignore is a set of paths relative to root to skip.
var ignore = map[string]struct{}{
".git": {},
"schema.json": {},
}
// TestConformance is the entrypoint test that runs all test vectors found
// in the corpus root directory.
//
// It locates all json files via a recursive walk, skipping over the ignore set,
// as well as files beginning with _. It parses each file as a test vector, and
// runs it via the Driver.
func TestConformance(t *testing.T) {
if skip := strings.TrimSpace(os.Getenv(EnvSkipConformance)); skip == "1" {
t.SkipNow()
}
// corpusRoot is the effective corpus root path, taken from the `-conformance.corpus` CLI flag,
// falling back to defaultCorpusRoot if not provided.
corpusRoot := defaultCorpusRoot
if dir := strings.TrimSpace(os.Getenv(EnvCorpusRootDir)); dir != "" {
corpusRoot = dir
}
var vectors []string
err := filepath.Walk(corpusRoot+"/", func(path string, info os.FileInfo, err error) error {
if err != nil {
t.Fatal(err)
}
filename := filepath.Base(path)
rel, err := filepath.Rel(corpusRoot, path)
if err != nil {
t.Fatal(err)
}
if _, ok := ignore[rel]; ok {
// skip over using the right error.
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
if info.IsDir() {
// dive into directories.
return nil
}
if filepath.Ext(path) != ".json" {
// skip if not .json.
return nil
}
if ignored := strings.HasPrefix(filename, "_"); ignored {
// ignore files starting with _.
t.Logf("ignoring: %s", rel)
return nil
}
vectors = append(vectors, rel)
return nil
})
if err != nil {
t.Fatal(err)
}
if len(vectors) == 0 {
t.Fatalf("no test vectors found")
}
// Run a test for each vector.
for _, v := range vectors {
path := filepath.Join(corpusRoot, v)
raw, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("failed to read test raw file: %s", path)
}
var vector schema.TestVector
err = json.Unmarshal(raw, &vector)
if err != nil {
t.Errorf("failed to parse test vector %s: %s; skipping", path, err)
continue
}
t.Run(v, func(t *testing.T) {
for _, h := range vector.Hints {
if h == schema.HintIncorrect {
t.Logf("skipping vector marked as incorrect: %s", vector.Meta.ID)
t.SkipNow()
}
}
// dispatch the execution depending on the vector class.
switch vector.Class {
case "message":
executeMessageVector(t, &vector)
case "tipset":
executeTipsetVector(t, &vector)
default:
t.Fatalf("test vector class not supported: %s", vector.Class)
}
})
}
}
// executeMessageVector executes a message-class test vector.
func executeMessageVector(t *testing.T, vector *schema.TestVector) {
var (
ctx = context.Background()
epoch = vector.Pre.Epoch
root = vector.Pre.StateTree.RootCID
)
// Load the CAR into a new temporary Blockstore.
bs := loadCAR(t, vector.CAR)
// Create a new Driver.
driver := NewDriver(ctx, vector.Selector)
// Apply every message.
for i, m := range vector.ApplyMessages {
msg, err := types.DecodeMessage(m.Bytes)
if err != nil {
t.Fatalf("failed to deserialize message: %s", err)
}
// add an epoch if one's set.
if m.Epoch != nil {
epoch = *m.Epoch
}
// Execute the message.
var ret *vm.ApplyRet
ret, root, err = driver.ExecuteMessage(bs, root, abi.ChainEpoch(epoch), msg)
if err != nil {
t.Fatalf("fatal failure when executing message: %s", err)
}
// Assert that the receipt matches what the test vector expects.
assertMsgResult(t, vector.Post.Receipts[i], ret, strconv.Itoa(i))
}
// Once all messages are applied, assert that the final state root matches
// the expected postcondition root.
if expected, actual := vector.Post.StateTree.RootCID, root; expected != actual {
t.Logf("actual state root CID doesn't match expected one; expected: %s, actual: %s", expected, actual)
dumpThreeWayStateDiff(t, vector, bs, root)
t.FailNow()
}
}
// executeTipsetVector executes a tipset-class test vector.
func executeTipsetVector(t *testing.T, vector *schema.TestVector) {
var (
ctx = context.Background()
prevEpoch = vector.Pre.Epoch
root = vector.Pre.StateTree.RootCID
tmpds = ds.NewMapDatastore()
)
// Load the CAR into a new temporary Blockstore.
bs := loadCAR(t, vector.CAR)
// Create a new Driver.
driver := NewDriver(ctx, vector.Selector)
// Apply every tipset.
var receiptsIdx int
for i, ts := range vector.ApplyTipsets {
ts := ts // capture
ret, err := driver.ExecuteTipset(bs, tmpds, root, abi.ChainEpoch(prevEpoch), &ts)
if err != nil {
t.Fatalf("failed to apply tipset %d message: %s", i, err)
}
for j, v := range ret.AppliedResults {
assertMsgResult(t, vector.Post.Receipts[receiptsIdx], v, fmt.Sprintf("%d of tipset %d", j, i))
receiptsIdx++
}
// Compare the receipts root.
if expected, actual := vector.Post.ReceiptsRoots[i], ret.ReceiptsRoot; expected != actual {
t.Errorf("post receipts root doesn't match; expected: %s, was: %s", expected, actual)
}
prevEpoch = ts.Epoch
root = ret.PostStateRoot
}
// Once all messages are applied, assert that the final state root matches
// the expected postcondition root.
if expected, actual := vector.Post.StateTree.RootCID, root; expected != actual {
t.Logf("actual state root CID doesn't match expected one; expected: %s, actual: %s", expected, actual)
dumpThreeWayStateDiff(t, vector, bs, root)
t.FailNow()
}
}
// assertMsgResult compares a message result. It takes the expected receipt
// encoded in the vector, the actual receipt returned by Lotus, and a message
// label to log in the assertion failure message to facilitate debugging.
func assertMsgResult(t *testing.T, expected *schema.Receipt, actual *vm.ApplyRet, label string) {
t.Helper()
if expected, actual := exitcode.ExitCode(expected.ExitCode), actual.ExitCode; expected != actual {
t.Errorf("exit code of msg %s did not match; expected: %s, got: %s", label, expected, actual)
}
if expected, actual := expected.GasUsed, actual.GasUsed; expected != actual {
t.Errorf("gas used of msg %s did not match; expected: %d, got: %d", label, expected, actual)
}
if expected, actual := []byte(expected.ReturnValue), actual.Return; !bytes.Equal(expected, actual) {
t.Errorf("return value of msg %s did not match; expected: %s, got: %s", label, base64.StdEncoding.EncodeToString(expected), base64.StdEncoding.EncodeToString(actual))
}
}
func dumpThreeWayStateDiff(t *testing.T, vector *schema.TestVector, bs blockstore.Blockstore, actual cid.Cid) {
// check if statediff exists; if not, skip.
if err := exec.Command("statediff", "--help").Run(); err != nil {
t.Log("could not dump 3-way state tree diff upon test failure: statediff command not found")
t.Log("install statediff with:")
t.Log("$ git clone https://github.com/filecoin-project/statediff.git")
t.Log("$ cd statediff")
t.Log("$ go generate ./...")
t.Log("$ go install ./cmd/statediff")
return
}
tmpCar := writeStateToTempCAR(t, bs,
vector.Pre.StateTree.RootCID,
vector.Post.StateTree.RootCID,
actual,
)
color.NoColor = false // enable colouring.
t.Errorf("wrong post root cid; expected %v, but got %v", vector.Post.StateTree.RootCID, actual)
var (
a = color.New(color.FgMagenta, color.Bold).Sprint("(A) expected final state")
b = color.New(color.FgYellow, color.Bold).Sprint("(B) actual final state")
c = color.New(color.FgCyan, color.Bold).Sprint("(C) initial state")
d1 = color.New(color.FgGreen, color.Bold).Sprint("[Δ1]")
d2 = color.New(color.FgGreen, color.Bold).Sprint("[Δ2]")
d3 = color.New(color.FgGreen, color.Bold).Sprint("[Δ3]")
)
printDiff := func(left, right cid.Cid) {
cmd := exec.Command("statediff", "car", "--file", tmpCar, left.String(), right.String())
b, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("statediff failed: %s", err)
}
t.Log(string(b))
}
bold := color.New(color.Bold).SprintfFunc()
// run state diffs.
t.Log(bold("=== dumping 3-way diffs between %s, %s, %s ===", a, b, c))
t.Log(bold("--- %s left: %s; right: %s ---", d1, a, b))
printDiff(vector.Post.StateTree.RootCID, actual)
t.Log(bold("--- %s left: %s; right: %s ---", d2, c, b))
printDiff(vector.Pre.StateTree.RootCID, actual)
t.Log(bold("--- %s left: %s; right: %s ---", d3, c, a))
printDiff(vector.Pre.StateTree.RootCID, vector.Post.StateTree.RootCID)
}
// writeStateToTempCAR writes the provided roots to a temporary CAR that'll be
// cleaned up via t.Cleanup(). It returns the full path of the temp file.
func writeStateToTempCAR(t *testing.T, bs blockstore.Blockstore, roots ...cid.Cid) string {
tmp, err := ioutil.TempFile("", "lotus-tests-*.car")
if err != nil {
t.Fatalf("failed to create temp file to dump CAR for diffing: %s", err)
}
// register a cleanup function to delete the CAR.
t.Cleanup(func() {
_ = os.Remove(tmp.Name())
})
carWalkFn := func(nd format.Node) (out []*format.Link, err error) {
for _, link := range nd.Links() {
if link.Cid.Prefix().Codec == cid.FilCommitmentSealed || link.Cid.Prefix().Codec == cid.FilCommitmentUnsealed {
continue
}
out = append(out, link)
}
return out, nil
}
var (
offl = offline.Exchange(bs)
blkserv = blockservice.New(bs, offl)
dserv = merkledag.NewDAGService(blkserv)
)
err = car.WriteCarWithWalker(context.Background(), dserv, roots, tmp, carWalkFn)
if err != nil {
t.Fatalf("failed to dump CAR for diffing: %s", err)
}
_ = tmp.Close()
return tmp.Name()
}
func loadCAR(t *testing.T, vectorCAR schema.Base64EncodedBytes) blockstore.Blockstore {
bs := blockstore.NewTemporary()
// Read the base64-encoded CAR from the vector, and inflate the gzip.
buf := bytes.NewReader(vectorCAR)
r, err := gzip.NewReader(buf)
if err != nil {
t.Fatalf("failed to inflate gzipped CAR: %s", err)
}
defer r.Close() // nolint
// Load the CAR embedded in the test vector into the Blockstore.
_, err = car.LoadCar(bs, r)
if err != nil {
t.Fatalf("failed to load state tree car from test vector: %s", err)
}
return bs
}
+1 -146
View File
@@ -1,146 +1 @@
{
"bellman": {
"title": "Bellman",
"value": "Bellman is a rust crate for building zk-SNARK circuits. It provides circuit traits and primitive structures, as well as basic gadget implementations such as booleans and number abstractions."
},
"nvme": {
"title": "NVMe",
"value": "(non-volatile memory express) is a host controller interface and storage protocol created to accelerate the transfer of data between enterprise and client systems and solid-state drives (SSDs) over a computer's high-speed Peripheral Component Interconnect Express (PCIe) bus."
},
"multiaddr": {
"title": "Multiaddr",
"value": "Multiaddr is a format for encoding addresses from various well-established network protocols. It is useful to write applications that future-proof their use of addresses, and allow multiple transport protocols and addresses to coexist."
},
"attofil": {
"title": "attoFIL",
"value": "AttoFIL is a word used to describe 10^-18 FIL. The word atto comes from the Norwegian and Danish term: atten eighteen."
},
"fil": {
"title": "FIL",
"value": "A ticker symbol is an abbreviation used to uniquely identify Filecoin when it is used in a wallet exchange or a cryptocurrency exchange."
},
"epost": {
"title": "Election Proof-of-Spacetime",
"value": "Election Proof-of-Spacetime couples the Proof-of-Spacetime process with block production, meaning that in order to produce a block, the miner must produce a valid Proof-of-Spacetime proof (snark output)."
},
"jwt": {
"title": "JWT",
"value": "JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties."
},
"json-rpc": {
"title": "JSON-RPC",
"value": "JSON-RPC is a remote procedure call protocol encoded in JSON. It is a very simple protocol (and very similar to XML-RPC), defining only a few data types and commands."
},
"bls-address": {
"title": "BLS Signature (Address)",
"value": "A BonehLynnShacham (BLS) signature is a digital signature scheme that allows a user to determine the authenticity of a signer, and is a commonly used signature scheme in the Filecoin Distributed Storage Network."
},
"faucet": {
"title": "Filecoin Test Faucet",
"value": "A webpage where you can get free test Filecoin to participate in the Testnet."
},
"chain": {
"title": "Chain",
"value": "The Filecoin Blockchain is a distributed virtual machine that achieves consensus, processes messages, accounts for storage, and maintains security in the Filecoin Protocol. It is the main interface linking various actors in the Filecoin system."
},
"miner-power": {
"title": "Miner Power",
"value": "Miner storage in relation to network storage, tracked in the power table."
},
"sector": {
"title": "Sector",
"value": "A fixed-size block of data of SECTOR_SIZE bytes which generally contains client's data."
},
"sealing": {
"title": "Sealing",
"value": "A slow encoding process that returns commitments and proofs for data being stored in a sector."
},
"seal": {
"title": "Seal",
"value": "A slow encoding process that returns commitments and proofs for data being stored in a sector."
},
"posts": {
"title": "Proof-of-Spacetime(s)",
"value": "Filecoin is a protocol token whose blockchain runs on a novel proof, called Proof-of-Spacetime, where blocks are created by miners that are storing data."
},
"filecoin-testnet": {
"title": "Filecoin Testnet",
"value": "Until we launch, we are making lots of changes to Lotus. The Testnet is expected to bring a few significant fixes/improvements. During Testnet, you can retrieve test filecoin from our network faucet to use as collateral to start mining. Test filecoin do not have any value the official filecoin tokens will not be released until Mainnet launch."
},
"filecoin-decentralized-storage-market": {
"title": "Filecoin Decentralized Storage Market",
"value": "Storage Market subsystem is the data entry point into the network. Miners only earn power from data stored in a storage deal and all deals live on the Filecoin network."
},
"filecoin-proof-parameters": {
"title": "Filecoin Proof Parameters",
"value": "The proving algorithms rely on a large binary parameter file."
},
"lotus-devnet": {
"title": "DevNet",
"value": "On the DevNets, you can store data as a storage client and also try how Filecoin mining works. The devnets are an important development tool for those who anticipate building applications on top of the Filecoin protocol or storing data on the decentralized storage market. "
},
"filecoin-distributed-storage-network": {
"title": "Filecoin Distributed Storage Network",
"value": "Filecoin is a distributed storage network based on a blockchain mechanism. Filecoin miners can elect to provide storage capacity for the network, and thereby earn units of the Filecoin cryptocurrency (FIL) by periodically producing cryptographic proofs that certify that they are providing the capacity specified."
},
"lotus-node": {
"title": "Lotus Node",
"value": "The Lotus Node is full of capabilities. It runs the Blockchain system, makes retrieval deals, does data transfer, supports block producer logic, and syncs and validates the chain."
},
"block-rewards": {
"title": "Block Reward",
"value": "Over the entire lifetime of the protocol, 1,400,000,000 FIL (TotalIssuance) will be given out to miners. The rate at which the funds are given out is set to halve every six years, smoothly (not a fixed jump like in Bitcoin)."
},
"block-producer-miner": {
"title": "Miner (Block Producer)",
"value": "The Block Producer Miner's logic. It currently shares an interface and process with the Lotus Node. A Block Producer chooses which messages to include in a block and is rewarded according to each messages gas price and consumption, forming a market."
},
"lotus-miner": {
"title": "Miner (lotus-miner)",
"value": "The Miner's logic. It has its own dedicated process. Contributes to the network through Sector commitments and Proofs of Spacetime to prove that it is storing the sectors it has commited to."
},
"swarm-port": {
"title": "Swarm Port (Libp2p)",
"value": "The LibP2P Swarm manages groups of connections to peers, handles incoming and outgoing streams, and is part of the miners implementation. The port value is part of the Host interface."
},
"daemon": {
"title": "Lotus Daemon",
"value": "A Daemon is a program that runs as a background process. A Daemon in the context of the Filecoin Distributed Storage Network may enable applications to communicate with peers, handle protocols, participate in pubsub, and interact with a distributed hash table (DHT)."
},
"storage-deal": {
"title": "Storage deal",
"value": "One of the two types of deals in Filecoin markets. Storage deals are recorded on the blockchain and enforced by the protocol."
},
"retrieval-deal": {
"title": "Retrieval deal",
"value": "One of the two types of deals in Filecoin markets. Retrieval deals are off chain and enabled by micropayment channel by transacting parties."
},
"deal-cid": {
"title": "Deal CID",
"value": "CID is a format for referencing content in distributed information systems, it is a way to store information so it can be retrieved based on its content, not its location. DealCID specifically is used in storage deals."
},
"data-cid": {
"title": "Data CID",
"value": "CID is a format for referencing content in distributed information systems, it is a way to store information so it can be retrieved based on its content, not its location. DataCID specifically is used to represent the file that is stored in the Filecoin Distributed Storage Network."
},
"cid": {
"title": "CID",
"value": "A CID is a self-describing content-addressed identifier. It uses cryptographic hashes to achieve content addressing. It uses several multiformats to achieve flexible self-description, namely multihash for hashes, multicodec for data content types, and multibase to encode the CID itself into strings."
},
"total-network-power": {
"title": "Total Network Power",
"value": "A reference to all the Power Tables for every subchain, accounting for each Lotus Miner on chain."
},
"chain-block-height": {
"title": "Chain Block Height",
"value": "Chain block height is defined as the number of blocks in the chain between any given block and the very first block in the blockchain."
},
"block-height": {
"title": "Block Height",
"value": "Height of the Merkle Tree of a sector. A sector is a contiguous array of bytes that a miner puts together, seals, and performs Proofs of Spacetime on."
},
"blocktime": {
"title": "Blocktime",
"value": "The time it takes for a Block to propagate to the whole network."
}
}
{}
+12 -219
View File
@@ -1,232 +1,25 @@
{
"posts": [
{
"title": "Hardware Requirements",
"slug": "en+hardware",
"github": "en/hardware.md",
"title": "About Lotus",
"slug": "",
"github": "en/about.md",
"value": null,
"posts": [
{
"title": "Testing Configuration",
"slug": "en+hardware-mining",
"github": "en/hardware-mining.md",
"value": null
}
]
"posts": []
},
{
"title": "Setup",
"slug": "en+getting-started",
"github": "en/getting-started.md",
"value": null,
"posts": [
{
"title": "Arch Linux Installation",
"slug": "en+install-lotus-arch",
"github": "en/install-lotus-arch.md",
"value": null
},
{
"title": "Ubuntu Installation",
"slug": "en+install-lotus-ubuntu",
"github": "en/install-lotus-ubuntu.md",
"value": null
},
{
"title": "Fedora Installation",
"slug": "en+install-lotus-fedora",
"github": "en/install-lotus-fedora.md",
"value": null
},
{
"title": "MacOS Installation",
"slug": "en+install-lotus-macos",
"github": "en/install-lotus-macos.md",
"value": null
},
{
"title": "Updating Lotus",
"slug": "en+updating-lotus",
"github": "en/updating-lotus.md",
"value": null
},
{
"title": "Join Testnet",
"slug": "en+join-testnet",
"github": "en/join-testnet.md",
"value": null
},
{
"title": "Use Lotus with systemd",
"slug": "en+install-systemd-services",
"github": "en/install-systemd-services.md",
"value": null
},
{
"title": "Setup Troubleshooting",
"slug": "en+setup-troubleshooting",
"github": "en/setup-troubleshooting.md",
"value": null
},
{
"title": "Environment Variables",
"slug": "en+env-vars",
"github": "en/environment-vars.md",
"value": null
}
]
},
{
"title": "Architecture",
"title": "Lotus Architecture (WIP)",
"slug": "en+arch",
"github": "en/architecture.md",
"github": "en/architecture/architecture.md",
"value": null,
"posts": [
{
"title": "The Message Pool",
"slug": "en+mpool",
"github": "en/mpool.md",
"value": null
}
{
"title": "The Message Pool",
"slug": "en+mpool",
"github": "en/architecture/mpool.md",
"value": null
}
]
},
{
"title": "Storage Mining",
"slug": "en+mining",
"github": "en/mining.md",
"value": null,
"posts": [
{
"title": "Lotus Worker",
"slug": "en+lotus-worker",
"github": "en/mining-lotus-worker.md",
"value": null
},
{
"title": "Static Ports",
"slug": "en+setting-a-static-port",
"github": "en/setting-a-static-port.md",
"value": null
},
{
"title": "Mining Troubleshooting",
"slug": "en+mining-troubleshooting",
"github": "en/mining-troubleshooting.md",
"value": null
}
]
},
{
"title": "Storing Data",
"slug": "en+storing-data",
"github": "en/storing-data.md",
"value": null,
"posts": [
{
"title": "Storage Troubleshooting",
"slug": "en+storing-data-troubleshooting",
"github": "en/storing-data-troubleshooting.md",
"value": null
},
{
"title": "Information for Miners",
"slug": "en+info-for-miners",
"github": "en/miner-deals.md",
"value": null
},
{
"title": "IPFS Integration",
"slug": "en+ipfs-client-integration",
"github": "en/storing-ipfs-integration.md",
"value": null
}
]
},
{
"title": "Retrieving Data",
"slug": "en+retrieving-data",
"github": "en/retrieving-data.md",
"value": null,
"posts": []
},
{
"title": "Payment Channels",
"slug": "en+payment-channels",
"github": "en/payment-channels.md",
"value": null,
"posts": []
},
{
"title": "Command Line Interface",
"slug": "en+cli",
"github": "en/cli.md",
"value": null,
"posts": []
},
{
"title": "API",
"slug": "en+api",
"github": "en/api.md",
"value": null,
"posts": [
{
"title": "Remote API Support",
"slug": "en+api-scripting-support",
"github": "en/api-scripting-support.md",
"value": null
},
{
"title": "API Methods",
"slug": "en+api-methods",
"github": "en/api-methods.md",
"value": null
},
{
"title": "API Troubleshooting",
"slug": "en+api-troubleshooting",
"github": "en/api-troubleshooting.md",
"value": null
}
]
},
{
"title": "Developer Tools",
"slug": "en+dev-tools",
"github": "en/dev-tools.md",
"value": null,
"posts": [
{
"title": "Setup Local Devnet",
"slug": "en+setup-local-dev-net",
"github": "en/local-dev-net.md",
"value": null,
"posts": []
},
{
"title": "Jaeger Tracing",
"slug": "en+dev-tools-jaeger-tracing",
"github": "en/dev-tools-jaeger-tracing.md",
"value": null,
"posts": []
}
]
},
{
"title": "FAQs",
"slug": "en+faqs",
"github": "en/faqs.md",
"value": null,
"posts": []
},
{
"title": "Glossary",
"slug": "en+glossary",
"github": "en/.glossary.json",
"value": null,
"custom": {
"glossary": true
},
"posts": []
}
]
}
+16
View File
@@ -0,0 +1,16 @@
# Lotus documentation
This folder contains some Lotus documentation mostly intended for Lotus developers.
User documentation (including documentation for miners) has been moved to specific Lotus sections in https://docs.filecoin.io:
- https://docs.filecoin.io/get-started/lotus
- https://docs.filecoin.io/store/lotus
- https://docs.filecoin.io/mine/lotus
- https://docs.filecoin.io/build/lotus
## The Lotu.sh site
The https://lotu.sh and https://docs.lotu.sh sites are generated from this folder based on the index provided by [.library.json](.library.json). This is done at the [lotus-docs repository](https://github.com/filecoin-project/lotus-docs), which contains Lotus as a git submodule.
To update the site, the lotus-docs repository should be updated with the desired version for the lotus git submodule. Once pushed to master, it will be auto-deployed.
+19
View File
@@ -0,0 +1,19 @@
# Lotus
Lotus is an implementation of the **Filecoin Distributed Storage Network**.
It is written in Go and provides a suite of command-line applications:
- Lotus Node (`lotus`): a Filecoin Node: validates network transactions, manages a FIL wallet, can perform storage and retrieval deals.
- Lotus Miner (`lotus-miner`): a Filecoin miner. See the the respective Lotus Miner section in the Mine documentation.
- Lotus Worker (`lotus-worker`): a worker that assists miners to perform mining-related tasks. See its respective guide for more information.
The [Lotus user documentation](https://docs.filecoin.io/get-started/lotus) is part of the [Filecoin documentation site](https://docs.filecoin.io):
* To install and get started with Lotus, visit the [Get Started section](https://docs.filecoin.io/get-started/lotus).
* Information about how to perform deals on the Filecoin network using Lotus can be found in the [Store section](https://docs.filecoin.io/store/lotus).
* Miners looking to provide storage to the Network can find the latest guides in the [Mine section](https://docs.filecoin.io/mine/lotus).
* Developers and integrators that wish to use the Lotus APIs can start in the [Build section](https://docs.filecoin.io/mine/lotus).
For more details about Filecoin, check out the [Filecoin Docs](https://docs.filecoin.io) and [Filecoin Spec](https://spec.filecoin.io/).
+50 -16
View File
@@ -169,6 +169,7 @@
* [SyncState](#SyncState)
* [SyncSubmitBlock](#SyncSubmitBlock)
* [SyncUnmarkBad](#SyncUnmarkBad)
* [SyncValidateTipset](#SyncValidateTipset)
* [Wallet](#Wallet)
* [WalletBalance](#WalletBalance)
* [WalletDefaultAddress](#WalletDefaultAddress)
@@ -181,6 +182,7 @@
* [WalletSetDefault](#WalletSetDefault)
* [WalletSign](#WalletSign)
* [WalletSignMessage](#WalletSignMessage)
* [WalletValidateAddress](#WalletValidateAddress)
* [WalletVerify](#WalletVerify)
##
@@ -1120,20 +1122,14 @@ Inputs:
Response:
```json
{
"Ask": {
"Price": "0",
"VerifiedPrice": "0",
"MinPieceSize": 1032,
"MaxPieceSize": 1032,
"Miner": "t01234",
"Timestamp": 10101,
"Expiry": 10101,
"SeqNo": 42
},
"Signature": {
"Type": 2,
"Data": "Ynl0ZSBhcnJheQ=="
}
"Price": "0",
"VerifiedPrice": "0",
"MinPieceSize": 1032,
"MaxPieceSize": 1032,
"Miner": "t01234",
"Timestamp": 10101,
"Expiry": 10101,
"SeqNo": 42
}
```
@@ -3825,7 +3821,7 @@ Inputs:
]
```
Response: `2`
Response: `3`
### StateReadState
StateReadState returns the indicated actor's state.
@@ -4303,7 +4299,8 @@ Inputs: `null`
Response:
```json
{
"ActiveSyncs": null
"ActiveSyncs": null,
"VMApplied": 42
}
```
@@ -4378,6 +4375,28 @@ Inputs:
Response: `{}`
### SyncValidateTipset
SyncValidateTipset indicates whether the provided tipset is valid or not
Perms: read
Inputs:
```json
[
[
{
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
},
{
"/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve"
}
]
]
```
Response: `true`
## Wallet
@@ -4584,6 +4603,21 @@ Response:
}
```
### WalletValidateAddress
WalletValidateAddress validates whether a given string can be decoded as a well-formed address
Perms: read
Inputs:
```json
[
"string value"
]
```
Response: `"t01234"`
### WalletVerify
WalletVerify takes an address, a signature, and some bytes, and indicates whether the signature is valid.
The address does not have to be in the wallet.
-25
View File
@@ -1,25 +0,0 @@
# Remote API Support
You may want to delegate the work **Lotus Miner** or **Lotus Node** performs to other machines.
Here is how to setup the necessary authorization and environment variables.
## Environment variables
Environmental variables are variables that are defined for the current shell and are inherited by any child shells or processes. Environmental variables are used to pass information into processes that are spawned from the shell.
Using the [JWT you generated](https://lotu.sh/en+api#how-do-i-generate-a-token-18865), you can assign it and the **multiaddr** to the appropriate environment variable.
```sh
# Lotus Node
FULLNODE_API_INFO="JWT_TOKEN:/ip4/127.0.0.1/tcp/1234/http"
# Lotus Miner
MINER_API_INFO="JWT_TOKEN:/ip4/127.0.0.1/tcp/2345/http"
```
You can also use `lotus auth api-info --perm admin` to quickly create _API_INFO env vars
- The **Lotus Node**'s `mutliaddr` is in `~/.lotus/api`.
- The default token is in `~/.lotus/token`.
- The **Lotus Miner**'s `multiaddr` is in `~/.lotusminer/config`.
- The default token is in `~/.lotusminer/token`.
-36
View File
@@ -1,36 +0,0 @@
# API Troubleshooting
## Types: params
`params` must be an array. If there are no `params` you should still pass an empty array.
## Types: TipSet
For methods such as `Filecoin.StateMinerPower`, where the method accepts the argument of the type `TipSet`, you can pass `null` to use the current chain head.
```sh
curl -X POST \
-H "Content-Type: application/json" \
--data '{ "jsonrpc": "2.0", "method": "Filecoin.StateMinerPower", "params": ["t0101", null], "id": 3 }' \
'http://127.0.0.1:1234/rpc/v0'
```
## Types: Sending a CID
If you do not serialize the CID as a [JSON IPLD link](https://did-ipid.github.io/ipid-did-method/#txref), you will receive an error. Here is an example of a broken CURL request:
```sh
curl -X POST \
-H "Content-Type: application/json" \
--data '{ "jsonrpc": "2.0", "method":"Filecoin.ClientGetDealInfo", "params": ["bafyreiaxl446wlnu6t6dpq4ivrjf4gda4gvsoi4rr6mpxau7z25xvk5pl4"], "id": 0 }' \
'http://127.0.0.1:1234/rpc/v0'
```
To fix it, change the `params` property to:
```sh
curl -X POST \
-H "Content-Type: application/json" \
--data '{ "jsonrpc": "2.0", "method":"Filecoin.ClientGetDealInfo", "params": [{"/": "bafyreiaxl446wlnu6t6dpq4ivrjf4gda4gvsoi4rr6mpxau7z25xvk5pl4"}], "id": 0 }' \
'http://127.0.0.1:1234/rpc/v0'
```
-85
View File
@@ -1,85 +0,0 @@
# API
Here is an early overview of how to make API calls.
Implementation details for the **JSON-RPC** package are [here](https://github.com/filecoin-project/go-jsonrpc).
## Overview: How do you modify the config.toml to change the API endpoint?
API requests are made against `127.0.0.1:1234` unless you modify `.lotus/config.toml`.
Options:
- `http://[api:port]/rpc/v0` - HTTP endpoint
- `ws://[api:port]/rpc/v0` - Websocket endpoint
- `PUT http://[api:port]/rest/v0/import` - File import, it requires write permissions.
## What methods can I use?
For now, you can look into different files to find methods available to you based on your needs:
- [Both Lotus node + miner APIs](https://github.com/filecoin-project/lotus/blob/master/api/api_common.go)
- [Lotus node API](https://github.com/filecoin-project/lotus/blob/master/api/api_full.go)
- [Lotus miner API](https://github.com/filecoin-project/lotus/blob/master/api/api_storage.go)
The necessary permissions for each are in [api/struct.go](https://github.com/filecoin-project/lotus/blob/master/api/struct.go).
## How do I make an API request?
To demonstrate making an API request, we will take the method `ChainHead` from [api/api_full.go](https://github.com/filecoin-project/lotus/blob/master/api/api_full.go).
```go
ChainHead(context.Context) (*types.TipSet, error)
```
And create a CURL command. In this command, `ChainHead` is included as `{ "method": "Filecoin.ChainHead" }`:
```sh
curl -X POST \
-H "Content-Type: application/json" \
--data '{ "jsonrpc": "2.0", "method": "Filecoin.ChainHead", "params": [], "id": 3 }' \
'http://127.0.0.1:1234/rpc/v0'
```
If the request requires authorization, add an authorization header:
```sh
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $(cat ~/.lotusminer/token)" \
--data '{ "jsonrpc": "2.0", "method": "Filecoin.ChainHead", "params": [], "id": 3 }' \
'http://127.0.0.1:1234/rpc/v0'
```
> In the future we will add a playground to make it easier to build and experiment with API requests.
## CURL authorization
To authorize your request, you will need to include the **JWT** in a HTTP header, for example:
```sh
-H "Authorization: Bearer $(cat ~/.lotusminer/token)"
```
Admin token is stored in `~/.lotus/token` for the **Lotus Node** or `~/.lotusminer/token` for the **Lotus Miner**.
## How do I generate a token?
To generate a JWT with custom permissions, use this command:
```sh
# Lotus Node
lotus auth create-token --perm admin
# Lotus Miner
lotus-miner auth create-token --perm admin
```
## What authorization level should I use?
When viewing [api/apistruct/struct.go](https://github.com/filecoin-project/lotus/blob/master/api/apistruct/struct.go), you will encounter these types:
- `read` - Read node state, no private data.
- `write` - Write to local store / chain, and `read` permissions.
- `sign` - Use private keys stored in wallet for signing, `read` and `write` permissions.
- `admin` - Manage permissions, `read`, `write`, and `sign` permissions.
@@ -6,7 +6,7 @@ Filecoin protocol, validating the blocks and state transitions.
The specification for the Filecoin protocol can be found [here](https://filecoin-project.github.io/specs/).
For information on how to setup and operate a Lotus node,
please follow the instructions [here](https://lotu.sh/en+getting-started).
please follow the instructions [here](en+getting-started).
# Components
-108
View File
@@ -1,108 +0,0 @@
# Lotus Command Line Interface
The Command Line Interface (CLI) is a convenient way to interact with
a Lotus node. You can use the CLI to operate your node,
get information about the blockchain,
manage your accounts and transfer funds,
create storage deals, and much more!
The CLI is intended to be self-documenting, so when in doubt, simply add `--help`
to whatever command you're trying to run! This will also display all of the
input parameters that can be provided to a command.
We highlight some of the commonly
used features of the CLI below.
All CLI commands should be run from the home directory of the Lotus project.
## Operating a Lotus node
### Starting up a node
```sh
lotus daemon
```
This command will start up your Lotus node, with its API port open at 1234.
You can pass `--api=<number>` to use a different port.
### Checking your sync progress
```sh
lotus sync status
```
This command will print your current tipset height under `Height`, and the target tipset height
under `Taregt`.
You can also run `lotus sync wait` to get constant updates on your sync progress.
### Getting the head tipset
```sh
lotus chain head
```
### Control the logging level
```sh
lotus log set-level
```
This command can be used to toggle the logging levels of the different
systems of a Lotus node. In decreasing order
of logging detail, the levels are `debug`, `info`, `warn`, and `error`.
As an example,
to set the `chain` and `blocksync` to log at the `debug` level, run
`lotus log set-level --system chain --system blocksync debug`.
To see the various logging system, run `lotus log list`.
### Find out what version of Lotus you're running
```sh
lotus version
```
## Managing your accounts
### Listing accounts in your wallet
```sh
lotus wallet list
```
### Creating a new account
```sh
lotus wallet new bls
```
This command will create a new BLS account in your wallet; these
addresses start with the prefix `t3`. Running `lotus wallet new secp256k1`
(or just `lotus wallet new`) will create
a new Secp256k1 account, which begins with the prefix `t1`.
### Getting an account's balance
```sh
lotus wallet balance <address>
```
### Transferring funds
```sh
lotus send --source=<source address> <destination address> <amount>
```
This command will transfer `amount` (in attoFIL) from `source address` to `destination address`.
### Importing an account into your wallet
```sh
lotus wallet import <path to private key>
```
This command will import an account whose private key is saved at the specified file.
### Exporting an account from your wallet
```sh
lotus wallet export <address>
```
This command will print out the private key of the specified address
if it is in your wallet. Always be careful with your private key!
-3
View File
@@ -1,3 +0,0 @@
# Developer Tools
> Running a local network can be a great way to understand how Lotus works and test your setup.
-65
View File
@@ -1,65 +0,0 @@
# Lotus Environment Variables
## Building
## Common
The environment variables are common across most lotus binaries.
### `LOTUS_FD_MAX`
Sets the file descriptor limit for the process. This should be set high (8192
or higher) if you ever notice 'too many open file descriptor' errors.
### `LOTUS_JAEGER`
This can be set to enable jaeger trace reporting. The value should be the url
of the jaeger trace collector, the default for most jaeger setups should be
`localhost:6831`.
### `LOTUS_DEV`
If set to a non-empty value, certain parts of the application will print more
verbose information to aid in development of the software. Not recommended for
end users.
## Lotus Daemon
### `LOTUS_PATH`
Sets the location for the lotus daemon on-disk repo. If left empty, this defaults to `~/.lotus`.
### `LOTUS_SKIP_GENESIS_CHECK`
Can be set to `_yes_` if you wish to run a lotus network with a different
genesis than the default one built into your lotus binary.
### `LOTUS_CHAIN_TIPSET_CACHE`
Sets the cache size for the chainstore tipset cache. The default value is 8192,
but if your usage of the lotus API involves frequent arbitrary tipset lookups,
you may want to increase this.
### `LOTUS_CHAIN_INDEX_CACHE`
Sets the cache size for the chainstore epoch index cache. The default value is 32768,
but if your usage of the lotus API involves frequent deep chain lookups for
block heights that are very far from the current chain height, you may want to
increase this.
### `LOTUS_BSYNC_MSG_WINDOW`
Set the initial maximum window size for message fetching blocksync requests. If
you have a slower internet connection and are having trouble syncing, you might
try lowering this down to 10-20 for a 'poor' internet connection.
## Lotus Miner
A number of environment variables are respected for configuring the behavior of the filecoin proving subsystem. For more details on those [see here](https://github.com/filecoin-project/rust-fil-proofs/#settings).
### `LOTUS_MINER_PATH`
Sets the location for the lotus miners on-disk repo. If left empty, this defaults to `~/.lotusminer`.
-138
View File
@@ -1,138 +0,0 @@
# Frequently Asked Questions
Here are some FAQs concerning the Lotus implementation and participation in
Testnet.
For questions concerning the broader Filecoin project, please
go [here](https://filecoin.io/faqs/).
## Introduction to Lotus
### What is Lotus?
Lotus is an implementation of the **Filecoin Distributed Storage Network**, written in Go.
It is designed to be modular and interoperable with any other implementation of the Filecoin Protocol.
More information about Lotus can be found [here](https://lotu.sh/).
### What are the components of Lotus?
Lotus is composed of two separate pieces that can talk to each other:
The Lotus Node can sync the blockchain, validating all blocks, transfers, and deals
along the way. It can also facilitate the creation of new storage deals. If you are not
interested in providing your own storage to the network, and do not want to produce blocks
yourself, then the Lotus Node is all you need!
The Lotus Miner does everything you need for the registration of storage, and the
production of new blocks. The Lotus Miner communicates with the network by talking
to a Lotus Node over the JSON-RPC API.
## Setting up a Lotus Node
### How do I set up a Lotus Node?
Follow the instructions found [here](https://lotu.sh/en+getting-started).
### Where can I get the latest version of Lotus?
Download the binary tagged as the `Latest Release` from the
[Lotus Github repo](https://github.com/filecoin-project/lotus/releases).
### What operating systems can Lotus run on?
Lotus can build and run on most Linux and MacOS systems with at least
8GB of RAM. Windows is not yet supported.
### How can I update to the latest version of Lotus?
To update Lotus, follow the instructions [here](https://lotu.sh/en+updating-lotus).
### How do I prepare a fresh installation of Lotus?
Stop the Lotus daemon, and delete all related files, including sealed and chain data by
running `rm ~/.lotus ~/.lotusminer`.
Then, install Lotus afresh by following the instructions
found [here](https://lotu.sh/en+getting-started).
### Can I configure where the node's config and data goes?
Yes! The `LOTUS_PATH` variable sets the path for where the Lotus node's data is written.
The `LOTUS_MINER_PATH` variable does the same for miner-specific information.
## Interacting with a Lotus Node
### How can I communicate with a Lotus Node?
Lotus Nodes have a command-line interface, as well as a JSON-RPC API.
### What are the commands I can send using the command-line interface?
The command-line interface is self-documenting, try running `lotus --help` from the `lotus` home
directory for more.
### How can I send a request over the JSON-RPC API?
Information on how to send a `cURL` request to the JSON-RPC API can be found
[here](https://lotu.sh/en+api). A JavaScript client is under development.
### What are the requests I can send over the JSON-RPC API?
Please have a look at the
[source code](https://github.com/filecoin-project/lotus/blob/master/api/api_full.go)
for a list of methods supported by the JSON-RPC API.
## The Test Network
### What is Testnet?
Testnet is a live network of Lotus Nodes run by the
community for testing purposes.
It has 2 PiB of storage (and growing!) dedicated to it.
### Is FIL on the Testnet worth anything?
Nothing at all! Real-world incentives may be provided in a future phase of Testnet, but this is
yet to be confirmed.
### How can I see the status of Testnet?
The [dashboard](https://stats.testnet.filecoin.io/) displays the status of the network as
well as a ton
of other metrics you might find interesting.
## Mining with a Lotus Node on Testnet
### How do I get started mining with Lotus?
Follow the instructions found [here](https://lotu.sh/en+mining).
### What are the minimum hardware requirements?
An example test configuration, and minimum hardware requirements can be found
[here](https://lotu.sh/en+hardware-mining).
Note that these might NOT be the minimum requirements for mining on Mainnet.
### What are some GPUs that have been tested?
A list of benchmarked GPUs can be found [here](https://lotu.sh/en+hardware-mining#benchmarked-gpus-7393).
### Why is my GPU not being used when sealing a sector?
Sealing a sector does not involve constant GPU operations. It's possible
that your GPU simply isn't necessary at the moment you checked.
## Advanced questions
### Is there a Docker image for lotus?
Community-contributed Docker and Docker Compose examples are available
[here](https://github.com/filecoin-project/lotus/tree/master/tools/dockers/docker-examples).
### How can I run two miners on the same machine?
You can do so by changing the storage path variable for the second miner, e.g.,
`LOTUS_MINER_PATH=~/.lotusminer2`. You will also need to make sure that no ports collide.
### How do I setup my own local devnet?
Follow the instructions found [here](https://lotu.sh/en+setup-local-dev-net).
-23
View File
@@ -1,23 +0,0 @@
# Lotus
Lotus is an implementation of the **Filecoin Distributed Storage Network**. You can run the Lotus software client to join the **Filecoin Testnet**.
For more details about Filecoin, check out the [Filecoin Docs](https://docs.filecoin.io) and [Filecoin Spec](https://filecoin-project.github.io/specs/).
## What can I learn here?
- How to install Lotus on [Arch Linux](https://lotu.sh/en+install-lotus-arch), [Ubuntu](https://lotu.sh/en+install-lotus-ubuntu), or [MacOS](https://lotu.sh/en+install-lotus-macos).
- Joining the [Lotus Testnet](https://lotu.sh/en+join-testnet).
- [Storing](https://lotu.sh/en+storing-data) or [retrieving](https://lotu.sh/en+retrieving-data) data.
- Mining Filecoin using the **Lotus Miner** in your [CLI](https://lotu.sh/en+mining).
## How is Lotus designed?
Lotus is architected modularly to keep clean API boundaries while using the same process. Installing Lotus will include two separate programs:
- The **Lotus Node**
- The **Lotus Miner**
The **Lotus Miner** is intended to be run on the machine that manages a single miner instance, and is meant to communicate with the **Lotus Node** via the websocket **JSON-RPC** API for all of the chain interaction needs.
This way, a mining operation may easily run a **Lotus Miner** or many of them, connected to one or many **Lotus Node** instances.
-54
View File
@@ -1,54 +0,0 @@
# Protocol Labs Standard Testing Configuration
> This documentation page describes the standard testing configuration the Protocol Labs team has used to test **Lotus Miner**s on Lotus. There is no guarantee this testing configuration will be suitable for Filecoin storage mining at MainNet launch. If you need to buy new hardware to join the Filecoin Testnet, we recommend to buy no more hardware than you require for testing. To learn more please read this [Protocol Labs Standard Testing Configuration post](https://filecoin.io/blog/filecoin-testnet-mining/).
**Sector sizes** and **minimum pledged storage** required to mine blocks are two very important Filecoin Testnet parameters that impact hardware decisions. We will continue to refine all parameters during Testnet.
BECAUSE OF THIS, OUR STANDARD TESTING CONFIGURATION FOR FILECOIN MAINNET CAN AND WILL CHANGE. YOU HAVE BEEN WARNED.
## Example configuration
The setup below is a minimal example for sealing 32 GiB sectors on Lotus:
- 2 TB of hard drive space.
- 8 core CPU
- 128 GiB of RAM
Note that 1GB sectors don't require as high of specs, but are likely to be removed as we improve the performance of 32GB sector sealing.
For the first part of the sealing process, AMD CPU's are __highly recommended__, because of the `Intel SHA Extensions` instruction set that is available there ever since the `Zen` microarchitecture. Hence, AMD CPU's seem to perform much better on the testnet than other CPU's. Contrary to what the name implies, this extended instruction set is not available on recent Intel desktop/server chips.
## Testnet discoveries
- If you only have 128GiB of ram, enabling 256GB of **NVMe** swap on an SSD will help you avoid out-of-memory issues while mining.
## Benchmarked GPUs
GPUs are a must for getting **block rewards**. Here are a few that have been confirmed to generate **SNARKs** quickly enough to successfully mine blocks on the Lotus Testnet.
- GeForce RTX 2080 Ti
- GeForce RTX 2080 SUPER
- GeForce RTX 2080
- GeForce GTX 1080 Ti
- GeForce GTX 1080
- GeForce GTX 1060
## Testing other GPUs
If you want to test a GPU that is not explicitly supported, use the following global **environment variable**:
```sh
BELLMAN_CUSTOM_GPU="<NAME>:<NUMBER_OF_CORES>"
```
Here is an example of trying a GeForce GTX 1660 Ti with 1536 cores.
```sh
BELLMAN_CUSTOM_GPU="GeForce GTX 1660 Ti:1536"
```
To get the number of cores for your GPU, you will need to check your cards specifications.
## Benchmarking
Here is a [benchmarking tool](https://github.com/filecoin-project/lotus/tree/master/cmd/lotus-bench) and a [GitHub issue thread](https://github.com/filecoin-project/lotus/issues/694) for those who wish to experiment with and contribute hardware setups for the **Filecoin Testnet**.
-7
View File
@@ -1,7 +0,0 @@
# Hardware
> This page is a work in progress. Exact mining requirements are still in the works.
Lotus can build and run on most [Linux](https://ubuntu.com/) and [MacOS](https://www.apple.com/macos) systems with at least 8GiB of RAM.
Windows is not yet supported.
-51
View File
@@ -1,51 +0,0 @@
# Arch Linux Instructions
These steps will install the following dependencies:
- go (1.14 or higher)
- gcc (7.4.0 or higher)
- git (version 2 or higher)
- bzr (some go dependency needs this)
- jq
- pkg-config
- opencl-icd-loader
- opencl driver (like nvidia-opencl on arch) (for GPU acceleration)
- opencl-headers (build)
- rustup (proofs build)
- llvm (proofs build)
- clang (proofs build)
### Install dependencies
```sh
sudo pacman -Syu opencl-icd-loader gcc git bzr jq pkg-config opencl-icd-loader opencl-headers
```
### Install Go 1.14
Install the latest version of Go by following [the docs on their website](https://golang.org/doc/install).
### Clone the Lotus repository
```sh
git clone https://github.com/filecoin-project/lotus.git
cd lotus/
```
### Build the Lotus binaries from source and install
! **If you are running an AMD platform or if your CPU supports SHA extensions you will want to build the Filecoin proofs natively**
```sh
make clean && make all
sudo make install
```
#### Native Filecoin FFI building
```sh
env env RUSTFLAGS="-C target-cpu=native -g" FFI_BUILD_FROM_SOURCE=1 make clean deps all
sudo make install
```
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://lotu.sh/en+join-testnet).
-54
View File
@@ -1,54 +0,0 @@
# Fedora Instructions
> tested on 30
**NOTE:** If you have an AMD GPU the opencl instructions may be incorrect...
These steps will install the following dependencies:
- go (1.14 or higher)
- gcc (7.4.0 or higher)
- git (version 2 or higher)
- bzr (some go dependency needs this)
- jq
- pkg-config
- rustup (proofs build)
- llvm (proofs build)
- clang (proofs build)
### Install dependencies
```sh
$ sudo dnf -y update
$ sudo dnf -y install gcc git bzr jq pkgconfig mesa-libOpenCL mesa-libOpenCL-devel opencl-headers ocl-icd ocl-icd-devel clang llvm
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
### Install Go 1.14
Install the latest version of Go by following [the docs on their website](https://golang.org/doc/install).
### Clone the Lotus repository
```sh
git clone https://github.com/filecoin-project/lotus.git
cd lotus/
```
### Build the Lotus binaries from source and install
! **If you are running an AMD platform or if your CPU supports SHA extensions you will want to build the Filecoin proofs natively**
```sh
$ make clean && make all
$ sudo make install
```
#### Native Filecoin FFI building
```sh
env env RUSTFLAGS="-C target-cpu=native -g" FFI_BUILD_FROM_SOURCE=1 make clean deps all
sudo make install
```
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus TestNet](https://lotu.sh/en+join-testnet).
-62
View File
@@ -1,62 +0,0 @@
# MacOS Instructions
## Get XCode Command Line Tools
To check if you already have the XCode Command Line Tools installed via the CLI, run:
```sh
xcode-select -p
```
If this command returns a path, you can move on to the next step. Otherwise, to install via the CLI, run:
```sh
xcode-select --install
```
To update, run:
```sh
sudo rm -rf /Library/Developer/CommandLineTools
xcode-select --install
```
## Get HomeBrew
We recommend that MacOS users use [HomeBrew](https://brew.sh) to install each the necessary packages.
Check if you have HomeBrew:
```sh
brew -v
```
This command returns a version number if you have HomeBrew installed and nothing otherwise.
In your terminal, enter this command to install Homebrew:
```sh
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```
Use the command `brew install` to install the following packages:
```sh
brew install go bzr jq pkg-config rustup
```
Clone
```sh
git clone https://github.com/filecoin-project/lotus.git
cd lotus/
```
Build
```sh
make clean && make all
sudo make install
```
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://lotu.sh/en+join-testnet).
-54
View File
@@ -1,54 +0,0 @@
# Ubuntu Instructions
These steps will install the following dependencies:
- go (1.14 or higher)
- gcc (7.4.0 or higher)
- git (version 2 or higher)
- bzr (some go dependency needs this)
- jq
- pkg-config
- opencl-icd-loader
- opencl driver (like nvidia-opencl on arch) (for GPU acceleration)
- opencl-headers (build)
- rustup (proofs build)
- llvm (proofs build)
- clang (proofs build)
### Install dependencies
```sh
sudo apt update
sudo apt install mesa-opencl-icd ocl-icd-opencl-dev gcc git bzr jq pkg-config curl
sudo apt upgrade
```
### Install Go 1.14
Install the latest version of Go by following [the docs on their website](https://golang.org/doc/install).
### Clone the Lotus repository
```sh
git clone https://github.com/filecoin-project/lotus.git
cd lotus/
```
### Build the Lotus binaries from source and install
! **If you are running an AMD platform or if your CPU supports SHA extensions you will want to build the Filecoin proofs natively**
```sh
make clean && make all
sudo make install
```
#### Native Filecoin FFI building
```sh
env env RUSTFLAGS="-C target-cpu=native -g" FFI_BUILD_FROM_SOURCE=1 make clean deps all
sudo make install
```
After installing Lotus, you can run the `lotus` command directly from your CLI to see usage documentation. Next, you can join the [Lotus Testnet](https://lotu.sh/en+join-testnet).
@@ -1,145 +0,0 @@
# Use Lotus with systemd
Lotus is capable of running as a systemd service daemon. You can find installable service files for systemd in the [lotus repo scripts directory](https://github.com/filecoin-project/lotus/tree/master/scripts) as files with `.service` extension. In order to install these service files, you can copy these `.service` files to the default systemd unit load path.
The services expect their binaries to be present in `/usr/local/bin/`. You can use `make` to install them by running:
```sh
$ sudo make install
```
for `lotus` and `lotus-miner` and
```sh
$ sudo make install-chainwatch
```
for the `chainwatch` tool.
## Installing services via `make`
If your host uses the default systemd unit load path, the `lotus-daemon` and `lotus-miner` services can be installed by running:
```sh
$ sudo make install-services
```
To install the the `lotus-chainwatch` service run:
```sh
$ sudo make install-chainwatch-service
```
You can install all services together by running:
```sh
$ sudo make install-all-services
```
The `lotus-daemon` and the `lotus-miner` services can be installed individually too by running:
```sh
$ sudo make install-daemon-service
```
and
```sh
$ sudo make install-miner-service
```
### Notes
When installing the `lotus-miner` and/or `lotus-chainwatch` service the `lotus-daemon` service gets automatically installed since the other two services depend on it being installed to run.
All `install-*-service*` commands will install the latest binaries in the lotus build folders to `/usr/local/bin/`. If you do not want to use the latest build binaries please copy the `*.service` files by hand.
## Removing via `make`
All services can beremoved via `make`. To remove all services together run:
```sh
$ sudo make clean-all-services
```
Individual services can be removed by running:
```sh
$ sudo make clean-chainwatch-services
$ sudo make clean-miner-services
$ sudo make clean-daemon-services
```
### Notes
The services will be stoppend and disabled when removed.
Removing the `lotus-daemon` service will automatically remove the depending services `lotus-miner` and `lotus-chainwatch`.
## Controlling services
All service can be controlled with the `systemctl`. A few basic control commands are listed below. To get detailed infos about the capabilities of the `systemctl` command please consult your distributions man pages by running:
```sh
$ man systemctl
```
### Start/Stop services
You can start the services by running:
```sh
$ sudo systemctl start lotus-daemon
$ sudo systemctl start lotus-miner
$ sudo systemctl start lotus-chainwatch
```
and can be stopped by running:
```sh
$ sudo systemctl stop lotus-daemon
$ sudo systemctl stop lotus-miner
$ sudo systemctl stop lotus-chainwatch
```
### Enabling services on startup
To enable the services to run automatically on startup execute:
```sh
$ sudo systemctl enable lotus-daemon
$ sudo systemctl enable lotus-miner
$ sudo systemctl enable lotus-chainwatch
```
To disable the services on startup run:
```sh
$ sudo systemctl disable lotus-daemon
$ sudo systemctl disable lotus-miner
$ sudo systemctl disable lotus-chainwatch
```
### Notes
Systemd will not let services be enabled or started without their requirements. Starting the `lotus-chainwatch` and/or `lotus-miner` service with automatically start the `lotus-daemon` service (if installed!). Stopping the `lotus-daemon` service will stop the other two services. The same pattern is executed for enabling and disabling the services.
## Interacting with service logs
Logs from the services can be reviewed using `journalctl`.
### Follow logs from a specific service unit
```sh
$ sudo journalctl -u lotus-daemon -f
```
### View logs in reverse order
```sh
$ sudo journalctl -u lotus-miner -r
```
### Log files
Besides the systemd service logs all services save their own log files in `/var/log/lotus/`.
-93
View File
@@ -1,93 +0,0 @@
# Join Testnet
## Introduction
Anyone can set up a **Lotus Node** and connect to the **Lotus Testnet**. This is the best way to explore the current CLI and the **Filecoin Decentralized Storage Market**.
## Note: Using the Lotus Node from China
If you are trying to use `lotus` from China. You should set this **environment variable** on your machine:
```sh
export IPFS_GATEWAY="https://proof-parameters.s3.cn-south-1.jdcloud-oss.com/ipfs/"
```
## Get started
Start the **daemon** using the default configuration in `./build`:
```sh
lotus daemon
```
In another terminal window, check your connection with peers:
```sh
lotus net peers | wc -l
```
In order to connect to the network, you need to be connected to at least 1 peer. If youre seeing 0 peers, read our [troubleshooting notes](https://lotu.sh/en+setup-troubleshooting).
Make sure that you have a reasonable "open files limit" set on your machine, such as 10000. If you're seeing a lower value, such as 256 (default on macOS), read our [troubleshooting notes](https://lotu.sh/en+setup-troubleshooting) on how to update it prior to starting the Lotus daemon.
## Chain sync
While the daemon is running, the next requirement is to sync the chain. Run the command below to view the chain sync progress. To see current chain height, visit the [network stats page](https://stats.testnet.filecoin.io/).
```sh
lotus sync wait
```
- This step will take anywhere between a few hours to a couple of days.
- You will be able to perform **Lotus Testnet** operations after it is finished.
## Create your first address
Initialize a new wallet:
```sh
lotus wallet new
```
Sometimes your operating system may limit file name length to under 150 characters. You need to use a file system that supports long filenames.
Here is an example of the response:
```sh
t1aswwvjsae63tcrniz6x5ykvsuotlgkvlulnqpsi
```
- Visit the [faucet](http://spacerace.faucet.glif.io/) to add funds.
- Paste the address you created under REQUEST.
- Press the Request button.
## Check wallet address balance
Wallet balances in the Lotus Testnet are in **FIL**, the smallest denomination of FIL is an **attoFil**, where 1 attoFil = 10^-18 FIL.
```sh
lotus wallet balance <YOUR_NEW_ADDRESS>
```
You will not see any attoFIL in your wallet if your **chain** is not fully synced.
## Send FIL to another wallet
To send FIL to another wallet from your default account, use this command:
```
lotus send <target> <amount>
```
## Configure your node's connectivity
To effectively accept incoming storage & retrieval deals, your Lotus node needs to be accessible to other nodes on the network. To improve your connectivity, be sure to:
- [Set the multiaddresses for you miner to listen on](https://docs.filecoin.io/mine/connectivity/#setting-multiaddresses)
- [Maintain a healthy peer count](https://docs.filecoin.io/mine/connectivity/#checking-peer-count)
- [Enable port forwarding](https://docs.filecoin.io/mine/connectivity/#port-forwarding)
- [Configure your public IP address and port](https://docs.filecoin.io/mine/connectivity/#setting-a-public-ip-address)
## Monitor the dashboard
To see the latest network activity, including **chain block height**, **block height**, **blocktime**, **total network power**, largest **block producer miner**, check out the [monitoring dashboard](https://stats.testnet.filecoin.io).
-54
View File
@@ -1,54 +0,0 @@
# Setup Local Devnet
Build the Lotus Binaries in debug mode, This enables the use of 2048 byte sectors.
```sh
make 2k
```
Set the `LOTUS_SKIP_GENESIS_CHECK` environment variable to `_yes_`. This tells your
Lotus node that it's okay if the genesis being used doesn't match any baked-in
genesis.
```sh
export LOTUS_SKIP_GENESIS_CHECK=_yes_
```
Download the 2048 byte parameters:
```sh
./lotus fetch-params 2048
```
Pre-seal some sectors:
```sh
./lotus-seed pre-seal --sector-size 2KiB --num-sectors 2
```
Create the genesis block and start up the first node:
```sh
./lotus-seed genesis new localnet.json
./lotus-seed genesis add-miner localnet.json ~/.genesis-sectors/pre-seal-t01000.json
./lotus daemon --lotus-make-genesis=devgen.car --genesis-template=localnet.json --bootstrap=false
```
Then, in another console, import the genesis miner key:
```sh
./lotus wallet import --as-default ~/.genesis-sectors/pre-seal-t01000.key
```
Set up the genesis miner:
```sh
./lotus-miner init --genesis-miner --actor=t01000 --sector-size=2KiB --pre-sealed-sectors=~/.genesis-sectors --pre-sealed-metadata=~/.genesis-sectors/pre-seal-t01000.json --nosync
```
Now, finally, start up the miner:
```sh
./lotus-miner run --nosync
```
If all went well, you will have your own local Lotus Devnet running.

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