Compare commits

..
184 Commits
Author SHA1 Message Date
Łukasz Magiera 7d166fd730 Merge pull request #1908 from filecoin-project/docs/cpu-choice
add a doc on amd vs intel cpus
2020-06-05 01:21:50 +02:00
Łukasz Magiera bada564c10 Merge pull request #1912 from filecoin-project/asr/statesearch
search for a message should stop if we reach sender nonce of 0
2020-06-04 23:42:16 +02:00
vyzo 6beabb5e8a Merge pull request #1914 from filecoin-project/feat/update-pubsub
update libp2p dependencies
2020-06-04 22:50:48 +03:00
vyzo 3719938849 gomod: update go-maddr-filter 2020-06-04 22:35:33 +03:00
vyzo 6420990007 gomod: update go-libp2p-quic-transport 2020-06-04 22:34:07 +03:00
vyzo 1fab24b9bc gomod: update go-libp2p-kad-dht 2020-06-04 22:32:11 +03:00
vyzo b339750757 gomod: update go-libp2p-yamux 2020-06-04 21:34:01 +03:00
vyzo 954a20ff4c gomod: update go-libp2p to v0.9.4 2020-06-04 14:25:17 +03:00
vyzo 8057b994cf go.mod: remove unecessary (and harmful) replace directive for go-systemd 2020-06-04 14:23:59 +03:00
vyzo f210a6e91a pubsub: set Dout parameter to 3 (from the default of 2)
we are using a higher D value than the default, so this can be safely increased.
2020-06-04 00:03:31 +03:00
vyzo aded0706be gomod: update go-libp2p@v0.9.3 and go-libp2p-pubsub@v0.3.2 2020-06-04 00:02:54 +03:00
Aayush Rajasekaran 4b01b666f6 Bugfix: searchback for a message should stop if we reach sender nonce of 0 2020-06-03 14:08:05 -04:00
Łukasz Magiera 35740895ed Merge pull request #1899 from filecoin-project/feat/1827-lotus-daemon-stop-cmd
add (sub)commands which shut down lotus daemon and storage miner
2020-06-03 19:15:20 +02:00
Jeromy a8915f0331 add a doc on amd vs intel cpus 2020-06-03 08:45:53 -07:00
Łukasz Magiera 8aa158d4e6 Merge pull request #1900 from filecoin-project/feat/subcall-timing-compute-state
add subcall timing to compute state output
2020-06-02 22:23:25 +02:00
Łukasz Magiera 3595b1f8e9 compute-state html: Add timing for internal executions 2020-06-02 22:06:03 +02:00
Jeromy 9b867a221a add subcall timing to compute state output 2020-06-02 12:53:24 -07:00
laser 46a016cdce name change, for consistency 2020-06-02 12:38:21 -07:00
laser bea9cd2fe2 rename to better convey intent + document 2020-06-02 12:35:11 -07:00
laser ee9d9a52a9 first take at a "lotus-storage-miner stop" command
Fixes #1827
2020-06-02 12:31:21 -07:00
laser 1e8a875edb shutdownChan instead of shutdownCh, for consistency 2020-06-02 12:31:01 -07:00
laser 36a93c90eb use a real ellipsis, kinda 2020-06-02 12:30:45 -07:00
laser 38d714a28a first take at "daemon stop" command
Fixes #1827

TODO: plumb the daemon-stopping into lotus-storage-miner, too
2020-06-02 12:29:58 -07:00
Whyrusleeping de5d16eeac Merge pull request #1897 from filecoin-project/deps/update-json-rpc
update to fixed jsonrpc library
2020-06-02 11:36:31 -07:00
Jeromy 1968195da9 update to fixed jsonrpc library 2020-06-02 11:21:54 -07:00
laser 5e727819ad stub the "daemon stop" command 2020-06-02 08:09:18 -07:00
Łukasz Magiera 0acc2a2a63 Merge pull request #1880 from filecoin-project/asr/minerfaults
Add an API method to list all faults occuring in a certain range
2020-06-01 18:47:09 +02:00
Łukasz Magiera 596f78f9e6 Merge pull request #1885 from filecoin-project/bugs/sector-size-usage-in-scripts
update scripts and docs to use human-readable sector size
2020-06-01 18:45:20 +02:00
Łukasz Magiera 17f1eec81f Merge pull request #1886 from filecoin-project/feat/1829-repo-already-locked
error message to tell user why repo is already locked
2020-06-01 18:43:05 +02:00
Łukasz Magiera f0d44ea396 Merge pull request #1884 from filecoin-project/feat/fountain-maddr-link
fountain/miner: Add a link to docs about creating owner address
2020-06-01 18:41:57 +02:00
laser 92b8d9ad17 tell user why repo is already locked
Fixes #1829
2020-06-01 09:37:10 -07:00
laser 12ff9fd76b update scripts and docs to use human-readable sector size
Commit #fa863595 modified various CLI commands to accept a 
human-readable sector size. We should update docs and scripts, too.
2020-06-01 09:20:44 -07:00
Łukasz Magiera e26cf4e003 fountain/miner: Add a link to docs about creating owner address 2020-06-01 17:49:15 +02:00
Łukasz Magiera c3916e3a7f Merge pull request #1875 from filecoin-project/feat/check-verifreg-cmds
add commands to check individual balances for verifiers and clients
2020-06-01 13:00:08 +02:00
Aayush Rajasekaran ec0baefc48 Add an API method to list all faults occuring in a certain range 2020-05-31 00:57:52 -04:00
Jeromy 2a3d79f055 add commands to check individual balances for verifiers and clients 2020-05-29 14:47:17 -07:00
Whyrusleeping 1c01799a4b Merge pull request #1855 from filecoin-project/feat/systemd-usage
Add systemd documention to table of contents
2020-05-29 14:07:09 -07:00
WhyrusleepingandJakub Sztandera 4ef330ba8b Update scripts/lotus-daemon.service
Co-authored-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-05-29 14:02:07 -07:00
Whyrusleeping f87054bde4 Merge pull request #1858 from hayeah/fix-miner-stale-tipset
Fix block production timing to avoid mining old tipset
2020-05-29 10:41:25 -07:00
Łukasz Magiera e954cc0d67 Merge pull request #1872 from filecoin-project/asr/docs
Fix broken link
2020-05-29 18:44:00 +02:00
Aayush Rajasekaran 43c887f2b6 Fix broken link 2020-05-29 12:35:21 -04:00
Howard Yeh 1cabecedee Fix block production timing to avoid mining old tipset 2020-05-29 18:13:58 +08:00
Whyrusleeping f17c32c593 Merge pull request #1749 from filecoin-project/feat/shed-verifreg
add some commands for interacting with verified registry actor to lot…
2020-05-28 17:41:28 -07:00
Whyrusleeping b59f88324c Merge pull request #1865 from filecoin-project/fix/missing-err-check-stored-ask
Add missing err check in storage ask initialization
2020-05-28 17:17:50 -07:00
hannahhoward 0c4db8cf6b fix(storageminer): add missing err check
Missing an err check in storage miner initialization in the stored ask
2020-05-28 17:05:17 -07:00
Jeromy 9c020e54b5 Add more verifreg commands to list verifiers and clients 2020-05-28 17:01:01 -07:00
Jeromy 9811375d18 add some commands for interacting with verified registry actor to lotus-shed 2020-05-28 17:01:00 -07:00
Whyrusleeping 2632621f7e Merge pull request #1862 from filecoin-project/mod-tidy/master
Mod-tidy on master
2020-05-28 14:52:00 -07:00
Jakub Sztandera d742efe165 Mod-tidy on master
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-05-28 23:50:29 +02:00
Jakub Sztandera 492ffac913 Merge pull request #1853 from shepf/master
update struct.go github url
2020-05-28 23:50:05 +02:00
Mike Greenberg 4acf0c7ec0 install service files in suitable path; tweak lotus svc restarting 2020-05-28 16:26:07 -04:00
Whyrusleeping d0eb4278ae Merge pull request #1860 from filecoin-project/fix/recovery-exit-code-check
check neq zero exit code
2020-05-28 12:59:29 -07:00
Jeromy b807bdbea0 check neq zero exit code 2020-05-28 08:45:34 -07:00
Mike Greenberg 386b818d16 add systemd documention to table of contents 2020-05-27 19:28:31 -04:00
shepf eb325e6814 update struct.go github url 2020-05-28 07:05:53 +08:00
Whyrusleeping 70e690dac0 Merge pull request #1852 from filecoin-project/feat/systemd-usage
Update systemd usage and docs
2020-05-27 15:23:45 -07:00
Mike Greenberg 78e0c96f22 update systemd services; include make directives and docs for use 2020-05-27 17:17:18 -04:00
Whyrusleeping 75041b96e4 Merge pull request #1815 from filecoin-project/frrist/new-chainval-syscalls
update chain-val syscalls interface
2020-05-27 11:00:16 -07:00
Whyrusleeping 8544bda14a Merge pull request #1846 from filecoin-project/feat/trim-token-file
cli: Trim spaces from token file
2020-05-27 10:58:09 -07:00
Whyrusleeping 745fba7d1f Merge pull request #1841 from filecoin-project/gomod/update-pubsub
Update go-libp2p-pubsub to v0.3.1
2020-05-27 10:50:19 -07:00
Łukasz Magiera af1d54501f Merge pull request #1843 from jsign/jsign/2stackedprs
Allow remote IPFS node support & IPFS for data retrieval & automatic env wiring
2020-05-27 14:59:18 +02:00
Ignacio Hagopian 009cd92010 fix
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
2020-05-27 09:50:32 -03:00
Łukasz Magiera b74e8f5922 Merge pull request #1847 from waynewyang/Deadline-Index
fix: add deadline index for wdpost
2020-05-27 11:40:31 +02:00
waynewyang 94a7ef1c10 add deadline index for wdpost 2020-05-27 17:01:48 +08:00
Łukasz Magiera 02afd7d71f cli: Trim spaces from token file 2020-05-27 10:13:06 +02:00
Ignacio Hagopian a10690359c revert file
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
2020-05-26 15:35:20 -03:00
Ignacio Hagopian bcd84cbe9d retrieve: make fileref optional
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
2020-05-26 14:52:52 -03:00
Ignacio Hagopian 61daaa774b allow using ipfs for retrieval
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
2020-05-26 14:52:52 -03:00
Ignacio Hagopian d5346f8326 remote ipfs support & automatic env
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
2020-05-26 14:52:51 -03:00
frrist 8ce6921b18 update chain-validation
- has new syscalls
2020-05-26 10:51:19 -07:00
frrist c8f28103f3 update chain-val syscalls interface 2020-05-26 10:49:39 -07:00
vyzo 3cd3785385 gomod: update go-libp2p-pubsub@v0.3.1 2020-05-26 20:47:00 +03:00
Łukasz Magiera 9089aadbbf Merge pull request #1821 from filecoin-project/deps/update-ffi
update to latest ffi
2020-05-26 19:34:02 +02:00
Łukasz Magiera 02ca1937f8 mod tidy 2020-05-26 19:07:21 +02:00
Łukasz Magiera 6897110fe5 Merge pull request #1838 from waynewyang/wdpost-input-correct
Fix:the input sectors of wpost  must match with the minerActor
2020-05-26 15:00:58 +02:00
waynewyang b799af3e9a Fix:the input sectors of wpost must match with the minerActor 2020-05-26 17:58:20 +08:00
Łukasz Magiera 9641d93615 Merge pull request #1836 from waynewyang/Wdpost-Gaslimit
Set largest gaslimit for wdpost
2020-05-26 08:53:59 +02:00
waynewyang da6768c94d Set largest gaslimit for wdpost 2020-05-26 11:43:17 +08:00
Lucas Molas 54a8b88d6c Merge pull request #1832 from filecoin-project/fix/disable-relay
Unconditionally disable relay
2020-05-25 10:38:48 -03:00
vyzo ba6217cc2c go mod tidy 2020-05-25 13:46:53 +03:00
vyzo 314da26da3 unconditionally disable relay 2020-05-25 13:39:50 +03:00
Jeromy 646800b8a8 update to latest ffi 2020-05-22 09:26:14 -07:00
Lucas Molas 92d58ab191 Merge pull request #1820 from filecoin-project/fix/sink-message-topic-deliveries
sink message topic deliveries
2020-05-22 11:33:43 -03:00
vyzo 5531f27798 sink the FirstMessageDeliveries boost for the messages topic 2020-05-22 17:25:56 +03:00
vyzo cc98117df2 log block validation time 2020-05-22 17:21:37 +03:00
Whyrusleeping ffd340086e Merge pull request #1817 from filecoin-project/feat/sort-miners-by-deal-count
Add command to sort miners by deal counts
2020-05-21 16:06:44 -07:00
Jeromy 805686be2e Add command to sort miners by deal counts 2020-05-21 15:52:20 -07:00
Whyrusleeping 0d3f602d58 Merge pull request #1804 from filecoin-project/feat/seal-verify-shed
put a command for verifying seals into the shed
2020-05-21 10:07:19 -07:00
Whyrusleeping 83436842c5 Merge pull request #1802 from filecoin-project/feat/gfm-v0.2.7
Use latest go-fil-markets
2020-05-21 10:05:54 -07:00
Whyrusleeping bafb922ff7 Merge pull request #1811 from filecoin-project/feat/update-pubsub
Update pubsub and libp2p
2020-05-21 09:51:00 -07:00
Jeromy 6e024d9994 parse ticket and seal randomness flags 2020-05-21 08:26:32 -07:00
vyzo d597c3fb0d increase OpportunisticGraftThreshold to 5 2020-05-21 14:38:11 +03:00
vyzo 24cebb8b2f gomod: update go-libp2p@v0.9.2 2020-05-21 12:13:14 +03:00
vyzo 0cc8446a79 configure Dout=0 for bootstrappers, turn off Mesh Failure Penalty for messages as well 2020-05-21 12:11:22 +03:00
vyzo 3e383d909a gomod: update go-libp2p-pubsub@v0.3.0 2020-05-21 12:08:13 +03:00
hannahhoward 3264a9faad feat(deps): update to latest go-fil-markets
update to go-fil-markets v0.2.7 with latest changes, modify constructors as needed to accomodate new
DS Prefix customization
2020-05-20 16:01:03 -07:00
hannahhoward c52b75d59e fix(deps): update to new 0.2.7 2020-05-20 16:00:03 -07:00
shannonwells 652c257763 passing w/ new namespace wraps 2020-05-20 15:58:59 -07:00
shannonwells 060f23d748 mod tidy 2020-05-20 15:56:58 -07:00
shannonwells e68ab286f6 upgrade lotus to go-fil-markets v0.2.5 2020-05-20 15:56:58 -07:00
Jeromy 92e5cc5ad7 add args usage 2020-05-20 15:28:19 -07:00
Łukasz Magiera 6831ca9e03 Merge pull request #1808 from filecoin-project/feat/extract-jsonrpc-auth
Extract auth utils to go-jsonrpc
2020-05-20 22:22:53 +02:00
Łukasz Magiera f7325a69c1 Extract auth utils to go-jsonrpc 2020-05-20 20:37:45 +02:00
Łukasz Magiera bb78292faf Merge pull request #1807 from filecoin-project/feat/extract-jsonrpc
Extract lib/jsonrpc to go-jsonrpc
2020-05-20 19:58:50 +02:00
Łukasz Magiera eeca031525 extract lib/jsonrpc to go-jsonrpc 2020-05-20 19:49:09 +02:00
Jeromy f82e0e61e1 put a command for verifying seals into the shed 2020-05-20 10:10:35 -07:00
Łukasz Magiera 75e47db520 Merge pull request #1771 from filecoin-project/feat/post-recover
post: Handle Recovering sectors
2020-05-20 19:08:09 +02:00
Łukasz Magiera 3da1a0d8cc post: Declare recoveries for the next deadline 2020-05-20 19:07:31 +02:00
Travis Person a293bd798f init map 2020-05-20 18:41:22 +02:00
Łukasz Magiera ae60001dcc post: Handle Recovering sectors 2020-05-20 18:41:20 +02:00
Łukasz Magiera cea7c41023 Merge pull request #1761 from dtynn/fix/dtynn/use_abs_path_for_client_filestore
use abs path to construct FileManager
2020-05-20 12:49:53 +02:00
Łukasz Magiera 0fe7a0b2e3 Merge pull request #1764 from filecoin-project/feat/has-cache-bs
add blockstore 'Has' caching
2020-05-20 12:48:50 +02:00
Łukasz Magiera 267368d56c Merge pull request #1767 from filecoin-project/feat/html-trace-improvements
add tipset key and height to html trace output
2020-05-20 12:48:05 +02:00
Łukasz Magiera f5451ad455 Merge pull request #1795 from shannon-6block/master
Pset represents the proving sectors, and Sset repensents all sectors
2020-05-20 12:46:22 +02:00
Łukasz Magiera 555537c528 Merge pull request #1791 from filecoin-project/feat/api-gen-2
improving the autogenerated api docs
2020-05-20 12:40:49 +02:00
shannon cc5875c6bd Pset represents the proving sectors, and Sset repensents all sectors 2020-05-20 12:12:13 +08:00
Jeromy c54d050cb7 slightly better methodgroup parsing, and more group docs 2020-05-19 09:49:47 -07:00
Jeromy cb88237305 improving the autogenerated api docs 2020-05-19 08:48:58 -07:00
Whyrusleeping cc44167f2c Merge pull request #1564 from filecoin-project/feat/api-doc-gen
working on api documentation autogeneration
2020-05-18 19:24:31 -07:00
Whyrusleeping 78c656e4c1 Merge pull request #1788 from filecoin-project/asr/issuetemplate
Create an issue template for mining issues
2020-05-18 17:27:55 -07:00
Jeromy 607bcc401c very basic api documentation autogeneration 2020-05-18 17:20:54 -07:00
Whyrusleeping 7fd481ea6f Merge pull request #1766 from filecoin-project/feat/chain-get-decoders
Feat/chain get decoders
2020-05-18 16:06:41 -07:00
Whyrusleeping 9c91daf19a Merge pull request #1787 from filecoin-project/fix/dont-mine-below-min
having power below the minimum means not having power
2020-05-18 16:05:06 -07:00
Aayush Rajasekaran c1ef9d1731 Create an issue template for mining issues 2020-05-18 18:31:53 -04:00
Jeromy eaf69b160c having power below the minimum means not having power 2020-05-18 14:51:42 -07:00
Łukasz Magiera 269286e587 Merge pull request #1785 from filecoin-project/frrist/chain-val-update
update chain-validation
2020-05-18 23:49:11 +02:00
Łukasz Magiera f99c5416ce Merge pull request #1786 from filecoin-project/asr/clideal
Prevent client list-deals from crashing
2020-05-18 23:48:33 +02:00
Aayush Rajasekaran 8f6c3c2cc7 prevent client list-deals from crashing 2020-05-18 17:44:00 -04:00
frrist a2e25897e9 go mod tidy 2020-05-18 14:35:47 -07:00
frrist fc9e046ca7 update chain-validation 2020-05-18 12:50:36 -07:00
Travis Person 5730d46179 Merge pull request #1728 from filecoin-project/frrist/vm-transfer-exitcodes
return correct exit codes from vm transfer
2020-05-18 19:28:48 +00:00
frrist 9165f14693 feedback: fatal errors in vm.transfer 2020-05-18 12:00:52 -07:00
frrist 9305eca97c don't ignore transfer error in delete actor 2020-05-18 12:00:52 -07:00
frrist 0c2e4e2d4f return correct exitcodes from vm transfer 2020-05-18 12:00:52 -07:00
Łukasz Magiera 59725b3af3 Merge pull request #1784 from filecoin-project/feat/update-serialization-vectors
update serialization vectors
2020-05-18 20:34:44 +02:00
Whyrusleeping a0ab3483ed Merge pull request #1770 from filecoin-project/asr/test
Add an interop test for block header serialisation
2020-05-18 11:28:06 -07:00
Jeromy e6f3b79b23 update serialization vectors 2020-05-18 11:02:34 -07:00
Whyrusleeping 1ed6183344 Merge pull request #1782 from filecoin-project/fix/no-metrics-drand
Change drand to one without metrics
2020-05-18 10:24:15 -07:00
Jakub Sztandera 29b1ac964b mod tidy
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-05-18 19:09:19 +02:00
Jakub Sztandera 8d637c515a Change drand to one without metrics
Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
2020-05-18 19:00:30 +02:00
Aayush Rajasekaran 5220a61fc5 Add an interop test for block header serialisation 2020-05-16 17:27:04 -04:00
Jeromy d9502693f0 add tipset key and height to html trace output 2020-05-16 12:53:51 -07:00
Whyrusleeping 9a0d0511c7 Merge pull request #1765 from filecoin-project/fix/miner-info-div-zero
avoid dividing by zero if pset is empty
2020-05-16 12:48:53 -07:00
Jeromy 72eb32ae3c add additional chain get decoders 2020-05-16 12:41:04 -07:00
Jeromy cf9e576028 avoid dividing by zero if pset is empty 2020-05-16 11:35:24 -07:00
Jeromy 9804bd3c4c add blockstore 'Has' caching 2020-05-16 11:31:14 -07:00
dtynn 30e2642f4d use abs path to construct FileManager 2020-05-16 09:36:38 +08:00
Whyrusleeping 54b3eafd6d Merge pull request #1758 from filecoin-project/fix/lotus-bench-secp
register secp signatures for lotus bench usage
2020-05-15 13:05:27 -07:00
Jeromy f6589407c1 register secp signatures for lotus bench usage 2020-05-15 13:01:45 -07:00
Łukasz Magiera 5c73602d24 Merge pull request #1757 from filecoin-project/feat/faster-mpool-add
cache signatures, and dont check them in a lock
2020-05-15 20:27:31 +02:00
Whyrusleeping 70657e35ba Merge pull request #1708 from filecoin-project/feat/block-validator
Improved pubsub block validator
2020-05-15 11:26:26 -07:00
Łukasz Magiera 908053015b Merge pull request #1753 from filecoin-project/feat/less-res-comm2
Update sector-storage with relaxed commit2 resource numbers
2020-05-15 20:17:41 +02:00
Whyrusleeping 8bae22ef61 Merge pull request #1746 from filecoin-project/bug/circleci-mac
install jq in /usr/local/bin
2020-05-15 11:17:12 -07:00
Łukasz Magiera 1012a64110 Merge pull request #1756 from filecoin-project/feat/ffi-88555ec98
Update FFI
2020-05-15 20:16:27 +02:00
Łukasz Magiera 9eb2399539 Merge pull request #1754 from filecoin-project/fix/initial-pledge
state: Calculate slightly larger initial pledge
2020-05-15 20:15:36 +02:00
Łukasz Magiera d539be4875 Merge pull request #1741 from filecoin-project/fix/json-out-chan-closing
properly select channel ID to close for handleOutChans
2020-05-15 20:10:10 +02:00
Jeromy 13fc3f2f54 remove stress test (not really a real test) 2020-05-15 11:00:13 -07:00
Jeromy 33ec4d332b cache signatures, and dont check them in a lock 2020-05-15 10:56:38 -07:00
Whyrusleeping d6bb0500df Merge pull request #1752 from filecoin-project/fix/mpool-slowness
miner: Optimize SelectMessages
2020-05-15 10:48:06 -07:00
Łukasz Magiera b90666b293 miner: optimize SelectMessages 2020-05-15 19:30:31 +02:00
Łukasz Magiera 173a72f374 Merge pull request #1755 from filecoin-project/fix/cw-tkt-crash
chainwatch: Don't crash with nil ticket
2020-05-15 19:17:45 +02:00
Łukasz Magiera 0ae0e89ddd Update FFI 2020-05-15 19:16:06 +02:00
Łukasz Magiera c4d01c04a4 chainwatch: don't crash with nil ticket 2020-05-15 19:06:52 +02:00
Łukasz Magiera 0b56ca3c21 Merge pull request #1751 from filecoin-project/fix/turnoff-block-mesh-penalty
Turn off Mesh Delivery Failure penalty
2020-05-15 19:02:46 +02:00
Łukasz Magiera 022348f426 state: Calculate slightly larger initial pledge 2020-05-15 16:53:35 +02:00
Łukasz Magiera ad108d6051 Update sector-storage with relaxed commit2 resource numbers 2020-05-15 16:00:14 +02:00
vyzo dbe2a1e2cd turn off Mesh Delivery Failure penalty 2020-05-15 12:50:20 +03:00
Aayush Rajasekaran f86a2ced06 Merge pull request #1747 from filecoin-project/asr/docs
Fix 3 typos
2020-05-14 18:34:59 -04:00
Aayush Rajasekaran f7a45f6c12 Fix 3 typos 2020-05-14 18:27:47 -04:00
ognots 658f78d75f install jq in /usr/local/bin
/home/ognots/.bin not in path, causing macos test to fail
2020-05-14 17:32:05 -04:00
Aayush Rajasekaran e9eaee6ef8 Merge pull request #1745 from filecoin-project/asr/docs
Docs finishing touches
2020-05-14 17:08:33 -04:00
Aayush Rajasekaran 505e30c7b2 Docs finishing touches 2020-05-14 16:27:20 -04:00
Jeromy edae6a3ae8 fix the problem 2020-05-14 12:05:01 -07:00
Jeromy 987571dd8c add test to demonstrate failure 2020-05-14 11:48:58 -07:00
vyzo 81bc159b3e cache successful block signature validation 2020-05-14 20:48:40 +03:00
vyzo 50e05ae1ba use sigs.CheckBlockSignature 2020-05-14 20:48:40 +03:00
vyzo 432c69c616 update message validator to provide rejection signal 2020-05-14 20:48:40 +03:00
vyzo e714294273 initialize map 2020-05-14 20:48:19 +03:00
vyzo c3a144bcb6 flesh out signature validation logic 2020-05-14 20:48:19 +03:00
vyzo dd86a289ce cache miner worker keys 2020-05-14 20:48:19 +03:00
vyzo 6e0dac06f4 utility to retrieve miner worker key from the chain 2020-05-14 20:48:19 +03:00
vyzo 5bd0e92068 remove source flagging from bogus CID blocks
it's ineffective.
2020-05-14 20:48:19 +03:00
vyzo b032d3524b configure behaviour penalty for gsv1.1 2020-05-14 20:48:19 +03:00
vyzo 6933df918a gomod: update go-libp2p-pubsub@master 2020-05-14 20:45:25 +03:00
108 changed files with 2637 additions and 2729 deletions
+2 -3
View File
@@ -225,9 +225,8 @@ jobs:
- run:
name: Install jq
command: |
mkdir $HOME/.bin
curl --location https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64 --output $HOME/.bin/jq
chmod +x $HOME/.bin/jq
curl --location https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64 --output /usr/local/bin/jq
chmod +x /usr/local/bin/jq
- restore_cache:
name: restore go mod and cargo cache
key: v3-go-deps-{{ arch }}-{{ checksum "~/go/src/github.com/filecoin-project/lotus/go.sum" }}
+34
View File
@@ -0,0 +1,34 @@
---
name: Sealing Issues
about: Create a report for help with sealing (commit) failures.
title: ''
labels: 'sealing'
assignees: ''
---
Please provide all the information requested here to help us troubleshoot "commit failed" issues.
If the information requested is missing, we will probably have to just ask you to provide it anyway,
before we can help debug.
**Describe the problem**
A brief description of the problem you encountered while proving (sealing) a sector.
Including what commands you ran, and a description of your setup, is very helpful.
**Sectors list**
The output of `./lotus-storage-miner sectors list`.
**Sectors status**
The output of `./lotus-storage-miner sectors status --log <sectorId>` for the failed sector(s).
**Lotus storage miner logs**
Please go through the logs of your storage miner, and include screenshots of any error-like messages you find.
**Version**
The output of `./lotus --version`.
+13
View File
@@ -103,6 +103,19 @@ install:
install -C ./lotus-storage-miner /usr/local/bin/lotus-storage-miner
install -C ./lotus-seal-worker /usr/local/bin/lotus-seal-worker
install-services: install
mkdir -p /usr/local/lib/systemd/system
install -C -m 0644 ./scripts/lotus-daemon.service /usr/local/lib/systemd/system/lotus-daemon.service
install -C -m 0644 ./scripts/lotus-miner.service /usr/local/lib/systemd/system/lotus-miner.service
systemctl daemon-reload
@echo
@echo "lotus and lotus-miner services installed. Don't forget to 'systemctl enable lotus|lotus-miner' for it to be enabled on startup."
clean-services:
rm -f /usr/local/lib/systemd/system/lotus-daemon.service
rm -f /usr/local/lib/systemd/system/lotus-miner.service
systemctl daemon-reload
# TOOLS
lotus-seed: $(BUILD_DEPS)
+9 -5
View File
@@ -4,17 +4,18 @@ import (
"context"
"fmt"
"github.com/filecoin-project/lotus/build"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
)
type Permission = string
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/build"
)
type Common interface {
// Auth
AuthVerify(ctx context.Context, token string) ([]Permission, error)
AuthNew(ctx context.Context, perms []Permission) ([]byte, error)
AuthVerify(ctx context.Context, token string) ([]auth.Permission, error)
AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error)
// network
@@ -33,6 +34,9 @@ type Common interface {
LogList(context.Context) ([]string, error)
LogSetLevel(context.Context, string, string) error
// trigger graceful shutdown
Shutdown(context.Context) error
}
// Version provides various build-time information
+45 -9
View File
@@ -28,13 +28,18 @@ type FullNode interface {
// TODO: TipSetKeys
// chain
// MethodGroup: Chain
// The Chain method group contains methods for interacting with the
// blockchain, but that do not require any form of state computation
// ChainNotify returns channel with chain head updates
// First message is guaranteed to be of len == 1, and type == 'current'
ChainNotify(context.Context) (<-chan []*HeadChange, error)
// ChainHead returns the current head of the chain
ChainHead(context.Context) (*types.TipSet, error)
// ChainGetRandomness is used to sample the chain for randomness
ChainGetRandomness(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error)
// ChainGetBlock returns the block specified by the given CID
ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error)
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
@@ -52,14 +57,23 @@ type FullNode interface {
ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*HeadChange, error)
ChainExport(context.Context, types.TipSetKey) (<-chan []byte, error)
// syncer
// MethodGroup: Sync
// The Sync method group contains methods for interacting with and
// observing the lotus sync service
// SyncState returns the current status of the lotus sync system
SyncState(context.Context) (*SyncState, error)
// SyncSubmitBlock can be used to submit a newly created block to the
// network through this node
SyncSubmitBlock(ctx context.Context, blk *types.BlockMsg) error
SyncIncomingBlocks(ctx context.Context) (<-chan *types.BlockHeader, error)
SyncMarkBad(ctx context.Context, bcid cid.Cid) error
SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error)
// messages
// MethodGroup: Mpool
// The Mpool methods are for interacting with the message pool. The message pool
// manages all incoming and outgoing 'messages' going over the network.
MpoolPending(context.Context, types.TipSetKey) ([]*types.SignedMessage, error)
MpoolPush(context.Context, *types.SignedMessage) (cid.Cid, error)
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error) // get nonce, sign, push
@@ -67,16 +81,14 @@ type FullNode interface {
MpoolSub(context.Context) (<-chan MpoolUpdate, error)
MpoolEstimateGasPrice(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error)
// FullNodeStruct
// miner
// MethodGroup: Miner
MinerGetBaseInfo(context.Context, address.Address, abi.ChainEpoch, types.TipSetKey) (*MiningBaseInfo, error)
MinerCreateBlock(context.Context, *BlockTemplate) (*types.BlockMsg, error)
// // UX ?
// wallet
// MethodGroup: Wallet
WalletNew(context.Context, crypto.SigType) (address.Address, error)
WalletHas(context.Context, address.Address) (bool, error)
@@ -92,14 +104,20 @@ type FullNode interface {
// Other
// MethodGroup: Client
// The Client methods all have to do with interacting with the storage and
// retrieval markets as a client
// ClientImport imports file under the specified path into filestore
ClientImport(ctx context.Context, ref FileRef) (cid.Cid, error)
// ClientStartDeal proposes a deal with a miner
ClientStartDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error)
// ClientGetDeal info returns the latest information about a given deal
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error)
ClientListDeals(ctx context.Context) ([]DealInfo, error)
ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error)
ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error)
ClientRetrieve(ctx context.Context, order RetrievalOrder, ref FileRef) error
ClientRetrieve(ctx context.Context, order RetrievalOrder, ref *FileRef) error
ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error)
ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error)
ClientGenCar(ctx context.Context, ref FileRef, outpath string) error
@@ -112,6 +130,9 @@ type FullNode interface {
//ClientListAsks() []Ask
// MethodGroup: State
// The State methods are used to query, inspect, and interact with chain state
// if tipset is nil, we'll use heaviest
StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error)
StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error)
@@ -126,7 +147,10 @@ type FullNode interface {
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*MinerPower, error)
StateMinerInfo(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error)
StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error)
StateMinerFaults(context.Context, address.Address, types.TipSetKey) ([]abi.SectorNumber, error)
StateMinerFaults(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error)
// Returns all non-expired Faults that occur within lookback epochs of the given tipset
StateAllMinerFaults(ctx context.Context, lookback abi.ChainEpoch, ts types.TipSetKey) ([]*Fault, error)
StateMinerRecoveries(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error)
StateMinerInitialPledgeCollateral(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (types.BigInt, error)
StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error)
@@ -146,6 +170,10 @@ type FullNode interface {
StateMinerSectorCount(context.Context, address.Address, types.TipSetKey) (MinerSectors, error)
StateCompute(context.Context, abi.ChainEpoch, []*types.Message, types.TipSetKey) (*ComputeStateOutput, error)
// MethodGroup: Msig
// The Msig methods are used to interact with multisig wallets on the
// filecoin network
MsigGetAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
MsigCreate(context.Context, int64, []address.Address, types.BigInt, address.Address, types.BigInt) (cid.Cid, error)
MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error)
@@ -155,6 +183,9 @@ type FullNode interface {
MarketEnsureAvailable(context.Context, address.Address, address.Address, types.BigInt) (cid.Cid, error)
// MarketFreeBalance
// MethodGroup: Paych
// The Paych methods are for interacting with and managing payment channels
PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error)
PaychList(context.Context) ([]address.Address, error)
PaychStatus(context.Context, address.Address) (*PaychStatus, error)
@@ -424,3 +455,8 @@ const (
MsigApprove MsigProposeResponse = iota
MsigCancel
)
type Fault struct {
Miner address.Address
Epoch abi.ChainEpoch
}
+12 -85
View File
@@ -1,111 +1,38 @@
package apistruct
import (
"context"
"reflect"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api"
)
type permKey int
var permCtxKey permKey
const (
// When changing these, update docs/API.md too
PermRead api.Permission = "read" // default
PermWrite api.Permission = "write"
PermSign api.Permission = "sign" // Use wallet keys for signing
PermAdmin api.Permission = "admin" // Manage permissions
PermRead auth.Permission = "read" // default
PermWrite auth.Permission = "write"
PermSign auth.Permission = "sign" // Use wallet keys for signing
PermAdmin auth.Permission = "admin" // Manage permissions
)
var AllPermissions = []api.Permission{PermRead, PermWrite, PermSign, PermAdmin}
var defaultPerms = []api.Permission{PermRead}
func WithPerm(ctx context.Context, perms []api.Permission) context.Context {
return context.WithValue(ctx, permCtxKey, perms)
}
var AllPermissions = []auth.Permission{PermRead, PermWrite, PermSign, PermAdmin}
var DefaultPerms = []auth.Permission{PermRead}
func PermissionedStorMinerAPI(a api.StorageMiner) api.StorageMiner {
var out StorageMinerStruct
permissionedAny(a, &out.Internal)
permissionedAny(a, &out.CommonStruct.Internal)
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.CommonStruct.Internal)
return &out
}
func PermissionedFullAPI(a api.FullNode) api.FullNode {
var out FullNodeStruct
permissionedAny(a, &out.Internal)
permissionedAny(a, &out.CommonStruct.Internal)
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.CommonStruct.Internal)
return &out
}
func PermissionedWorkerAPI(a api.WorkerApi) api.WorkerApi {
var out WorkerStruct
permissionedAny(a, &out.Internal)
auth.PermissionedProxy(AllPermissions, DefaultPerms, a, &out.Internal)
return &out
}
func HasPerm(ctx context.Context, perm api.Permission) bool {
callerPerms, ok := ctx.Value(permCtxKey).([]api.Permission)
if !ok {
callerPerms = defaultPerms
}
for _, callerPerm := range callerPerms {
if callerPerm == perm {
return true
}
}
return false
}
func permissionedAny(in interface{}, out interface{}) {
rint := reflect.ValueOf(out).Elem()
ra := reflect.ValueOf(in)
for f := 0; f < rint.NumField(); f++ {
field := rint.Type().Field(f)
requiredPerm := api.Permission(field.Tag.Get("perm"))
if requiredPerm == "" {
panic("missing 'perm' tag on " + field.Name) // ok
}
// Validate perm tag
ok := false
for _, perm := range AllPermissions {
if requiredPerm == perm {
ok = true
break
}
}
if !ok {
panic("unknown 'perm' tag on " + field.Name) // ok
}
fn := ra.MethodByName(field.Name)
rint.Field(f).Set(reflect.MakeFunc(field.Type, func(args []reflect.Value) (results []reflect.Value) {
ctx := args[0].Interface().(context.Context)
if HasPerm(ctx, requiredPerm) {
return fn.Call(args)
}
err := xerrors.Errorf("missing permission to invoke '%s' (need '%s')", field.Name, requiredPerm)
rerr := reflect.ValueOf(&err).Elem()
if field.Type.NumOut() == 2 {
return []reflect.Value{
reflect.Zero(field.Type.Out(0)),
rerr,
}
} else {
return []reflect.Value{rerr}
}
}))
}
}
+26 -8
View File
@@ -9,6 +9,8 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/sector-storage/sealtasks"
"github.com/filecoin-project/sector-storage/stores"
"github.com/filecoin-project/sector-storage/storiface"
@@ -30,8 +32,8 @@ var _ = AllPermissions
type CommonStruct struct {
Internal struct {
AuthVerify func(ctx context.Context, token string) ([]api.Permission, error) `perm:"read"`
AuthNew func(ctx context.Context, perms []api.Permission) ([]byte, error) `perm:"admin"`
AuthVerify func(ctx context.Context, token string) ([]auth.Permission, error) `perm:"read"`
AuthNew func(ctx context.Context, perms []auth.Permission) ([]byte, error) `perm:"admin"`
NetConnectedness func(context.Context, peer.ID) (network.Connectedness, error) `perm:"read"`
NetPeers func(context.Context) ([]peer.AddrInfo, error) `perm:"read"`
@@ -45,6 +47,8 @@ type CommonStruct struct {
LogList func(context.Context) ([]string, error) `perm:"write"`
LogSetLevel func(context.Context, string, string) error `perm:"write"`
Shutdown func(context.Context) error `perm:"admin"`
}
}
@@ -108,7 +112,7 @@ type FullNodeStruct struct {
ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref api.FileRef) error `perm:"admin"`
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error `perm:"admin"`
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
ClientCalcCommP func(ctx context.Context, inpath string, miner address.Address) (*api.CommPRet, error) `perm:"read"`
ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"`
@@ -120,7 +124,9 @@ type FullNodeStruct struct {
StateMinerPower func(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error) `perm:"read"`
StateMinerInfo func(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error) `perm:"read"`
StateMinerDeadlines func(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error) `perm:"read"`
StateMinerFaults func(context.Context, address.Address, types.TipSetKey) ([]abi.SectorNumber, error) `perm:"read"`
StateMinerFaults func(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error) `perm:"read"`
StateAllMinerFaults func(context.Context, abi.ChainEpoch, types.TipSetKey) ([]*api.Fault, error) `perm:"read"`
StateMinerRecoveries func(context.Context, address.Address, types.TipSetKey) (*abi.BitField, error) `perm:"read"`
StateMinerInitialPledgeCollateral func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (types.BigInt, error) `perm:"read"`
StateMinerAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
StateSectorPreCommitInfo func(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error) `perm:"read"`
@@ -239,11 +245,11 @@ type WorkerStruct struct {
// CommonStruct
func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]api.Permission, error) {
func (c *CommonStruct) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) {
return c.Internal.AuthVerify(ctx, token)
}
func (c *CommonStruct) AuthNew(ctx context.Context, perms []api.Permission) ([]byte, error) {
func (c *CommonStruct) AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error) {
return c.Internal.AuthNew(ctx, perms)
}
@@ -289,6 +295,10 @@ func (c *CommonStruct) LogSetLevel(ctx context.Context, group, level string) err
return c.Internal.LogSetLevel(ctx, group, level)
}
func (c *CommonStruct) Shutdown(ctx context.Context) error {
return c.Internal.Shutdown(ctx)
}
// FullNodeStruct
func (c *FullNodeStruct) ClientListImports(ctx context.Context) ([]api.Import, error) {
@@ -318,7 +328,7 @@ func (c *FullNodeStruct) ClientListDeals(ctx context.Context) ([]api.DealInfo, e
return c.Internal.ClientListDeals(ctx)
}
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref api.FileRef) error {
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error {
return c.Internal.ClientRetrieve(ctx, order, ref)
}
@@ -533,10 +543,18 @@ func (c *FullNodeStruct) StateMinerDeadlines(ctx context.Context, m address.Addr
return c.Internal.StateMinerDeadlines(ctx, m, tsk)
}
func (c *FullNodeStruct) StateMinerFaults(ctx context.Context, actor address.Address, tsk types.TipSetKey) ([]abi.SectorNumber, error) {
func (c *FullNodeStruct) StateMinerFaults(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*abi.BitField, error) {
return c.Internal.StateMinerFaults(ctx, actor, tsk)
}
func (c *FullNodeStruct) StateAllMinerFaults(ctx context.Context, cutoff abi.ChainEpoch, endTsk types.TipSetKey) ([]*api.Fault, error) {
return c.Internal.StateAllMinerFaults(ctx, cutoff, endTsk)
}
func (c *FullNodeStruct) StateMinerRecoveries(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*abi.BitField, error) {
return c.Internal.StateMinerRecoveries(ctx, actor, tsk)
}
func (c *FullNodeStruct) StateMinerInitialPledgeCollateral(ctx context.Context, maddr address.Address, snum abi.SectorNumber, tsk types.TipSetKey) (types.BigInt, error) {
return c.Internal.StateMinerInitialPledgeCollateral(ctx, maddr, snum, tsk)
}
+3 -2
View File
@@ -1,11 +1,12 @@
package client
import (
"github.com/filecoin-project/lotus/api/apistruct"
"net/http"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"github.com/filecoin-project/lotus/api/apistruct"
)
// NewCommonRPC creates a new http jsonrpc client.
+338
View File
@@ -0,0 +1,338 @@
package main
import (
"encoding/json"
"fmt"
"go/ast"
"go/parser"
"go/token"
"reflect"
"sort"
"strings"
"time"
"unicode"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-filestore"
"github.com/libp2p/go-libp2p-core/network"
peer "github.com/libp2p/go-libp2p-peer"
"github.com/multiformats/go-multiaddr"
)
var ExampleValues = map[reflect.Type]interface{}{
reflect.TypeOf(auth.Permission("")): auth.Permission("write"),
reflect.TypeOf(""): "string value",
reflect.TypeOf(uint64(42)): uint64(42),
reflect.TypeOf(byte(7)): byte(7),
reflect.TypeOf([]byte{}): []byte("byte array"),
}
func addExample(v interface{}) {
ExampleValues[reflect.TypeOf(v)] = v
}
func init() {
c, err := cid.Decode("bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4")
if err != nil {
panic(err)
}
ExampleValues[reflect.TypeOf(c)] = c
c2, err := cid.Decode("bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve")
if err != nil {
panic(err)
}
tsk := types.NewTipSetKey(c, c2)
ExampleValues[reflect.TypeOf(tsk)] = tsk
addr, err := address.NewIDAddress(1234)
if err != nil {
panic(err)
}
ExampleValues[reflect.TypeOf(addr)] = addr
pid, err := peer.IDB58Decode("12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf")
if err != nil {
panic(err)
}
addExample(pid)
addExample(bitfield.NewFromSet([]uint64{5}))
addExample(abi.RegisteredProof_StackedDRG32GiBPoSt)
addExample(abi.ChainEpoch(10101))
addExample(crypto.SigTypeBLS)
addExample(int64(9))
addExample(abi.MethodNum(1))
addExample(exitcode.ExitCode(0))
addExample(crypto.DomainSeparationTag_ElectionProofProduction)
addExample(true)
addExample(abi.UnpaddedPieceSize(1024))
addExample(abi.UnpaddedPieceSize(1024).Padded())
addExample(abi.DealID(5432))
addExample(filestore.StatusFileChanged)
addExample(abi.SectorNumber(9))
addExample(abi.SectorSize(32 * 1024 * 1024 * 1024))
addExample(api.MpoolChange(0))
addExample(network.Connected)
addExample(dtypes.NetworkName("lotus"))
addExample(api.SyncStateStage(1))
addExample(build.APIVersion)
addExample(api.PCHInbound)
addExample(time.Minute)
addExample(&types.ExecutionResult{
Msg: exampleValue(reflect.TypeOf(&types.Message{})).(*types.Message),
MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{})).(*types.MessageReceipt),
})
addExample(map[string]types.Actor{
"t01236": exampleValue(reflect.TypeOf(types.Actor{})).(types.Actor),
})
addExample(map[string]api.MarketDeal{
"t026363": exampleValue(reflect.TypeOf(api.MarketDeal{})).(api.MarketDeal),
})
addExample(map[string]api.MarketBalance{
"t026363": exampleValue(reflect.TypeOf(api.MarketBalance{})).(api.MarketBalance),
})
maddr, err := multiaddr.NewMultiaddr("/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior")
if err != nil {
panic(err)
}
// because reflect.TypeOf(maddr) returns the concrete type...
ExampleValues[reflect.TypeOf(struct{ A multiaddr.Multiaddr }{}).Field(0).Type] = maddr
}
func exampleValue(t reflect.Type) interface{} {
v, ok := ExampleValues[t]
if ok {
return v
}
switch t.Kind() {
case reflect.Slice:
out := reflect.New(t).Elem()
reflect.Append(out, reflect.ValueOf(exampleValue(t.Elem())))
return out.Interface()
case reflect.Chan:
return exampleValue(t.Elem())
case reflect.Struct:
es := exampleStruct(t)
v := reflect.ValueOf(es).Elem().Interface()
ExampleValues[t] = v
return v
case reflect.Array:
out := reflect.New(t).Elem()
for i := 0; i < t.Len(); i++ {
out.Index(i).Set(reflect.ValueOf(exampleValue(t.Elem())))
}
return out.Interface()
case reflect.Ptr:
if t.Elem().Kind() == reflect.Struct {
es := exampleStruct(t.Elem())
//ExampleValues[t] = es
return es
}
case reflect.Interface:
return struct{}{}
}
panic(fmt.Sprintf("No example value for type: %s", t))
}
func exampleStruct(t reflect.Type) interface{} {
ns := reflect.New(t)
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if strings.Title(f.Name) == f.Name {
ns.Elem().Field(i).Set(reflect.ValueOf(exampleValue(f.Type)))
}
}
return ns.Interface()
}
type Visitor struct {
Methods map[string]ast.Node
}
func (v *Visitor) Visit(node ast.Node) ast.Visitor {
st, ok := node.(*ast.TypeSpec)
if !ok {
return v
}
if st.Name.Name != "FullNode" {
return nil
}
iface := st.Type.(*ast.InterfaceType)
for _, m := range iface.Methods.List {
if len(m.Names) > 0 {
v.Methods[m.Names[0].Name] = m
}
}
return v
}
const noComment = "There are not yet any comments for this method."
func parseApiASTInfo() (map[string]string, map[string]string) {
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, "./api", nil, parser.AllErrors|parser.ParseComments)
if err != nil {
fmt.Println("parse error: ", err)
}
ap := pkgs["api"]
f := ap.Files["api/api_full.go"]
cmap := ast.NewCommentMap(fset, f, f.Comments)
v := &Visitor{make(map[string]ast.Node)}
ast.Walk(v, pkgs["api"])
groupDocs := make(map[string]string)
out := make(map[string]string)
for mn, node := range v.Methods {
cs := cmap.Filter(node).Comments()
if len(cs) == 0 {
out[mn] = noComment
} else {
for _, c := range cs {
if strings.HasPrefix(c.Text(), "MethodGroup:") {
parts := strings.Split(c.Text(), "\n")
groupName := strings.TrimSpace(parts[0][12:])
comment := strings.Join(parts[1:], "\n")
groupDocs[groupName] = comment
break
}
}
last := cs[len(cs)-1].Text()
if !strings.HasPrefix(last, "MethodGroup:") {
out[mn] = last
} else {
out[mn] = noComment
}
}
}
return out, groupDocs
}
type MethodGroup struct {
GroupName string
Header string
Methods []*Method
}
type Method struct {
Comment string
Name string
InputExample string
ResponseExample string
}
func methodGroupFromName(mn string) string {
i := strings.IndexFunc(mn[1:], func(r rune) bool {
return unicode.IsUpper(r)
})
if i < 0 {
return ""
}
return mn[:i+1]
}
func main() {
comments, groupComments := parseApiASTInfo()
groups := make(map[string]*MethodGroup)
var api struct{ api.FullNode }
t := reflect.TypeOf(api)
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
groupName := methodGroupFromName(m.Name)
g, ok := groups[groupName]
if !ok {
g = new(MethodGroup)
g.Header = groupComments[groupName]
g.GroupName = groupName
groups[groupName] = g
}
var args []interface{}
ft := m.Func.Type()
for j := 2; j < ft.NumIn(); j++ {
inp := ft.In(j)
args = append(args, exampleValue(inp))
}
v, err := json.Marshal(args)
if err != nil {
panic(err)
}
outv := exampleValue(ft.Out(0))
ov, err := json.Marshal(outv)
if err != nil {
panic(err)
}
g.Methods = append(g.Methods, &Method{
Name: m.Name,
Comment: comments[m.Name],
InputExample: string(v),
ResponseExample: string(ov),
})
}
var groupslice []*MethodGroup
for _, g := range groups {
groupslice = append(groupslice, g)
}
sort.Slice(groupslice, func(i, j int) bool {
return groupslice[i].GroupName < groupslice[j].GroupName
})
for _, g := range groupslice {
fmt.Printf("## %s\n", g.GroupName)
fmt.Printf("%s\n\n", g.Header)
sort.Slice(g.Methods, func(i, j int) bool {
return g.Methods[i].Name < g.Methods[j].Name
})
for _, m := range g.Methods {
fmt.Printf("### %s\n", m.Name)
fmt.Printf("%s\n\n", m.Comment)
fmt.Printf("Inputs: `%s`\n\n", m.InputExample)
fmt.Printf("Response: `%s`\n\n", m.ResponseExample)
}
}
}
+1 -1
View File
@@ -200,7 +200,7 @@ func testRetrieval(t *testing.T, ctx context.Context, err error, client *impl.Fu
t.Fatal(err)
}
ref := api.FileRef{
ref := &api.FileRef{
Path: filepath.Join(rpath, "ret"),
IsCAR: carExport,
}
+4
View File
@@ -109,6 +109,10 @@ const BadBlockCacheSize = 1 << 15
// 10 block reorg.
const BlsSignatureCacheSize = 40000
// Size of signature verification cache
// 32k keeps the cache around 10MB in size, max
const VerifSigCacheSize = 32000
// ///////
// Limits
+1 -1
View File
@@ -166,7 +166,7 @@ func (e *Events) listenHeadChangesOnce(ctx context.Context) error {
}
// sync with fake chainstore (for tests)
if fcs, ok := e.api.(interface{notifDone()}); ok {
if fcs, ok := e.api.(interface{ notifDone() }); ok {
fcs.notifDone()
}
}
+1 -1
View File
@@ -671,7 +671,7 @@ func TestCalled(t *testing.T) {
0: n2msg,
})
require.Equal(t, true, applied) // msg from H=7, which had reverted execution
require.Equal(t, true, applied) // msg from H=7, which had reverted execution
require.Equal(t, false, reverted)
require.Equal(t, abi.ChainEpoch(10), appliedH)
applied = false
+49 -7
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"errors"
"fmt"
"math"
"sort"
"sync"
@@ -87,6 +88,8 @@ type MessagePool struct {
localMsgs datastore.Datastore
netName dtypes.NetworkName
sigValCache *lru.TwoQueueCache
}
type msgSet struct {
@@ -180,6 +183,8 @@ func (mpp *mpoolProvider) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error)
func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*MessagePool, error) {
cache, _ := lru.New2Q(build.BlsSignatureCacheSize)
verifcache, _ := lru.New2Q(build.VerifSigCacheSize)
mp := &MessagePool{
closer: make(chan struct{}),
repubTk: time.NewTicker(build.BlockDelay * 10 * time.Second),
@@ -188,6 +193,7 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
minGasPrice: types.NewInt(0),
maxTxPoolSize: 5000,
blsSigCache: cache,
sigValCache: verifcache,
changes: lps.New(50),
localMsgs: namespace.Wrap(ds, datastore.NewKey(localMsgsDs)),
api: api,
@@ -313,12 +319,6 @@ func (mp *MessagePool) Push(m *types.SignedMessage) (cid.Cid, error) {
}
func (mp *MessagePool) Add(m *types.SignedMessage) error {
mp.curTsLk.Lock()
defer mp.curTsLk.Unlock()
return mp.addTs(m, mp.curTs)
}
func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error {
// big messages are bad, anti DOS
if m.Size() > 32*1024 {
return xerrors.Errorf("mpool message too large (%dB): %w", m.Size(), ErrMessageTooBig)
@@ -332,11 +332,53 @@ func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error
return ErrMessageValueTooHigh
}
if err := sigs.Verify(&m.Signature, m.Message.From, m.Message.Cid().Bytes()); err != nil {
if err := mp.VerifyMsgSig(m); err != nil {
log.Warnf("mpooladd signature verification failed: %s", err)
return err
}
mp.curTsLk.Lock()
defer mp.curTsLk.Unlock()
return mp.addTs(m, mp.curTs)
}
func sigCacheKey(m *types.SignedMessage) (string, error) {
switch m.Signature.Type {
case crypto.SigTypeBLS:
if len(m.Signature.Data) < 90 {
return "", fmt.Errorf("bls signature too short")
}
return string(m.Cid().Bytes()) + string(m.Signature.Data[64:]), nil
case crypto.SigTypeSecp256k1:
return string(m.Cid().Bytes()), nil
default:
return "", xerrors.Errorf("unrecognized signature type: %d", m.Signature.Type)
}
}
func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error {
sck, err := sigCacheKey(m)
if err != nil {
return err
}
_, ok := mp.sigValCache.Get(sck)
if ok {
// already validated, great
return nil
}
if err := sigs.Verify(&m.Signature, m.Message.From, m.Message.Cid().Bytes()); err != nil {
return err
}
mp.sigValCache.Add(sck, struct{}{})
return nil
}
func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error {
snonce, err := mp.getStateNonce(m.Message.From, curTs)
if err != nil {
return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrBroadcastAnyway)
+3 -3
View File
@@ -610,9 +610,9 @@ func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet
return nil, nil, err
}
if act.Nonce < m.VMMessage().Nonce {
// nonce on chain is before message nonce we're looking for, its
// not going to be here
// we either have no messages from the sender, or the latest message we found has a lower nonce than the one being searched for,
// either way, no reason to lookback, it ain't there
if act.Nonce == 0 || act.Nonce < m.VMMessage().Nonce {
return nil, nil, nil
}
+10 -10
View File
@@ -248,24 +248,24 @@ func GetMinerDeadlines(ctx context.Context, sm *StateManager, ts *types.TipSet,
return mas.LoadDeadlines(sm.cs.Store(ctx))
}
func GetMinerFaults(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) ([]abi.SectorNumber, error) {
func GetMinerFaults(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (*abi.BitField, error) {
var mas miner.State
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
if err != nil {
return nil, xerrors.Errorf("(get ssize) failed to load miner actor state: %w", err)
return nil, xerrors.Errorf("(get faults) failed to load miner actor state: %w", err)
}
faults, err := mas.Faults.All(miner.SectorsMax)
return mas.Faults, nil
}
func GetMinerRecoveries(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (*abi.BitField, error) {
var mas miner.State
_, err := sm.LoadActorState(ctx, maddr, &mas, ts)
if err != nil {
return nil, xerrors.Errorf("reading fault bit set: %w", err)
return nil, xerrors.Errorf("(get recoveries) failed to load miner actor state: %w", err)
}
out := make([]abi.SectorNumber, len(faults))
for i, fault := range faults {
out[i] = abi.SectorNumber(fault)
}
return out, nil
return mas.Recoveries, nil
}
func GetStorageDeal(ctx context.Context, sm *StateManager, dealId abi.DealID, ts *types.TipSet) (*api.MarketDeal, error) {
+196 -15
View File
@@ -1,30 +1,45 @@
package sub
import (
"bytes"
"context"
"fmt"
"sync"
"time"
"golang.org/x/xerrors"
address "github.com/filecoin-project/go-address"
amt "github.com/filecoin-project/go-amt-ipld/v2"
miner "github.com/filecoin-project/specs-actors/actors/builtin/miner"
lru "github.com/hashicorp/golang-lru"
"github.com/ipfs/go-cid"
dstore "github.com/ipfs/go-datastore"
bstore "github.com/ipfs/go-ipfs-blockstore"
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log/v2"
connmgr "github.com/libp2p/go-libp2p-core/connmgr"
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
cbg "github.com/whyrusleeping/cbor-gen"
"go.opencensus.io/stats"
"go.opencensus.io/tag"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/bufbstore"
"github.com/filecoin-project/lotus/lib/sigs"
"github.com/filecoin-project/lotus/metrics"
)
var log = logging.Logger("sub")
func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, cmgr connmgr.ConnManager, bv *BlockValidator) {
func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, cmgr connmgr.ConnManager) {
for {
msg, err := bsub.Next(ctx)
if err != nil {
@@ -52,15 +67,13 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha
log.Debug("about to fetch messages for block from pubsub")
bmsgs, err := s.Bsync.FetchMessagesByCids(context.TODO(), blk.BlsMessages)
if err != nil {
log.Errorf("failed to fetch all bls messages for block received over pubusb: %s; flagging source %s", err, src)
bv.flagPeer(src)
log.Errorf("failed to fetch all bls messages for block received over pubusb: %s; source: %s", err, src)
return
}
smsgs, err := s.Bsync.FetchSignedMessagesByCids(context.TODO(), blk.SecpkMessages)
if err != nil {
log.Errorf("failed to fetch all secpk messages for block received over pubusb: %s; flagging source %s", err, src)
bv.flagPeer(src)
log.Errorf("failed to fetch all secpk messages for block received over pubusb: %s; source: %s", err, src)
return
}
@@ -89,15 +102,25 @@ type BlockValidator struct {
recvBlocks *blockReceiptCache
blacklist func(peer.ID)
// necessary for block validation
chain *store.ChainStore
stmgr *stmgr.StateManager
mx sync.Mutex
keycache map[string]address.Address
}
func NewBlockValidator(blacklist func(peer.ID)) *BlockValidator {
func NewBlockValidator(chain *store.ChainStore, stmgr *stmgr.StateManager, blacklist func(peer.ID)) *BlockValidator {
p, _ := lru.New2Q(4096)
return &BlockValidator{
peers: p,
killThresh: 10,
blacklist: blacklist,
recvBlocks: newBlockReceiptCache(),
chain: chain,
stmgr: stmgr,
keycache: make(map[string]address.Address),
}
}
@@ -120,35 +143,191 @@ func (bv *BlockValidator) flagPeer(p peer.ID) {
}
func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult {
// track validation time
begin := time.Now()
defer func() {
end := time.Now()
log.Infof("block validation time: %s", end.Sub(begin))
}()
stats.Record(ctx, metrics.BlockReceived.M(1))
recordFailure := func(what string) {
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, what))
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
bv.flagPeer(pid)
}
// make sure the block can be decoded
blk, err := types.DecodeBlockMsg(msg.GetData())
if err != nil {
log.Error("got invalid block over pubsub: ", err)
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, "invalid"))
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
bv.flagPeer(pid)
recordFailure("invalid")
return pubsub.ValidationReject
}
// check the message limit constraints
if len(blk.BlsMessages)+len(blk.SecpkMessages) > build.BlockMessageLimit {
log.Warnf("received block with too many messages over pubsub")
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, "too_many_messages"))
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
bv.flagPeer(pid)
recordFailure("too_many_messages")
return pubsub.ValidationReject
}
// make sure we have a signature
if blk.Header.BlockSig == nil {
log.Warnf("received block without a signature over pubsub")
recordFailure("missing_signature")
return pubsub.ValidationReject
}
// validate the block meta: the Message CID in the header must match the included messages
err = bv.validateMsgMeta(ctx, blk)
if err != nil {
log.Warnf("error validating message metadata: %s", err)
recordFailure("invalid_block_meta")
return pubsub.ValidationReject
}
// we want to ensure that it is a block from a known miner; we reject blocks from unknown miners
// to prevent spam attacks.
// the logic works as follows: we lookup the miner in the chain for its key.
// if we can find it then it's a known miner and we can validate the signature.
// if we can't find it, we check whether we are (near) synced in the chain.
// if we are not synced we cannot validate the block and we must ignore it.
// if we are synced and the miner is unknown, then the block is rejcected.
key, err := bv.getMinerWorkerKey(ctx, blk)
if err != nil {
if bv.isChainNearSynced() {
log.Warnf("received block message from unknown miner over pubsub; rejecting message")
recordFailure("unknown_miner")
return pubsub.ValidationReject
} else {
log.Warnf("cannot validate block message; unknown miner in unsynced chain")
return pubsub.ValidationIgnore
}
}
err = sigs.CheckBlockSignature(blk.Header, ctx, key)
if err != nil {
log.Errorf("block signature verification failed: %s", err)
recordFailure("signature_verification_failed")
return pubsub.ValidationReject
}
// it's a good block! make sure we've only seen it once
if bv.recvBlocks.add(blk.Header.Cid()) > 0 {
// TODO: once these changes propagate to the network, we can consider
// dropping peers who send us the same block multiple times
return pubsub.ValidationIgnore
}
// all good, accept the block
msg.ValidatorData = blk
stats.Record(ctx, metrics.BlockValidationSuccess.M(1))
return pubsub.ValidationAccept
}
func (bv *BlockValidator) isChainNearSynced() bool {
ts := bv.chain.GetHeaviestTipSet()
timestamp := ts.MinTimestamp()
now := time.Now().UnixNano()
cutoff := uint64(now) - uint64(6*time.Hour)
return timestamp > cutoff
}
func (bv *BlockValidator) validateMsgMeta(ctx context.Context, msg *types.BlockMsg) error {
var bcids, scids []cbg.CBORMarshaler
for _, m := range msg.BlsMessages {
c := cbg.CborCid(m)
bcids = append(bcids, &c)
}
for _, m := range msg.SecpkMessages {
c := cbg.CborCid(m)
scids = append(scids, &c)
}
// TODO there has to be a simpler way to do this without the blockstore dance
bs := cbor.NewCborStore(bstore.NewBlockstore(dstore.NewMapDatastore()))
bmroot, err := amt.FromArray(ctx, bs, bcids)
if err != nil {
return err
}
smroot, err := amt.FromArray(ctx, bs, scids)
if err != nil {
return err
}
mrcid, err := bs.Put(ctx, &types.MsgMeta{
BlsMessages: bmroot,
SecpkMessages: smroot,
})
if err != nil {
return err
}
if msg.Header.Messages != mrcid {
return fmt.Errorf("messages didn't match root cid in header")
}
return nil
}
func (bv *BlockValidator) getMinerWorkerKey(ctx context.Context, msg *types.BlockMsg) (address.Address, error) {
addr := msg.Header.Miner
bv.mx.Lock()
key, ok := bv.keycache[addr.String()]
bv.mx.Unlock()
if ok {
return key, nil
}
// TODO I have a feeling all this can be simplified by cleverer DI to use the API
ts := bv.chain.GetHeaviestTipSet()
st, _, err := bv.stmgr.TipSetState(ctx, ts)
if err != nil {
return address.Undef, err
}
buf := bufbstore.NewBufferedBstore(bv.chain.Blockstore())
cst := cbor.NewCborStore(buf)
state, err := state.LoadStateTree(cst, st)
if err != nil {
return address.Undef, err
}
act, err := state.GetActor(addr)
if err != nil {
return address.Undef, err
}
blk, err := bv.chain.Blockstore().Get(act.Head)
if err != nil {
return address.Undef, err
}
aso := blk.RawData()
var mst miner.State
err = mst.UnmarshalCBOR(bytes.NewReader(aso))
if err != nil {
return address.Undef, err
}
worker := mst.Info.Worker
key, err = bv.stmgr.ResolveToKeyAddress(ctx, worker, ts)
if err != nil {
return address.Undef, err
}
bv.mx.Lock()
bv.keycache[addr.String()] = key
bv.mx.Unlock()
return key, nil
}
type blockReceiptCache struct {
blocks *lru.TwoQueueCache
}
@@ -197,10 +376,12 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
tag.Insert(metrics.FailureType, "add"),
)
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
if xerrors.Is(err, messagepool.ErrBroadcastAnyway) {
return pubsub.ValidationAccept
switch {
case xerrors.Is(err, messagepool.ErrBroadcastAnyway):
return pubsub.ValidationIgnore
default:
return pubsub.ValidationReject
}
return pubsub.ValidationIgnore
}
stats.Record(ctx, metrics.MessageValidationSuccess.M(1))
return pubsub.ValidationAccept
+11
View File
@@ -69,6 +69,9 @@ type BlockHeader struct {
BlockSig *crypto.Signature // 13
ForkSignaling uint64 // 14
// internal
validated bool // true if the signature has been validated
}
func (b *BlockHeader) ToStorageBlock() (block.Block, error) {
@@ -124,6 +127,14 @@ func (blk *BlockHeader) SigningBytes() ([]byte, error) {
return blkcopy.Serialize()
}
func (blk *BlockHeader) SetValidated() {
blk.validated = true
}
func (blk *BlockHeader) IsValidated() bool {
return blk.validated
}
type MsgMeta struct {
BlsMessages cid.Cid
SecpkMessages cid.Cid
+58
View File
@@ -2,7 +2,9 @@ package types
import (
"bytes"
"encoding/hex"
"fmt"
"github.com/filecoin-project/specs-actors/actors/abi"
"reflect"
"testing"
@@ -63,6 +65,62 @@ func TestBlockHeaderSerialization(t *testing.T) {
}
}
func TestInteropBH(t *testing.T) {
newAddr, err := address.NewSecp256k1Address([]byte("address0"))
if err != nil {
t.Fatal(err)
}
mcid, err := cid.Parse("bafy2bzaceaxyj7xq27gc2747adjcirpxx52tt7owqx6z6kckun7tqivvoym4y")
if err != nil {
t.Fatal(err)
}
posts := []abi.PoStProof{
{abi.RegisteredProof_StackedDRG2KiBWinningPoSt, []byte{0x07}},
}
bh := &BlockHeader{
Miner: newAddr,
Ticket: &Ticket{[]byte{0x01, 0x02, 0x03}},
ElectionProof: &ElectionProof{[]byte{0x0a, 0x0b}},
BeaconEntries: []BeaconEntry{
{
Round: 5,
Data: []byte{0x0c},
//prevRound: 0,
},
},
Height: 2,
Messages: mcid,
ParentMessageReceipts: mcid,
Parents: []cid.Cid{mcid},
ParentWeight: NewInt(1000),
ForkSignaling: 3,
ParentStateRoot: mcid,
Timestamp: 1,
WinPoStProof: posts,
BlockSig: &crypto.Signature{
Type: crypto.SigTypeBLS,
Data: []byte{0x3},
},
BLSAggregate: &crypto.Signature{},
}
bhsb, err := bh.SigningBytes()
if err != nil {
t.Fatal(err)
}
// acquired from go-filecoin
gfc := "8f5501d04cb15021bf6bd003073d79e2238d4e61f1ad22814301020381420a0b818205410c818209410781d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc430003e802d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc410001f603"
if gfc != hex.EncodeToString(bhsb) {
t.Fatal("not equal!")
}
}
func BenchmarkBlockHeaderMarshal(b *testing.B) {
bh := testBlockHeader(b)
+6 -3
View File
@@ -1,9 +1,12 @@
package types
import "time"
type ExecutionResult struct {
Msg *Message
MsgRct *MessageReceipt
Error string
Msg *Message
MsgRct *MessageReceipt
Error string
Duration time.Duration
Subcalls []*ExecutionResult
}
+6 -5
View File
@@ -9,10 +9,10 @@ import (
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/puppet"
"github.com/filecoin-project/specs-actors/actors/runtime"
"github.com/ipfs/go-cid"
vtypes "github.com/filecoin-project/chain-validation/chain/types"
vdrivers "github.com/filecoin-project/chain-validation/drivers"
vstate "github.com/filecoin-project/chain-validation/state"
"github.com/filecoin-project/lotus/chain/stmgr"
@@ -24,12 +24,13 @@ import (
// Applier applies messages to state trees and storage.
type Applier struct {
stateWrapper *StateWrapper
syscalls runtime.Syscalls
}
var _ vstate.Applier = &Applier{}
func NewApplier(sw *StateWrapper) *Applier {
return &Applier{sw}
func NewApplier(sw *StateWrapper, syscalls runtime.Syscalls) *Applier {
return &Applier{sw, syscalls}
}
func (a *Applier) ApplyMessage(epoch abi.ChainEpoch, message *vtypes.Message) (vtypes.ApplyMessageResult, error) {
@@ -65,7 +66,7 @@ func (a *Applier) ApplySignedMessage(epoch abi.ChainEpoch, msg *vtypes.SignedMes
}
func (a *Applier) ApplyTipSetMessages(epoch abi.ChainEpoch, blocks []vtypes.BlockMessagesInfo, rnd vstate.RandomnessSource) (vtypes.ApplyTipSetResult, error) {
cs := store.NewChainStore(a.stateWrapper.bs, a.stateWrapper.ds, vdrivers.NewChainValidationSyscalls())
cs := store.NewChainStore(a.stateWrapper.bs, a.stateWrapper.ds, a.syscalls)
sm := stmgr.NewStateManager(cs)
var bms []stmgr.BlockMessages
@@ -134,7 +135,7 @@ func (a *Applier) applyMessage(epoch abi.ChainEpoch, lm types.ChainMsg) (vtypes.
ctx := context.TODO()
base := a.stateWrapper.Root()
lotusVM, err := vm.NewVM(base, epoch, &vmRand{}, a.stateWrapper.bs, vdrivers.NewChainValidationSyscalls())
lotusVM, err := vm.NewVM(base, epoch, &vmRand{}, a.stateWrapper.bs, a.syscalls)
// need to modify the VM invoker to add the puppet actor
chainValInvoker := vm.NewInvoker()
chainValInvoker.Register(puppet.PuppetActorCodeID, puppet.Actor{}, puppet.State{})
+3 -16
View File
@@ -1,11 +1,9 @@
package validation
import (
"context"
"github.com/filecoin-project/specs-actors/actors/runtime"
vstate "github.com/filecoin-project/chain-validation/state"
"github.com/filecoin-project/specs-actors/actors/abi"
acrypto "github.com/filecoin-project/specs-actors/actors/crypto"
)
type Factories struct {
@@ -18,26 +16,15 @@ func NewFactories() *Factories {
return &Factories{}
}
func (f *Factories) NewStateAndApplier() (vstate.VMWrapper, vstate.Applier) {
func (f *Factories) NewStateAndApplier(syscalls runtime.Syscalls) (vstate.VMWrapper, vstate.Applier) {
st := NewState()
return st, NewApplier(st)
return st, NewApplier(st, syscalls)
}
func (f *Factories) NewKeyManager() vstate.KeyManager {
return newKeyManager()
}
type fakeRandSrc struct {
}
func (r fakeRandSrc) Randomness(_ context.Context, _ acrypto.DomainSeparationTag, _ abi.ChainEpoch, _ []byte) (abi.Randomness, error) {
return abi.Randomness("sausages"), nil
}
func (f *Factories) NewRandomnessSource() vstate.RandomnessSource {
return &fakeRandSrc{}
}
func (f *Factories) NewValidationConfig() vstate.ValidationConfig {
trackGas := true
checkExit := true
+40 -1
View File
@@ -1,11 +1,15 @@
package vectors
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/filecoin-project/lotus/chain/types"
)
func LoadVector(t *testing.T, f string, out interface{}) {
@@ -42,5 +46,40 @@ func TestBlockHeaderVectors(t *testing.T) {
}
func TestMessageSigningVectors(t *testing.T) {
// TODO:
var msvs []MessageSigningVector
LoadVector(t, "message_signing.json", &msvs)
for i, msv := range msvs {
smsg := &types.SignedMessage{
Message: *msv.Unsigned,
Signature: *msv.Signature,
}
if smsg.Cid().String() != msv.Cid {
t.Fatalf("cid of message in vector %d mismatches", i)
}
// TODO: check signature
}
}
func TestUnsignedMessageVectors(t *testing.T) {
var msvs []UnsignedMessageVector
LoadVector(t, "unsigned_messages.json", &msvs)
for i, msv := range msvs {
b, err := msv.Message.Serialize()
if err != nil {
t.Fatal(err)
}
dec, err := hex.DecodeString(msv.HexCbor)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(b, dec) {
t.Fatalf("serialization vector %d mismatches bytes", i)
}
}
}
+9 -3
View File
@@ -5,6 +5,8 @@ import (
"context"
"encoding/binary"
"fmt"
"time"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
@@ -271,7 +273,9 @@ func (rt *Runtime) DeleteActor(addr address.Address) {
panic(aerrors.Fatalf("failed to get actor: %s", err))
}
if !act.Balance.IsZero() {
rt.vm.transfer(rt.Message().Receiver(), builtin.BurntFundsActorAddr, act.Balance)
if err := rt.vm.transfer(rt.Message().Receiver(), builtin.BurntFundsActorAddr, act.Balance); err != nil {
panic(aerrors.Fatalf("failed to transfer balance to burnt funds actor: %s", err))
}
}
if err := rt.state.DeleteActor(rt.Message().Receiver()); err != nil {
@@ -364,6 +368,7 @@ func (rs *Runtime) Send(to address.Address, method abi.MethodNum, m vmr.CBORMars
}
func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) {
start := time.Now()
ctx, span := trace.StartSpan(rt.ctx, "vmc.Send")
defer span.End()
if span.IsRecordingEvents() {
@@ -403,8 +408,9 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
}
er := types.ExecutionResult{
Msg: msg,
MsgRct: &mr,
Msg: msg,
MsgRct: &mr,
Duration: time.Since(start),
}
if errSend != nil {
+2 -2
View File
@@ -225,10 +225,10 @@ func (ss *syscallShim) VerifySeal(info abi.SealVerifyInfo) error {
}
ticket := []byte(info.Randomness)
proof := []byte(info.OnChain.Proof)
proof := []byte(info.Proof)
seed := []byte(info.InteractiveRandomness)
log.Debugf("Verif r:%x; d:%x; m:%s; t:%x; s:%x; N:%d; p:%x", info.OnChain.SealedCID, info.UnsealedCID, miner, ticket, seed, info.SectorID.Number, proof)
log.Debugf("Verif r:%x; d:%x; m:%s; t:%x; s:%x; N:%d; p:%x", info.SealedCID, info.UnsealedCID, miner, ticket, seed, info.SectorID.Number, proof)
//func(ctx context.Context, maddr address.Address, ssize abi.SectorSize, commD, commR, ticket, proof, seed []byte, sectorID abi.SectorNumber)
ok, err := ss.verifier.VerifySeal(info)
+8 -8
View File
@@ -210,7 +210,7 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
if err := vm.transfer(msg.From, msg.To, msg.Value); err != nil {
return nil, aerrors.Absorb(err, 1, "failed to transfer funds"), nil
return nil, aerrors.Wrap(err, "failed to transfer funds"), nil
}
}
@@ -602,36 +602,36 @@ func (vm *VM) incrementNonce(addr address.Address) error {
})
}
func (vm *VM) transfer(from, to address.Address, amt types.BigInt) error {
func (vm *VM) transfer(from, to address.Address, amt types.BigInt) aerrors.ActorError {
if from == to {
return nil
}
if amt.LessThan(types.NewInt(0)) {
return xerrors.Errorf("attempted to transfer negative value")
return aerrors.Newf(exitcode.SysErrForbidden, "attempted to transfer negative value: %s", amt)
}
f, err := vm.cstate.GetActor(from)
if err != nil {
return xerrors.Errorf("transfer failed when retrieving sender actor")
return aerrors.Fatalf("transfer failed when retrieving sender actor: %s", err)
}
t, err := vm.cstate.GetActor(to)
if err != nil {
return xerrors.Errorf("transfer failed when retrieving receiver actor")
return aerrors.Fatalf("transfer failed when retrieving receiver actor: %s", err)
}
if err := deductFunds(f, amt); err != nil {
return err
return aerrors.Newf(exitcode.SysErrInsufficientFunds, "transfer failed when deducting funds: %s", err)
}
depositFunds(t, amt)
if err := vm.cstate.SetActor(from, f); err != nil {
return err
return aerrors.Fatalf("transfer failed when setting receiver actor: %s", err)
}
if err := vm.cstate.SetActor(to, t); err != nil {
return err
return aerrors.Fatalf("transfer failed when setting sender actor: %s", err)
}
return nil
+4 -2
View File
@@ -6,6 +6,8 @@ import (
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/node/repo"
)
@@ -45,7 +47,7 @@ var authCreateAdminToken = &cli.Command{
perm := cctx.String("perm")
idx := 0
for i, p := range apistruct.AllPermissions {
if perm == p {
if auth.Permission(perm) == p {
idx = i + 1
}
}
@@ -93,7 +95,7 @@ var authApiInfoToken = &cli.Command{
perm := cctx.String("perm")
idx := 0
for i, p := range apistruct.AllPermissions {
if perm == p {
if auth.Permission(perm) == p {
idx = i + 1
}
}
+7
View File
@@ -18,6 +18,7 @@ import (
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/account"
"github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/specs-actors/actors/builtin/power"
"github.com/filecoin-project/specs-actors/actors/util/adt"
@@ -521,6 +522,12 @@ var chainGetCmd = &cli.Command{
cbu = new(power.CronEvent)
case "account-state":
cbu = new(account.State)
case "miner-state":
cbu = new(miner.State)
case "power-state":
cbu = new(power.State)
case "market-state":
cbu = new(market.State)
default:
return fmt.Errorf("unknown type: %q", t)
}
+21 -10
View File
@@ -384,7 +384,7 @@ var clientRetrieveCmd = &cli.Command{
return nil
}
ref := lapi.FileRef{
ref := &lapi.FileRef{
Path: cctx.Args().Get(1),
IsCAR: cctx.Bool("car"),
}
@@ -501,16 +501,27 @@ var clientListDeals = &cli.Command{
}
var deals []deal
for idx := range localDeals {
onChain, err := api.StateMarketStorageDeal(ctx, localDeals[idx].DealID, head.Key())
if err != nil {
return err
}
for _, v := range localDeals {
if v.DealID == 0 {
deals = append(deals, deal{
LocalDeal: v,
OnChainDealState: market.DealState{
SectorStartEpoch: -1,
LastUpdatedEpoch: -1,
SlashEpoch: -1,
},
})
} else {
onChain, err := api.StateMarketStorageDeal(ctx, v.DealID, head.Key())
if err != nil {
return err
}
deals = append(deals, deal{
LocalDeal: localDeals[idx],
OnChainDealState: onChain.State,
})
deals = append(deals, deal{
LocalDeal: v,
OnChainDealState: onChain.State,
})
}
}
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
+2 -1
View File
@@ -16,9 +16,10 @@ import (
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/client"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"github.com/filecoin-project/lotus/node/repo"
)
+53 -5
View File
@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"reflect"
"sort"
"strconv"
"strings"
"time"
@@ -493,6 +494,12 @@ var stateGetDealSetCmd = &cli.Command{
var stateListMinersCmd = &cli.Command{
Name: "list-miners",
Usage: "list all miners in the network",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "sort-by",
Usage: "criteria to sort miners by (none, num-deals)",
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
@@ -512,6 +519,26 @@ var stateListMinersCmd = &cli.Command{
return err
}
switch cctx.String("sort-by") {
case "num-deals":
ndm, err := getDealsCounts(ctx, api)
if err != nil {
return err
}
sort.Slice(miners, func(i, j int) bool {
return ndm[miners[i]] > ndm[miners[j]]
})
for i := 0; i < 50 && i < len(miners); i++ {
fmt.Printf("%s %d\n", miners[i], ndm[miners[i]])
}
return nil
default:
return fmt.Errorf("unrecognized sorting order")
case "", "none":
}
for _, m := range miners {
fmt.Println(m.String())
}
@@ -520,6 +547,22 @@ var stateListMinersCmd = &cli.Command{
},
}
func getDealsCounts(ctx context.Context, lapi api.FullNode) (map[address.Address]int, error) {
allDeals, err := lapi.StateMarketDeals(ctx, types.EmptyTSK)
if err != nil {
return nil, err
}
out := make(map[address.Address]int)
for _, d := range allDeals {
if d.State.SectorStartEpoch != -1 {
out[d.Proposal.Provider]++
}
}
return out, nil
}
var stateListActorsCmd = &cli.Command{
Name: "list-actors",
Usage: "list all actors in the network",
@@ -892,7 +935,7 @@ var stateComputeStateCmd = &cli.Command{
return c.Code, nil
}
return computeStateHtml(stout, getCode)
return computeStateHtml(ts, stout, getCode)
}
fmt.Println("computed state cid: ", stout.Root)
@@ -921,7 +964,7 @@ func codeStr(c cid.Cid) string {
return string(cmh.Digest)
}
func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
fmt.Printf(`<html>
<head>
<style>
@@ -947,8 +990,10 @@ func computeStateHtml(o *api.ComputeStateOutput, getCode func(addr address.Addre
</style>
</head>
<body>
<div>Tipset: <b>%s</b></div>
<div>Height: %d</div>
<div>State CID: <b>%s</b></div>
<div>Calls</div>`, o.Root)
<div>Calls</div>`, ts.Key(), ts.Height(), o.Root)
for _, ir := range o.Trace {
toCode, err := getCode(ir.Msg.To)
@@ -1037,12 +1082,15 @@ func printInternalExecutionsHtml(trace []*types.ExecutionResult, getCode func(ad
ret = `, Return</div><div><pre class="ret">` + ret + `</pre></div>`
}
slow := im.Duration > 10*time.Millisecond
veryslow := im.Duration > 50*time.Millisecond
fmt.Printf(`<div class="exec">
<div><h4 class="call">%s:%s</h4></div>
<div><b>%s</b> -&gt; <b>%s</b> (%s FIL), M%d</div>
%s
<div><span class="exit%d">Exit: <b>%d</b></span>%s
`, codeStr(toCode), methods[toCode][im.Msg.Method].name, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, params, im.MsgRct.ExitCode, im.MsgRct.ExitCode, ret)
<div><span class="slow-%t-%t">Took %s</span>, <span class="exit%d">Exit: <b>%d</b></span>%s
`, codeStr(toCode), methods[toCode][im.Msg.Method].name, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, params, slow, veryslow, im.Duration, im.MsgRct.ExitCode, im.MsgRct.ExitCode, ret)
if im.MsgRct.ExitCode != 0 {
fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, im.Error)
}
+15 -1
View File
@@ -15,6 +15,8 @@ import (
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/filecoin-project/sector-storage/ffiwrapper"
"github.com/filecoin-project/specs-actors/actors/abi"
"golang.org/x/xerrors"
@@ -65,6 +67,11 @@ var importBenchCmd = &cli.Command{
return err
}
bs := blockstore.NewBlockstore(bds)
cbs, err := blockstore.CachedBlockstore(context.TODO(), bs, blockstore.DefaultCacheOpts())
if err != nil {
return err
}
bs = cbs
ds := datastore.NewMapDatastore()
cs := store.NewChainStore(bs, ds, vm.Syscalls(ffiwrapper.ProofVerifier))
stm := stmgr.NewStateManager(cs)
@@ -111,7 +118,14 @@ var importBenchCmd = &cli.Command{
cur := tschain[i]
log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids())
if cur.ParentState() != lastState {
return xerrors.Errorf("tipset chain had state mismatch at height %d", cur.Height())
lastTrace := out[len(out)-1].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)
+5 -10
View File
@@ -527,16 +527,11 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, mid
if !skipc2 {
svi := abi.SealVerifyInfo{
SectorID: abi.SectorID{Miner: mid, Number: i},
OnChain: abi.OnChainSealVerifyInfo{
SealedCID: cids.Sealed,
InteractiveEpoch: seed.Epoch,
RegisteredProof: sb.SealProofType(),
Proof: proof,
DealIDs: nil,
SectorNumber: i,
SealRandEpoch: 0,
},
SectorID: abi.SectorID{Miner: mid, Number: i},
SealedCID: cids.Sealed,
RegisteredProof: sb.SealProofType(),
Proof: proof,
DealIDs: nil,
Randomness: ticket,
InteractiveRandomness: seed.Value,
UnsealedCID: cids.Unsealed,
+8
View File
@@ -653,6 +653,14 @@ create temp table b (like blocks excluding constraints) on commit drop;
eprof = bh.ElectionProof.VRFProof
}
if bh.Ticket == nil {
log.Warnf("got a block with nil ticket")
bh.Ticket = &types.Ticket{
VRFProof: []byte{},
}
}
if _, err := stmt2.Exec(
bh.Cid().String(),
bh.ParentWeight.String(),
+5 -2
View File
@@ -12,8 +12,8 @@
</div>
<div class="Index-node" id="formnd">
<form id="f" action='/mkminer' method='POST'>
<span>Enter destination address:</span>
<input type='text' name='address' style="width: 300px">
<span>Enter owner/worker address:</span>
<input type='text' name='address' style="width: 300px" placeholder="t3...">
<select name="sectorSize">
<option selected value="34359738368">32GiB sectors</option>
<option value="68719476736">64GiB sectors</option>
@@ -27,6 +27,9 @@
<div class="Index-node">
<span>When creating storage miner, DO NOT REFRESH THE PAGE, wait for it to load. This can take more than 5min.</span>
</div>
<div class="Index-node">
<span>If you don't have an owner/worker address, you can create it by following <a target="_blank" href="https://docs.lotu.sh/en+mining#get-started-22083">these instructions</a>.</span>
</div>
</div>
<div class="Index-footer">
<div>
+6 -4
View File
@@ -8,14 +8,16 @@ import (
"syscall"
"time"
cid "github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/lib/jsonrpc"
cid "github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log"
"gopkg.in/urfave/cli.v2"
)
type CidWindow [][]cid.Cid
+3 -3
View File
@@ -15,6 +15,8 @@ import (
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-jsonrpc/auth"
paramfetch "github.com/filecoin-project/go-paramfetch"
"github.com/filecoin-project/sector-storage/ffiwrapper"
@@ -22,8 +24,6 @@ import (
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/build"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/lib/auth"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"github.com/filecoin-project/lotus/lib/lotuslog"
"github.com/filecoin-project/lotus/node/repo"
"github.com/filecoin-project/sector-storage"
@@ -290,7 +290,7 @@ var runCmd = &cli.Command{
go func() {
<-ctx.Done()
log.Warn("Shutting down..")
log.Warn("Shutting down...")
if err := srv.Shutdown(context.TODO()); err != nil {
log.Errorf("shutting down RPC server failed: %s", err)
}
+2
View File
@@ -26,6 +26,8 @@ func main() {
importCarCmd,
commpToCidCmd,
fetchParamCmd,
proofsCmd,
verifRegCmd,
}
app := &cli.App{
+108
View File
@@ -0,0 +1,108 @@
package main
import (
"encoding/hex"
"fmt"
"gopkg.in/urfave/cli.v2"
ffi "github.com/filecoin-project/filecoin-ffi"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/ipfs/go-cid"
)
var proofsCmd = &cli.Command{
Name: "proofs",
Subcommands: []*cli.Command{
verifySealProofCmd,
},
}
var verifySealProofCmd = &cli.Command{
Name: "verify-seal",
ArgsUsage: "<commr> <commd> <proof>",
Description: "Verify a seal proof with manual inputs",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "ticket",
},
&cli.StringFlag{
Name: "proof-rand",
},
&cli.StringFlag{
Name: "miner",
},
&cli.Uint64Flag{
Name: "sector-id",
},
&cli.Int64Flag{
Name: "proof-type",
},
},
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 3 {
return fmt.Errorf("must specify commR, commD, and proof to verify")
}
commr, err := cid.Decode(cctx.Args().Get(0))
if err != nil {
return err
}
commd, err := cid.Decode(cctx.Args().Get(1))
if err != nil {
return err
}
proof, err := hex.DecodeString(cctx.Args().Get(2))
if err != nil {
return fmt.Errorf("failed to decode hex proof input: %w", err)
}
maddr, err := address.NewFromString(cctx.String("miner"))
if err != nil {
return err
}
mid, err := address.IDFromAddress(maddr)
if err != nil {
return err
}
ticket, err := hex.DecodeString(cctx.String("ticket"))
if err != nil {
return err
}
proofRand, err := hex.DecodeString(cctx.String("proof-rand"))
if err != nil {
return err
}
snum := abi.SectorNumber(cctx.Uint64("sector-id"))
ok, err := ffi.VerifySeal(abi.SealVerifyInfo{
SectorID: abi.SectorID{
Miner: abi.ActorID(mid),
Number: snum,
},
SealedCID: commr,
RegisteredProof: abi.RegisteredProof(cctx.Int64("proof-type")),
Proof: proof,
DealIDs: nil,
Randomness: abi.SealRandomness(ticket),
InteractiveRandomness: abi.InteractiveSealRandomness(proofRand),
UnsealedCID: commd,
})
if err != nil {
return err
}
if !ok {
return fmt.Errorf("invalid proof")
}
fmt.Println("proof valid!")
return nil
},
}
+377
View File
@@ -0,0 +1,377 @@
package main
import (
"bytes"
"fmt"
"github.com/filecoin-project/go-address"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/api/apibstore"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
"github.com/ipfs/go-hamt-ipld"
cbor "github.com/ipfs/go-ipld-cbor"
cbg "github.com/whyrusleeping/cbor-gen"
)
var verifRegCmd = &cli.Command{
Name: "verifreg",
Usage: "Interact with the verified registry actor",
Flags: []cli.Flag{},
Subcommands: []*cli.Command{
verifRegAddVerifierCmd,
verifRegVerifyClientCmd,
verifRegListVerifiersCmd,
verifRegListClientsCmd,
verifRegCheckClientCmd,
verifRegCheckVerifierCmd,
},
}
var verifRegAddVerifierCmd = &cli.Command{
Name: "add-verifier",
Usage: "make a given account a verifier",
Action: func(cctx *cli.Context) error {
fromk, err := address.NewFromString("t3qfoulel6fy6gn3hjmbhpdpf6fs5aqjb5fkurhtwvgssizq4jey5nw4ptq5up6h7jk7frdvvobv52qzmgjinq")
if err != nil {
return err
}
if cctx.Args().Len() != 2 {
return fmt.Errorf("must specify two arguments: address and allowance")
}
target, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
allowance, err := types.BigFromString(cctx.Args().Get(1))
if err != nil {
return err
}
params, err := actors.SerializeParams(&verifreg.AddVerifierParams{Address: target, Allowance: allowance})
if err != nil {
return err
}
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
msg := &types.Message{
To: builtin.VerifiedRegistryActorAddr,
From: fromk,
Method: builtin.MethodsVerifiedRegistry.AddVerifier,
GasPrice: types.NewInt(1),
GasLimit: 300000,
Params: params,
}
smsg, err := api.MpoolPushMessage(ctx, msg)
if err != nil {
return err
}
fmt.Printf("message sent, now waiting on cid: %s\n", smsg.Cid())
mwait, err := api.StateWaitMsg(ctx, smsg.Cid())
if err != nil {
return err
}
if mwait.Receipt.ExitCode != 0 {
return fmt.Errorf("failed to add verifier: %d", mwait.Receipt.ExitCode)
}
return nil
},
}
var verifRegVerifyClientCmd = &cli.Command{
Name: "verify-client",
Usage: "make a given account a verified client",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "from",
Usage: "specify your verifier address to send the message from",
},
},
Action: func(cctx *cli.Context) error {
froms := cctx.String("from")
if froms == "" {
return fmt.Errorf("must specify from address with --from")
}
fromk, err := address.NewFromString(froms)
if err != nil {
return err
}
if cctx.Args().Len() != 2 {
return fmt.Errorf("must specify two arguments: address and allowance")
}
target, err := address.NewFromString(cctx.Args().Get(0))
if err != nil {
return err
}
allowance, err := types.BigFromString(cctx.Args().Get(1))
if err != nil {
return err
}
params, err := actors.SerializeParams(&verifreg.AddVerifiedClientParams{Address: target, Allowance: allowance})
if err != nil {
return err
}
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
msg := &types.Message{
To: builtin.VerifiedRegistryActorAddr,
From: fromk,
Method: builtin.MethodsVerifiedRegistry.AddVerifiedClient,
GasPrice: types.NewInt(1),
GasLimit: 300000,
Params: params,
}
smsg, err := api.MpoolPushMessage(ctx, msg)
if err != nil {
return err
}
fmt.Printf("message sent, now waiting on cid: %s\n", smsg.Cid())
mwait, err := api.StateWaitMsg(ctx, smsg.Cid())
if err != nil {
return err
}
if mwait.Receipt.ExitCode != 0 {
return fmt.Errorf("failed to add verified client: %d", mwait.Receipt.ExitCode)
}
return nil
},
}
var verifRegListVerifiersCmd = &cli.Command{
Name: "list-verifiers",
Usage: "list all verifiers",
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
act, err := api.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK)
if err != nil {
return err
}
apibs := apibstore.NewAPIBlockstore(api)
cst := cbor.NewCborStore(apibs)
var st verifreg.State
if err := cst.Get(ctx, act.Head, &st); err != nil {
return err
}
vh, err := hamt.LoadNode(ctx, cst, st.Verifiers)
if err != nil {
return err
}
if err := vh.ForEach(ctx, func(k string, val interface{}) error {
addr, err := address.NewFromBytes([]byte(k))
if err != nil {
return err
}
var dcap verifreg.DataCap
if err := dcap.UnmarshalCBOR(bytes.NewReader(val.(*cbg.Deferred).Raw)); err != nil {
return err
}
fmt.Printf("%s: %s\n", addr, dcap)
return nil
}); err != nil {
return err
}
return nil
},
}
var verifRegListClientsCmd = &cli.Command{
Name: "list-clients",
Usage: "list all verified clients",
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
act, err := api.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK)
if err != nil {
return err
}
apibs := apibstore.NewAPIBlockstore(api)
cst := cbor.NewCborStore(apibs)
var st verifreg.State
if err := cst.Get(ctx, act.Head, &st); err != nil {
return err
}
vh, err := hamt.LoadNode(ctx, cst, st.VerifiedClients)
if err != nil {
return err
}
if err := vh.ForEach(ctx, func(k string, val interface{}) error {
addr, err := address.NewFromBytes([]byte(k))
if err != nil {
return err
}
var dcap verifreg.DataCap
if err := dcap.UnmarshalCBOR(bytes.NewReader(val.(*cbg.Deferred).Raw)); err != nil {
return err
}
fmt.Printf("%s: %s\n", addr, dcap)
return nil
}); err != nil {
return err
}
return nil
},
}
var verifRegCheckClientCmd = &cli.Command{
Name: "check-client",
Usage: "check verified client remaining bytes",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("must specify client address to check")
}
caddr, err := address.NewFromString(cctx.Args().First())
if err != nil {
return err
}
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
act, err := api.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK)
if err != nil {
return err
}
apibs := apibstore.NewAPIBlockstore(api)
cst := cbor.NewCborStore(apibs)
var st verifreg.State
if err := cst.Get(ctx, act.Head, &st); err != nil {
return err
}
vh, err := hamt.LoadNode(ctx, cst, st.VerifiedClients)
if err != nil {
return err
}
var dcap verifreg.DataCap
if err := vh.Find(ctx, string(caddr.Bytes()), &dcap); err != nil {
return err
}
fmt.Println(dcap)
return nil
},
}
var verifRegCheckVerifierCmd = &cli.Command{
Name: "check-verifier",
Usage: "check verifiers remaining bytes",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("must specify verifier address to check")
}
vaddr, err := address.NewFromString(cctx.Args().First())
if err != nil {
return err
}
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
act, err := api.StateGetActor(ctx, builtin.VerifiedRegistryActorAddr, types.EmptyTSK)
if err != nil {
return err
}
apibs := apibstore.NewAPIBlockstore(api)
cst := cbor.NewCborStore(apibs)
var st verifreg.State
if err := cst.Get(ctx, act.Head, &st); err != nil {
return err
}
vh, err := hamt.LoadNode(ctx, cst, st.Verifiers)
if err != nil {
return err
}
var dcap verifreg.DataCap
if err := vh.Find(ctx, string(vaddr.Bytes()), &dcap); err != nil {
return err
}
fmt.Println(dcap)
return nil
},
}
+13 -4
View File
@@ -98,14 +98,23 @@ var infoCmd = &cli.Command{
return err
}
nfaults, err := faults.Count()
if err != nil {
return xerrors.Errorf("counting faults: %w", err)
}
fmt.Printf("\tCommitted: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Sset), types.NewInt(uint64(mi.SectorSize)))))
if len(faults) == 0 {
if nfaults == 0 {
fmt.Printf("\tProving: %s\n", types.SizeStr(types.BigMul(types.NewInt(secCounts.Pset), types.NewInt(uint64(mi.SectorSize)))))
} else {
var faultyPercentage float64
if secCounts.Sset != 0 {
faultyPercentage = float64(10000*nfaults/secCounts.Sset) / 100.
}
fmt.Printf("\tProving: %s (%s Faulty, %.2f%%)\n",
types.SizeStr(types.BigMul(types.NewInt(secCounts.Pset-uint64(len(faults))), types.NewInt(uint64(mi.SectorSize)))),
types.SizeStr(types.BigMul(types.NewInt(uint64(len(faults))), types.NewInt(uint64(mi.SectorSize)))),
float64(10000*uint64(len(faults))/secCounts.Pset)/100.)
types.SizeStr(types.BigMul(types.NewInt(secCounts.Pset), types.NewInt(uint64(mi.SectorSize)))),
types.SizeStr(types.BigMul(types.NewInt(nfaults), types.NewInt(uint64(mi.SectorSize)))),
faultyPercentage)
}
fmt.Println()
+1 -1
View File
@@ -362,7 +362,7 @@ func migratePreSealMeta(ctx context.Context, api lapi.FullNode, metadata string,
buf := make([]byte, binary.MaxVarintLen64)
size := binary.PutUvarint(buf, uint64(maxSectorID+1))
return mds.Put(datastore.NewKey("/storage/nextid"), buf[:size])
return mds.Put(datastore.NewKey(modules.StorageCounterDSPrefix), buf[:size])
}
func findMarketDealID(ctx context.Context, api lapi.FullNode, deal market.DealProposal) (abi.DealID, error) {
+1
View File
@@ -27,6 +27,7 @@ func main() {
initCmd,
rewardsCmd,
runCmd,
stopCmd,
sectorsCmd,
storageCmd,
setPriceCmd,
+2 -1
View File
@@ -3,6 +3,8 @@ package main
import (
"bytes"
"fmt"
"time"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
@@ -10,7 +12,6 @@ import (
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
"time"
)
var provingCmd = &cli.Command{
+12 -4
View File
@@ -14,12 +14,13 @@ import (
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/build"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/lib/auth"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"github.com/filecoin-project/lotus/lib/ulimit"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/impl"
@@ -99,9 +100,12 @@ var runCmd = &cli.Command{
return xerrors.Errorf("repo at '%s' is not initialized, run 'lotus-storage-miner init' to set it up", storageRepoPath)
}
shutdownChan := make(chan struct{})
var minerapi api.StorageMiner
stop, err := node.New(ctx,
node.StorageMiner(&minerapi),
node.Override(new(dtypes.ShutdownChan), shutdownChan),
node.Online(),
node.Repo(r),
@@ -155,8 +159,12 @@ var runCmd = &cli.Command{
sigChan := make(chan os.Signal, 2)
go func() {
<-sigChan
log.Warn("Shutting down..")
select {
case <-sigChan:
case <-shutdownChan:
}
log.Warn("Shutting down...")
if err := stop(context.TODO()); err != nil {
log.Errorf("graceful shutting down failed: %s", err)
}
+29
View File
@@ -0,0 +1,29 @@
package main
import (
_ "net/http/pprof"
"gopkg.in/urfave/cli.v2"
lcli "github.com/filecoin-project/lotus/cli"
)
var stopCmd = &cli.Command{
Name: "stop",
Usage: "Stop a running lotus storage miner",
Flags: []cli.Flag{},
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetAPI(cctx)
if err != nil {
return err
}
defer closer()
err = api.Shutdown(lcli.ReqContext(cctx))
if err != nil {
return err
}
return nil
},
}
+28 -1
View File
@@ -29,6 +29,7 @@ import (
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/vm"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/lib/peermgr"
"github.com/filecoin-project/lotus/metrics"
"github.com/filecoin-project/lotus/node"
@@ -44,6 +45,26 @@ const (
preTemplateFlag = "genesis-template"
)
var daemonStopCmd = &cli.Command{
Name: "stop",
Usage: "Stop a running lotus daemon",
Flags: []cli.Flag{},
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetAPI(cctx)
if err != nil {
return err
}
defer closer()
err = api.Shutdown(lcli.ReqContext(cctx))
if err != nil {
return err
}
return nil
},
}
// DaemonCmd is the `go-lotus daemon` command
var DaemonCmd = &cli.Command{
Name: "daemon",
@@ -170,12 +191,15 @@ var DaemonCmd = &cli.Command{
genesis = node.Override(new(modules.Genesis), testing.MakeGenesis(cctx.String(makeGenFlag), cctx.String(preTemplateFlag)))
}
shutdownChan := make(chan struct{})
var api api.FullNode
stop, err := node.New(ctx,
node.FullAPI(&api),
node.Override(new(dtypes.Bootstrapper), isBootstrapper),
node.Override(new(dtypes.ShutdownChan), shutdownChan),
node.Online(),
node.Repo(r),
@@ -221,7 +245,10 @@ var DaemonCmd = &cli.Command{
}
// TODO: properly parse api endpoint (or make it a URL)
return serveRPC(api, stop, endpoint)
return serveRPC(api, stop, endpoint, shutdownChan)
},
Subcommands: []*cli.Command{
daemonStopCmd,
},
}
-2
View File
@@ -17,8 +17,6 @@ import (
var AdvanceBlockCmd *cli.Command
func main() {
os.Setenv("BELLMAN_NO_GPU", "1")
lotuslog.SetupLogLevels()
local := []*cli.Command{
+19 -13
View File
@@ -9,14 +9,6 @@ import (
"os/signal"
"syscall"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/lib/auth"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/impl"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
"github.com/multiformats/go-multiaddr"
@@ -24,11 +16,19 @@ import (
"golang.org/x/xerrors"
"contrib.go.opencensus.io/exporter/prometheus"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/impl"
)
var log = logging.Logger("main")
func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr) error {
func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr, shutdownCh <-chan struct{}) error {
rpcServer := jsonrpc.NewServer()
rpcServer.Register("Filecoin", apistruct.PermissionedFullAPI(a))
@@ -62,17 +62,23 @@ func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr) erro
srv := &http.Server{Handler: http.DefaultServeMux}
sigChan := make(chan os.Signal, 2)
sigCh := make(chan os.Signal, 2)
go func() {
<-sigChan
select {
case <-sigCh:
case <-shutdownCh:
}
log.Warn("Shutting down...")
if err := srv.Shutdown(context.TODO()); err != nil {
log.Errorf("shutting down RPC server failed: %s", err)
}
if err := stop(context.TODO()); err != nil {
log.Errorf("graceful shutting down failed: %s", err)
}
log.Warn("Graceful shutdown successful")
}()
signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
return srv.Serve(manet.NetListener(lst))
}
@@ -83,7 +89,7 @@ func handleImport(a *impl.FullNodeAPI) func(w http.ResponseWriter, r *http.Reque
w.WriteHeader(404)
return
}
if !apistruct.HasPerm(r.Context(), apistruct.PermWrite) {
if !auth.HasPerm(r.Context(), nil, apistruct.PermWrite) {
w.WriteHeader(401)
json.NewEncoder(w).Encode(struct{ Error string }{"unauthorized: missing write permission"})
return
+7 -1
View File
@@ -56,6 +56,12 @@
"github": "en/join-testnet.md",
"value": null
},
{
"title": "Use Lotus with systemd",
"slug": "en+install-system-services",
"github": "en/install-system-services.md",
"value": null
},
{
"title": "Setup Troubleshooting",
"slug": "en+setup-troubleshooting",
@@ -137,7 +143,7 @@
"value": null,
"posts": [
{
"title": "API Scripting Support",
"title": "Remote API Support",
"slug": "en+api-scripting-support",
"github": "en/api-scripting-support.md",
"value": null
+4 -12
View File
@@ -1,21 +1,13 @@
# API Scripting Support
# Remote API Support
You may want to delegate the work **Lotus Storage Miner** or **Lotus Node** perform to other machines. Here is how to setup the necessary authorization and environment variables.
## Generate a JWT
To generate a JWT for your environment variables, use this command:
```sh
lotus auth create-token --perm admin
lotus-storage-miner auth create-token --perm admin
```
You may want to delegate the work **Lotus Storage 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, you can assign it and the **multiaddr** to the appropriate environment variable.
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
+2 -2
View File
@@ -2,7 +2,7 @@
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/lotus/tree/master/lib/jsonrpc).
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?
@@ -77,7 +77,7 @@ lotus-storage-miner auth create-token --perm admin
## What authorization level should I use?
When viewing [api/struct.go](https://github.com/filecoin-project/lotus/blob/master/api/struct.go), you will encounter these types:
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.
@@ -0,0 +1,29 @@
# 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 service path.
## Installing via `make`
NOTE: Before using lotus and lotus-miner as systemd services, don't forget to `sudo make install` to ensure the binaries are accessible by the root user.
If your host uses the default systemd service path, it can be installed with `sudo make install-services`:
```sh
$ sudo make install-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
```
+1 -1
View File
@@ -55,7 +55,7 @@ Here is an example of the response:
t1aswwvjsae63tcrniz6x5ykvsuotlgkvlulnqpsi
```
- Visit the [faucet](https://facuet.testnet.filecoin.io) to add funds.
- Visit the [faucet](https://faucet.testnet.filecoin.io) to add funds.
- Paste the address you created.
- Press the send button.
+2 -2
View File
@@ -14,7 +14,7 @@ Download the 2048 byte parameters:
Pre-seal some sectors:
```sh
./lotus-seed pre-seal --sector-size 2048 --num-sectors 2
./lotus-seed pre-seal --sector-size 2KiB --num-sectors 2
```
Create the genesis block and start up the first node:
@@ -34,7 +34,7 @@ Then, in another console, import the genesis miner key:
Set up the genesis miner:
```sh
./lotus-storage-miner init --genesis-miner --actor=t01000 --sector-size=2048 --pre-sealed-sectors=~/.genesis-sectors --pre-sealed-metadata=~/.genesis-sectors/pre-seal-t01000.json --nosync
./lotus-storage-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:
+1 -1
View File
@@ -51,7 +51,7 @@ If you suspect that your GPU is not being used, first make sure it is properly c
First, to watch GPU utilization run `nvtop` in one terminal, then in a separate terminal, run:
```sh
lotus-bench --sector-size=2048
lotus-bench --sector-size=2KiB
```
This process uses a fair amount of GPU, and generally takes ~4 minutes to complete. If you do not see any activity in nvtop from lotus during the entire process, it is likely something is misconfigured with your GPU.
+1 -1
View File
@@ -28,7 +28,7 @@ lotus wallet new bls
With your wallet address:
- Visit the [faucet](https://facuet.testnet.filecoin.io)
- Visit the [faucet](https://faucet.testnet.filecoin.io)
- Click "Create Miner"
- DO NOT REFRESH THE PAGE. THIS OPERATION CAN TAKE SOME TIME.
+70
View File
@@ -0,0 +1,70 @@
# Why does Filecoin mining work best on AMD?
Currently, Filecoin's Proof of Replication (PoRep) prefers to be run on AMD
processors. More accurately, it runs much much slower on Intel CPUs (it runs
competitively fast on some ARM processors, like the ones in newer Samsung
phones, but they lack the RAM to seal the larger sector sizes). The main reason
that we see this benefit on AMD processors is due to their implementation of
the SHA hardware instructions. Now, why do we use the SHA instruction?
## PoRep security assumptions
Our research team has two different models for the security of Proofs of
Replication. These are the Latency Assumption, and the Cost Assumption. These
assumptions are arguments for why an attacker cannot pull off a 'regeneration
attack'. That is, the attacker cannot seal and commit random data (generated by
a function), delete it, and then reseal it on the fly to respond to PoSt
challenges, without actually storing the data for that time period.
### Cost Assumptions
The cost assumption states that the real money cost (hardware, electricity,
etc) of generating a sector is higher than the real money cost of simply
storing it on disks. NSE is a new PoRep our research team is working on that is
based on the cost assumption, and is thus able to be very parallelizable (In
comparison to schemes based on a latency assumption, as will be explained
next). However, cost assumptions vary greatly with available and hypothetical
hardware. For example, someone making an ASIC for NSE could break the cost
assumption by lowering the cost of sealing too much. This is one of our main
hesitations around shipping NSE.
### Latency Assumptions
A Proof of Replication that is secure under a latency assumption is secure
because an attacker cannot regenerate the data in time. We use this assumption
for SDR, where we assume that an attacker cannot regenerate enough of a sector
fast enough to respond to a PoSt. The way we achieve this is through the use
of depth-robust graphs. Without going into too much detail, depth-robust
graphs guarantee a minimum number of serial operations to compute an encoding
based on the graph. Each edge in the graph represents an operation we need to
perform. We thus have a guarantee that someone has to perform some operation
N times in a row in order to compute the encoding. That means that the
computation of the encoding must take at least as long as N times the fastest
someone can do that operation.
Now, to make this secure, we need to choose an operation that can't be made
much faster. There are many potential candidates here, depending on what
hardware you want to require. We opted not to require ASICs in order to mine
Filecoin, so that limits our choices severely. We have to look at what
operations CPUs are really good at. One candidate was AES encryption, which
also has hardware instructions. However, the difference between the performance
of CPU AES instructions, and the hypothetical 'best' performance you get was
still too great. This gap is generally called 'Amax', an attackers maximum
advantage. The higher the Amax of an algorithm we choose, the more expensive
the overall process has to become in order to bound how fast the attacker could
do it.
As we were doing our research, we noticed that AMD shipped their new processors
with a builtin SHA function, and we looked into how fast someone could possibly
compute a SHA hash. We found that AMDs implementation is only around 3 times
slower than anyone could reasonably do (given estimates by the hardware
engineers at [Supranational](https://www.supranational.net/) ). This is
incredibly impressive for something you can get in consumer hardware. With
this, we were able to make SDR sealing reasonably performant for people with
off-the-shelf hardware.
## Super Optimized CPUs
Given all of the above, with a latency assumption that we're basing our proofs
on right now, you need a processor that can do iterated SHA hashes really fast.
As mentioned earlier, this isnt just AMD processors, but many ARM processors
also have support for this. Hopefully, new Intel processors also follow suit.
But for now, Filecoin works best on AMD processors.
+3 -1
View File
@@ -2,7 +2,9 @@
Lotus supports making deals with data stored in IPFS, without having to re-import it into lotus.
To enable this integration, open up `~/.lotus/config.toml` (Or if you manually set `LOTUS_PATH`, look under that directory) and look for the Client field, and set `UseIpfs` to `true`.
To enable this integration, you need to have an IPFS daemon running in the background.
Then, open up `~/.lotus/config.toml` (or if you manually set `LOTUS_PATH`, look under that directory)
and look for the Client field, and set `UseIpfs` to `true`.
```toml
[Client]
+31 -29
View File
@@ -8,13 +8,13 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/GeertJohan/go.rice v1.0.0
github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/coreos/go-systemd/v22 v22.0.0
github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f // indirect
github.com/docker/go-units v0.4.0
github.com/drand/drand v0.8.1
github.com/drand/drand v0.8.2-0.20200518165838-d61135e6e2c8
github.com/fatih/color v1.8.0
github.com/filecoin-project/chain-validation v0.0.6-0.20200512234642-6304037e1db6
github.com/filecoin-project/filecoin-ffi v0.0.0-20200427223233-a0014b17f124
github.com/filecoin-project/chain-validation v0.0.6-0.20200526171800-c56c1882dc99
github.com/filecoin-project/filecoin-ffi v0.26.1-0.20200508175440-05b30afeb00d
github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef
github.com/filecoin-project/go-amt-ipld/v2 v2.0.1-0.20200424220931-6263827e49f2
github.com/filecoin-project/go-bitfield v0.0.1
@@ -22,16 +22,18 @@ require (
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03
github.com/filecoin-project/go-data-transfer v0.3.0
github.com/filecoin-project/go-fil-commcid v0.0.0-20200208005934-2b8bd03caca5
github.com/filecoin-project/go-fil-markets v0.2.3
github.com/filecoin-project/go-fil-markets v0.2.7
github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24
github.com/filecoin-project/go-padreader v0.0.0-20200210211231-548257017ca6
github.com/filecoin-project/go-paramfetch v0.0.2-0.20200505180321-973f8949ea8e
github.com/filecoin-project/go-statestore v0.1.0
github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b
github.com/filecoin-project/sector-storage v0.0.0-20200513185232-4051533cc4bd
github.com/filecoin-project/specs-actors v0.5.3
github.com/filecoin-project/sector-storage v0.0.0-20200522011946-a59ca7536a95
github.com/filecoin-project/specs-actors v0.5.4-0.20200521014528-0df536f7e461
github.com/filecoin-project/specs-storage v0.0.0-20200417134612-61b2d91a6102
github.com/filecoin-project/storage-fsm v0.0.0-20200427182014-01487d5ad3c8
github.com/filecoin-project/storage-fsm v0.0.0-20200522010518-83fd743db8bc
github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/google/uuid v1.1.1
github.com/gorilla/mux v1.7.4
github.com/gorilla/websocket v1.4.2
@@ -59,42 +61,43 @@ require (
github.com/ipfs/go-ipld-cbor v0.0.5-0.20200428170625-a0bd04d3cbdf
github.com/ipfs/go-ipld-format v0.2.0
github.com/ipfs/go-log v1.0.4
github.com/ipfs/go-log/v2 v2.0.5
github.com/ipfs/go-log/v2 v2.0.8
github.com/ipfs/go-merkledag v0.3.1
github.com/ipfs/go-path v0.0.7
github.com/ipfs/go-unixfs v0.2.4
github.com/ipfs/interface-go-ipfs-core v0.2.3
github.com/ipld/go-car v0.1.1-0.20200430185908-8ff2e52a4c88
github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0
github.com/lib/pq v1.2.0
github.com/libp2p/go-eventbus v0.1.0
github.com/libp2p/go-libp2p v0.8.1
github.com/libp2p/go-libp2p-circuit v0.2.1
github.com/libp2p/go-libp2p-connmgr v0.1.1
github.com/libp2p/go-libp2p-core v0.5.3
github.com/libp2p/go-eventbus v0.2.1
github.com/libp2p/go-libp2p v0.9.4
github.com/libp2p/go-libp2p-connmgr v0.2.4
github.com/libp2p/go-libp2p-core v0.5.7
github.com/libp2p/go-libp2p-discovery v0.4.0
github.com/libp2p/go-libp2p-kad-dht v0.7.6
github.com/libp2p/go-libp2p-kad-dht v0.8.1
github.com/libp2p/go-libp2p-mplex v0.2.3
github.com/libp2p/go-libp2p-peer v0.2.0
github.com/libp2p/go-libp2p-peerstore v0.2.3
github.com/libp2p/go-libp2p-pubsub v0.2.7-0.20200505181014-5bbe37191afb
github.com/libp2p/go-libp2p-quic-transport v0.1.1
github.com/libp2p/go-libp2p-peerstore v0.2.4
github.com/libp2p/go-libp2p-pubsub v0.3.2
github.com/libp2p/go-libp2p-quic-transport v0.5.0
github.com/libp2p/go-libp2p-record v0.1.2
github.com/libp2p/go-libp2p-routing-helpers v0.2.1
github.com/libp2p/go-libp2p-routing-helpers v0.2.3
github.com/libp2p/go-libp2p-secio v0.2.2
github.com/libp2p/go-libp2p-swarm v0.2.3
github.com/libp2p/go-libp2p-swarm v0.2.6
github.com/libp2p/go-libp2p-tls v0.1.3
github.com/libp2p/go-libp2p-yamux v0.2.7
github.com/libp2p/go-maddr-filter v0.0.5
github.com/libp2p/go-libp2p-yamux v0.2.8
github.com/libp2p/go-maddr-filter v0.1.0
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1
github.com/mitchellh/go-homedir v1.1.0
github.com/multiformats/go-base32 v0.0.3
github.com/multiformats/go-multiaddr v0.2.1
github.com/multiformats/go-multiaddr v0.2.2
github.com/multiformats/go-multiaddr-dns v0.2.0
github.com/multiformats/go-multiaddr-net v0.1.5
github.com/multiformats/go-multihash v0.0.13
github.com/opentracing/opentracing-go v1.1.0
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.5.1
github.com/whyrusleeping/bencher v0.0.0-20190829221104-bb6607aa8bba
github.com/whyrusleeping/cbor-gen v0.0.0-20200504204219-64967432584d
@@ -104,17 +107,16 @@ require (
go.uber.org/dig v1.8.0 // indirect
go.uber.org/fx v1.9.0
go.uber.org/multierr v1.5.0
go.uber.org/zap v1.14.1
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
golang.org/x/sys v0.0.0-20200427175716-29b57079015a
go.uber.org/zap v1.15.0
go4.org v0.0.0-20190313082347-94abd6928b1d // indirect
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8
gotest.tools v2.2.0+incompatible
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect
)
replace github.com/golangci/golangci-lint => github.com/golangci/golangci-lint v1.18.0
replace github.com/filecoin-project/filecoin-ffi => ./extern/filecoin-ffi
replace github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
+242 -119
View File
File diff suppressed because it is too large Load Diff
-51
View File
@@ -1,51 +0,0 @@
package auth
import (
"context"
"net/http"
"strings"
logging "github.com/ipfs/go-log/v2"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/apistruct"
)
var log = logging.Logger("auth")
type Handler struct {
Verify func(ctx context.Context, token string) ([]api.Permission, error)
Next http.HandlerFunc
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
token := r.Header.Get("Authorization")
if token == "" {
token = r.FormValue("token")
if token != "" {
token = "Bearer " + token
}
}
if token != "" {
if !strings.HasPrefix(token, "Bearer ") {
log.Warn("missing Bearer prefix in auth header")
w.WriteHeader(401)
return
}
token = strings.TrimPrefix(token, "Bearer ")
allow, err := h.Verify(ctx, token)
if err != nil {
log.Warnf("JWT Verification failed: %s", err)
w.WriteHeader(401)
return
}
ctx = apistruct.WithPerm(ctx, allow)
}
h.Next(w, r.WithContext(ctx))
}
+13
View File
@@ -7,6 +7,7 @@ import (
"golang.org/x/xerrors"
"github.com/multiformats/go-multiaddr"
"github.com/multiformats/go-multihash"
blocks "github.com/ipfs/go-block-format"
@@ -35,6 +36,18 @@ func NewIpfsBstore(ctx context.Context) (*IpfsBstore, error) {
}, nil
}
func NewRemoteIpfsBstore(ctx context.Context, maddr multiaddr.Multiaddr) (*IpfsBstore, error) {
api, err := httpapi.NewApi(maddr)
if err != nil {
return nil, xerrors.Errorf("getting remote ipfs api: %w", err)
}
return &IpfsBstore{
ctx: ctx,
api: api,
}, nil
}
func (i *IpfsBstore) DeleteBlock(cid cid.Cid) error {
return xerrors.Errorf("not supported")
}
-435
View File
@@ -1,435 +0,0 @@
package jsonrpc
import (
"container/list"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"reflect"
"sync/atomic"
"time"
"github.com/gorilla/websocket"
logging "github.com/ipfs/go-log/v2"
"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
"golang.org/x/xerrors"
)
const (
methodRetryFrequency = time.Second * 3
)
var (
errorType = reflect.TypeOf(new(error)).Elem()
contextType = reflect.TypeOf(new(context.Context)).Elem()
log = logging.Logger("rpc")
)
// ErrClient is an error which occurred on the client side the library
type ErrClient struct {
err error
}
func (e *ErrClient) Error() string {
return fmt.Sprintf("RPC client error: %s", e.err)
}
// Unwrap unwraps the actual error
func (e *ErrClient) Unwrap(err error) error {
return e.err
}
type clientResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result json.RawMessage `json:"result"`
ID int64 `json:"id"`
Error *respError `json:"error,omitempty"`
}
type makeChanSink func() (context.Context, func([]byte, bool))
type clientRequest struct {
req request
ready chan clientResponse
// retCh provides a context and sink for handling incoming channel messages
retCh makeChanSink
}
// ClientCloser is used to close Client from further use
type ClientCloser func()
// NewClient creates new jsonrpc 2.0 client
//
// handler must be pointer to a struct with function fields
// Returned value closes the client connection
// TODO: Example
func NewClient(addr string, namespace string, handler interface{}, requestHeader http.Header) (ClientCloser, error) {
return NewMergeClient(addr, namespace, []interface{}{handler}, requestHeader)
}
type client struct {
namespace string
requests chan clientRequest
exiting <-chan struct{}
idCtr int64
}
// NewMergeClient is like NewClient, but allows to specify multiple structs
// to be filled in the same namespace, using one connection
func NewMergeClient(addr string, namespace string, outs []interface{}, requestHeader http.Header, opts ...Option) (ClientCloser, error) {
var config Config
for _, o := range opts {
o(&config)
}
connFactory := func() (*websocket.Conn, error) {
conn, _, err := websocket.DefaultDialer.Dial(addr, requestHeader)
return conn, err
}
if config.proxyConnFactory != nil {
connFactory = config.proxyConnFactory(connFactory)
}
conn, err := connFactory()
if err != nil {
return nil, err
}
c := client{
namespace: namespace,
}
stop := make(chan struct{})
exiting := make(chan struct{})
c.requests = make(chan clientRequest)
c.exiting = exiting
handlers := map[string]rpcHandler{}
go (&wsConn{
conn: conn,
connFactory: connFactory,
reconnectInterval: config.ReconnectInterval,
handler: handlers,
requests: c.requests,
stop: stop,
exiting: exiting,
}).handleWsConn(context.TODO())
for _, handler := range outs {
htyp := reflect.TypeOf(handler)
if htyp.Kind() != reflect.Ptr {
return nil, xerrors.New("expected handler to be a pointer")
}
typ := htyp.Elem()
if typ.Kind() != reflect.Struct {
return nil, xerrors.New("handler should be a struct")
}
val := reflect.ValueOf(handler)
for i := 0; i < typ.NumField(); i++ {
fn, err := c.makeRpcFunc(typ.Field(i))
if err != nil {
return nil, err
}
val.Elem().Field(i).Set(fn)
}
}
return func() {
close(stop)
<-exiting
}, nil
}
func (c *client) makeOutChan(ctx context.Context, ftyp reflect.Type, valOut int) (func() reflect.Value, makeChanSink) {
retVal := reflect.Zero(ftyp.Out(valOut))
chCtor := func() (context.Context, func([]byte, bool)) {
// unpack chan type to make sure it's reflect.BothDir
ctyp := reflect.ChanOf(reflect.BothDir, ftyp.Out(valOut).Elem())
ch := reflect.MakeChan(ctyp, 0) // todo: buffer?
chCtx, chCancel := context.WithCancel(ctx)
retVal = ch.Convert(ftyp.Out(valOut))
incoming := make(chan reflect.Value, 32)
// gorotuine to handle buffering of items
go func() {
buf := (&list.List{}).Init()
for {
front := buf.Front()
cases := []reflect.SelectCase{
{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(chCtx.Done()),
},
{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(incoming),
},
}
if front != nil {
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectSend,
Chan: ch,
Send: front.Value.(reflect.Value).Elem(),
})
}
chosen, val, _ := reflect.Select(cases)
switch chosen {
case 0:
ch.Close()
return
case 1:
vvval := val.Interface().(reflect.Value)
buf.PushBack(vvval)
if buf.Len() > 1 {
if buf.Len() > 10 {
log.Warnw("rpc output message buffer", "n", buf.Len())
} else {
log.Infow("rpc output message buffer", "n", buf.Len())
}
}
case 2:
buf.Remove(front)
}
}
}()
return ctx, func(result []byte, ok bool) {
if !ok {
chCancel()
return
}
val := reflect.New(ftyp.Out(valOut).Elem())
if err := json.Unmarshal(result, val.Interface()); err != nil {
log.Errorf("error unmarshaling chan response: %s", err)
return
}
if ctx.Err() != nil {
log.Errorf("got rpc message with cancelled context: %s", ctx.Err())
return
}
select {
case incoming <- val:
case <-chCtx.Done():
}
}
}
return func() reflect.Value { return retVal }, chCtor
}
func (c *client) sendRequest(ctx context.Context, req request, chCtor makeChanSink) (clientResponse, error) {
rchan := make(chan clientResponse, 1)
creq := clientRequest{
req: req,
ready: rchan,
retCh: chCtor,
}
select {
case c.requests <- creq:
case <-c.exiting:
return clientResponse{}, fmt.Errorf("websocket routine exiting")
}
var ctxDone <-chan struct{}
var resp clientResponse
if ctx != nil {
ctxDone = ctx.Done()
}
// wait for response, handle context cancellation
loop:
for {
select {
case resp = <-rchan:
break loop
case <-ctxDone: // send cancel request
ctxDone = nil
cancelReq := clientRequest{
req: request{
Jsonrpc: "2.0",
Method: wsCancel,
Params: []param{{v: reflect.ValueOf(*req.ID)}},
},
}
select {
case c.requests <- cancelReq:
case <-c.exiting:
log.Warn("failed to send request cancellation, websocket routing exited")
}
}
}
return resp, nil
}
type rpcFunc struct {
client *client
ftyp reflect.Type
name string
nout int
valOut int
errOut int
hasCtx int
returnValueIsChannel bool
retry bool
}
func (fn *rpcFunc) processResponse(resp clientResponse, rval reflect.Value) []reflect.Value {
out := make([]reflect.Value, fn.nout)
if fn.valOut != -1 {
out[fn.valOut] = rval
}
if fn.errOut != -1 {
out[fn.errOut] = reflect.New(errorType).Elem()
if resp.Error != nil {
out[fn.errOut].Set(reflect.ValueOf(resp.Error))
}
}
return out
}
func (fn *rpcFunc) processError(err error) []reflect.Value {
out := make([]reflect.Value, fn.nout)
if fn.valOut != -1 {
out[fn.valOut] = reflect.New(fn.ftyp.Out(fn.valOut)).Elem()
}
if fn.errOut != -1 {
out[fn.errOut] = reflect.New(errorType).Elem()
out[fn.errOut].Set(reflect.ValueOf(&ErrClient{err}))
}
return out
}
func (fn *rpcFunc) handleRpcCall(args []reflect.Value) (results []reflect.Value) {
id := atomic.AddInt64(&fn.client.idCtr, 1)
params := make([]param, len(args)-fn.hasCtx)
for i, arg := range args[fn.hasCtx:] {
params[i] = param{
v: arg,
}
}
var ctx context.Context
var span *trace.Span
if fn.hasCtx == 1 {
ctx = args[0].Interface().(context.Context)
ctx, span = trace.StartSpan(ctx, "api.call")
defer span.End()
}
retVal := func() reflect.Value { return reflect.Value{} }
// if the function returns a channel, we need to provide a sink for the
// messages
var chCtor makeChanSink
if fn.returnValueIsChannel {
retVal, chCtor = fn.client.makeOutChan(ctx, fn.ftyp, fn.valOut)
}
req := request{
Jsonrpc: "2.0",
ID: &id,
Method: fn.client.namespace + "." + fn.name,
Params: params,
}
if span != nil {
span.AddAttributes(trace.StringAttribute("method", req.Method))
eSC := base64.StdEncoding.EncodeToString(
propagation.Binary(span.SpanContext()))
req.Meta = map[string]string{
"SpanContext": eSC,
}
}
var resp clientResponse
var err error
// keep retrying if got a forced closed websocket conn and calling method
// has retry annotation
for {
resp, err = fn.client.sendRequest(ctx, req, chCtor)
if err != nil {
return fn.processError(fmt.Errorf("sendRequest failed: %w", err))
}
if resp.ID != *req.ID {
return fn.processError(xerrors.New("request and response id didn't match"))
}
if fn.valOut != -1 && !fn.returnValueIsChannel {
val := reflect.New(fn.ftyp.Out(fn.valOut))
if resp.Result != nil {
log.Debugw("rpc result", "type", fn.ftyp.Out(fn.valOut))
if err := json.Unmarshal(resp.Result, val.Interface()); err != nil {
log.Warnw("unmarshaling failed", "message", string(resp.Result))
return fn.processError(xerrors.Errorf("unmarshaling result: %w", err))
}
}
retVal = func() reflect.Value { return val.Elem() }
}
retry := resp.Error != nil && resp.Error.Code == 2 && fn.retry
if !retry {
break
}
time.Sleep(methodRetryFrequency)
}
return fn.processResponse(resp, retVal())
}
func (c *client) makeRpcFunc(f reflect.StructField) (reflect.Value, error) {
ftyp := f.Type
if ftyp.Kind() != reflect.Func {
return reflect.Value{}, xerrors.New("handler field not a func")
}
fun := &rpcFunc{
client: c,
ftyp: ftyp,
name: f.Name,
retry: f.Tag.Get("retry") == "true",
}
fun.valOut, fun.errOut, fun.nout = processFuncOut(ftyp)
if ftyp.NumIn() > 0 && ftyp.In(0) == contextType {
fun.hasCtx = 1
}
fun.returnValueIsChannel = fun.valOut != -1 && ftyp.Out(fun.valOut).Kind() == reflect.Chan
return reflect.MakeFunc(ftyp, fun.handleRpcCall), nil
}
-275
View File
@@ -1,275 +0,0 @@
package jsonrpc
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"reflect"
"github.com/filecoin-project/lotus/metrics"
"go.opencensus.io/stats"
"go.opencensus.io/tag"
"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
"golang.org/x/xerrors"
)
type rpcHandler struct {
paramReceivers []reflect.Type
nParams int
receiver reflect.Value
handlerFunc reflect.Value
hasCtx int
errOut int
valOut int
}
type handlers map[string]rpcHandler
// Request / response
type request struct {
Jsonrpc string `json:"jsonrpc"`
ID *int64 `json:"id,omitempty"`
Method string `json:"method"`
Params []param `json:"params"`
Meta map[string]string `json:"meta,omitempty"`
}
type respError struct {
Code int `json:"code"`
Message string `json:"message"`
}
func (e *respError) Error() string {
if e.Code >= -32768 && e.Code <= -32000 {
return fmt.Sprintf("RPC error (%d): %s", e.Code, e.Message)
}
return e.Message
}
type response struct {
Jsonrpc string `json:"jsonrpc"`
Result interface{} `json:"result,omitempty"`
ID int64 `json:"id"`
Error *respError `json:"error,omitempty"`
}
// Register
func (h handlers) register(namespace string, r interface{}) {
val := reflect.ValueOf(r)
//TODO: expect ptr
for i := 0; i < val.NumMethod(); i++ {
method := val.Type().Method(i)
funcType := method.Func.Type()
hasCtx := 0
if funcType.NumIn() >= 2 && funcType.In(1) == contextType {
hasCtx = 1
}
ins := funcType.NumIn() - 1 - hasCtx
recvs := make([]reflect.Type, ins)
for i := 0; i < ins; i++ {
recvs[i] = method.Type.In(i + 1 + hasCtx)
}
valOut, errOut, _ := processFuncOut(funcType)
h[namespace+"."+method.Name] = rpcHandler{
paramReceivers: recvs,
nParams: ins,
handlerFunc: method.Func,
receiver: val,
hasCtx: hasCtx,
errOut: errOut,
valOut: valOut,
}
}
}
// Handle
type rpcErrFunc func(w func(func(io.Writer)), req *request, code int, err error)
type chanOut func(reflect.Value, int64) error
func (h handlers) handleReader(ctx context.Context, r io.Reader, w io.Writer, rpcError rpcErrFunc) {
wf := func(cb func(io.Writer)) {
cb(w)
}
var req request
if err := json.NewDecoder(r).Decode(&req); err != nil {
rpcError(wf, &req, rpcParseError, xerrors.Errorf("unmarshaling request: %w", err))
return
}
h.handle(ctx, req, wf, rpcError, func(bool) {}, nil)
}
func doCall(methodName string, f reflect.Value, params []reflect.Value) (out []reflect.Value, err error) {
defer func() {
if i := recover(); i != nil {
err = xerrors.Errorf("panic in rpc method '%s': %s", methodName, i)
log.Error(err)
}
}()
out = f.Call(params)
return out, nil
}
func (handlers) getSpan(ctx context.Context, req request) (context.Context, *trace.Span) {
if req.Meta == nil {
return ctx, nil
}
if eSC, ok := req.Meta["SpanContext"]; ok {
bSC := make([]byte, base64.StdEncoding.DecodedLen(len(eSC)))
_, err := base64.StdEncoding.Decode(bSC, []byte(eSC))
if err != nil {
log.Errorf("SpanContext: decode", "error", err)
return ctx, nil
}
sc, ok := propagation.FromBinary(bSC)
if !ok {
log.Errorf("SpanContext: could not create span", "data", bSC)
return ctx, nil
}
ctx, span := trace.StartSpanWithRemoteParent(ctx, "api.handle", sc)
span.AddAttributes(trace.StringAttribute("method", req.Method))
return ctx, span
}
return ctx, nil
}
func (h handlers) handle(ctx context.Context, req request, w func(func(io.Writer)), rpcError rpcErrFunc, done func(keepCtx bool), chOut chanOut) {
// Not sure if we need to sanitize the incoming req.Method or not.
ctx, span := h.getSpan(ctx, req)
ctx, _ = tag.New(ctx, tag.Insert(metrics.RPCMethod, req.Method))
defer span.End()
handler, ok := h[req.Method]
if !ok {
rpcError(w, &req, rpcMethodNotFound, fmt.Errorf("method '%s' not found", req.Method))
stats.Record(ctx, metrics.RPCInvalidMethod.M(1))
done(false)
return
}
if len(req.Params) != handler.nParams {
rpcError(w, &req, rpcInvalidParams, fmt.Errorf("wrong param count"))
stats.Record(ctx, metrics.RPCRequestError.M(1))
done(false)
return
}
outCh := handler.valOut != -1 && handler.handlerFunc.Type().Out(handler.valOut).Kind() == reflect.Chan
defer done(outCh)
if chOut == nil && outCh {
rpcError(w, &req, rpcMethodNotFound, fmt.Errorf("method '%s' not supported in this mode (no out channel support)", req.Method))
stats.Record(ctx, metrics.RPCRequestError.M(1))
return
}
callParams := make([]reflect.Value, 1+handler.hasCtx+handler.nParams)
callParams[0] = handler.receiver
if handler.hasCtx == 1 {
callParams[1] = reflect.ValueOf(ctx)
}
for i := 0; i < handler.nParams; i++ {
rp := reflect.New(handler.paramReceivers[i])
if err := json.NewDecoder(bytes.NewReader(req.Params[i].data)).Decode(rp.Interface()); err != nil {
rpcError(w, &req, rpcParseError, xerrors.Errorf("unmarshaling params for '%s' (param: %T): %w", req.Method, rp.Interface(), err))
stats.Record(ctx, metrics.RPCRequestError.M(1))
return
}
callParams[i+1+handler.hasCtx] = reflect.ValueOf(rp.Elem().Interface())
}
///////////////////
callResult, err := doCall(req.Method, handler.handlerFunc, callParams)
if err != nil {
rpcError(w, &req, 0, xerrors.Errorf("fatal error calling '%s': %w", req.Method, err))
stats.Record(ctx, metrics.RPCRequestError.M(1))
return
}
if req.ID == nil {
return // notification
}
///////////////////
resp := response{
Jsonrpc: "2.0",
ID: *req.ID,
}
if handler.errOut != -1 {
err := callResult[handler.errOut].Interface()
if err != nil {
log.Warnf("error in RPC call to '%s': %+v", req.Method, err)
stats.Record(ctx, metrics.RPCResponseError.M(1))
resp.Error = &respError{
Code: 1,
Message: err.(error).Error(),
}
}
}
var kind reflect.Kind
var res interface{}
var nonZero bool
if handler.valOut != -1 {
res = callResult[handler.valOut].Interface()
kind = callResult[handler.valOut].Kind()
nonZero = !callResult[handler.valOut].IsZero()
}
if res != nil && kind == reflect.Chan {
// Channel responses are sent from channel control goroutine.
// Sending responses here could cause deadlocks on writeLk, or allow
// sending channel messages before this rpc call returns
//noinspection GoNilness // already checked above
err = chOut(callResult[handler.valOut], *req.ID)
if err == nil {
return // channel goroutine handles responding
}
log.Warnf("failed to setup channel in RPC call to '%s': %+v", req.Method, err)
stats.Record(ctx, metrics.RPCResponseError.M(1))
resp.Error = &respError{
Code: 1,
Message: err.(error).Error(),
}
} else if resp.Error == nil {
// check error as JSON-RPC spec prohibits error and value at the same time
resp.Result = res
}
if resp.Error != nil && nonZero {
log.Errorw("error and res returned", "request", req, "r.err", resp.Error, "res", res)
}
w(func(w io.Writer) {
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Error(err)
stats.Record(ctx, metrics.RPCResponseError.M(1))
return
}
})
}
-25
View File
@@ -1,25 +0,0 @@
package jsonrpc
import (
"time"
"github.com/gorilla/websocket"
)
type Config struct {
ReconnectInterval time.Duration
proxyConnFactory func(func() (*websocket.Conn, error)) func() (*websocket.Conn, error) // for testing
}
var defaultConfig = Config{
ReconnectInterval: time.Second * 5,
}
type Option func(c *Config)
func WithReconnectInterval(d time.Duration) func(c *Config) {
return func(c *Config) {
c.ReconnectInterval = d
}
}
-571
View File
@@ -1,571 +0,0 @@
package jsonrpc
import (
"context"
"errors"
"fmt"
"net"
"net/http/httptest"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/gorilla/websocket"
logging "github.com/ipfs/go-log/v2"
"github.com/stretchr/testify/require"
)
func init() {
logging.SetLogLevel("rpc", "DEBUG")
}
type SimpleServerHandler struct {
n int
}
type TestType struct {
S string
I int
}
type TestOut struct {
TestType
Ok bool
}
func (h *SimpleServerHandler) Add(in int) error {
if in == -3546 {
return errors.New("test")
}
h.n += in
return nil
}
func (h *SimpleServerHandler) AddGet(in int) int {
h.n += in
return h.n
}
func (h *SimpleServerHandler) StringMatch(t TestType, i2 int64) (out TestOut, err error) {
if strconv.FormatInt(i2, 10) == t.S {
out.Ok = true
}
if i2 != int64(t.I) {
return TestOut{}, errors.New(":(")
}
out.I = t.I
out.S = t.S
return
}
func TestRPC(t *testing.T) {
// setup server
serverHandler := &SimpleServerHandler{}
rpcServer := NewServer()
rpcServer.Register("SimpleServerHandler", serverHandler)
// httptest stuff
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
// setup client
var client struct {
Add func(int) error
AddGet func(int) int
StringMatch func(t TestType, i2 int64) (out TestOut, err error)
}
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &client, nil)
require.NoError(t, err)
defer closer()
// Add(int) error
require.NoError(t, client.Add(2))
require.Equal(t, 2, serverHandler.n)
err = client.Add(-3546)
require.EqualError(t, err, "test")
// AddGet(int) int
n := client.AddGet(3)
require.Equal(t, 5, n)
require.Equal(t, 5, serverHandler.n)
// StringMatch
o, err := client.StringMatch(TestType{S: "0"}, 0)
require.NoError(t, err)
require.Equal(t, "0", o.S)
require.Equal(t, 0, o.I)
_, err = client.StringMatch(TestType{S: "5"}, 5)
require.EqualError(t, err, ":(")
o, err = client.StringMatch(TestType{S: "8", I: 8}, 8)
require.NoError(t, err)
require.Equal(t, "8", o.S)
require.Equal(t, 8, o.I)
// Invalid client handlers
var noret struct {
Add func(int)
}
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &noret, nil)
require.NoError(t, err)
// this one should actually work
noret.Add(4)
require.Equal(t, 9, serverHandler.n)
closer()
var noparam struct {
Add func()
}
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &noparam, nil)
require.NoError(t, err)
// shouldn't panic
noparam.Add()
closer()
var erronly struct {
AddGet func() (int, error)
}
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &erronly, nil)
require.NoError(t, err)
_, err = erronly.AddGet()
if err == nil || err.Error() != "RPC error (-32602): wrong param count" {
t.Error("wrong error:", err)
}
closer()
var wrongtype struct {
Add func(string) error
}
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &wrongtype, nil)
require.NoError(t, err)
err = wrongtype.Add("not an int")
if err == nil || !strings.Contains(err.Error(), "RPC error (-32700):") || !strings.Contains(err.Error(), "json: cannot unmarshal string into Go value of type int") {
t.Error("wrong error:", err)
}
closer()
var notfound struct {
NotThere func(string) error
}
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "SimpleServerHandler", &notfound, nil)
require.NoError(t, err)
err = notfound.NotThere("hello?")
if err == nil || err.Error() != "RPC error (-32601): method 'SimpleServerHandler.NotThere' not found" {
t.Error("wrong error:", err)
}
closer()
}
type CtxHandler struct {
lk sync.Mutex
cancelled bool
i int
}
func (h *CtxHandler) Test(ctx context.Context) {
h.lk.Lock()
defer h.lk.Unlock()
timeout := time.After(300 * time.Millisecond)
h.i++
select {
case <-timeout:
case <-ctx.Done():
h.cancelled = true
}
}
func TestCtx(t *testing.T) {
// setup server
serverHandler := &CtxHandler{}
rpcServer := NewServer()
rpcServer.Register("CtxHandler", serverHandler)
// httptest stuff
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
// setup client
var client struct {
Test func(ctx context.Context)
}
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "CtxHandler", &client, nil)
require.NoError(t, err)
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
client.Test(ctx)
serverHandler.lk.Lock()
if !serverHandler.cancelled {
t.Error("expected cancellation on the server side")
}
serverHandler.cancelled = false
serverHandler.lk.Unlock()
closer()
var noCtxClient struct {
Test func()
}
closer, err = NewClient("ws://"+testServ.Listener.Addr().String(), "CtxHandler", &noCtxClient, nil)
if err != nil {
t.Fatal(err)
}
noCtxClient.Test()
serverHandler.lk.Lock()
if serverHandler.cancelled || serverHandler.i != 2 {
t.Error("wrong serverHandler state")
}
serverHandler.lk.Unlock()
closer()
}
type UnUnmarshalable int
func (*UnUnmarshalable) UnmarshalJSON([]byte) error {
return errors.New("nope")
}
type UnUnmarshalableHandler struct{}
func (*UnUnmarshalableHandler) GetUnUnmarshalableStuff() (UnUnmarshalable, error) {
return UnUnmarshalable(5), nil
}
func TestUnmarshalableResult(t *testing.T) {
var client struct {
GetUnUnmarshalableStuff func() (UnUnmarshalable, error)
}
rpcServer := NewServer()
rpcServer.Register("Handler", &UnUnmarshalableHandler{})
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "Handler", &client, nil)
require.NoError(t, err)
defer closer()
_, err = client.GetUnUnmarshalableStuff()
require.EqualError(t, err, "RPC client error: unmarshaling result: nope")
}
type ChanHandler struct {
wait chan struct{}
ctxdone <-chan struct{}
}
func (h *ChanHandler) Sub(ctx context.Context, i int, eq int) (<-chan int, error) {
out := make(chan int)
h.ctxdone = ctx.Done()
wait := h.wait
log.Warnf("SERVER SUB!")
go func() {
defer close(out)
var n int
for {
select {
case <-ctx.Done():
fmt.Println("ctxdone1")
return
case <-wait:
}
n += i
if n == eq {
fmt.Println("eq")
return
}
select {
case <-ctx.Done():
fmt.Println("ctxdone2")
return
case out <- n:
}
}
}()
return out, nil
}
func TestChan(t *testing.T) {
var client struct {
Sub func(context.Context, int, int) (<-chan int, error)
}
serverHandler := &ChanHandler{
wait: make(chan struct{}, 5),
}
rpcServer := NewServer()
rpcServer.Register("ChanHandler", serverHandler)
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
require.NoError(t, err)
defer closer()
serverHandler.wait <- struct{}{}
ctx, cancel := context.WithCancel(context.Background())
// sub
sub, err := client.Sub(ctx, 2, -1)
require.NoError(t, err)
// recv one
require.Equal(t, 2, <-sub)
// recv many (order)
serverHandler.wait <- struct{}{}
serverHandler.wait <- struct{}{}
serverHandler.wait <- struct{}{}
require.Equal(t, 4, <-sub)
require.Equal(t, 6, <-sub)
require.Equal(t, 8, <-sub)
// close (through ctx)
cancel()
_, ok := <-sub
require.Equal(t, false, ok)
// sub (again)
serverHandler.wait = make(chan struct{}, 5)
serverHandler.wait <- struct{}{}
ctx, cancel = context.WithCancel(context.Background())
defer cancel()
log.Warnf("last sub")
sub, err = client.Sub(ctx, 3, 6)
require.NoError(t, err)
log.Warnf("waiting for value now")
require.Equal(t, 3, <-sub)
log.Warnf("not equal")
// close (remote)
serverHandler.wait <- struct{}{}
_, ok = <-sub
require.Equal(t, false, ok)
}
func TestChanServerClose(t *testing.T) {
var client struct {
Sub func(context.Context, int, int) (<-chan int, error)
}
serverHandler := &ChanHandler{
wait: make(chan struct{}, 5),
}
rpcServer := NewServer()
rpcServer.Register("ChanHandler", serverHandler)
tctx, tcancel := context.WithCancel(context.Background())
testServ := httptest.NewServer(rpcServer)
testServ.Config.ConnContext = func(ctx context.Context, c net.Conn) context.Context {
return tctx
}
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
require.NoError(t, err)
defer closer()
serverHandler.wait <- struct{}{}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// sub
sub, err := client.Sub(ctx, 2, -1)
require.NoError(t, err)
// recv one
require.Equal(t, 2, <-sub)
// make sure we're blocked
select {
case <-time.After(200 * time.Millisecond):
case <-sub:
t.Fatal("didn't expect to get anything from sub")
}
// close server
tcancel()
testServ.Close()
_, ok := <-sub
require.Equal(t, false, ok)
}
func TestServerChanLockClose(t *testing.T) {
var client struct {
Sub func(context.Context, int, int) (<-chan int, error)
}
serverHandler := &ChanHandler{
wait: make(chan struct{}),
}
rpcServer := NewServer()
rpcServer.Register("ChanHandler", serverHandler)
testServ := httptest.NewServer(rpcServer)
var closeConn func() error
_, err := NewMergeClient("ws://"+testServ.Listener.Addr().String(),
"ChanHandler",
[]interface{}{&client}, nil,
func(c *Config) {
c.proxyConnFactory = func(f func() (*websocket.Conn, error)) func() (*websocket.Conn, error) {
return func() (*websocket.Conn, error) {
c, err := f()
if err != nil {
return nil, err
}
closeConn = c.UnderlyingConn().Close
return c, nil
}
}
})
require.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// sub
sub, err := client.Sub(ctx, 2, -1)
require.NoError(t, err)
// recv one
go func() {
serverHandler.wait <- struct{}{}
}()
require.Equal(t, 2, <-sub)
for i := 0; i < 100; i++ {
serverHandler.wait <- struct{}{}
}
if err := closeConn(); err != nil {
t.Fatal(err)
}
<-serverHandler.ctxdone
}
func TestControlChanDeadlock(t *testing.T) {
for r := 0; r < 20; r++ {
testControlChanDeadlock(t)
}
}
func testControlChanDeadlock(t *testing.T) {
var client struct {
Sub func(context.Context, int, int) (<-chan int, error)
}
n := 5000
serverHandler := &ChanHandler{
wait: make(chan struct{}, n),
}
rpcServer := NewServer()
rpcServer.Register("ChanHandler", serverHandler)
testServ := httptest.NewServer(rpcServer)
defer testServ.Close()
closer, err := NewClient("ws://"+testServ.Listener.Addr().String(), "ChanHandler", &client, nil)
require.NoError(t, err)
defer closer()
for i := 0; i < n; i++ {
serverHandler.wait <- struct{}{}
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sub, err := client.Sub(ctx, 1, -1)
require.NoError(t, err)
done := make(chan struct{})
go func() {
defer close(done)
for i := 0; i < n; i++ {
if <-sub != i+1 {
panic("bad!")
//require.Equal(t, i+1, <-sub)
}
}
}()
// reset this channel so its not shared between the sub requests...
serverHandler.wait = make(chan struct{}, n)
for i := 0; i < n; i++ {
serverHandler.wait <- struct{}{}
}
_, err = client.Sub(ctx, 2, -1)
require.NoError(t, err)
<-done
}
-114
View File
@@ -1,114 +0,0 @@
package jsonrpc
import (
"context"
"encoding/json"
"io"
"net/http"
"strings"
"github.com/gorilla/websocket"
)
const (
rpcParseError = -32700
rpcMethodNotFound = -32601
rpcInvalidParams = -32602
)
// RPCServer provides a jsonrpc 2.0 http server handler
type RPCServer struct {
methods handlers
}
// NewServer creates new RPCServer instance
func NewServer() *RPCServer {
return &RPCServer{
methods: map[string]rpcHandler{},
}
}
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func (s *RPCServer) handleWS(ctx context.Context, w http.ResponseWriter, r *http.Request) {
// TODO: allow setting
// (note that we still are mostly covered by jwt tokens)
w.Header().Set("Access-Control-Allow-Origin", "*")
if r.Header.Get("Sec-WebSocket-Protocol") != "" {
w.Header().Set("Sec-WebSocket-Protocol", r.Header.Get("Sec-WebSocket-Protocol"))
}
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Error(err)
w.WriteHeader(500)
return
}
(&wsConn{
conn: c,
handler: s.methods,
exiting: make(chan struct{}),
}).handleWsConn(ctx)
if err := c.Close(); err != nil {
log.Error(err)
return
}
}
// TODO: return errors to clients per spec
func (s *RPCServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
h := strings.ToLower(r.Header.Get("Connection"))
if strings.Contains(h, "upgrade") {
s.handleWS(ctx, w, r)
return
}
s.methods.handleReader(ctx, r.Body, w, rpcError)
}
func rpcError(wf func(func(io.Writer)), req *request, code int, err error) {
log.Errorf("RPC Error: %s", err)
wf(func(w io.Writer) {
if hw, ok := w.(http.ResponseWriter); ok {
hw.WriteHeader(500)
}
log.Warnf("rpc error: %s", err)
if req.ID == nil { // notification
return
}
resp := response{
Jsonrpc: "2.0",
ID: *req.ID,
Error: &respError{
Code: code,
Message: err.Error(),
},
}
err = json.NewEncoder(w).Encode(resp)
if err != nil {
log.Warnf("failed to write rpc error: %s", err)
return
}
})
}
// Register registers new RPC handler
//
// Handler is any value with methods defined
func (s *RPCServer) Register(namespace string, handler interface{}) {
s.methods.register(namespace, handler)
}
var _ error = &respError{}
-55
View File
@@ -1,55 +0,0 @@
package jsonrpc
import (
"encoding/json"
"fmt"
"reflect"
)
type param struct {
data []byte // from unmarshal
v reflect.Value // to marshal
}
func (p *param) UnmarshalJSON(raw []byte) error {
p.data = make([]byte, len(raw))
copy(p.data, raw)
return nil
}
func (p *param) MarshalJSON() ([]byte, error) {
if p.v.Kind() == reflect.Invalid {
return p.data, nil
}
return json.Marshal(p.v.Interface())
}
// processFuncOut finds value and error Outs in function
func processFuncOut(funcType reflect.Type) (valOut int, errOut int, n int) {
errOut = -1 // -1 if not found
valOut = -1
n = funcType.NumOut()
switch n {
case 0:
case 1:
if funcType.Out(0) == errorType {
errOut = 0
} else {
valOut = 0
}
case 2:
valOut = 0
errOut = 1
if funcType.Out(1) != errorType {
panic("expected error as second return value")
}
default:
errstr := fmt.Sprintf("too many return values: %s", funcType)
panic(errstr)
}
return
}
-559
View File
@@ -1,559 +0,0 @@
package jsonrpc
import (
"context"
"encoding/json"
"errors"
"io"
"io/ioutil"
"reflect"
"sync"
"sync/atomic"
"time"
"github.com/gorilla/websocket"
"golang.org/x/xerrors"
)
const wsCancel = "xrpc.cancel"
const chValue = "xrpc.ch.val"
const chClose = "xrpc.ch.close"
type frame struct {
// common
Jsonrpc string `json:"jsonrpc"`
ID *int64 `json:"id,omitempty"`
Meta map[string]string `json:"meta,omitempty"`
// request
Method string `json:"method,omitempty"`
Params []param `json:"params,omitempty"`
// response
Result json.RawMessage `json:"result,omitempty"`
Error *respError `json:"error,omitempty"`
}
type outChanReg struct {
reqID int64
chID uint64
ch reflect.Value
}
type wsConn struct {
// outside params
conn *websocket.Conn
connFactory func() (*websocket.Conn, error)
reconnectInterval time.Duration
handler handlers
requests <-chan clientRequest
stop <-chan struct{}
exiting chan struct{}
// incoming messages
incoming chan io.Reader
incomingErr error
// outgoing messages
writeLk sync.Mutex
// ////
// Client related
// inflight are requests we've sent to the remote
inflight map[int64]clientRequest
// chanHandlers is a map of client-side channel handlers
chanHandlers map[uint64]func(m []byte, ok bool)
// ////
// Server related
// handling are the calls we handle
handling map[int64]context.CancelFunc
handlingLk sync.Mutex
spawnOutChanHandlerOnce sync.Once
// chanCtr is a counter used for identifying output channels on the server side
chanCtr uint64
registerCh chan outChanReg
}
// //
// WebSocket Message utils //
// //
// nextMessage wait for one message and puts it to the incoming channel
func (c *wsConn) nextMessage() {
msgType, r, err := c.conn.NextReader()
if err != nil {
c.incomingErr = err
close(c.incoming)
return
}
if msgType != websocket.BinaryMessage && msgType != websocket.TextMessage {
c.incomingErr = errors.New("unsupported message type")
close(c.incoming)
return
}
c.incoming <- r
}
// nextWriter waits for writeLk and invokes the cb callback with WS message
// writer when the lock is acquired
func (c *wsConn) nextWriter(cb func(io.Writer)) {
c.writeLk.Lock()
defer c.writeLk.Unlock()
wcl, err := c.conn.NextWriter(websocket.TextMessage)
if err != nil {
log.Error("handle me:", err)
return
}
cb(wcl)
if err := wcl.Close(); err != nil {
log.Error("handle me:", err)
return
}
}
func (c *wsConn) sendRequest(req request) error {
c.writeLk.Lock()
defer c.writeLk.Unlock()
if err := c.conn.WriteJSON(req); err != nil {
return err
}
return nil
}
// //
// Output channels //
// //
// handleOutChans handles channel communication on the server side
// (forwards channel messages to client)
func (c *wsConn) handleOutChans() {
regV := reflect.ValueOf(c.registerCh)
exitV := reflect.ValueOf(c.exiting)
cases := []reflect.SelectCase{
{ // registration chan always 0
Dir: reflect.SelectRecv,
Chan: regV,
},
{ // exit chan always 1
Dir: reflect.SelectRecv,
Chan: exitV,
},
}
internal := len(cases)
var caseToID []uint64
for {
chosen, val, ok := reflect.Select(cases)
switch chosen {
case 0: // registration channel
if !ok {
// control channel closed - signals closed connection
// This shouldn't happen, instead the exiting channel should get closed
log.Warn("control channel closed")
return
}
registration := val.Interface().(outChanReg)
caseToID = append(caseToID, registration.chID)
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: registration.ch,
})
c.nextWriter(func(w io.Writer) {
resp := &response{
Jsonrpc: "2.0",
ID: registration.reqID,
Result: registration.chID,
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Error(err)
return
}
})
continue
case 1: // exiting channel
if !ok {
// exiting channel closed - signals closed connection
//
// We're not closing any channels as we're on receiving end.
// Also, context cancellation below should take care of any running
// requests
return
}
log.Warn("exiting channel received a message")
continue
}
if !ok {
// Output channel closed, cleanup, and tell remote that this happened
n := len(cases) - 1
if n > 0 {
cases[chosen] = cases[n]
caseToID[chosen-internal] = caseToID[n-internal]
}
id := caseToID[chosen-internal]
cases = cases[:n]
caseToID = caseToID[:n-internal]
if err := c.sendRequest(request{
Jsonrpc: "2.0",
ID: nil, // notification
Method: chClose,
Params: []param{{v: reflect.ValueOf(id)}},
}); err != nil {
log.Warnf("closed out channel sendRequest failed: %s", err)
}
continue
}
// forward message
if err := c.sendRequest(request{
Jsonrpc: "2.0",
ID: nil, // notification
Method: chValue,
Params: []param{{v: reflect.ValueOf(caseToID[chosen-internal])}, {v: val}},
}); err != nil {
log.Warnf("sendRequest failed: %s", err)
return
}
}
}
// handleChanOut registers output channel for forwarding to client
func (c *wsConn) handleChanOut(ch reflect.Value, req int64) error {
c.spawnOutChanHandlerOnce.Do(func() {
go c.handleOutChans()
})
id := atomic.AddUint64(&c.chanCtr, 1)
select {
case c.registerCh <- outChanReg{
reqID: req,
chID: id,
ch: ch,
}:
return nil
case <-c.exiting:
return xerrors.New("connection closing")
}
}
// //
// Context.Done propagation //
// //
// handleCtxAsync handles context lifetimes for client
// TODO: this should be aware of events going through chanHandlers, and quit
// when the related channel is closed.
// This should also probably be a single goroutine,
// Note that not doing this should be fine for now as long as we are using
// contexts correctly (cancelling when async functions are no longer is use)
func (c *wsConn) handleCtxAsync(actx context.Context, id int64) {
<-actx.Done()
c.sendRequest(request{
Jsonrpc: "2.0",
Method: wsCancel,
Params: []param{{v: reflect.ValueOf(id)}},
})
}
// cancelCtx is a built-in rpc which handles context cancellation over rpc
func (c *wsConn) cancelCtx(req frame) {
if req.ID != nil {
log.Warnf("%s call with ID set, won't respond", wsCancel)
}
var id int64
if err := json.Unmarshal(req.Params[0].data, &id); err != nil {
log.Error("handle me:", err)
return
}
c.handlingLk.Lock()
defer c.handlingLk.Unlock()
cf, ok := c.handling[id]
if ok {
cf()
}
}
// //
// Main Handling logic //
// //
func (c *wsConn) handleChanMessage(frame frame) {
var chid uint64
if err := json.Unmarshal(frame.Params[0].data, &chid); err != nil {
log.Error("failed to unmarshal channel id in xrpc.ch.val: %s", err)
return
}
hnd, ok := c.chanHandlers[chid]
if !ok {
log.Errorf("xrpc.ch.val: handler %d not found", chid)
return
}
hnd(frame.Params[1].data, true)
}
func (c *wsConn) handleChanClose(frame frame) {
var chid uint64
if err := json.Unmarshal(frame.Params[0].data, &chid); err != nil {
log.Error("failed to unmarshal channel id in xrpc.ch.val: %s", err)
return
}
hnd, ok := c.chanHandlers[chid]
if !ok {
log.Errorf("xrpc.ch.val: handler %d not found", chid)
return
}
delete(c.chanHandlers, chid)
hnd(nil, false)
}
func (c *wsConn) handleResponse(frame frame) {
req, ok := c.inflight[*frame.ID]
if !ok {
log.Error("client got unknown ID in response")
return
}
if req.retCh != nil && frame.Result != nil {
// output is channel
var chid uint64
if err := json.Unmarshal(frame.Result, &chid); err != nil {
log.Errorf("failed to unmarshal channel id response: %s, data '%s'", err, string(frame.Result))
return
}
var chanCtx context.Context
chanCtx, c.chanHandlers[chid] = req.retCh()
go c.handleCtxAsync(chanCtx, *frame.ID)
}
req.ready <- clientResponse{
Jsonrpc: frame.Jsonrpc,
Result: frame.Result,
ID: *frame.ID,
Error: frame.Error,
}
delete(c.inflight, *frame.ID)
}
func (c *wsConn) handleCall(ctx context.Context, frame frame) {
req := request{
Jsonrpc: frame.Jsonrpc,
ID: frame.ID,
Meta: frame.Meta,
Method: frame.Method,
Params: frame.Params,
}
ctx, cancel := context.WithCancel(ctx)
nextWriter := func(cb func(io.Writer)) {
cb(ioutil.Discard)
}
done := func(keepCtx bool) {
if !keepCtx {
cancel()
}
}
if frame.ID != nil {
nextWriter = c.nextWriter
c.handlingLk.Lock()
c.handling[*frame.ID] = cancel
c.handlingLk.Unlock()
done = func(keepctx bool) {
c.handlingLk.Lock()
defer c.handlingLk.Unlock()
if !keepctx {
cancel()
delete(c.handling, *frame.ID)
}
}
}
go c.handler.handle(ctx, req, nextWriter, rpcError, done, c.handleChanOut)
}
// handleFrame handles all incoming messages (calls and responses)
func (c *wsConn) handleFrame(ctx context.Context, frame frame) {
// Get message type by method name:
// "" - response
// "xrpc.*" - builtin
// anything else - incoming remote call
switch frame.Method {
case "": // Response to our call
c.handleResponse(frame)
case wsCancel:
c.cancelCtx(frame)
case chValue:
c.handleChanMessage(frame)
case chClose:
c.handleChanClose(frame)
default: // Remote call
c.handleCall(ctx, frame)
}
}
func (c *wsConn) closeInFlight() {
for id, req := range c.inflight {
req.ready <- clientResponse{
Jsonrpc: "2.0",
ID: id,
Error: &respError{
Message: "handler: websocket connection closed",
Code: 2,
},
}
}
c.handlingLk.Lock()
for _, cancel := range c.handling {
cancel()
}
c.handlingLk.Unlock()
c.inflight = map[int64]clientRequest{}
c.handling = map[int64]context.CancelFunc{}
}
func (c *wsConn) closeChans() {
for chid := range c.chanHandlers {
hnd := c.chanHandlers[chid]
delete(c.chanHandlers, chid)
hnd(nil, false)
}
}
func (c *wsConn) handleWsConn(ctx context.Context) {
c.incoming = make(chan io.Reader)
c.inflight = map[int64]clientRequest{}
c.handling = map[int64]context.CancelFunc{}
c.chanHandlers = map[uint64]func(m []byte, ok bool){}
c.registerCh = make(chan outChanReg)
defer close(c.exiting)
// ////
// on close, make sure to return from all pending calls, and cancel context
// on all calls we handle
defer c.closeInFlight()
// wait for the first message
go c.nextMessage()
for {
select {
case r, ok := <-c.incoming:
if !ok {
if c.incomingErr != nil {
if !websocket.IsCloseError(c.incomingErr, websocket.CloseNormalClosure) {
log.Debugw("websocket error", "error", c.incomingErr)
// connection dropped unexpectedly, do our best to recover it
c.closeInFlight()
c.closeChans()
c.incoming = make(chan io.Reader) // listen again for responses
go func() {
if c.connFactory == nil { // likely the server side, don't try to reconnect
return
}
var conn *websocket.Conn
for conn == nil {
time.Sleep(c.reconnectInterval)
var err error
if conn, err = c.connFactory(); err != nil {
log.Debugw("websocket connection retry failed", "error", err)
}
}
c.writeLk.Lock()
c.conn = conn
c.incomingErr = nil
c.writeLk.Unlock()
go c.nextMessage()
}()
continue
}
}
return // remote closed
}
// debug util - dump all messages to stderr
// r = io.TeeReader(r, os.Stderr)
var frame frame
if err := json.NewDecoder(r).Decode(&frame); err != nil {
log.Error("handle me:", err)
return
}
c.handleFrame(ctx, frame)
go c.nextMessage()
case req := <-c.requests:
c.writeLk.Lock()
if req.req.ID != nil {
if c.incomingErr != nil { // No conn?, immediate fail
req.ready <- clientResponse{
Jsonrpc: "2.0",
ID: *req.req.ID,
Error: &respError{
Message: "handler: websocket connection closed",
Code: 2,
},
}
c.writeLk.Unlock()
break
}
c.inflight[*req.req.ID] = req
}
c.writeLk.Unlock()
if err := c.sendRequest(req.req); err != nil {
log.Errorf("sendReqest failed (Handle me): %s", err)
}
case <-c.stop:
c.writeLk.Lock()
cmsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")
if err := c.conn.WriteMessage(websocket.CloseMessage, cmsg); err != nil {
log.Warn("failed to write close message: ", err)
}
if err := c.conn.Close(); err != nil {
log.Warnw("websocket close error", "error", err)
}
c.writeLk.Unlock()
return
}
}
}
+10 -2
View File
@@ -72,6 +72,10 @@ func CheckBlockSignature(blk *types.BlockHeader, ctx context.Context, worker add
_, span := trace.StartSpan(ctx, "checkBlockSignature")
defer span.End()
if blk.IsValidated() {
return nil
}
if blk.BlockSig == nil {
return xerrors.New("block signature not present")
}
@@ -81,8 +85,12 @@ func CheckBlockSignature(blk *types.BlockHeader, ctx context.Context, worker add
return xerrors.Errorf("failed to get block signing bytes: %w", err)
}
_ = sigb
return Verify(blk.BlockSig, worker, sigb)
err = Verify(blk.BlockSig, worker, sigb)
if err == nil {
blk.SetValidated()
}
return err
}
// SigShim is used for introducing signature functions
+2 -1
View File
@@ -9,7 +9,8 @@ import (
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/node/repo"
)
+2 -2
View File
@@ -8,9 +8,9 @@ import (
"path"
"strconv"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/go-jsonrpc"
)
const listenAddr = "127.0.0.1:2222"
+4 -26
View File
@@ -4,13 +4,14 @@ import (
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
rpcmetrics "github.com/filecoin-project/go-jsonrpc/metrics"
)
// Global Tags
var (
Version, _ = tag.NewKey("version")
Commit, _ = tag.NewKey("commit")
RPCMethod, _ = tag.NewKey("method")
PeerID, _ = tag.NewKey("peer_id")
FailureType, _ = tag.NewKey("failure_type")
MessageFrom, _ = tag.NewKey("message_from")
@@ -31,9 +32,6 @@ var (
BlockValidationFailure = stats.Int64("block/failure", "Counter for block validation failures", stats.UnitDimensionless)
BlockValidationSuccess = stats.Int64("block/success", "Counter for block validation successes", stats.UnitDimensionless)
PeerCount = stats.Int64("peer/count", "Current number of FIL peers", stats.UnitDimensionless)
RPCInvalidMethod = stats.Int64("rpc/invalid_method", "Total number of invalid RPC methods called", stats.UnitDimensionless)
RPCRequestError = stats.Int64("rpc/request_error", "Total number of request errors handled", stats.UnitDimensionless)
RPCResponseError = stats.Int64("rpc/response_error", "Total number of responses errors handled", stats.UnitDimensionless)
)
var (
@@ -82,26 +80,10 @@ var (
Measure: PeerCount,
Aggregation: view.LastValue(),
}
// All RPC related metrics should at the very least tag the RPCMethod
RPCInvalidMethodView = &view.View{
Measure: RPCInvalidMethod,
Aggregation: view.Count(),
TagKeys: []tag.Key{RPCMethod},
}
RPCRequestErrorView = &view.View{
Measure: RPCRequestError,
Aggregation: view.Count(),
TagKeys: []tag.Key{RPCMethod},
}
RPCResponseErrorView = &view.View{
Measure: RPCResponseError,
Aggregation: view.Count(),
TagKeys: []tag.Key{RPCMethod},
}
)
// DefaultViews is an array of OpenCensus views for metric gathering purposes
var DefaultViews = []*view.View{
var DefaultViews = append([]*view.View{
InfoView,
ChainNodeHeightView,
ChainNodeWorkerHeightView,
@@ -111,8 +93,4 @@ var DefaultViews = []*view.View{
MessageReceivedView,
MessageValidationFailureView,
MessageValidationSuccessView,
PeerCountView,
RPCInvalidMethodView,
RPCRequestErrorView,
RPCResponseErrorView,
}
PeerCountView}, rpcmetrics.DefaultViews...)
+97 -6
View File
@@ -9,6 +9,7 @@ import (
address "github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/power"
"github.com/filecoin-project/specs-actors/actors/crypto"
lru "github.com/hashicorp/golang-lru"
@@ -189,7 +190,11 @@ func (m *Miner) mine(ctx context.Context) {
log.Errorf("failed to submit newly mined block: %s", err)
}
} else {
nextRound := time.Unix(int64(base.TipSet.MinTimestamp()+uint64(build.BlockDelay*base.NullRounds)), 0)
// Wait until the next epoch, plus the propagation delay, so a new tipset
// has enough time to form.
//
// See: https://github.com/filecoin-project/lotus/issues/1845
nextRound := time.Unix(int64(base.TipSet.MinTimestamp()+uint64(build.BlockDelay*base.NullRounds))+int64(build.PropagationDelay), 0)
select {
case <-time.After(time.Until(nextRound)):
@@ -239,12 +244,12 @@ func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error)
}
func (m *Miner) hasPower(ctx context.Context, addr address.Address, ts *types.TipSet) (bool, error) {
power, err := m.api.StateMinerPower(ctx, addr, ts.Key())
mpower, err := m.api.StateMinerPower(ctx, addr, ts.Key())
if err != nil {
return false, err
}
return !power.MinerPower.QualityAdjPower.Equals(types.NewInt(0)), nil
return mpower.MinerPower.QualityAdjPower.GreaterThanEqual(power.ConsensusMinerMinPower), nil
}
func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, error) {
@@ -262,6 +267,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
return nil, nil
}
tMBI := time.Now()
beaconPrev := mbi.PrevBeaconEntry
bvals, err := beacon.BeaconEntriesForBlock(ctx, m.beacon, round, beaconPrev)
@@ -269,6 +276,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
return nil, xerrors.Errorf("get beacon entries failed: %w", err)
}
tDrand := time.Now()
hasPower, err := m.hasPower(ctx, m.address, base.TipSet)
if err != nil {
return nil, xerrors.Errorf("checking if miner is slashed: %w", err)
@@ -279,6 +288,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
return nil, nil
}
tPowercheck := time.Now()
log.Infof("Time delta between now and our mining base: %ds (nulls: %d)", uint64(time.Now().Unix())-base.TipSet.MinTimestamp(), base.NullRounds)
rbase := beaconPrev
@@ -301,6 +312,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
return nil, nil
}
tTicket := time.Now()
buf := new(bytes.Buffer)
if err := m.address.MarshalCBOR(buf); err != nil {
return nil, xerrors.Errorf("failed to marshal miner address: %w", err)
@@ -313,6 +326,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
prand := abi.PoStRandomness(rand)
tSeed := time.Now()
postProof, err := m.epp.ComputeProof(ctx, mbi.Sectors, prand)
if err != nil {
return nil, xerrors.Errorf("failed to compute winning post proof: %w", err)
@@ -324,16 +339,27 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg,
return nil, xerrors.Errorf("failed to get pending messages: %w", err)
}
tPending := time.Now()
// TODO: winning post proof
b, err := m.createBlock(base, m.address, ticket, winner, bvals, postProof, pending)
if err != nil {
return nil, xerrors.Errorf("failed to create block: %w", err)
}
dur := time.Since(start)
tCreateBlock := time.Now()
dur := tCreateBlock.Sub(start)
log.Infow("mined new block", "cid", b.Cid(), "height", b.Header.Height, "took", dur)
if dur > time.Second*build.BlockDelay {
log.Warn("CAUTION: block production took longer than the block delay. Your computer may not be fast enough to keep up")
log.Warnw("tMinerBaseInfo ", "duration", tMBI.Sub(start))
log.Warnw("tDrand ", "duration", tDrand.Sub(tMBI))
log.Warnw("tPowercheck ", "duration", tPowercheck.Sub(tDrand))
log.Warnw("tTicket ", "duration", tTicket.Sub(tPowercheck))
log.Warnw("tSeed ", "duration", tSeed.Sub(tTicket))
log.Warnw("tPending ", "duration", tPending.Sub(tSeed))
log.Warnw("tCreateBlock ", "duration", tCreateBlock.Sub(tPending))
}
return b, nil
@@ -403,6 +429,34 @@ func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *type
})
}
type actCacheEntry struct {
act *types.Actor
err error
}
type cachedActorLookup struct {
tsk types.TipSetKey
cache map[address.Address]actCacheEntry
fallback ActorLookup
}
func (c *cachedActorLookup) StateGetActor(ctx context.Context, a address.Address, tsk types.TipSetKey) (*types.Actor, error) {
if c.tsk == tsk {
e, has := c.cache[a]
if has {
return e.act, e.err
}
}
e, err := c.fallback(ctx, a, tsk)
if c.tsk == tsk {
c.cache[a] = actCacheEntry{
act: e, err: err,
}
}
return e, err
}
type ActorLookup func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error)
func countFrom(msgs []*types.SignedMessage, from address.Address) (out int) {
@@ -415,12 +469,26 @@ func countFrom(msgs []*types.SignedMessage, from address.Address) (out int) {
}
func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs []*types.SignedMessage) ([]*types.SignedMessage, error) {
al = (&cachedActorLookup{
tsk: ts.Key(),
cache: map[address.Address]actCacheEntry{},
fallback: al,
}).StateGetActor
out := make([]*types.SignedMessage, 0, build.BlockMessageLimit)
inclNonces := make(map[address.Address]uint64)
inclBalances := make(map[address.Address]types.BigInt)
inclCount := make(map[address.Address]int)
tooLowFundMsgs := 0
tooHighNonceMsgs := 0
start := time.Now()
vmValid := time.Duration(0)
getbal := time.Duration(0)
for _, msg := range msgs {
vmstart := time.Now()
minGas := vm.PricelistByEpoch(ts.Height()).OnChainMessage(msg.ChainLength()) // TODO: really should be doing just msg.ChainLength() but the sync side of this code doesnt seem to have access to that
if err := msg.VMMessage().ValidForBlockInclusion(minGas); err != nil {
@@ -428,6 +496,8 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
continue
}
vmValid += time.Since(vmstart)
// TODO: this should be in some more general 'validate message' call
if msg.Message.GasLimit > build.BlockGasLimit {
log.Warnf("message in mempool had too high of a gas limit (%d)", msg.Message.GasLimit)
@@ -441,6 +511,7 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
from := msg.Message.From
getBalStart := time.Now()
if _, ok := inclNonces[from]; !ok {
act, err := al(ctx, from, ts.Key())
if err != nil {
@@ -451,14 +522,16 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
inclNonces[from] = act.Nonce
inclBalances[from] = act.Balance
}
getbal += time.Since(getBalStart)
if inclBalances[from].LessThan(msg.Message.RequiredFunds()) {
log.Warnf("message in mempool does not have enough funds: %s", msg.Cid())
tooLowFundMsgs++
// todo: drop from mpool
continue
}
if msg.Message.Nonce > inclNonces[from] {
log.Debugf("message in mempool has too high of a nonce (%d > %d, from %s, inclcount %d) %s (%d pending for orig)", msg.Message.Nonce, inclNonces[from], from, inclCount[from], msg.Cid(), countFrom(msgs, from))
tooHighNonceMsgs++
continue
}
@@ -476,5 +549,23 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
break
}
}
if tooLowFundMsgs > 0 {
log.Warnf("%d messages in mempool does not have enough funds", tooLowFundMsgs)
}
if tooHighNonceMsgs > 0 {
log.Warnf("%d messages in mempool had too high nonce", tooLowFundMsgs)
}
sm := time.Now()
if sm.Sub(start) > time.Second {
log.Warnw("SelectMessages took a long time",
"duration", sm.Sub(start),
"vmvalidate", vmValid,
"getbalance", getbal,
"msgs", len(msgs))
}
return out, nil
}
+9 -6
View File
@@ -23,7 +23,7 @@ import (
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/retrievalmarket/discovery"
"github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/storedask"
"github.com/filecoin-project/specs-actors/actors/runtime"
storage2 "github.com/filecoin-project/specs-storage/storage"
@@ -150,6 +150,7 @@ func defaults() []Option {
Override(new(helpers.MetricsCtx), context.Background),
Override(new(record.Validator), modules.RecordValidator),
Override(new(dtypes.Bootstrapper), dtypes.Bootstrapper(false)),
Override(new(dtypes.ShutdownChan), make(chan struct{})),
// Filecoin modules
@@ -169,7 +170,7 @@ func libp2p() Option {
Override(DiscoveryHandlerKey, lp2p.DiscoveryHandler),
Override(AddrsFactoryKey, lp2p.AddrsFactory(nil, nil)),
Override(SmuxTransportKey, lp2p.SmuxTransport(true)),
Override(RelayKey, lp2p.Relay(true, false)),
Override(RelayKey, lp2p.NoRelay()),
Override(SecurityKey, lp2p.Security(true, true)),
Override(BaseRoutingKey, lp2p.BaseRouting),
@@ -259,7 +260,7 @@ func Online() Option {
Override(new(dtypes.ClientDealStore), modules.NewClientDealStore),
Override(new(dtypes.ClientDatastore), modules.NewClientDatastore),
Override(new(dtypes.ClientDataTransfer), modules.NewClientGraphsyncDataTransfer),
Override(new(*requestvalidation.ClientRequestValidator), modules.NewClientRequestValidator),
Override(new(dtypes.ClientRequestValidator), modules.NewClientRequestValidator),
Override(new(storagemarket.StorageClient), modules.StorageClient),
Override(new(storagemarket.StorageClientNode), storageadapter.NewClientNodeAdapter),
Override(RegisterClientValidatorKey, modules.RegisterClientValidator),
@@ -300,8 +301,9 @@ func Online() Option {
Override(new(retrievalmarket.RetrievalProvider), modules.RetrievalProvider),
Override(new(dtypes.ProviderDealStore), modules.NewProviderDealStore),
Override(new(dtypes.ProviderDataTransfer), modules.NewProviderDAGServiceDataTransfer),
Override(new(*requestvalidation.ProviderRequestValidator), modules.NewProviderRequestValidator),
Override(new(dtypes.ProviderRequestValidator), modules.NewProviderRequestValidator),
Override(new(dtypes.ProviderPieceStore), modules.NewProviderPieceStore),
Override(new(*storedask.StoredAsk), modules.NewStorageAsk),
Override(new(storagemarket.StorageProvider), modules.StorageProvider),
Override(new(storagemarket.StorageProviderNode), storageadapter.NewProviderNodeAdapter),
Override(RegisterProviderValidatorKey, modules.RegisterProviderValidator),
@@ -377,12 +379,13 @@ func ConfigFullNode(c interface{}) Option {
return Error(xerrors.Errorf("invalid config from repo, got: %T", c))
}
ipfsMaddr := cfg.Client.IpfsMAddr
useForRetrieval := cfg.Client.IpfsUseForRetrieval
return Options(
ConfigCommon(&cfg.Common),
If(cfg.Client.UseIpfs,
Override(new(dtypes.ClientBlockstore), modules.IpfsClientBlockstore),
Override(new(dtypes.ClientBlockstore), modules.IpfsClientBlockstore(ipfsMaddr, useForRetrieval)),
),
If(cfg.Metrics.HeadNotifs,
Override(HeadMetricsKey, metrics.SendHeadNotifs(cfg.Metrics.Nickname)),
),
+3 -1
View File
@@ -62,7 +62,9 @@ type Metrics struct {
}
type Client struct {
UseIpfs bool
UseIpfs bool
IpfsMAddr string
IpfsUseForRetrieval bool
}
func defCommon() Common {
+7
View File
@@ -2,10 +2,12 @@ package config
import (
"bytes"
"fmt"
"io"
"os"
"github.com/BurntSushi/toml"
"github.com/kelseyhightower/envconfig"
"golang.org/x/xerrors"
)
@@ -32,6 +34,11 @@ func FromReader(reader io.Reader, def interface{}) (interface{}, error) {
return nil, err
}
err = envconfig.Process("LOTUS", cfg)
if err != nil {
return nil, fmt.Errorf("processing env vars overrides: %s", err)
}
return cfg, nil
}
+1 -1
View File
@@ -63,7 +63,7 @@ func (hs *Service) HandleStream(s inet.Stream) {
var hmsg HelloMessage
if err := cborutil.ReadCborRPC(s, &hmsg); err != nil {
log.Infow("failed to read hello message, diconnecting", "error", err)
log.Infow("failed to read hello message, disconnecting", "error", err)
s.Conn().Close()
return
}
+10 -2
View File
@@ -302,7 +302,7 @@ func (a *API) ClientListImports(ctx context.Context) ([]api.Import, error) {
}
}
func (a *API) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref api.FileRef) error {
func (a *API) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error {
if order.MinerPeerID == "" {
mi, err := a.StateMinerInfo(ctx, order.Miner, types.EmptyTSK)
if err != nil {
@@ -331,7 +331,7 @@ func (a *API) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref
ppb := types.BigDiv(order.Total, types.NewInt(order.Size))
a.Retrieval.Retrieve(
_, err := a.Retrieval.Retrieve(
ctx,
order.Root,
retrievalmarket.NewParamsV0(ppb, order.PaymentInterval, order.PaymentIntervalIncrease),
@@ -339,6 +339,9 @@ func (a *API) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref
order.MinerPeerID,
order.Client,
order.Miner)
if err != nil {
return xerrors.Errorf("Retrieve failed: %w", err)
}
select {
case <-ctx.Done():
return xerrors.New("Retrieval Timed Out")
@@ -350,6 +353,11 @@ func (a *API) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref
unsubscribe()
// If ref is nil, it only fetches the data into the configured blockstore.
if ref == nil {
return nil
}
if ref.IsCAR {
f, err := os.OpenFile(ref.Path, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
+15 -8
View File
@@ -3,8 +3,6 @@ package common
import (
"context"
"github.com/filecoin-project/lotus/node/modules/lp2p"
logging "github.com/ipfs/go-log/v2"
"github.com/gbrlsnchs/jwt/v3"
@@ -16,24 +14,28 @@ import (
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/modules/lp2p"
)
type CommonAPI struct {
fx.In
APISecret *dtypes.APIAlg
Host host.Host
Router lp2p.BaseIpfsRouting
APISecret *dtypes.APIAlg
Host host.Host
Router lp2p.BaseIpfsRouting
ShutdownChan dtypes.ShutdownChan
}
type jwtPayload struct {
Allow []string
Allow []auth.Permission
}
func (a *CommonAPI) AuthVerify(ctx context.Context, token string) ([]api.Permission, error) {
func (a *CommonAPI) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) {
var payload jwtPayload
if _, err := jwt.Verify([]byte(token), (*jwt.HMACSHA)(a.APISecret), &payload); err != nil {
return nil, xerrors.Errorf("JWT Verification failed: %w", err)
@@ -42,7 +44,7 @@ func (a *CommonAPI) AuthVerify(ctx context.Context, token string) ([]api.Permiss
return payload.Allow, nil
}
func (a *CommonAPI) AuthNew(ctx context.Context, perms []api.Permission) ([]byte, error) {
func (a *CommonAPI) AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error) {
p := jwtPayload{
Allow: perms, // TODO: consider checking validity
}
@@ -114,4 +116,9 @@ func (a *CommonAPI) LogSetLevel(ctx context.Context, subsystem, level string) er
return logging.SetLogLevel(subsystem, level)
}
func (a *CommonAPI) Shutdown(ctx context.Context) error {
a.ShutdownChan <- struct{}{}
return nil
}
var _ api.Common = &CommonAPI{}
+61 -3
View File
@@ -108,7 +108,7 @@ func (a *StateAPI) StateMinerProvingDeadline(ctx context.Context, addr address.A
return miner.ComputeProvingPeriodDeadline(mas.ProvingPeriodStart, ts.Height()), nil
}
func (a *StateAPI) StateMinerFaults(ctx context.Context, addr address.Address, tsk types.TipSetKey) ([]abi.SectorNumber, error) {
func (a *StateAPI) StateMinerFaults(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.BitField, error) {
ts, err := a.Chain.GetTipSetFromKey(tsk)
if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
@@ -116,6 +116,55 @@ func (a *StateAPI) StateMinerFaults(ctx context.Context, addr address.Address, t
return stmgr.GetMinerFaults(ctx, a.StateManager, ts, addr)
}
func (a *StateAPI) StateAllMinerFaults(ctx context.Context, lookback abi.ChainEpoch, endTsk types.TipSetKey) ([]*api.Fault, error) {
endTs, err := a.Chain.GetTipSetFromKey(endTsk)
if err != nil {
return nil, xerrors.Errorf("loading end tipset %s: %w", endTsk, err)
}
cutoff := endTs.Height() - lookback
miners, err := stmgr.ListMinerActors(ctx, a.StateManager, endTs)
if err != nil {
return nil, xerrors.Errorf("loading miners: %w", err)
}
var allFaults []*api.Fault
for _, m := range miners {
var mas miner.State
_, err := a.StateManager.LoadActorState(ctx, m, &mas, endTs)
if err != nil {
return nil, xerrors.Errorf("failed to load miner actor state %s: %w", m, err)
}
err = mas.ForEachFaultEpoch(a.Chain.Store(ctx), func(faultStart abi.ChainEpoch, faults *abi.BitField) error {
if faultStart >= cutoff {
allFaults = append(allFaults, &api.Fault{
Miner: m,
Epoch: faultStart,
})
return nil
}
return nil
})
if err != nil {
return nil, xerrors.Errorf("failure when iterating over miner states: %w", err)
}
}
return allFaults, nil
}
func (a *StateAPI) StateMinerRecoveries(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.BitField, error) {
ts, err := a.Chain.GetTipSetFromKey(tsk)
if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err)
}
return stmgr.GetMinerRecoveries(ctx, a.StateManager, ts, addr)
}
func (a *StateAPI) StateMinerPower(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*api.MinerPower, error) {
ts, err := a.Chain.GetTipSetFromKey(tsk)
if err != nil {
@@ -437,7 +486,13 @@ func (a *StateAPI) StateMarketDeals(ctx context.Context, tsk types.TipSetKey) (m
var s market.DealState
if err := sa.Get(ctx, i, &s); err != nil {
return err
if err != nil {
if _, ok := err.(*amt.ErrNotFound); !ok {
return xerrors.Errorf("failed to get state for deal in proposals array: %w", err)
}
s.SectorStartEpoch = -1
}
}
out[strconv.FormatInt(int64(i), 10)] = api.MarketDeal{
Proposal: d,
@@ -624,6 +679,9 @@ func (a *StateAPI) MsigGetAvailableBalance(ctx context.Context, addr address.Add
return types.BigSub(act.Balance, minBalance), nil
}
var initialPledgeNum = types.NewInt(103)
var initialPledgeDen = types.NewInt(100)
func (a *StateAPI) StateMinerInitialPledgeCollateral(ctx context.Context, maddr address.Address, snum abi.SectorNumber, tsk types.TipSetKey) (types.BigInt, error) {
ts, err := a.Chain.GetTipSetFromKey(tsk)
if err != nil {
@@ -715,7 +773,7 @@ func (a *StateAPI) StateMinerInitialPledgeCollateral(ctx context.Context, maddr
}
}
return initialPledge, nil
return types.BigDiv(types.BigMul(initialPledge, initialPledgeNum), initialPledgeDen), nil
}
func (a *StateAPI) StateMinerAvailableBalance(ctx context.Context, maddr address.Address, tsk types.TipSetKey) (types.BigInt, error) {
+3 -2
View File
@@ -6,12 +6,13 @@ import (
"golang.org/x/xerrors"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/specs-actors/actors/abi"
storage2 "github.com/filecoin-project/specs-storage/storage"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/client"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"github.com/filecoin-project/sector-storage"
)
@@ -29,7 +30,7 @@ func (r *remoteWorker) AddPiece(ctx context.Context, sector abi.SectorID, pieceS
}
func connectRemoteWorker(ctx context.Context, fa api.Common, url string) (*remoteWorker, error) {
token, err := fa.AuthNew(ctx, []api.Permission{"admin"})
token, err := fa.AuthNew(ctx, []auth.Permission{"admin"})
if err != nil {
return nil, xerrors.Errorf("creating auth token for remote connection: %w", err)
}
+2 -1
View File
@@ -12,6 +12,7 @@ import (
"github.com/filecoin-project/go-address"
storagemarket "github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-jsonrpc/auth"
sectorstorage "github.com/filecoin-project/sector-storage"
"github.com/filecoin-project/sector-storage/ffiwrapper"
"github.com/filecoin-project/sector-storage/stores"
@@ -43,7 +44,7 @@ type StorageMinerAPI struct {
}
func (sm *StorageMinerAPI) ServeRemote(w http.ResponseWriter, r *http.Request) {
if !apistruct.HasPerm(r.Context(), apistruct.PermAdmin) {
if !auth.HasPerm(r.Context(), nil, apistruct.PermAdmin) {
w.WriteHeader(401)
json.NewEncoder(w).Encode(struct{ Error string }{"unauthorized: missing write permission"})
return
+7 -2
View File
@@ -60,14 +60,19 @@ func MessagePool(lc fx.Lifecycle, sm *stmgr.StateManager, ps *pubsub.PubSub, ds
return mp, nil
}
func ChainBlockstore(r repo.LockedRepo) (dtypes.ChainBlockstore, error) {
func ChainBlockstore(lc fx.Lifecycle, mctx helpers.MetricsCtx, r repo.LockedRepo) (dtypes.ChainBlockstore, error) {
blocks, err := r.Datastore("/blocks")
if err != nil {
return nil, err
}
bs := blockstore.NewBlockstore(blocks)
return blockstore.NewIdStore(bs), nil
cbs, err := blockstore.CachedBlockstore(helpers.LifecycleCtx(mctx, lc), bs, blockstore.DefaultCacheOpts())
if err != nil {
return nil, err
}
return blockstore.NewIdStore(cbs), nil
}
func ChainGCBlockstore(bs dtypes.ChainBlockstore, gcl dtypes.ChainGCLocker) dtypes.ChainGCBlockstore {
+11 -6
View File
@@ -44,7 +44,12 @@ func ClientFstore(r repo.LockedRepo) (dtypes.ClientFilestore, error) {
}
blocks := namespace.Wrap(clientds, datastore.NewKey("blocks"))
fm := filestore.NewFileManager(clientds, filepath.Dir(r.Path()))
absPath, err := filepath.Abs(r.Path())
if err != nil {
return nil, err
}
fm := filestore.NewFileManager(clientds, filepath.Dir(absPath))
fm.AllowFiles = true
// TODO: fm.AllowUrls (needs more code in client import)
@@ -59,8 +64,8 @@ func ClientBlockstore(fstore dtypes.ClientFilestore) dtypes.ClientBlockstore {
// RegisterClientValidator is an initialization hook that registers the client
// request validator with the data transfer module as the validator for
// StorageDataTransferVoucher types
func RegisterClientValidator(crv *requestvalidation.ClientRequestValidator, dtm dtypes.ClientDataTransfer) {
if err := dtm.RegisterVoucherType(&requestvalidation.StorageDataTransferVoucher{}, crv); err != nil {
func RegisterClientValidator(crv dtypes.ClientRequestValidator, dtm dtypes.ClientDataTransfer) {
if err := dtm.RegisterVoucherType(&requestvalidation.StorageDataTransferVoucher{}, (*requestvalidation.UnifiedRequestValidator)(crv)); err != nil {
panic(err)
}
}
@@ -100,8 +105,8 @@ func ClientDAG(mctx helpers.MetricsCtx, lc fx.Lifecycle, ibs dtypes.ClientBlocks
return dag
}
func NewClientRequestValidator(deals dtypes.ClientDealStore) *requestvalidation.ClientRequestValidator {
return requestvalidation.NewClientRequestValidator(deals)
func NewClientRequestValidator(deals dtypes.ClientDealStore) dtypes.ClientRequestValidator {
return requestvalidation.NewUnifiedRequestValidator(nil, deals)
}
func StorageClient(lc fx.Lifecycle, h host.Host, ibs dtypes.ClientBlockstore, r repo.LockedRepo, dataTransfer dtypes.ClientDataTransfer, discovery *discovery.Local, deals dtypes.ClientDatastore, scn storagemarket.StorageClientNode) (storagemarket.StorageClient, error) {
@@ -128,5 +133,5 @@ func RetrievalClient(h host.Host, bs dtypes.ClientBlockstore, pmgr *paychmgr.Man
adapter := retrievaladapter.NewRetrievalClientNode(pmgr, payapi, chainapi)
network := rmnet.NewFromLibp2pHost(h)
sc := storedcounter.New(ds, datastore.NewKey("/retr"))
return retrievalimpl.NewClient(network, bs, adapter, resolver, ds, sc)
return retrievalimpl.NewClient(network, bs, adapter, resolver, namespace.Wrap(ds, datastore.NewKey("/retrievals/client")), sc)
}
+10 -8
View File
@@ -7,18 +7,20 @@ import (
"io"
"io/ioutil"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/addrutil"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/repo"
"github.com/gbrlsnchs/jwt/v3"
logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p-core/peerstore"
record "github.com/libp2p/go-libp2p-record"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/addrutil"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/repo"
)
var log = logging.Logger("modules")
@@ -35,7 +37,7 @@ func RecordValidator(ps peerstore.Peerstore) record.Validator {
const JWTSecretName = "auth-jwt-private"
type jwtPayload struct {
Allow []string
Allow []auth.Permission
}
func APISecret(keystore types.KeyStore, lr repo.LockedRepo) (*dtypes.APIAlg, error) {
+5
View File
@@ -0,0 +1,5 @@
package dtypes
// ShutdownChan is a channel to which you send a value if you intend to shut
// down the daemon (or storage miner), including the node and RPC server.
type ShutdownChan chan struct{}
+3
View File
@@ -1,6 +1,7 @@
package dtypes
import (
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
bserv "github.com/ipfs/go-blockservice"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-filestore"
@@ -29,6 +30,7 @@ type ClientFilestore *filestore.Filestore
type ClientBlockstore blockstore.Blockstore
type ClientDAG format.DAGService
type ClientDealStore *statestore.StateStore
type ClientRequestValidator *requestvalidation.UnifiedRequestValidator
type ClientDatastore datastore.Batching
type Graphsync graphsync.GraphExchange
@@ -38,6 +40,7 @@ type ClientDataTransfer datatransfer.Manager
type ProviderDealStore *statestore.StateStore
type ProviderPieceStore piecestore.PieceStore
type ProviderRequestValidator *requestvalidation.UnifiedRequestValidator
// ProviderDataTransfer is a data transfer manager for the provider
type ProviderDataTransfer datatransfer.Manager
+28 -9
View File
@@ -6,6 +6,7 @@ import (
"github.com/ipfs/go-filestore"
blockstore "github.com/ipfs/go-ipfs-blockstore"
"github.com/multiformats/go-multiaddr"
"github.com/filecoin-project/lotus/lib/bufbstore"
"github.com/filecoin-project/lotus/lib/ipfsbstore"
@@ -13,14 +14,32 @@ import (
"github.com/filecoin-project/lotus/node/modules/helpers"
)
func IpfsClientBlockstore(mctx helpers.MetricsCtx, lc fx.Lifecycle, fstore dtypes.ClientFilestore) (dtypes.ClientBlockstore, error) {
ipfsbs, err := ipfsbstore.NewIpfsBstore(helpers.LifecycleCtx(mctx, lc))
if err != nil {
return nil, xerrors.Errorf("constructing ipfs blockstore: %w", err)
// IpfsClientBlockstore returns a ClientBlockstore implementation backed by an IPFS node.
// If ipfsMaddr is empty, a local IPFS node is assumed considering IPFS_PATH configuration.
// If ipfsMaddr is not empty, it will connect to the remote IPFS node with the provided multiaddress.
// The flag useForRetrieval indicates if the IPFS node will also be used for storing retrieving deals.
func IpfsClientBlockstore(ipfsMaddr string, useForRetrieval bool) func(helpers.MetricsCtx, fx.Lifecycle, dtypes.ClientFilestore) (dtypes.ClientBlockstore, error) {
return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, fstore dtypes.ClientFilestore) (dtypes.ClientBlockstore, error) {
var err error
var ipfsbs *ipfsbstore.IpfsBstore
if ipfsMaddr != "" {
var ma multiaddr.Multiaddr
ma, err = multiaddr.NewMultiaddr(ipfsMaddr)
if err != nil {
return nil, xerrors.Errorf("parsing ipfs multiaddr: %w", err)
}
ipfsbs, err = ipfsbstore.NewRemoteIpfsBstore(helpers.LifecycleCtx(mctx, lc), ma)
} else {
ipfsbs, err = ipfsbstore.NewIpfsBstore(helpers.LifecycleCtx(mctx, lc))
}
if err != nil {
return nil, xerrors.Errorf("constructing ipfs blockstore: %w", err)
}
var ws blockstore.Blockstore
ws = ipfsbs
if !useForRetrieval {
ws = blockstore.NewIdStore((*filestore.Filestore)(fstore))
}
return bufbstore.NewTieredBstore(ipfsbs, ws), nil
}
return bufbstore.NewTieredBstore(
ipfsbs,
blockstore.NewIdStore((*filestore.Filestore)(fstore)),
), nil
}
+45 -25
View File
@@ -22,6 +22,7 @@ func init() {
// configure larger overlay parameters
pubsub.GossipSubD = 8
pubsub.GossipSubDscore = 6
pubsub.GossipSubDout = 3
pubsub.GossipSubDlo = 6
pubsub.GossipSubDhi = 12
pubsub.GossipSubDlazy = 12
@@ -62,6 +63,10 @@ func GossipSub(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, nn dtyp
// TODO we want to whitelist IPv6 /64s that belong to datacenters etc
// IPColocationFactorWhitelist: map[string]struct{}{},
// P7: behavioural penalties, decay after 1hr
BehaviourPenaltyWeight: -10,
BehaviourPenaltyDecay: pubsub.ScoreParameterDecay(time.Hour),
DecayInterval: pubsub.DefaultDecayInterval,
DecayToZero: pubsub.DefaultDecayToZero,
@@ -84,18 +89,28 @@ func GossipSub(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, nn dtyp
FirstMessageDeliveriesDecay: pubsub.ScoreParameterDecay(time.Hour),
FirstMessageDeliveriesCap: 100, // 100 blocks in an hour
// tracks deliveries in the last minute
// penalty activates at 1 minute and expects ~0.4 blocks
MeshMessageDeliveriesWeight: -576, // max penalty is -100
MeshMessageDeliveriesDecay: pubsub.ScoreParameterDecay(time.Minute),
MeshMessageDeliveriesCap: 10, // 10 blocks in a minute
MeshMessageDeliveriesThreshold: 0.41666, // 10/12/2 blocks/min
MeshMessageDeliveriesWindow: 10 * time.Millisecond,
MeshMessageDeliveriesActivation: time.Minute,
// decays after 15 min
MeshFailurePenaltyWeight: -576,
MeshFailurePenaltyDecay: pubsub.ScoreParameterDecay(15 * time.Minute),
// Mesh Delivery Failure is currently turned off for blocks
// This is on purpose as
// - the traffic is very low for meaningful distribution of incoming edges.
// - the reaction time needs to be very slow -- in the order of 10 min at least
// so we might as well let opportunistic grafting repair the mesh on its own
// pace.
// - the network is too small, so large asymmetries can be expected between mesh
// edges.
// We should revisit this once the network grows.
//
// // tracks deliveries in the last minute
// // penalty activates at 1 minute and expects ~0.4 blocks
// MeshMessageDeliveriesWeight: -576, // max penalty is -100
// MeshMessageDeliveriesDecay: pubsub.ScoreParameterDecay(time.Minute),
// MeshMessageDeliveriesCap: 10, // 10 blocks in a minute
// MeshMessageDeliveriesThreshold: 0.41666, // 10/12/2 blocks/min
// MeshMessageDeliveriesWindow: 10 * time.Millisecond,
// MeshMessageDeliveriesActivation: time.Minute,
//
// // decays after 15 min
// MeshFailurePenaltyWeight: -576,
// MeshFailurePenaltyDecay: pubsub.ScoreParameterDecay(15 * time.Minute),
// invalid messages decay after 1 hour
InvalidMessageDeliveriesWeight: -1000,
@@ -113,20 +128,24 @@ func GossipSub(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, nn dtyp
// deliveries decay after 10min, cap at 1000 tx
FirstMessageDeliveriesWeight: 0.5, // max value is 500
FirstMessageDeliveriesDecay: pubsub.ScoreParameterDecay(10 * time.Minute),
FirstMessageDeliveriesCap: 1000,
//FirstMessageDeliveriesCap: 1000,
FirstMessageDeliveriesCap: 1, // we can't yet properly validate them so only confer a tiny boost from delivery
// tracks deliveries in the last minute
// penalty activates at 1 min and expects 2.5 txs
MeshMessageDeliveriesWeight: -16, // max penalty is -100
MeshMessageDeliveriesDecay: pubsub.ScoreParameterDecay(time.Minute),
MeshMessageDeliveriesCap: 100, // 100 txs in a minute
MeshMessageDeliveriesThreshold: 2.5, // 60/12/2 txs/minute
MeshMessageDeliveriesWindow: 10 * time.Millisecond,
MeshMessageDeliveriesActivation: time.Minute,
// Mesh Delivery Failure is currently turned off for messages
// This is on purpose as the network is still too small, which results in
// asymmetries and potential unmeshing from negative scores.
// // tracks deliveries in the last minute
// // penalty activates at 1 min and expects 2.5 txs
// MeshMessageDeliveriesWeight: -16, // max penalty is -100
// MeshMessageDeliveriesDecay: pubsub.ScoreParameterDecay(time.Minute),
// MeshMessageDeliveriesCap: 100, // 100 txs in a minute
// MeshMessageDeliveriesThreshold: 2.5, // 60/12/2 txs/minute
// MeshMessageDeliveriesWindow: 10 * time.Millisecond,
// MeshMessageDeliveriesActivation: time.Minute,
// decays after 5min
MeshFailurePenaltyWeight: -16,
MeshFailurePenaltyDecay: pubsub.ScoreParameterDecay(5 * time.Minute),
// // decays after 5min
// MeshFailurePenaltyWeight: -16,
// MeshFailurePenaltyDecay: pubsub.ScoreParameterDecay(5 * time.Minute),
// invalid messages decay after 1 hour
InvalidMessageDeliveriesWeight: -2000,
@@ -139,7 +158,7 @@ func GossipSub(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, nn dtyp
PublishThreshold: -1000,
GraylistThreshold: -2500,
AcceptPXThreshold: 1000,
OpportunisticGraftThreshold: 2.5,
OpportunisticGraftThreshold: 5,
},
),
}
@@ -151,6 +170,7 @@ func GossipSub(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, nn dtyp
pubsub.GossipSubDscore = 0
pubsub.GossipSubDlo = 0
pubsub.GossipSubDhi = 0
pubsub.GossipSubDout = 0
pubsub.GossipSubDlazy = 1024
pubsub.GossipSubGossipFactor = 0.5
// turn on PX
+3 -31
View File
@@ -4,29 +4,15 @@ import (
"fmt"
"github.com/libp2p/go-libp2p"
circuit "github.com/libp2p/go-libp2p-circuit"
coredisc "github.com/libp2p/go-libp2p-core/discovery"
routing "github.com/libp2p/go-libp2p-core/routing"
discovery "github.com/libp2p/go-libp2p-discovery"
basichost "github.com/libp2p/go-libp2p/p2p/host/basic"
relay "github.com/libp2p/go-libp2p/p2p/host/relay"
"go.uber.org/fx"
"github.com/filecoin-project/lotus/node/modules/helpers"
)
func Relay(disable, enableHop bool) func() (opts Libp2pOpts, err error) {
func NoRelay() func() (opts Libp2pOpts, err error) {
return func() (opts Libp2pOpts, err error) {
if disable {
// Enabled by default.
opts.Opts = append(opts.Opts, libp2p.DisableRelay())
} else {
relayOpts := []circuit.RelayOpt{circuit.OptDiscovery}
if enableHop {
relayOpts = append(relayOpts, circuit.OptHop)
}
opts.Opts = append(opts.Opts, libp2p.EnableRelay(relayOpts...))
}
// always disabled, it's an eclipse attack vector
opts.Opts = append(opts.Opts, libp2p.DisableRelay())
return
}
}
@@ -40,17 +26,3 @@ func Discovery(router BaseIpfsRouting) (coredisc.Discovery, error) {
return discovery.NewRoutingDiscovery(crouter), nil
}
// NOTE: only set when relayHop is set
func AdvertiseRelay(mctx helpers.MetricsCtx, d coredisc.Discovery) {
relay.Advertise(mctx, d)
}
// NOTE: only set when relayHop is not set
func AutoRelay(mctx helpers.MetricsCtx, lc fx.Lifecycle, router BaseIpfsRouting, h RawHost, d coredisc.Discovery) error {
ctx := helpers.LifecycleCtx(mctx, lc)
// TODO: review: LibP2P doesn't set this as host in config.go, why?
_ = relay.NewAutoRelay(ctx, h.(*basichost.BasicHost), d, router, nil)
return nil
}
+12 -7
View File
@@ -3,6 +3,8 @@ package modules
import (
"context"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
eventbus "github.com/libp2p/go-eventbus"
event "github.com/libp2p/go-libp2p-core/event"
"github.com/libp2p/go-libp2p-core/host"
@@ -20,6 +22,7 @@ import (
"github.com/filecoin-project/lotus/chain/beacon/drand"
"github.com/filecoin-project/lotus/chain/blocksync"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/sub"
"github.com/filecoin-project/lotus/lib/peermgr"
@@ -58,7 +61,7 @@ func RunBlockSync(h host.Host, svc *blocksync.BlockSyncService) {
h.SetStreamHandler(blocksync.BlockSyncProtocolID, svc.HandleStream)
}
func HandleIncomingBlocks(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, s *chain.Syncer, h host.Host, nn dtypes.NetworkName) {
func HandleIncomingBlocks(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, s *chain.Syncer, chain *store.ChainStore, stmgr *stmgr.StateManager, h host.Host, nn dtypes.NetworkName) {
ctx := helpers.LifecycleCtx(mctx, lc)
blocksub, err := ps.Subscribe(build.BlocksTopic(nn))
@@ -66,16 +69,18 @@ func HandleIncomingBlocks(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.P
panic(err)
}
v := sub.NewBlockValidator(func(p peer.ID) {
ps.BlacklistPeer(p)
h.ConnManager().TagPeer(p, "badblock", -1000)
})
v := sub.NewBlockValidator(
chain, stmgr,
func(p peer.ID) {
ps.BlacklistPeer(p)
h.ConnManager().TagPeer(p, "badblock", -1000)
})
if err := ps.RegisterTopicValidator(build.BlocksTopic(nn), v.Validate); err != nil {
panic(err)
}
go sub.HandleIncomingBlocks(ctx, blocksub, s, h.ConnManager(), v)
go sub.HandleIncomingBlocks(ctx, blocksub, s, h.ConnManager())
}
func HandleIncomingMessages(mctx helpers.MetricsCtx, lc fx.Lifecycle, ps *pubsub.PubSub, mpool *messagepool.MessagePool, nn dtypes.NetworkName) {
@@ -111,7 +116,7 @@ func RunDealClient(mctx helpers.MetricsCtx, lc fx.Lifecycle, c storagemarket.Sto
}
func NewLocalDiscovery(ds dtypes.MetadataDS) *discovery.Local {
return discovery.NewLocal(ds)
return discovery.NewLocal(namespace.Wrap(ds, datastore.NewKey("/deals/local")))
}
func RetrievalResolver(l *discovery.Local) retrievalmarket.PeerResolver {
+43 -44
View File
@@ -29,10 +29,18 @@ import (
"github.com/filecoin-project/go-fil-markets/storagemarket"
storageimpl "github.com/filecoin-project/go-fil-markets/storagemarket/impl"
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/requestvalidation"
"github.com/filecoin-project/go-fil-markets/storagemarket/impl/storedask"
smnet "github.com/filecoin-project/go-fil-markets/storagemarket/network"
"github.com/filecoin-project/go-jsonrpc/auth"
paramfetch "github.com/filecoin-project/go-paramfetch"
"github.com/filecoin-project/go-statestore"
"github.com/filecoin-project/go-storedcounter"
sectorstorage "github.com/filecoin-project/sector-storage"
"github.com/filecoin-project/sector-storage/ffiwrapper"
"github.com/filecoin-project/sector-storage/stores"
"github.com/filecoin-project/specs-actors/actors/abi"
sealing "github.com/filecoin-project/storage-fsm"
lapi "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/beacon"
@@ -45,13 +53,10 @@ import (
"github.com/filecoin-project/lotus/node/modules/helpers"
"github.com/filecoin-project/lotus/node/repo"
"github.com/filecoin-project/lotus/storage"
sectorstorage "github.com/filecoin-project/sector-storage"
"github.com/filecoin-project/sector-storage/ffiwrapper"
"github.com/filecoin-project/sector-storage/stores"
"github.com/filecoin-project/specs-actors/actors/abi"
sealing "github.com/filecoin-project/storage-fsm"
)
var StorageCounterDSPrefix = "/storage/nextid"
func minerAddrFromDS(ds dtypes.MetadataDS) (address.Address, error) {
maddrb, err := ds.Get(datastore.NewKey("miner-address"))
if err != nil {
@@ -116,7 +121,7 @@ func (s *sidsc) Next() (abi.SectorNumber, error) {
}
func SectorIDCounter(ds dtypes.MetadataDS) sealing.SectorIDCounter {
sc := storedcounter.New(ds, datastore.NewKey("/storage/nextid"))
sc := storedcounter.New(ds, datastore.NewKey(StorageCounterDSPrefix))
return &sidsc{sc}
}
@@ -138,7 +143,7 @@ func StorageMiner(mctx helpers.MetricsCtx, lc fx.Lifecycle, api lapi.FullNode, h
return nil, err
}
fps, err := storage.NewWindowedPoStScheduler(api, sealer, maddr, worker)
fps, err := storage.NewWindowedPoStScheduler(api, sealer, sealer, maddr, worker)
if err != nil {
return nil, err
}
@@ -190,8 +195,8 @@ func HandleDeals(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, h sto
// RegisterProviderValidator is an initialization hook that registers the provider
// request validator with the data transfer module as the validator for
// StorageDataTransferVoucher types
func RegisterProviderValidator(mrv *requestvalidation.ProviderRequestValidator, dtm dtypes.ProviderDataTransfer) {
if err := dtm.RegisterVoucherType(&requestvalidation.StorageDataTransferVoucher{}, mrv); err != nil {
func RegisterProviderValidator(mrv dtypes.ProviderRequestValidator, dtm dtypes.ProviderDataTransfer) {
if err := dtm.RegisterVoucherType(&requestvalidation.StorageDataTransferVoucher{}, (*requestvalidation.UnifiedRequestValidator)(mrv)); err != nil {
panic(err)
}
}
@@ -205,13 +210,13 @@ func NewProviderDAGServiceDataTransfer(h host.Host, gs dtypes.StagingGraphsync,
// NewProviderDealStore creates a statestore for the client to store its deals
func NewProviderDealStore(ds dtypes.MetadataDS) dtypes.ProviderDealStore {
return statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/client")))
return statestore.New(namespace.Wrap(ds, datastore.NewKey("/deals/provider")))
}
// NewProviderPieceStore creates a statestore for storing metadata about pieces
// shared by the storage and retrieval providers
func NewProviderPieceStore(ds dtypes.MetadataDS) dtypes.ProviderPieceStore {
return piecestore.NewPieceStore(ds)
return piecestore.NewPieceStore(namespace.Wrap(ds, datastore.NewKey("/storagemarket")))
}
// StagingBlockstore creates a blockstore for staging blocks for a miner
@@ -281,44 +286,38 @@ func SetupBlockProducer(lc fx.Lifecycle, ds dtypes.MetadataDS, api lapi.FullNode
return m, nil
}
func NewProviderRequestValidator(deals dtypes.ProviderDealStore) *requestvalidation.ProviderRequestValidator {
return requestvalidation.NewProviderRequestValidator(deals)
func NewProviderRequestValidator(deals dtypes.ProviderDealStore) dtypes.ProviderRequestValidator {
return requestvalidation.NewUnifiedRequestValidator(deals, nil)
}
func StorageProvider(ctx helpers.MetricsCtx, fapi lapi.FullNode, h host.Host, ds dtypes.MetadataDS, ibs dtypes.StagingBlockstore, r repo.LockedRepo, pieceStore dtypes.ProviderPieceStore, dataTransfer dtypes.ProviderDataTransfer, spn storagemarket.StorageProviderNode) (storagemarket.StorageProvider, error) {
func NewStorageAsk(ctx helpers.MetricsCtx, fapi lapi.FullNode, ds dtypes.MetadataDS, minerAddress dtypes.MinerAddress, spn storagemarket.StorageProviderNode) (*storedask.StoredAsk, error) {
mi, err := fapi.StateMinerInfo(ctx, address.Address(minerAddress), types.EmptyTSK)
if err != nil {
return nil, err
}
storedAsk, err := storedask.NewStoredAsk(namespace.Wrap(ds, datastore.NewKey("/deals/provider")), datastore.NewKey("latest-ask"), spn, address.Address(minerAddress))
if err != nil {
return nil, err
}
// Hacky way to set max piece size to the sector size
a := storedAsk.GetAsk(address.Address(minerAddress)).Ask
err = storedAsk.AddAsk(a.Price, a.Expiry-a.Timestamp, storagemarket.MaxPieceSize(abi.PaddedPieceSize(mi.SectorSize)))
if err != nil {
return storedAsk, err
}
return storedAsk, nil
}
func StorageProvider(minerAddress dtypes.MinerAddress, ffiConfig *ffiwrapper.Config, storedAsk *storedask.StoredAsk, h host.Host, ds dtypes.MetadataDS, ibs dtypes.StagingBlockstore, r repo.LockedRepo, pieceStore dtypes.ProviderPieceStore, dataTransfer dtypes.ProviderDataTransfer, spn storagemarket.StorageProviderNode) (storagemarket.StorageProvider, error) {
net := smnet.NewFromLibp2pHost(h)
store, err := piecefilestore.NewLocalFileStore(piecefilestore.OsPath(r.Path()))
if err != nil {
return nil, err
}
net := smnet.NewFromLibp2pHost(h)
addr, err := ds.Get(datastore.NewKey("miner-address"))
if err != nil {
return nil, err
}
minerAddress, err := address.NewFromBytes(addr)
if err != nil {
return nil, err
}
mi, err := fapi.StateMinerInfo(ctx, minerAddress, types.EmptyTSK)
if err != nil {
return nil, err
}
rt, err := ffiwrapper.SealProofTypeFromSectorSize(mi.SectorSize)
if err != nil {
return nil, err
}
p, err := storageimpl.NewProvider(net, ds, ibs, store, pieceStore, dataTransfer, spn, minerAddress, rt)
if err != nil {
return p, err
}
// Hacky way to set max piece size to the sector size
a := p.ListAsks(minerAddress)[0].Ask
err = p.AddAsk(a.Price, a.Expiry-a.Timestamp, storagemarket.MaxPieceSize(abi.PaddedPieceSize(mi.SectorSize)))
p, err := storageimpl.NewProvider(net, namespace.Wrap(ds, datastore.NewKey("/deals/provider")), ibs, store, pieceStore, dataTransfer, spn, address.Address(minerAddress), ffiConfig.SealProofType, storedAsk)
if err != nil {
return p, err
}
@@ -334,7 +333,7 @@ func RetrievalProvider(h host.Host, miner *storage.Miner, sealer sectorstorage.S
return nil, err
}
network := rmnet.NewFromLibp2pHost(h)
return retrievalimpl.NewProvider(address, adapter, network, pieceStore, ibs, ds)
return retrievalimpl.NewProvider(address, adapter, network, pieceStore, ibs, namespace.Wrap(ds, datastore.NewKey("/retrievals/provider")))
}
func SectorStorage(mctx helpers.MetricsCtx, lc fx.Lifecycle, ls stores.LocalStorage, si stores.SectorIndex, cfg *ffiwrapper.Config, sc sectorstorage.SealerConfig, urls sectorstorage.URLs, sa sectorstorage.StorageAuth) (*sectorstorage.Manager, error) {
@@ -359,7 +358,7 @@ func SectorStorage(mctx helpers.MetricsCtx, lc fx.Lifecycle, ls stores.LocalStor
}
func StorageAuth(ctx helpers.MetricsCtx, ca lapi.Common) (sectorstorage.StorageAuth, error) {
token, err := ca.AuthNew(ctx, []lapi.Permission{"admin"})
token, err := ca.AuthNew(ctx, []auth.Permission{"admin"})
if err != nil {
return nil, xerrors.Errorf("creating storage auth header: %w", err)
}
+2 -2
View File
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
@@ -39,7 +40,6 @@ import (
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
genesis "github.com/filecoin-project/lotus/genesis"
"github.com/filecoin-project/lotus/lib/jsonrpc"
"github.com/filecoin-project/lotus/miner"
"github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/modules"
@@ -82,7 +82,7 @@ func testStorageNode(ctx context.Context, t *testing.T, waddr address.Address, a
err = ds.Put(datastore.NewKey("miner-address"), act.Bytes())
require.NoError(t, err)
nic := storedcounter.New(ds, datastore.NewKey("/storage/nextid"))
nic := storedcounter.New(ds, datastore.NewKey(modules.StorageCounterDSPrefix))
for i := 0; i < nGenesisPreseals; i++ {
nic.Next()
}
+9 -2
View File
@@ -1,9 +1,9 @@
package repo
import (
"bytes"
"encoding/json"
"fmt"
"github.com/filecoin-project/sector-storage/stores"
"io"
"io/ioutil"
"os"
@@ -21,6 +21,8 @@ import (
"github.com/multiformats/go-multiaddr"
"golang.org/x/xerrors"
"github.com/filecoin-project/sector-storage/stores"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/config"
)
@@ -191,7 +193,12 @@ func (fsr *FsRepo) APIToken() ([]byte, error) {
}
defer f.Close() //nolint: errcheck // Read only op
return ioutil.ReadAll(f)
tb, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
return bytes.TrimSpace(tb), nil
}
// Lock acquires exclusive lock on this repo
+2 -1
View File
@@ -2,6 +2,7 @@ package repo
import (
"errors"
"github.com/filecoin-project/sector-storage/stores"
"github.com/ipfs/go-datastore"
@@ -13,7 +14,7 @@ import (
var (
ErrNoAPIEndpoint = errors.New("API not running (no endpoint)")
ErrNoAPIToken = errors.New("API token not set")
ErrRepoAlreadyLocked = errors.New("repo is already locked")
ErrRepoAlreadyLocked = errors.New("repo is already locked (lotus daemon already running)")
ErrClosedRepo = errors.New("repo is no longer open")
)
+1 -1
View File
@@ -4,7 +4,7 @@ set -o xtrace
export TRUST_PARAMS=1
go run -tags=debug ./cmd/lotus-seed pre-seal --sector-size 2048 --num-sectors 2
go run -tags=debug ./cmd/lotus-seed pre-seal --sector-size 2KiB --num-sectors 2
go run -tags=debug ./cmd/lotus-seed genesis new localnet.json
go run -tags=debug ./cmd/lotus-seed genesis add-miner localnet.json ~/.genesis-sectors/pre-seal-t01000.json
go run -tags=debug ./cmd/lotus daemon --lotus-make-genesis=devel.gen --genesis-template=localnet.json --bootstrap=false

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