Compare commits

..
1 Commits
Author SHA1 Message Date
Travis Person 96a5e579cf New interop network info 2020-06-08 18:43:25 +00:00
157 changed files with 1622 additions and 7190 deletions
+2 -2
View File
@@ -45,7 +45,7 @@ commands:
- 'v25-2k-lotus-params' - 'v25-2k-lotus-params'
paths: paths:
- /var/tmp/filecoin-proof-parameters/ - /var/tmp/filecoin-proof-parameters/
- run: ./lotus fetch-params 2048 - run: ./lotus fetch-params --proving-params 2048
- save_cache: - save_cache:
name: Save parameters cache name: Save parameters cache
key: 'v25-2k-lotus-params' key: 'v25-2k-lotus-params'
@@ -314,7 +314,7 @@ workflows:
ci: ci:
jobs: jobs:
- lint-changes: - lint-changes:
args: "--new-from-rev origin/master" args: "--new-from-rev origin/next"
- mod-tidy-check - mod-tidy-check
- gofmt - gofmt
- test: - test:
-1
View File
@@ -35,4 +35,3 @@ build/paramfetch.sh
bin/ipget bin/ipget
bin/tmp/* bin/tmp/*
.idea .idea
scratchpad
+2 -17
View File
@@ -6,7 +6,7 @@ all: build
unexport GOFLAGS unexport GOFLAGS
GOVERSION:=$(shell go version | cut -d' ' -f 3 | cut -d. -f 2) GOVERSION:=$(shell go version | cut -d' ' -f 3 | cut -d. -f 2)
ifeq ($(shell expr $(GOVERSION) \< 14), 1) ifeq ($(shell expr $(GOVERSION) \< 13), 1)
$(warning Your Golang version is go 1.$(GOVERSION)) $(warning Your Golang version is go 1.$(GOVERSION))
$(error Update Golang to version $(shell grep '^go' go.mod)) $(error Update Golang to version $(shell grep '^go' go.mod))
endif endif
@@ -105,17 +105,15 @@ install:
install-services: install install-services: install
mkdir -p /usr/local/lib/systemd/system mkdir -p /usr/local/lib/systemd/system
mkdir -p /var/log/lotus
install -C -m 0644 ./scripts/lotus-daemon.service /usr/local/lib/systemd/system/lotus-daemon.service 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 install -C -m 0644 ./scripts/lotus-miner.service /usr/local/lib/systemd/system/lotus-miner.service
systemctl daemon-reload systemctl daemon-reload
@echo @echo
@echo "lotus-daemon and lotus-miner services installed. Don't forget to 'systemctl enable lotus-daemon|lotus-miner' for it to be enabled on startup." @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: clean-services:
rm -f /usr/local/lib/systemd/system/lotus-daemon.service rm -f /usr/local/lib/systemd/system/lotus-daemon.service
rm -f /usr/local/lib/systemd/system/lotus-miner.service rm -f /usr/local/lib/systemd/system/lotus-miner.service
rm -f /usr/local/lib/systemd/system/chainwatch.service
systemctl daemon-reload systemctl daemon-reload
# TOOLS # TOOLS
@@ -162,13 +160,6 @@ chainwatch:
.PHONY: chainwatch .PHONY: chainwatch
BINS+=chainwatch BINS+=chainwatch
install-chainwatch-service: chainwatch
install -C ./chainwatch /usr/local/bin/chainwatch
install -C -m 0644 ./scripts/chainwatch.service /usr/local/lib/systemd/system/chainwatch.service
systemctl daemon-reload
@echo
@echo "chainwatch installed. Don't forget to 'systemctl enable chainwatch' for it to be enabled on startup."
bench: bench:
rm -f bench rm -f bench
go build -o bench ./cmd/lotus-bench go build -o bench ./cmd/lotus-bench
@@ -191,12 +182,6 @@ health:
.PHONY: health .PHONY: health
BINS+=health BINS+=health
testground:
go build -tags testground -o /dev/null ./cmd/lotus
.PHONY: testground
BINS+=testground
# MISC # MISC
buildall: $(BINS) buildall: $(BINS)
+3 -12
View File
@@ -4,22 +4,13 @@
Lotus is an implementation of the Filecoin Distributed Storage Network. For more details about Filecoin, check out the [Filecoin Spec](https://github.com/filecoin-project/specs). Lotus is an implementation of the Filecoin Distributed Storage Network. For more details about Filecoin, check out the [Filecoin Spec](https://github.com/filecoin-project/specs).
## Building & Documentation
For instructions on how to build lotus from source, please visit [https://docs.lotu.sh](https://docs.lotu.sh) or read the source [here](https://github.com/filecoin-project/lotus/tree/master/documentation).
## Reporting a Vulnerability
Please send an email to security@filecoin.org. See our [security policy](SECURITY.md) for more details.
## Development ## Development
All work is tracked via issues. An attempt at keeping an up-to-date view on remaining work is in the [lotus testnet github project board](https://github.com/filecoin-project/lotus/projects/1). All work is tracked via issues. An attempt at keeping an up-to-date view on remaining work is in the [lotus testnet github project board](https://github.com/filecoin-project/lotus/projects/1).
The main branches under development at the moment are: ## Building & Documentation
* [`master`](https://github.com/filecoin-project/lotus): current testnet.
* [`next`](https://github.com/filecoin-project/lotus/tree/next): working branch with chain-breaking changes. For instructions on how to build lotus from source, please visit [https://docs.lotu.sh](https://docs.lotu.sh) or read the source [here](https://github.com/filecoin-project/lotus/tree/master/documentation).
* [`interopnet`](https://github.com/filecoin-project/lotus/tree/interopnet): devnet running one of `next` commits.
## License ## License
-29
View File
@@ -1,29 +0,0 @@
# Security Policy
## Reporting a Vulnerability
For *critical* bugs, please send an email to security@filecoin.org.
The bug reporting process differs between bugs that are critical and may crash the network, and others that are unlikely to cause problems if malicious parties know about it. For non-critical bugs, please simply file a GitHub [issue](https://github.com/filecoin-project/lotus/issues/new?template=bug_report.md).
Please try to provide a clear description of any bugs reported, along with how to reproduce the bug if possible. More detailed bug reports (especially those with a PoC included) will help us move forward much faster. Additionally, please avoid reporting bugs that already have open issues. Take a moment to search the issue list of the related GitHub repositories before writing up a new report.
Here are some examples of bugs we would consider 'critical':
* If you can spend from a `multisig` wallet you do not control the keys for.
* If you can cause a miner to be slashed without them actually misbehaving.
* If you can maintain power without submitting windowed posts regularly.
* If you can craft a message that causes lotus nodes to panic.
* If you can cause your miner to win significantly more blocks than it should.
* If you can craft a message that causes a persistent fork in the network.
* If you can cause the total amount of Filecoin in the network to no longer be 2 billion.
This is not an exhaustive list, but should provide some idea of what we consider 'critical'.
## Supported Versions
* TODO: This should be defined and set up by Mainnet launch.
| Version | Supported |
| ------- | ------------------ |
| Testnet | :white_check_mark: |
+2 -8
View File
@@ -13,13 +13,11 @@ import (
) )
type Common interface { type Common interface {
// Auth
// MethodGroup: Auth
AuthVerify(ctx context.Context, token string) ([]auth.Permission, error) AuthVerify(ctx context.Context, token string) ([]auth.Permission, error)
AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error) AuthNew(ctx context.Context, perms []auth.Permission) ([]byte, error)
// MethodGroup: Net // network
NetConnectedness(context.Context, peer.ID) (network.Connectedness, error) NetConnectedness(context.Context, peer.ID) (network.Connectedness, error)
NetPeers(context.Context) ([]peer.AddrInfo, error) NetPeers(context.Context) ([]peer.AddrInfo, error)
@@ -29,8 +27,6 @@ type Common interface {
NetFindPeer(context.Context, peer.ID) (peer.AddrInfo, error) NetFindPeer(context.Context, peer.ID) (peer.AddrInfo, error)
NetPubsubScores(context.Context) ([]PubsubScore, error) NetPubsubScores(context.Context) ([]PubsubScore, error)
// MethodGroup: Common
// ID returns peerID of libp2p node backing this API // ID returns peerID of libp2p node backing this API
ID(context.Context) (peer.ID, error) ID(context.Context) (peer.ID, error)
@@ -42,8 +38,6 @@ type Common interface {
// trigger graceful shutdown // trigger graceful shutdown
Shutdown(context.Context) error Shutdown(context.Context) error
Closing(context.Context) (<-chan struct{}, error)
} }
// Version provides various build-time information // Version provides various build-time information
+12 -80
View File
@@ -35,71 +35,26 @@ type FullNode interface {
// ChainNotify returns channel with chain head updates // ChainNotify returns channel with chain head updates
// First message is guaranteed to be of len == 1, and type == 'current' // First message is guaranteed to be of len == 1, and type == 'current'
ChainNotify(context.Context) (<-chan []*HeadChange, error) ChainNotify(context.Context) (<-chan []*HeadChange, error)
// ChainHead returns the current head of the chain // ChainHead returns the current head of the chain
ChainHead(context.Context) (*types.TipSet, error) ChainHead(context.Context) (*types.TipSet, error)
// ChainGetRandomness is used to sample the chain for randomness // 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) 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 returns the block specified by the given CID
ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error) ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error)
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error) ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
// ChainGetBlockMessages returns messages stored in the specified block ChainGetParentReceipts(context.Context, cid.Cid) ([]*types.MessageReceipt, error)
ChainGetBlockMessages(ctx context.Context, blockCid cid.Cid) (*BlockMessages, error) ChainGetParentMessages(context.Context, cid.Cid) ([]Message, error)
// ChainGetParentReceipts returns receipts for messages in parent tipset of
// the specified block
ChainGetParentReceipts(ctx context.Context, blockCid cid.Cid) ([]*types.MessageReceipt, error)
// ChainGetParentReceipts returns messages stored in parent tipset of the
// specified block
ChainGetParentMessages(ctx context.Context, blockCid cid.Cid) ([]Message, error)
// ChainGetTipSetByHeight looks back for a tipset at the specified epoch.
// If there are no blocks at the specified epoch, a tipset at higher epoch
// will be returned
ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error) ChainGetTipSetByHeight(context.Context, abi.ChainEpoch, types.TipSetKey) (*types.TipSet, error)
// ChainReadObj reads ipld nodes referenced by the specified CID from chain
// blockstore and returns raw bytes
ChainReadObj(context.Context, cid.Cid) ([]byte, error) ChainReadObj(context.Context, cid.Cid) ([]byte, error)
// ChainHasObj checks if a given CID exists in the chain blockstore
ChainHasObj(context.Context, cid.Cid) (bool, error) ChainHasObj(context.Context, cid.Cid) (bool, error)
ChainStatObj(context.Context, cid.Cid, cid.Cid) (ObjStat, error) ChainStatObj(context.Context, cid.Cid, cid.Cid) (ObjStat, error)
// ChainSetHead forcefully sets current chain head. Use with caution
ChainSetHead(context.Context, types.TipSetKey) error ChainSetHead(context.Context, types.TipSetKey) error
// ChainGetGenesis returns the genesis tipset
ChainGetGenesis(context.Context) (*types.TipSet, error) ChainGetGenesis(context.Context) (*types.TipSet, error)
// ChainTipSetWeight computes weight for the specified tipset
ChainTipSetWeight(context.Context, types.TipSetKey) (types.BigInt, error) ChainTipSetWeight(context.Context, types.TipSetKey) (types.BigInt, error)
ChainGetNode(ctx context.Context, p string) (*IpldObject, error) ChainGetNode(ctx context.Context, p string) (*IpldObject, error)
// ChainGetMessage reads a message referenced by the specified CID from the
// chain blockstore
ChainGetMessage(context.Context, cid.Cid) (*types.Message, error) ChainGetMessage(context.Context, cid.Cid) (*types.Message, error)
// ChainGetPath returns a set of revert/apply operations needed to get from
// one tipset to another, for example:
//```
// to
// ^
// from tAA
// ^ ^
// tBA tAB
// ^---*--^
// ^
// tRR
//```
// Would return `[revert(tBA), apply(tAB), apply(tAA)]`
ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*HeadChange, error) ChainGetPath(ctx context.Context, from types.TipSetKey, to types.TipSetKey) ([]*HeadChange, error)
// ChainExport returns a stream of bytes with CAR dump of chain data
ChainExport(context.Context, types.TipSetKey) (<-chan []byte, error) ChainExport(context.Context, types.TipSetKey) (<-chan []byte, error)
// MethodGroup: Sync // MethodGroup: Sync
@@ -108,45 +63,23 @@ type FullNode interface {
// SyncState returns the current status of the lotus sync system // SyncState returns the current status of the lotus sync system
SyncState(context.Context) (*SyncState, error) SyncState(context.Context) (*SyncState, error)
// SyncSubmitBlock can be used to submit a newly created block to the // SyncSubmitBlock can be used to submit a newly created block to the
// network through this node // network through this node
SyncSubmitBlock(ctx context.Context, blk *types.BlockMsg) error SyncSubmitBlock(ctx context.Context, blk *types.BlockMsg) error
// SyncIncomingBlocks returns a channel streaming incoming, potentially not
// yet synced block headers.
SyncIncomingBlocks(ctx context.Context) (<-chan *types.BlockHeader, error) SyncIncomingBlocks(ctx context.Context) (<-chan *types.BlockHeader, error)
// SyncMarkBad marks a blocks as bad, meaning that it won't ever by synced.
// Use with extreme caution
SyncMarkBad(ctx context.Context, bcid cid.Cid) error SyncMarkBad(ctx context.Context, bcid cid.Cid) error
// SyncCheckBad checks if a block was marked as bad, and if it was, returns
// the reason
SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error) SyncCheckBad(ctx context.Context, bcid cid.Cid) (string, error)
// MethodGroup: Mpool // MethodGroup: Mpool
// The Mpool methods are for interacting with the message pool. The message pool // The Mpool methods are for interacting with the message pool. The message pool
// manages all incoming and outgoing 'messages' going over the network. // manages all incoming and outgoing 'messages' going over the network.
// MpoolPending returns pending mempool messages
MpoolPending(context.Context, types.TipSetKey) ([]*types.SignedMessage, error) MpoolPending(context.Context, types.TipSetKey) ([]*types.SignedMessage, error)
// MpoolPush pushes a signed message to mempool
MpoolPush(context.Context, *types.SignedMessage) (cid.Cid, error) MpoolPush(context.Context, *types.SignedMessage) (cid.Cid, error)
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error) // get nonce, sign, push
// MpoolPushMessage atomically assigns a nonce, signs, and pushes a message
// to mempool
MpoolPushMessage(context.Context, *types.Message) (*types.SignedMessage, error)
// MpoolGetNonce gets next nonce for the specified sender.
// Note that this method may not be atomic. Use MpoolPushMessage instead
MpoolGetNonce(context.Context, address.Address) (uint64, error) MpoolGetNonce(context.Context, address.Address) (uint64, error)
MpoolSub(context.Context) (<-chan MpoolUpdate, error) MpoolSub(context.Context) (<-chan MpoolUpdate, error)
MpoolEstimateGasPrice(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error)
// MpoolEstimateGasPrice estimates what gas price should be used for a
// message to have high likelihood of inclusion in `nblocksincl` epochs
MpoolEstimateGasPrice(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error)
// MethodGroup: Miner // MethodGroup: Miner
@@ -185,7 +118,6 @@ type FullNode interface {
ClientListDeals(ctx context.Context) ([]DealInfo, error) ClientListDeals(ctx context.Context) ([]DealInfo, error)
ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error) ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error)
ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error) ClientFindData(ctx context.Context, root cid.Cid) ([]QueryOffer, error)
ClientMinerQueryOffer(ctx context.Context, root cid.Cid, miner address.Address) (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) ClientQueryAsk(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error)
ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error) ClientCalcCommP(ctx context.Context, inpath string, miner address.Address) (*CommPRet, error)
@@ -206,7 +138,7 @@ type FullNode interface {
StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error) StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error)
StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error) StateReplay(context.Context, types.TipSetKey, cid.Cid) (*InvocResult, error)
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error)
StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*ActorState, error) StateReadState(ctx context.Context, act *types.Actor, tsk types.TipSetKey) (*ActorState, error)
StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error) StateListMessages(ctx context.Context, match *types.Message, tsk types.TipSetKey, toht abi.ChainEpoch) ([]cid.Cid, error)
StateNetworkName(context.Context) (dtypes.NetworkName, error) StateNetworkName(context.Context) (dtypes.NetworkName, error)
@@ -214,7 +146,7 @@ type FullNode interface {
StateMinerProvingSet(context.Context, address.Address, types.TipSetKey) ([]*ChainSectorInfo, error) StateMinerProvingSet(context.Context, address.Address, types.TipSetKey) ([]*ChainSectorInfo, error)
StateMinerProvingDeadline(context.Context, address.Address, types.TipSetKey) (*miner.DeadlineInfo, error) StateMinerProvingDeadline(context.Context, address.Address, types.TipSetKey) (*miner.DeadlineInfo, error)
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*MinerPower, error) StateMinerPower(context.Context, address.Address, types.TipSetKey) (*MinerPower, error)
StateMinerInfo(context.Context, address.Address, types.TipSetKey) (MinerInfo, error) StateMinerInfo(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error)
StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error) StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error)
StateMinerFaults(context.Context, address.Address, types.TipSetKey) (*abi.BitField, 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 // Returns all non-expired Faults that occur within lookback epochs of the given tipset
@@ -418,11 +350,11 @@ type RetrievalOrder struct {
} }
type InvocResult struct { type InvocResult struct {
Msg *types.Message Msg *types.Message
MsgRct *types.MessageReceipt MsgRct *types.MessageReceipt
ExecutionTrace types.ExecutionTrace InternalExecutions []*types.ExecutionResult
Error string Error string
Duration time.Duration Duration time.Duration
} }
type MethodCall struct { type MethodCall struct {
+1 -13
View File
@@ -36,7 +36,6 @@ type StorageMiner interface {
SectorsRefs(context.Context) (map[string][]SealedRef, error) SectorsRefs(context.Context) (map[string][]SealedRef, error)
SectorsUpdate(context.Context, abi.SectorNumber, SectorState) error SectorsUpdate(context.Context, abi.SectorNumber, SectorState) error
SectorRemove(context.Context, abi.SectorNumber) error
StorageList(ctx context.Context) (map[stores.ID][]stores.Decl, error) StorageList(ctx context.Context) (map[stores.ID][]stores.Decl, error)
StorageLocal(ctx context.Context) (map[stores.ID]string, error) StorageLocal(ctx context.Context) (map[stores.ID]string, error)
@@ -51,21 +50,10 @@ type StorageMiner interface {
MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error
MarketListDeals(ctx context.Context) ([]storagemarket.StorageDeal, error) MarketListDeals(ctx context.Context) ([]storagemarket.StorageDeal, error)
MarketListIncompleteDeals(ctx context.Context) ([]storagemarket.MinerDeal, error) MarketListIncompleteDeals(ctx context.Context) ([]storagemarket.MinerDeal, error)
MarketSetAsk(ctx context.Context, price types.BigInt, duration abi.ChainEpoch, minPieceSize abi.PaddedPieceSize, maxPieceSize abi.PaddedPieceSize) error MarketSetPrice(context.Context, types.BigInt) error
MarketGetAsk(ctx context.Context) (*storagemarket.SignedStorageAsk, error)
DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error
DealsList(ctx context.Context) ([]storagemarket.StorageDeal, error) DealsList(ctx context.Context) ([]storagemarket.StorageDeal, error)
DealsConsiderOnlineStorageDeals(context.Context) (bool, error)
DealsSetConsiderOnlineStorageDeals(context.Context, bool) error
DealsConsiderOnlineRetrievalDeals(context.Context) (bool, error)
DealsSetConsiderOnlineRetrievalDeals(context.Context, bool) error
DealsPieceCidBlocklist(context.Context) ([]cid.Cid, error)
DealsSetPieceCidBlocklist(context.Context, []cid.Cid) error
DealsConsiderOfflineStorageDeals(context.Context) (bool, error)
DealsSetConsiderOfflineStorageDeals(context.Context, bool) error
DealsConsiderOfflineRetrievalDeals(context.Context) (bool, error)
DealsSetConsiderOfflineRetrievalDeals(context.Context, bool) error
StorageAddLocal(ctx context.Context, path string) error StorageAddLocal(ctx context.Context, path string) error
} }
+45 -130
View File
@@ -50,8 +50,7 @@ type CommonStruct struct {
LogList func(context.Context) ([]string, error) `perm:"write"` LogList func(context.Context) ([]string, error) `perm:"write"`
LogSetLevel func(context.Context, string, string) error `perm:"write"` LogSetLevel func(context.Context, string, string) error `perm:"write"`
Shutdown func(context.Context) error `perm:"admin"` Shutdown func(context.Context) error `perm:"admin"`
Closing func(context.Context) (<-chan struct{}, error) `perm:"read"`
} }
} }
@@ -109,25 +108,24 @@ type FullNodeStruct struct {
WalletImport func(context.Context, *types.KeyInfo) (address.Address, error) `perm:"admin"` WalletImport func(context.Context, *types.KeyInfo) (address.Address, error) `perm:"admin"`
WalletDelete func(context.Context, address.Address) error `perm:"write"` WalletDelete func(context.Context, address.Address) error `perm:"write"`
ClientImport func(ctx context.Context, ref api.FileRef) (cid.Cid, error) `perm:"admin"` ClientImport func(ctx context.Context, ref api.FileRef) (cid.Cid, error) `perm:"admin"`
ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"` ClientListImports func(ctx context.Context) ([]api.Import, error) `perm:"write"`
ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"` ClientHasLocal func(ctx context.Context, root cid.Cid) (bool, error) `perm:"write"`
ClientFindData func(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) `perm:"read"` ClientFindData func(ctx context.Context, root cid.Cid) ([]api.QueryOffer, error) `perm:"read"`
ClientMinerQueryOffer func(ctx context.Context, root cid.Cid, miner address.Address) (api.QueryOffer, error) `perm:"read"` ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"` ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"` ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
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"`
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"`
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"`
ClientGenCar func(ctx context.Context, ref api.FileRef, outpath string) error `perm:"write"`
StateNetworkName func(context.Context) (dtypes.NetworkName, error) `perm:"read"` StateNetworkName func(context.Context) (dtypes.NetworkName, error) `perm:"read"`
StateMinerSectors func(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"` StateMinerSectors func(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
StateMinerProvingSet func(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"` StateMinerProvingSet func(context.Context, address.Address, types.TipSetKey) ([]*api.ChainSectorInfo, error) `perm:"read"`
StateMinerProvingDeadline func(context.Context, address.Address, types.TipSetKey) (*miner.DeadlineInfo, error) `perm:"read"` StateMinerProvingDeadline func(context.Context, address.Address, types.TipSetKey) (*miner.DeadlineInfo, error) `perm:"read"`
StateMinerPower func(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error) `perm:"read"` StateMinerPower func(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error) `perm:"read"`
StateMinerInfo func(context.Context, address.Address, types.TipSetKey) (api.MinerInfo, 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"` StateMinerDeadlines func(context.Context, address.Address, types.TipSetKey) (*miner.Deadlines, error) `perm:"read"`
StateMinerFaults func(context.Context, address.Address, types.TipSetKey) (*abi.BitField, 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"` StateAllMinerFaults func(context.Context, abi.ChainEpoch, types.TipSetKey) ([]*api.Fault, error) `perm:"read"`
@@ -139,7 +137,7 @@ type FullNodeStruct struct {
StateCall func(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) `perm:"read"` StateCall func(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) `perm:"read"`
StateReplay func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"` StateReplay func(context.Context, types.TipSetKey, cid.Cid) (*api.InvocResult, error) `perm:"read"`
StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"` StateGetActor func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) `perm:"read"`
StateReadState func(context.Context, address.Address, types.TipSetKey) (*api.ActorState, error) `perm:"read"` StateReadState func(context.Context, *types.Actor, types.TipSetKey) (*api.ActorState, error) `perm:"read"`
StatePledgeCollateral func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"` StatePledgeCollateral func(context.Context, types.TipSetKey) (types.BigInt, error) `perm:"read"`
StateWaitMsg func(ctx context.Context, cid cid.Cid, confidence uint64) (*api.MsgLookup, error) `perm:"read"` StateWaitMsg func(ctx context.Context, cid cid.Cid, confidence uint64) (*api.MsgLookup, error) `perm:"read"`
StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"` StateSearchMsg func(context.Context, cid.Cid) (*api.MsgLookup, error) `perm:"read"`
@@ -194,11 +192,10 @@ type StorageMinerStruct struct {
MiningBase func(context.Context) (*types.TipSet, error) `perm:"read"` MiningBase func(context.Context) (*types.TipSet, error) `perm:"read"`
MarketImportDealData func(context.Context, cid.Cid, string) error `perm:"write"` MarketImportDealData func(context.Context, cid.Cid, string) error `perm:"write"`
MarketListDeals func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"` MarketListDeals func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"`
MarketListIncompleteDeals func(ctx context.Context) ([]storagemarket.MinerDeal, error) `perm:"read"` MarketListIncompleteDeals func(ctx context.Context) ([]storagemarket.MinerDeal, error) `perm:"read"`
MarketSetAsk func(ctx context.Context, price types.BigInt, duration abi.ChainEpoch, minPieceSize abi.PaddedPieceSize, maxPieceSize abi.PaddedPieceSize) error `perm:"admin"` MarketSetPrice func(context.Context, types.BigInt) error `perm:"admin"`
MarketGetAsk func(ctx context.Context) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
PledgeSector func(context.Context) error `perm:"write"` PledgeSector func(context.Context) error `perm:"write"`
@@ -206,36 +203,24 @@ type StorageMinerStruct struct {
SectorsList func(context.Context) ([]abi.SectorNumber, error) `perm:"read"` SectorsList func(context.Context) ([]abi.SectorNumber, error) `perm:"read"`
SectorsRefs func(context.Context) (map[string][]api.SealedRef, error) `perm:"read"` SectorsRefs func(context.Context) (map[string][]api.SealedRef, error) `perm:"read"`
SectorsUpdate func(context.Context, abi.SectorNumber, api.SectorState) error `perm:"write"` SectorsUpdate func(context.Context, abi.SectorNumber, api.SectorState) error `perm:"write"`
SectorRemove func(context.Context, abi.SectorNumber) error `perm:"admin"`
WorkerConnect func(context.Context, string) error `perm:"admin"` // TODO: worker perm WorkerConnect func(context.Context, string) error `perm:"admin"` // TODO: worker perm
WorkerStats func(context.Context) (map[uint64]storiface.WorkerStats, error) `perm:"admin"` WorkerStats func(context.Context) (map[uint64]storiface.WorkerStats, error) `perm:"admin"`
StorageList func(context.Context) (map[stores.ID][]stores.Decl, error) `perm:"admin"` StorageList func(context.Context) (map[stores.ID][]stores.Decl, error) `perm:"admin"`
StorageLocal func(context.Context) (map[stores.ID]string, error) `perm:"admin"` StorageLocal func(context.Context) (map[stores.ID]string, error) `perm:"admin"`
StorageStat func(context.Context, stores.ID) (stores.FsStat, error) `perm:"admin"` StorageStat func(context.Context, stores.ID) (stores.FsStat, error) `perm:"admin"`
StorageAttach func(context.Context, stores.StorageInfo, stores.FsStat) error `perm:"admin"` StorageAttach func(context.Context, stores.StorageInfo, stores.FsStat) error `perm:"admin"`
StorageDeclareSector func(context.Context, stores.ID, abi.SectorID, stores.SectorFileType, bool) error `perm:"admin"` StorageDeclareSector func(context.Context, stores.ID, abi.SectorID, stores.SectorFileType, bool) error `perm:"admin"`
StorageDropSector func(context.Context, stores.ID, abi.SectorID, stores.SectorFileType) error `perm:"admin"` StorageDropSector func(context.Context, stores.ID, abi.SectorID, stores.SectorFileType) error `perm:"admin"`
StorageFindSector func(context.Context, abi.SectorID, stores.SectorFileType, bool) ([]stores.SectorStorageInfo, error) `perm:"admin"` StorageFindSector func(context.Context, abi.SectorID, stores.SectorFileType, bool) ([]stores.SectorStorageInfo, error) `perm:"admin"`
StorageInfo func(context.Context, stores.ID) (stores.StorageInfo, error) `perm:"admin"` StorageInfo func(context.Context, stores.ID) (stores.StorageInfo, error) `perm:"admin"`
StorageBestAlloc func(ctx context.Context, allocate stores.SectorFileType, spt abi.RegisteredSealProof, sealing stores.PathType) ([]stores.StorageInfo, error) `perm:"admin"` StorageBestAlloc func(ctx context.Context, allocate stores.SectorFileType, spt abi.RegisteredProof, sealing stores.PathType) ([]stores.StorageInfo, error) `perm:"admin"`
StorageReportHealth func(ctx context.Context, id stores.ID, report stores.HealthReport) error `perm:"admin"` StorageReportHealth func(ctx context.Context, id stores.ID, report stores.HealthReport) error `perm:"admin"`
StorageLock func(ctx context.Context, sector abi.SectorID, read stores.SectorFileType, write stores.SectorFileType) error `perm:"admin"` StorageLock func(ctx context.Context, sector abi.SectorID, read stores.SectorFileType, write stores.SectorFileType) error `perm:"admin"`
StorageTryLock func(ctx context.Context, sector abi.SectorID, read stores.SectorFileType, write stores.SectorFileType) (bool, error) `perm:"admin"`
DealsImportData func(ctx context.Context, dealPropCid cid.Cid, file string) error `perm:"write"` DealsImportData func(ctx context.Context, dealPropCid cid.Cid, file string) error `perm:"write"`
DealsList func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"` DealsList func(ctx context.Context) ([]storagemarket.StorageDeal, error) `perm:"read"`
DealsConsiderOnlineStorageDeals func(context.Context) (bool, error) `perm:"read"`
DealsSetConsiderOnlineStorageDeals func(context.Context, bool) error `perm:"admin"`
DealsConsiderOnlineRetrievalDeals func(context.Context) (bool, error) `perm:"read"`
DealsSetConsiderOnlineRetrievalDeals func(context.Context, bool) error `perm:"admin"`
DealsConsiderOfflineStorageDeals func(context.Context) (bool, error) `perm:"read"`
DealsSetConsiderOfflineStorageDeals func(context.Context, bool) error `perm:"admin"`
DealsConsiderOfflineRetrievalDeals func(context.Context) (bool, error) `perm:"read"`
DealsSetConsiderOfflineRetrievalDeals func(context.Context, bool) error `perm:"admin"`
DealsPieceCidBlocklist func(context.Context) ([]cid.Cid, error) `perm:"read"`
DealsSetPieceCidBlocklist func(context.Context, []cid.Cid) error `perm:"admin"`
StorageAddLocal func(ctx context.Context, path string) error `perm:"admin"` StorageAddLocal func(ctx context.Context, path string) error `perm:"admin"`
} }
@@ -251,14 +236,12 @@ type WorkerStruct struct {
Paths func(context.Context) ([]stores.StoragePath, error) `perm:"admin"` Paths func(context.Context) ([]stores.StoragePath, error) `perm:"admin"`
Info func(context.Context) (storiface.WorkerInfo, error) `perm:"admin"` Info func(context.Context) (storiface.WorkerInfo, error) `perm:"admin"`
SealPreCommit1 func(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, pieces []abi.PieceInfo) (storage.PreCommit1Out, error) `perm:"admin"` SealPreCommit1 func(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, pieces []abi.PieceInfo) (storage.PreCommit1Out, error) `perm:"admin"`
SealPreCommit2 func(context.Context, abi.SectorID, storage.PreCommit1Out) (cids storage.SectorCids, err error) `perm:"admin"` SealPreCommit2 func(context.Context, abi.SectorID, storage.PreCommit1Out) (cids storage.SectorCids, err error) `perm:"admin"`
SealCommit1 func(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (storage.Commit1Out, error) `perm:"admin"` SealCommit1 func(ctx context.Context, sector abi.SectorID, ticket abi.SealRandomness, seed abi.InteractiveSealRandomness, pieces []abi.PieceInfo, cids storage.SectorCids) (storage.Commit1Out, error) `perm:"admin"`
SealCommit2 func(context.Context, abi.SectorID, storage.Commit1Out) (storage.Proof, error) `perm:"admin"` SealCommit2 func(context.Context, abi.SectorID, storage.Commit1Out) (storage.Proof, error) `perm:"admin"`
FinalizeSector func(context.Context, abi.SectorID, []storage.Range) error `perm:"admin"` FinalizeSector func(context.Context, abi.SectorID) error `perm:"admin"`
ReleaseUnsealed func(ctx context.Context, sector abi.SectorID, safeToFree []storage.Range) error `perm:"admin"` MoveStorage func(ctx context.Context, sector abi.SectorID) error `perm:"admin"`
Remove func(ctx context.Context, sector abi.SectorID) error `perm:"admin"`
MoveStorage func(ctx context.Context, sector abi.SectorID) error `perm:"admin"`
UnsealPiece func(context.Context, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize, abi.SealRandomness, cid.Cid) error `perm:"admin"` UnsealPiece func(context.Context, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize, abi.SealRandomness, cid.Cid) error `perm:"admin"`
ReadPiece func(context.Context, io.Writer, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) error `perm:"admin"` ReadPiece func(context.Context, io.Writer, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) error `perm:"admin"`
@@ -328,10 +311,6 @@ func (c *CommonStruct) Shutdown(ctx context.Context) error {
return c.Internal.Shutdown(ctx) return c.Internal.Shutdown(ctx)
} }
func (c *CommonStruct) Closing(ctx context.Context) (<-chan struct{}, error) {
return c.Internal.Closing(ctx)
}
// FullNodeStruct // FullNodeStruct
func (c *FullNodeStruct) ClientListImports(ctx context.Context) ([]api.Import, error) { func (c *FullNodeStruct) ClientListImports(ctx context.Context) ([]api.Import, error) {
@@ -350,10 +329,6 @@ func (c *FullNodeStruct) ClientFindData(ctx context.Context, root cid.Cid) ([]ap
return c.Internal.ClientFindData(ctx, root) return c.Internal.ClientFindData(ctx, root)
} }
func (c *FullNodeStruct) ClientMinerQueryOffer(ctx context.Context, root cid.Cid, miner address.Address) (api.QueryOffer, error) {
return c.Internal.ClientMinerQueryOffer(ctx, root, miner)
}
func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) { func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) {
return c.Internal.ClientStartDeal(ctx, params) return c.Internal.ClientStartDeal(ctx, params)
} }
@@ -576,7 +551,7 @@ func (c *FullNodeStruct) StateMinerPower(ctx context.Context, a address.Address,
return c.Internal.StateMinerPower(ctx, a, tsk) return c.Internal.StateMinerPower(ctx, a, tsk)
} }
func (c *FullNodeStruct) StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (api.MinerInfo, error) { func (c *FullNodeStruct) StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (miner.MinerInfo, error) {
return c.Internal.StateMinerInfo(ctx, actor, tsk) return c.Internal.StateMinerInfo(ctx, actor, tsk)
} }
@@ -624,8 +599,8 @@ func (c *FullNodeStruct) StateGetActor(ctx context.Context, actor address.Addres
return c.Internal.StateGetActor(ctx, actor, tsk) return c.Internal.StateGetActor(ctx, actor, tsk)
} }
func (c *FullNodeStruct) StateReadState(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*api.ActorState, error) { func (c *FullNodeStruct) StateReadState(ctx context.Context, act *types.Actor, tsk types.TipSetKey) (*api.ActorState, error) {
return c.Internal.StateReadState(ctx, addr, tsk) return c.Internal.StateReadState(ctx, act, tsk)
} }
func (c *FullNodeStruct) StatePledgeCollateral(ctx context.Context, tsk types.TipSetKey) (types.BigInt, error) { func (c *FullNodeStruct) StatePledgeCollateral(ctx context.Context, tsk types.TipSetKey) (types.BigInt, error) {
@@ -796,10 +771,6 @@ func (c *StorageMinerStruct) SectorsUpdate(ctx context.Context, id abi.SectorNum
return c.Internal.SectorsUpdate(ctx, id, state) return c.Internal.SectorsUpdate(ctx, id, state)
} }
func (c *StorageMinerStruct) SectorRemove(ctx context.Context, number abi.SectorNumber) error {
return c.Internal.SectorRemove(ctx, number)
}
func (c *StorageMinerStruct) WorkerConnect(ctx context.Context, url string) error { func (c *StorageMinerStruct) WorkerConnect(ctx context.Context, url string) error {
return c.Internal.WorkerConnect(ctx, url) return c.Internal.WorkerConnect(ctx, url)
} }
@@ -840,7 +811,7 @@ func (c *StorageMinerStruct) StorageInfo(ctx context.Context, id stores.ID) (sto
return c.Internal.StorageInfo(ctx, id) return c.Internal.StorageInfo(ctx, id)
} }
func (c *StorageMinerStruct) StorageBestAlloc(ctx context.Context, allocate stores.SectorFileType, spt abi.RegisteredSealProof, pt stores.PathType) ([]stores.StorageInfo, error) { func (c *StorageMinerStruct) StorageBestAlloc(ctx context.Context, allocate stores.SectorFileType, spt abi.RegisteredProof, pt stores.PathType) ([]stores.StorageInfo, error) {
return c.Internal.StorageBestAlloc(ctx, allocate, spt, pt) return c.Internal.StorageBestAlloc(ctx, allocate, spt, pt)
} }
@@ -852,10 +823,6 @@ func (c *StorageMinerStruct) StorageLock(ctx context.Context, sector abi.SectorI
return c.Internal.StorageLock(ctx, sector, read, write) return c.Internal.StorageLock(ctx, sector, read, write)
} }
func (c *StorageMinerStruct) StorageTryLock(ctx context.Context, sector abi.SectorID, read stores.SectorFileType, write stores.SectorFileType) (bool, error) {
return c.Internal.StorageTryLock(ctx, sector, read, write)
}
func (c *StorageMinerStruct) MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error { func (c *StorageMinerStruct) MarketImportDealData(ctx context.Context, propcid cid.Cid, path string) error {
return c.Internal.MarketImportDealData(ctx, propcid, path) return c.Internal.MarketImportDealData(ctx, propcid, path)
} }
@@ -868,12 +835,8 @@ func (c *StorageMinerStruct) MarketListIncompleteDeals(ctx context.Context) ([]s
return c.Internal.MarketListIncompleteDeals(ctx) return c.Internal.MarketListIncompleteDeals(ctx)
} }
func (c *StorageMinerStruct) MarketSetAsk(ctx context.Context, price types.BigInt, duration abi.ChainEpoch, minPieceSize abi.PaddedPieceSize, maxPieceSize abi.PaddedPieceSize) error { func (c *StorageMinerStruct) MarketSetPrice(ctx context.Context, p types.BigInt) error {
return c.Internal.MarketSetAsk(ctx, price, duration, minPieceSize, maxPieceSize) return c.Internal.MarketSetPrice(ctx, p)
}
func (c *StorageMinerStruct) MarketGetAsk(ctx context.Context) (*storagemarket.SignedStorageAsk, error) {
return c.Internal.MarketGetAsk(ctx)
} }
func (c *StorageMinerStruct) DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error { func (c *StorageMinerStruct) DealsImportData(ctx context.Context, dealPropCid cid.Cid, file string) error {
@@ -884,46 +847,6 @@ func (c *StorageMinerStruct) DealsList(ctx context.Context) ([]storagemarket.Sto
return c.Internal.DealsList(ctx) return c.Internal.DealsList(ctx)
} }
func (c *StorageMinerStruct) DealsConsiderOnlineStorageDeals(ctx context.Context) (bool, error) {
return c.Internal.DealsConsiderOnlineStorageDeals(ctx)
}
func (c *StorageMinerStruct) DealsSetConsiderOnlineStorageDeals(ctx context.Context, b bool) error {
return c.Internal.DealsSetConsiderOnlineStorageDeals(ctx, b)
}
func (c *StorageMinerStruct) DealsConsiderOnlineRetrievalDeals(ctx context.Context) (bool, error) {
return c.Internal.DealsConsiderOnlineRetrievalDeals(ctx)
}
func (c *StorageMinerStruct) DealsSetConsiderOnlineRetrievalDeals(ctx context.Context, b bool) error {
return c.Internal.DealsSetConsiderOnlineRetrievalDeals(ctx, b)
}
func (c *StorageMinerStruct) DealsPieceCidBlocklist(ctx context.Context) ([]cid.Cid, error) {
return c.Internal.DealsPieceCidBlocklist(ctx)
}
func (c *StorageMinerStruct) DealsSetPieceCidBlocklist(ctx context.Context, cids []cid.Cid) error {
return c.Internal.DealsSetPieceCidBlocklist(ctx, cids)
}
func (c *StorageMinerStruct) DealsConsiderOfflineStorageDeals(ctx context.Context) (bool, error) {
return c.Internal.DealsConsiderOfflineStorageDeals(ctx)
}
func (c *StorageMinerStruct) DealsSetConsiderOfflineStorageDeals(ctx context.Context, b bool) error {
return c.Internal.DealsSetConsiderOfflineStorageDeals(ctx, b)
}
func (c *StorageMinerStruct) DealsConsiderOfflineRetrievalDeals(ctx context.Context) (bool, error) {
return c.Internal.DealsConsiderOfflineRetrievalDeals(ctx)
}
func (c *StorageMinerStruct) DealsSetConsiderOfflineRetrievalDeals(ctx context.Context, b bool) error {
return c.Internal.DealsSetConsiderOfflineRetrievalDeals(ctx, b)
}
func (c *StorageMinerStruct) StorageAddLocal(ctx context.Context, path string) error { func (c *StorageMinerStruct) StorageAddLocal(ctx context.Context, path string) error {
return c.Internal.StorageAddLocal(ctx, path) return c.Internal.StorageAddLocal(ctx, path)
} }
@@ -962,16 +885,8 @@ func (w *WorkerStruct) SealCommit2(ctx context.Context, sector abi.SectorID, c1o
return w.Internal.SealCommit2(ctx, sector, c1o) return w.Internal.SealCommit2(ctx, sector, c1o)
} }
func (w *WorkerStruct) FinalizeSector(ctx context.Context, sector abi.SectorID, keepUnsealed []storage.Range) error { func (w *WorkerStruct) FinalizeSector(ctx context.Context, sector abi.SectorID) error {
return w.Internal.FinalizeSector(ctx, sector, keepUnsealed) return w.Internal.FinalizeSector(ctx, sector)
}
func (w *WorkerStruct) ReleaseUnsealed(ctx context.Context, sector abi.SectorID, safeToFree []storage.Range) error {
return w.Internal.ReleaseUnsealed(ctx, sector, safeToFree)
}
func (w *WorkerStruct) Remove(ctx context.Context, sector abi.SectorID) error {
return w.Internal.Remove(ctx, sector)
} }
func (w *WorkerStruct) MoveStorage(ctx context.Context, sector abi.SectorID) error { func (w *WorkerStruct) MoveStorage(ctx context.Context, sector abi.SectorID) error {
+21 -62
View File
@@ -16,7 +16,6 @@ import (
"github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-bitfield"
"github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/modules/dtypes"
@@ -73,14 +72,10 @@ func init() {
addExample(pid) addExample(pid)
addExample(bitfield.NewFromSet([]uint64{5})) addExample(bitfield.NewFromSet([]uint64{5}))
addExample(abi.RegisteredSealProof_StackedDrg32GiBV1) addExample(abi.RegisteredProof_StackedDRG32GiBPoSt)
addExample(abi.RegisteredPoStProof_StackedDrgWindow32GiBV1)
addExample(abi.ChainEpoch(10101)) addExample(abi.ChainEpoch(10101))
addExample(crypto.SigTypeBLS) addExample(crypto.SigTypeBLS)
addExample(int64(9)) addExample(int64(9))
addExample(12.3)
addExample(123)
addExample(uintptr(0))
addExample(abi.MethodNum(1)) addExample(abi.MethodNum(1))
addExample(exitcode.ExitCode(0)) addExample(exitcode.ExitCode(0))
addExample(crypto.DomainSeparationTag_ElectionProofProduction) addExample(crypto.DomainSeparationTag_ElectionProofProduction)
@@ -98,18 +93,18 @@ func init() {
addExample(build.APIVersion) addExample(build.APIVersion)
addExample(api.PCHInbound) addExample(api.PCHInbound)
addExample(time.Minute) addExample(time.Minute)
addExample(&types.ExecutionTrace{ addExample(&types.ExecutionResult{
Msg: exampleValue(reflect.TypeOf(&types.Message{}), nil).(*types.Message), Msg: exampleValue(reflect.TypeOf(&types.Message{})).(*types.Message),
MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{}), nil).(*types.MessageReceipt), MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{})).(*types.MessageReceipt),
}) })
addExample(map[string]types.Actor{ addExample(map[string]types.Actor{
"t01236": exampleValue(reflect.TypeOf(types.Actor{}), nil).(types.Actor), "t01236": exampleValue(reflect.TypeOf(types.Actor{})).(types.Actor),
}) })
addExample(map[string]api.MarketDeal{ addExample(map[string]api.MarketDeal{
"t026363": exampleValue(reflect.TypeOf(api.MarketDeal{}), nil).(api.MarketDeal), "t026363": exampleValue(reflect.TypeOf(api.MarketDeal{})).(api.MarketDeal),
}) })
addExample(map[string]api.MarketBalance{ addExample(map[string]api.MarketBalance{
"t026363": exampleValue(reflect.TypeOf(api.MarketBalance{}), nil).(api.MarketBalance), "t026363": exampleValue(reflect.TypeOf(api.MarketBalance{})).(api.MarketBalance),
}) })
maddr, err := multiaddr.NewMultiaddr("/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior") maddr, err := multiaddr.NewMultiaddr("/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior")
@@ -122,7 +117,7 @@ func init() {
} }
func exampleValue(t, parent reflect.Type) interface{} { func exampleValue(t reflect.Type) interface{} {
v, ok := ExampleValues[t] v, ok := ExampleValues[t]
if ok { if ok {
return v return v
@@ -131,25 +126,25 @@ func exampleValue(t, parent reflect.Type) interface{} {
switch t.Kind() { switch t.Kind() {
case reflect.Slice: case reflect.Slice:
out := reflect.New(t).Elem() out := reflect.New(t).Elem()
reflect.Append(out, reflect.ValueOf(exampleValue(t.Elem(), t))) reflect.Append(out, reflect.ValueOf(exampleValue(t.Elem())))
return out.Interface() return out.Interface()
case reflect.Chan: case reflect.Chan:
return exampleValue(t.Elem(), nil) return exampleValue(t.Elem())
case reflect.Struct: case reflect.Struct:
es := exampleStruct(t, parent) es := exampleStruct(t)
v := reflect.ValueOf(es).Elem().Interface() v := reflect.ValueOf(es).Elem().Interface()
ExampleValues[t] = v ExampleValues[t] = v
return v return v
case reflect.Array: case reflect.Array:
out := reflect.New(t).Elem() out := reflect.New(t).Elem()
for i := 0; i < t.Len(); i++ { for i := 0; i < t.Len(); i++ {
out.Index(i).Set(reflect.ValueOf(exampleValue(t.Elem(), t))) out.Index(i).Set(reflect.ValueOf(exampleValue(t.Elem())))
} }
return out.Interface() return out.Interface()
case reflect.Ptr: case reflect.Ptr:
if t.Elem().Kind() == reflect.Struct { if t.Elem().Kind() == reflect.Struct {
es := exampleStruct(t.Elem(), t) es := exampleStruct(t.Elem())
//ExampleValues[t] = es //ExampleValues[t] = es
return es return es
} }
@@ -160,15 +155,12 @@ func exampleValue(t, parent reflect.Type) interface{} {
panic(fmt.Sprintf("No example value for type: %s", t)) panic(fmt.Sprintf("No example value for type: %s", t))
} }
func exampleStruct(t, parent reflect.Type) interface{} { func exampleStruct(t reflect.Type) interface{} {
ns := reflect.New(t) ns := reflect.New(t)
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
f := t.Field(i) f := t.Field(i)
if f.Type == parent {
continue
}
if strings.Title(f.Name) == f.Name { if strings.Title(f.Name) == f.Name {
ns.Elem().Field(i).Set(reflect.ValueOf(exampleValue(f.Type, t))) ns.Elem().Field(i).Set(reflect.ValueOf(exampleValue(f.Type)))
} }
} }
@@ -294,17 +286,17 @@ func main() {
ft := m.Func.Type() ft := m.Func.Type()
for j := 2; j < ft.NumIn(); j++ { for j := 2; j < ft.NumIn(); j++ {
inp := ft.In(j) inp := ft.In(j)
args = append(args, exampleValue(inp, nil)) args = append(args, exampleValue(inp))
} }
v, err := json.MarshalIndent(args, "", " ") v, err := json.Marshal(args)
if err != nil { if err != nil {
panic(err) panic(err)
} }
outv := exampleValue(ft.Out(0), nil) outv := exampleValue(ft.Out(0))
ov, err := json.MarshalIndent(outv, "", " ") ov, err := json.Marshal(outv)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -326,18 +318,6 @@ func main() {
return groupslice[i].GroupName < groupslice[j].GroupName return groupslice[i].GroupName < groupslice[j].GroupName
}) })
fmt.Printf("# Groups\n")
for _, g := range groupslice {
fmt.Printf("* [%s](#%s)\n", g.GroupName, g.GroupName)
for _, method := range g.Methods {
fmt.Printf(" * [%s](#%s)\n", method.Name, method.Name)
}
}
permStruct := reflect.TypeOf(apistruct.FullNodeStruct{}.Internal)
commonPermStruct := reflect.TypeOf(apistruct.CommonStruct{}.Internal)
for _, g := range groupslice { for _, g := range groupslice {
g := g g := g
fmt.Printf("## %s\n", g.GroupName) fmt.Printf("## %s\n", g.GroupName)
@@ -351,29 +331,8 @@ func main() {
fmt.Printf("### %s\n", m.Name) fmt.Printf("### %s\n", m.Name)
fmt.Printf("%s\n\n", m.Comment) fmt.Printf("%s\n\n", m.Comment)
meth, ok := permStruct.FieldByName(m.Name) fmt.Printf("Inputs: `%s`\n\n", m.InputExample)
if !ok { fmt.Printf("Response: `%s`\n\n", m.ResponseExample)
meth, ok = commonPermStruct.FieldByName(m.Name)
if !ok {
panic("no perms for method: " + m.Name)
}
}
perms := meth.Tag.Get("perm")
fmt.Printf("Perms: %s\n\n", perms)
if strings.Count(m.InputExample, "\n") > 0 {
fmt.Printf("Inputs:\n```json\n%s\n```\n\n", m.InputExample)
} else {
fmt.Printf("Inputs: `%s`\n\n", m.InputExample)
}
if strings.Count(m.ResponseExample, "\n") > 0 {
fmt.Printf("Response:\n```json\n%s\n```\n\n", m.ResponseExample)
} else {
fmt.Printf("Response: `%s`\n\n", m.ResponseExample)
}
} }
} }
} }
+6 -7
View File
@@ -8,7 +8,6 @@ import (
"math/rand" "math/rand"
"os" "os"
"path/filepath" "path/filepath"
"sync/atomic"
"testing" "testing"
"time" "time"
@@ -53,11 +52,11 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport
} }
time.Sleep(time.Second) time.Sleep(time.Second)
mine := int64(1) mine := true
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
defer close(done) defer close(done)
for atomic.LoadInt64(&mine) == 1 { for mine {
time.Sleep(blocktime) time.Sleep(blocktime)
if err := sn[0].MineOne(ctx, func(bool) {}); err != nil { if err := sn[0].MineOne(ctx, func(bool) {}); err != nil {
t.Error(err) t.Error(err)
@@ -67,7 +66,7 @@ func TestDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, carExport
makeDeal(t, ctx, 6, client, miner, carExport) makeDeal(t, ctx, 6, client, miner, carExport)
atomic.AddInt64(&mine, -1) mine = false
fmt.Println("shutting down mining") fmt.Println("shutting down mining")
<-done <-done
} }
@@ -90,12 +89,12 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
} }
time.Sleep(time.Second) time.Sleep(time.Second)
mine := int64(1) mine := true
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
defer close(done) defer close(done)
for atomic.LoadInt64(&mine) == 1 { for mine {
time.Sleep(blocktime) time.Sleep(blocktime)
if err := sn[0].MineOne(ctx, func(bool) {}); err != nil { if err := sn[0].MineOne(ctx, func(bool) {}); err != nil {
t.Error(err) t.Error(err)
@@ -106,7 +105,7 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
makeDeal(t, ctx, 6, client, miner, false) makeDeal(t, ctx, 6, client, miner, false)
makeDeal(t, ctx, 7, client, miner, false) makeDeal(t, ctx, 7, client, miner, false)
atomic.AddInt64(&mine, -1) mine = false
fmt.Println("shutting down mining") fmt.Println("shutting down mining")
<-done <-done
} }
+2 -3
View File
@@ -126,7 +126,6 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo
minedTwo := make(chan struct{}) minedTwo := make(chan struct{})
go func() { go func() {
doneMinedTwo := false
defer close(done) defer close(done)
prevExpect := 0 prevExpect := 0
@@ -176,9 +175,9 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo
time.Sleep(blocktime) time.Sleep(blocktime)
} }
if prevExpect == 2 && expect == 2 && !doneMinedTwo { if prevExpect == 2 && expect == 2 && minedTwo != nil {
close(minedTwo) close(minedTwo)
doneMinedTwo = true minedTwo = nil
} }
prevExpect = expect prevExpect = expect
-36
View File
@@ -2,9 +2,6 @@ package api
import ( import (
"encoding/json" "encoding/json"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr"
@@ -42,36 +39,3 @@ type PubsubScore struct {
ID peer.ID ID peer.ID
Score float64 Score float64
} }
type MinerInfo struct {
Owner address.Address // Must be an ID-address.
Worker address.Address // Must be an ID-address.
NewWorker address.Address // Must be an ID-address.
WorkerChangeEpoch abi.ChainEpoch
PeerId peer.ID
Multiaddrs []abi.Multiaddrs
SealProofType abi.RegisteredSealProof
SectorSize abi.SectorSize
WindowPoStPartitionSectors uint64
}
func NewApiMinerInfo(info miner.MinerInfo) MinerInfo {
mi := MinerInfo{
Owner: info.Owner,
Worker: info.Worker,
NewWorker: address.Undef,
WorkerChangeEpoch: -1,
PeerId: peer.ID(info.PeerId),
Multiaddrs: info.Multiaddrs,
SealProofType: info.SealProofType,
SectorSize: info.SectorSize,
WindowPoStPartitionSectors: info.WindowPoStPartitionSectors,
}
if info.PendingWorkerKey != nil {
mi.NewWorker = info.PendingWorkerKey.NewWorker
mi.WorkerChangeEpoch = info.PendingWorkerKey.EffectiveAt
}
return mi
}
-4
View File
@@ -13,10 +13,6 @@ import (
) )
func BuiltinBootstrap() ([]peer.AddrInfo, error) { func BuiltinBootstrap() ([]peer.AddrInfo, error) {
if DisableBuiltinAssets {
return nil, nil
}
var out []peer.AddrInfo var out []peer.AddrInfo
b := rice.MustFindBox("bootstrap") b := rice.MustFindBox("bootstrap")
+6 -12
View File
@@ -1,12 +1,6 @@
/dns4/bootstrap-0-sin.fil-test.net/tcp/1347/p2p/12D3KooWPdUquftaQvoQEtEdsRBAhwD6jopbF2oweVTzR59VbHEd /dns4/t01000.miner.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWLVgeFhXA7ztuSS1uvk4NVzZwRZLxg8PBy1mJuHfc8J6E
/ip4/86.109.15.57/tcp/1347/p2p/12D3KooWPdUquftaQvoQEtEdsRBAhwD6jopbF2oweVTzR59VbHEd /ip4/34.217.110.132/tcp/1347/p2p/12D3KooWLVgeFhXA7ztuSS1uvk4NVzZwRZLxg8PBy1mJuHfc8J6E
/dns4/bootstrap-0-dfw.fil-test.net/tcp/1347/p2p/12D3KooWQSCkHCzosEyrh8FgYfLejKgEPM5VB6qWzZE3yDAuXn8d /dns4/peer0.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWBkXWpT1hLHXz7VzR8ZHCJ42P6c9Nb9532ATSguBpLTKJ
/ip4/139.178.84.45/tcp/1347/p2p/12D3KooWQSCkHCzosEyrh8FgYfLejKgEPM5VB6qWzZE3yDAuXn8d /ip4/54.187.182.170/tcp/1347/p2p/12D3KooWBkXWpT1hLHXz7VzR8ZHCJ42P6c9Nb9532ATSguBpLTKJ
/dns4/bootstrap-0-fra.fil-test.net/tcp/1347/p2p/12D3KooWEXN2eQmoyqnNjde9PBAQfQLHN67jcEdWU6JougWrgXJK /dns4/peer1.interopnet.kittyhawk.wtf/tcp/1347/p2p/12D3KooWJk5hQHNdCsidtX4jhKQMBXKJdo2kjhrNHUZBPmCZKwKt
/ip4/136.144.49.17/tcp/1347/p2p/12D3KooWEXN2eQmoyqnNjde9PBAQfQLHN67jcEdWU6JougWrgXJK /ip4/52.24.84.39/tcp/1347/p2p/12D3KooWJk5hQHNdCsidtX4jhKQMBXKJdo2kjhrNHUZBPmCZKwKt
/dns4/bootstrap-1-sin.fil-test.net/tcp/1347/p2p/12D3KooWLmJkZd33mJhjg5RrpJ6NFep9SNLXWc4uVngV4TXKwzYw
/ip4/86.109.15.123/tcp/1347/p2p/12D3KooWLmJkZd33mJhjg5RrpJ6NFep9SNLXWc4uVngV4TXKwzYw
/dns4/bootstrap-1-dfw.fil-test.net/tcp/1347/p2p/12D3KooWGXLHjiz6pTRu7x2pkgTVCoxcCiVxcNLpMnWcJ3JiNEy5
/ip4/139.178.86.3/tcp/1347/p2p/12D3KooWGXLHjiz6pTRu7x2pkgTVCoxcCiVxcNLpMnWcJ3JiNEy5
/dns4/bootstrap-1-fra.fil-test.net/tcp/1347/p2p/12D3KooW9szZmKttS9A1FafH3Zc2pxKwwmvCWCGKkRP4KmbhhC4R
/ip4/136.144.49.131/tcp/1347/p2p/12D3KooW9szZmKttS9A1FafH3Zc2pxKwwmvCWCGKkRP4KmbhhC4R
-15
View File
@@ -1,15 +0,0 @@
package build
// DisableBuiltinAssets disables the resolution of go.rice boxes that store
// built-in assets, such as proof parameters, bootstrap peers, genesis blocks,
// etc.
//
// When this value is set to true, it is expected that the user will
// provide any such configurations through the Lotus API itself.
//
// This is useful when you're using Lotus as a library, such as to orchestrate
// test scenarios, or for other purposes where you don't need to use the
// defaults shipped with the binary.
//
// For this flag to be effective, it must be enabled _before_ instantiating Lotus.
var DisableBuiltinAssets = false
Binary file not shown.
+5 -4
View File
@@ -12,17 +12,18 @@ import (
func init() { func init() {
power.ConsensusMinerMinPower = big.NewInt(2048) power.ConsensusMinerMinPower = big.NewInt(2048)
miner.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{ miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {}, abi.RegisteredProof_StackedDRG2KiBSeal: {},
} }
verifreg.MinVerifiedDealSize = big.NewInt(256) verifreg.MinVerifiedDealSize = big.NewInt(256)
BuildType |= Build2k BuildType |= Build2k
} }
const BlockDelaySecs = uint64(2) // Seconds
const BlockDelay = 2
const PropagationDelaySecs = uint64(3) const PropagationDelay = 3
// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after // SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after
// which the miner is slashed // which the miner is slashed
@@ -1,9 +1,10 @@
// +build !testground
package build package build
import ( import (
"math/big" "math/big"
"sort"
"github.com/libp2p/go-libp2p-core/protocol"
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
@@ -12,6 +13,32 @@ import (
"github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/lotus/node/modules/dtypes"
) )
func DefaultSectorSize() abi.SectorSize {
szs := make([]abi.SectorSize, 0, len(miner.SupportedProofTypes))
for spt := range miner.SupportedProofTypes {
ss, err := spt.SectorSize()
if err != nil {
panic(err)
}
szs = append(szs, ss)
}
sort.Slice(szs, func(i, j int) bool {
return szs[i] < szs[j]
})
return szs[0]
}
// Core network constants
func BlocksTopic(netName dtypes.NetworkName) string { return "/fil/blocks/" + string(netName) }
func MessagesTopic(netName dtypes.NetworkName) string { return "/fil/msgs/" + string(netName) }
func DhtProtocolName(netName dtypes.NetworkName) protocol.ID {
return protocol.ID("/fil/kad/" + string(netName))
}
// ///// // /////
// Storage // Storage
@@ -21,7 +48,8 @@ const UnixfsLinksPerLevel = 1024
// ///// // /////
// Consensus / Network // Consensus / Network
const AllowableClockDriftSecs = uint64(1) // Seconds
const AllowableClockDrift = 1
// Epochs // Epochs
const ForkLengthThreshold = Finality const ForkLengthThreshold = Finality
@@ -31,12 +59,12 @@ var BlocksPerEpoch = uint64(builtin.ExpectedLeadersPerEpoch)
// Epochs // Epochs
const Finality = miner.ChainFinalityish const Finality = miner.ChainFinalityish
const MessageConfidence = uint64(5) const MessageConfidence = 5
// constants for Weight calculation // constants for Weight calculation
// The ratio of weight contributed by short-term vs long-term factors in a given round // The ratio of weight contributed by short-term vs long-term factors in a given round
const WRatioNum = int64(1) const WRatioNum = int64(1)
const WRatioDen = uint64(2) const WRatioDen = 2
// ///// // /////
// Proofs // Proofs
@@ -54,25 +82,25 @@ const MaxSealLookback = SealRandomnessLookbackLimit + 2000 // TODO: Get from spe
// Mining // Mining
// Epochs // Epochs
const TicketRandomnessLookback = abi.ChainEpoch(1) const TicketRandomnessLookback = 1
const WinningPoStSectorSetLookback = abi.ChainEpoch(10) const WinningPoStSectorSetLookback = 10
// ///// // /////
// Devnet settings // Devnet settings
const TotalFilecoin = uint64(2_000_000_000) const TotalFilecoin = 2_000_000_000
const MiningRewardTotal = uint64(1_400_000_000) const MiningRewardTotal = 1_400_000_000
const FilecoinPrecision = uint64(1_000_000_000_000_000_000) const FilecoinPrecision = 1_000_000_000_000_000_000
var InitialRewardBalance *big.Int var InitialRewardBalance *big.Int
// TODO: Move other important consts here // TODO: Move other important consts here
func init() { func init() {
InitialRewardBalance = big.NewInt(int64(MiningRewardTotal)) InitialRewardBalance = big.NewInt(MiningRewardTotal)
InitialRewardBalance = InitialRewardBalance.Mul(InitialRewardBalance, big.NewInt(int64(FilecoinPrecision))) InitialRewardBalance = InitialRewardBalance.Mul(InitialRewardBalance, big.NewInt(FilecoinPrecision))
} }
// Sync // Sync
@@ -93,11 +121,4 @@ const VerifSigCacheSize = 32000
const BlockMessageLimit = 512 const BlockMessageLimit = 512
const BlockGasLimit = 100_000_000_000 const BlockGasLimit = 100_000_000_000
var DrandConfig = dtypes.DrandConfig{ var DrandChain = `{"public_key":"922a2e93828ff83345bae533f5172669a26c02dc76d6bf59c80892e12ab1455c229211886f35bb56af6d5bea981024df","period":25,"genesis_time":1590445175,"hash":"138a324aa6540f93d0dad002aa89454b1bec2b6e948682cde6bd4db40f4b7c9b"}`
Servers: []string{
"https://pl-eu.testnet.drand.sh",
"https://pl-us.testnet.drand.sh",
"https://pl-sin.testnet.drand.sh",
},
ChainInfoJSON: `{"public_key":"922a2e93828ff83345bae533f5172669a26c02dc76d6bf59c80892e12ab1455c229211886f35bb56af6d5bea981024df","period":25,"genesis_time":1590445175,"hash":"138a324aa6540f93d0dad002aa89454b1bec2b6e948682cde6bd4db40f4b7c9b"}`,
}
-38
View File
@@ -1,38 +0,0 @@
package build
import (
"sort"
"github.com/libp2p/go-libp2p-core/protocol"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/lotus/node/modules/dtypes"
)
func DefaultSectorSize() abi.SectorSize {
szs := make([]abi.SectorSize, 0, len(miner.SupportedProofTypes))
for spt := range miner.SupportedProofTypes {
ss, err := spt.SectorSize()
if err != nil {
panic(err)
}
szs = append(szs, ss)
}
sort.Slice(szs, func(i, j int) bool {
return szs[i] < szs[j]
})
return szs[0]
}
// Core network constants
func BlocksTopic(netName dtypes.NetworkName) string { return "/fil/blocks/" + string(netName) }
func MessagesTopic(netName dtypes.NetworkName) string { return "/fil/msgs/" + string(netName) }
func DhtProtocolName(netName dtypes.NetworkName) protocol.ID {
return protocol.ID("/fil/kad/" + string(netName))
}
-73
View File
@@ -1,73 +0,0 @@
// +build testground
// This file makes hardcoded parameters (const) configurable as vars.
//
// Its purpose is to unlock various degrees of flexibility and parametrization
// when writing Testground plans for Lotus.
//
package build
import (
"math/big"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
)
var (
UnixfsChunkSize = uint64(1 << 20)
UnixfsLinksPerLevel = 1024
BlocksPerEpoch = uint64(builtin.ExpectedLeadersPerEpoch)
BlockMessageLimit = 512
BlockGasLimit = int64(100_000_000_000)
BlockDelaySecs = uint64(builtin.EpochDurationSeconds)
PropagationDelaySecs = uint64(6)
AllowableClockDriftSecs = uint64(1)
Finality = miner.ChainFinalityish
ForkLengthThreshold = Finality
SlashablePowerDelay = 20
InteractivePoRepConfidence = 6
MessageConfidence uint64 = 5
WRatioNum = int64(1)
WRatioDen = uint64(2)
BadBlockCacheSize = 1 << 15
BlsSignatureCacheSize = 40000
VerifSigCacheSize = 32000
SealRandomnessLookback = Finality
SealRandomnessLookbackLimit = SealRandomnessLookback + 2000
MaxSealLookback = SealRandomnessLookbackLimit + 2000
TicketRandomnessLookback = abi.ChainEpoch(1)
WinningPoStSectorSetLookback = abi.ChainEpoch(10)
TotalFilecoin uint64 = 2_000_000_000
MiningRewardTotal uint64 = 1_400_000_000
FilecoinPrecision uint64 = 1_000_000_000_000_000_000
InitialRewardBalance = func() *big.Int {
v := big.NewInt(int64(MiningRewardTotal))
v = v.Mul(v, big.NewInt(int64(FilecoinPrecision)))
return v
}()
DrandConfig = dtypes.DrandConfig{
Servers: []string{
"https://pl-eu.testnet.drand.sh",
"https://pl-us.testnet.drand.sh",
"https://pl-sin.testnet.drand.sh",
},
ChainInfoJSON: `{"public_key":"922a2e93828ff83345bae533f5172669a26c02dc76d6bf59c80892e12ab1455c229211886f35bb56af6d5bea981024df","period":25,"genesis_time":1590445175,"hash":"138a324aa6540f93d0dad002aa89454b1bec2b6e948682cde6bd4db40f4b7c9b"}`,
}
)
+8 -7
View File
@@ -1,6 +1,5 @@
// +build !debug // +build !debug
// +build !2k // +build !2k
// +build !testground
package build package build
@@ -13,13 +12,15 @@ import (
) )
func init() { func init() {
power.ConsensusMinerMinPower = big.NewInt(1024 << 30) power.ConsensusMinerMinPower = big.NewInt(1024 << 20)
miner.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{ miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
abi.RegisteredSealProof_StackedDrg32GiBV1: {}, abi.RegisteredProof_StackedDRG512MiBSeal: {},
abi.RegisteredSealProof_StackedDrg64GiBV1: {}, abi.RegisteredProof_StackedDRG32GiBSeal: {},
abi.RegisteredProof_StackedDRG64GiBSeal: {},
} }
} }
const BlockDelaySecs = uint64(builtin.EpochDurationSeconds) // Seconds
const BlockDelay = builtin.EpochDurationSeconds
const PropagationDelaySecs = uint64(6) const PropagationDelay = 6
+90 -90
View File
@@ -1,152 +1,152 @@
{ {
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.params": {
"cid": "QmeDRyxek34F1H6xJY6AkFdWvPsy5F6dKTrebV3ZtWT4ky", "cid": "QmYkygifkXnrnsN4MJsjBFHTQJHx294CyikDgDK8nYxdGh",
"digest": "f5827f2d8801c62c831e0f972f6dc8bb", "digest": "df3f30442a6d6b4192f5071fb17e820c",
"sector_size": 2048 "sector_size": 2048
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0170db1f394b35d995252228ee359194b13199d259380541dc529fb0099096b0.vk": {
"cid": "QmUw1ZmG4BBbX19MsbH3zAEGKUc42iFJc5ZAyomDHeJTsA", "cid": "QmdXyqbmy2bkJA9Kyhh6z25GrTCq48LwX6c1mxPsm54wi7",
"digest": "398fecdb4b2de445125852bc3c080b35", "digest": "0bea3951abf9557a3569f68e52a30c6c",
"sector_size": 2048 "sector_size": 2048
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.params": {
"cid": "QmUeNKp9YZpiAFm81RV5KuxH1FDGJx2DuwcbU2XNSZLLSv", "cid": "Qmf5XZZtP5VcYTf65MbKjLVabcS6cYMbr2rFShmfJzh5e5",
"digest": "2b6d2972ac9e862e8134d98fb695b0c5", "digest": "655e6277638edc8c658094f6f0b33d54",
"sector_size": 536870912 "sector_size": 536870912
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-0cfb4f178bbb71cf2ecfcd42accce558b27199ab4fb59cb78f2483fe21ef36d9.vk": {
"cid": "QmQaQmTXX995Akd66ggtJY5bNx6Gkxk8P34JTdMMq8393G", "cid": "QmPuhdWnAXBks43emnkqi9FQzyU1gASKyz23zrD27BPGs8",
"digest": "3688c9eb256b7b17f411dad78d5ef74a", "digest": "57690e3a6a94c3f704802a674b34f36b",
"sector_size": 536870912 "sector_size": 536870912
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.params": {
"cid": "QmfEYTMSkwGJTumQx26iKXGNKiYh3mmAC4SkdybZpJCj5p", "cid": "QmPNVgTN7N5vDtD5u7ERMTLcvUtrKRBfYVUDr6uW3pKhX7",
"digest": "09bff16aed893349d94485cfae366a9c", "digest": "3d390654f58e603b896ac70c653f5676",
"sector_size": 2048 "sector_size": 2048
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-3ea05428c9d11689f23529cde32fd30aabd50f7d2c93657c1d3650bca3e8ea9e.vk": {
"cid": "QmP4ThPieSUJyRanjibWpT5R5cCMzMAU4j8Y7kBn7CSW1Q", "cid": "Qmbj61Zez7v5xA7nSCnmWbyLYznWJDWeusz7Yg8EcgVdoN",
"digest": "142f2f7e8f1b1779290315cabfd2c803", "digest": "8c170a164743c39576a7f47a1b51e6f3",
"sector_size": 2048 "sector_size": 2048
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.params": {
"cid": "QmcAixrHsz29DgvtZiMc2kQjvPRvWxYUp36QYmRDZbmREm", "cid": "QmRApb8RZoBK3cqicT7V3ydXg8yVvqPFMPrQNXP33aBihp",
"digest": "8f987f64d434365562180b96ec12e299", "digest": "b1b58ff9a297b82885e8a7dfb035f83c",
"sector_size": 8388608 "sector_size": 8388608
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-50c7368dea9593ed0989e70974d28024efa9d156d585b7eea1be22b2e753f331.vk": {
"cid": "QmT4iFnbL6r4txS5PXsiV7NTzbhCxHy54PvdkJJGV2VFXb", "cid": "QmcytF1dTdqMFoyXi931j1RgmGtLfR9LLLaBznRt1tPQyD",
"digest": "94b6c24ac01924f4feeecedd16b5d77d", "digest": "1a09e00c641f192f55af3433a028f050",
"sector_size": 8388608 "sector_size": 8388608
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.params": {
"cid": "QmbjFst6SFCK1KsTQrfwPdxf3VTNa1raed574tEZZ9PoyQ", "cid": "QmPvr54tWaVeP4WnekivzUAJitTqsQfvikBvAHNEaDNQSw",
"digest": "2c245fe8179839dd6c6cdea207c67ae8", "digest": "9380e41368ed4083dbc922b290d3b786",
"sector_size": 8388608 "sector_size": 8388608
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-5294475db5237a2e83c3e52fd6c2b03859a1831d45ed08c4f35dbf9a803165a9.vk": {
"cid": "QmQJKmvZN1a5cQ1Nw6CDyXs3nuRPzvyU5NvCFMUL2BfcZC", "cid": "QmXyVLVDRCcxA9SjT7PeK8HFtyxZ2ZH3SHa8KoGLw8VGJt",
"digest": "56ae47bfda53bb8d22981ed8d8d27d72", "digest": "f0731a7e20f90704bd38fc5d27882f6d",
"sector_size": 8388608 "sector_size": 8388608
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.params": {
"cid": "QmQCABxeTpdvXTyjDyk7nPBxkQzCh7MXfGztWnSXEPKMLW", "cid": "Qmf5f6ko3dqj7qauzXpZqxM9B2x2sL977K6gE2ppNwuJPv",
"digest": "7e6b2eb5ecbb11ac651ad66ebbb2075a", "digest": "273ebb8c896326b7c292bee8b775fd38",
"sector_size": 536870912 "sector_size": 536870912
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-0-0-7d739b8cf60f1b0709eeebee7730e297683552e4b69cab6984ec0285663c5781.vk": {
"cid": "QmPBweyugh5Sx4umk8ULhgEGbjY8xmWLfU6M7EMpc8Mad6", "cid": "QmfP3MQe8koW63n5MkDENENVHxib78MJYYyZvbneCsuze8",
"digest": "94a8d9e25a9ab9674d339833664eba25", "digest": "3dd94da9da64e51b3445bc528d84e76d",
"sector_size": 536870912 "sector_size": 536870912
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.params": {
"cid": "QmY5yax1E9KymBnCeHksE9Zi8NieZbmwcpoDGoabkeeb9h", "cid": "QmYEeeCE8uT2bsVkxcqqUYeMmMEbe6rfmo8wQCv7jFHqqm",
"digest": "c909ea9e3fe25ab9b391a64593afdbba", "digest": "c947f2021304ed43b7216f7a8436e294",
"sector_size": 34359738368 "sector_size": 34359738368
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-0377ded656c6f524f1618760bffe4e0a1c51d5a70c4509eedae8a27555733edc.vk": {
"cid": "QmXnPo4yH5mwMguwrvqgRfduSttbmPrXtbBfbwU21wQWHt", "cid": "QmXB63ExriFjB4ywWnXTnFwCcLFfCeEP3h15qtL5i7F4aX",
"digest": "caf900461e988bbf86dbcaca087b7864", "digest": "ab20d7b253e7e9a0d2ccdf7599ec8ec3",
"sector_size": 34359738368 "sector_size": 34359738368
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.params": {
"cid": "QmZtzzPWwmZEgR7MSMvXRbt9KVK8k4XZ5RLWHybHJW9SdE", "cid": "QmW5Yxg3L1NSzuQVcRMHMbG3uvVoi4dTLzVaDpnEUPQpnA",
"digest": "a2844f0703f186d143a06146a04577d8", "digest": "079ba19645828ae42b22b0e3f4866e8d",
"sector_size": 34359738368 "sector_size": 34359738368
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-0-559e581f022bb4e4ec6e719e563bf0e026ad6de42e56c18714a2c692b1b88d7e.vk": {
"cid": "QmWxEA7EdQCUJTzjNpxg5XTF45D2uVyYnN1QRUb5TRYU8M", "cid": "QmQzZ5dJ11tcSBees38WX41tZLXS9BqpEti253m5QcnTNs",
"digest": "2306247a1e616dbe07f01b88196c2044", "digest": "c76125a50a7de315165de359b5174ae4",
"sector_size": 34359738368 "sector_size": 34359738368
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.params": {
"cid": "QmP676KwuvyF9Y64uJnXvLtvD1xcuWQ6wD23RzYtQ6dd4f", "cid": "QmNk3wga1tS53FUu1QnkK8ehWA2cqpCnSEAPv3KLxdJxNa",
"digest": "215b1c667a4f46a1d0178338df568615", "digest": "421e4790c0b80e0107a7ff67acf14084",
"sector_size": 68719476736 "sector_size": 68719476736
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-2627e4006b67f99cef990c0a47d5426cb7ab0a0ad58fc1061547bf2d28b09def.vk": {
"cid": "QmPvPwbJtcSGyqB1rQJhSF5yvFbX9ZBSsHVej5F8JUyHUJ", "cid": "QmVQCHGsrUtbn9RjHs1e6GXfeXDW5m9w4ge48PSX3Z2as2",
"digest": "0c9c423b28b1455fcbc329a1045fd4dd", "digest": "8b60e9cc1470a6729c687d6cf0a1f79c",
"sector_size": 68719476736 "sector_size": 68719476736
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.params": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.params": {
"cid": "QmUxPQfvckzm1t6MFRdDZ1fDK5UJzAjK7pTZ97cwyachdr", "cid": "QmTL3VvydaMFWKvE5VzxjgKsJYgL9JMM4JVYNtQxdj9JK1",
"digest": "965132f51ae445b0e6d32692b7561995", "digest": "2685f31124b22ea6b2857e5a5e87ffa3",
"sector_size": 68719476736 "sector_size": 68719476736
}, },
"v27-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.vk": { "v26-proof-of-spacetime-fallback-merkletree-poseidon_hasher-8-8-2-b62098629d07946e9028127e70295ed996fe3ed25b0f9f88eb610a0ab4385a3c.vk": {
"cid": "QmTxq2EBnQWb5R8tS4MHdchj4vNfLYGoSXxwJFvs5xgW4K", "cid": "QmSVWbLqQYbUbbJyfsRMzEib2rfSqMtnPks1Nw22omcBQm",
"digest": "fc8c3d26e0e56373ad96cb41520d55a6", "digest": "efe703cd2839597c7ca5c2a906b74296",
"sector_size": 68719476736 "sector_size": 68719476736
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.params": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.params": {
"cid": "QmRjgZHERgqGoRagR788Kh6ybi26csVYa8mqbqhmZm57Jx", "cid": "QmU9dH31nZZUJnsogR4Ld4ySUcH6wm2RgmGiujwnqtbU6k",
"digest": "cfc7b0897d1eee48c586f7beb89e67f7", "digest": "fcef8e87ae2afd7a28aae44347b804cf",
"sector_size": 2048 "sector_size": 2048
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.vk": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-032d3138d22506ec0082ed72b2dcba18df18477904e35bafee82b3793b06832f.vk": {
"cid": "QmNjvnvFP7KgovHUddULoB19fBHT81iz7NcUbzEHZUUPsm", "cid": "QmdJ15DMGPooye5NaPcRfXUdHUDibcN7hKjbmTGuu1K4AQ",
"digest": "fb59bd061c987eac7068008c44de346b", "digest": "2ee2b3518229680db15161d4f582af37",
"sector_size": 2048 "sector_size": 2048
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.params": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.params": {
"cid": "QmTpRPBA4dt8fgGpcVzi4L1KA1U2eBHCE8WVmS2GUygMvT", "cid": "QmZgtxcY3tMXXQxZTA7ZTUDXLVUnfxNcerXgeW4gG2NnfP",
"digest": "36d465915b0afbf96bd08e7915e00952", "digest": "3273c7135cb75684248b475781b738ee",
"sector_size": 536870912 "sector_size": 536870912
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.vk": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-6babf46ce344ae495d558e7770a585b2382d54f225af8ed0397b8be7c3fcd472.vk": {
"cid": "QmRzDyVfQCLsxspoVsed5bcQRsG6KiktngJfcNBL3TJPZe", "cid": "QmSS6ZkAV2aGZcgKgdPpEEgihXF1ryZX8PSAZDWSoeL1d4",
"digest": "99d16df0eb6a7e227a4f4570c4f6b6f1", "digest": "1519b5f61d9044a59f2bdc57537c094b",
"sector_size": 536870912 "sector_size": 536870912
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.params": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.params": {
"cid": "QmV8ZjTSGzDUWmFvsq9NSyPBR7eDDUcvCPNgj2yE7HMAFu", "cid": "QmQBGXeiNn6hVwbR6qFarQqiNGDdKk4h9ucfyvcXyfYz2N",
"digest": "34f3ddf1d1c9f41c0cd73b91e8b4bc27", "digest": "7d5f896f435c38e93bcda6dd168d860b",
"sector_size": 8388608 "sector_size": 8388608
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.vk": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-0-0-sha256_hasher-ecd683648512ab1765faa2a5f14bab48f676e633467f0aa8aad4b55dcb0652bb.vk": {
"cid": "QmTa3VbjTiqJWU6r4WKayaQrUaaBsrpp5UDqYvPDd2C5hs", "cid": "QmPrZgBVGMckEAeu5eSJnLmiAwcPQjKjZe5ir6VaQ5AxKs",
"digest": "ec62d59651daa5631d3d1e9c782dd940", "digest": "fe6d2de44580a0db5a4934688899b92f",
"sector_size": 8388608 "sector_size": 8388608
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.params": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.params": {
"cid": "Qmf8ngfArxrv9tFWDqBcNegdBMymvuakwyHKd1pbW3pbsb", "cid": "QmZL2cq45XJn5BFzagAZwgFmLrcM1W6CXoiEF9C5j5tjEF",
"digest": "a16d6f4c6424fb280236739f84b24f97", "digest": "acdfed9f0512bc85a01a9fb871d475d5",
"sector_size": 34359738368 "sector_size": 34359738368
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.vk": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-0-sha256_hasher-82a357d2f2ca81dc61bb45f4a762807aedee1b0a53fd6c4e77b46a01bfef7820.vk": {
"cid": "QmfQgVFerArJ6Jupwyc9tKjLD9n1J9ajLHBdpY465tRM7M", "cid": "QmQ4zB7nNa1tDYNifBkExRnZtwtxZw775iaqvVsZyRi6Q2",
"digest": "7a139d82b8a02e35279d657e197f5c1f", "digest": "524a2f3e9d6826593caebc41bb545c40",
"sector_size": 34359738368 "sector_size": 34359738368
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.params": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.params": {
"cid": "QmfDha8271nXJn14Aq3qQeghjMBWbs6HNSGa6VuzCVk4TW", "cid": "QmY7DitNKXFeLQt9QoVQkfjM1EvRnprqUVxjmkTXkHDNka",
"digest": "5d3cd3f107a3bea8a96d1189efd2965c", "digest": "f27271c0537ba65ade2ec045f8fbd069",
"sector_size": 68719476736 "sector_size": 68719476736
}, },
"v27-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.vk": { "v26-stacked-proof-of-replication-merkletree-poseidon_hasher-8-8-2-sha256_hasher-96f1b4a04c5c51e4759bbf224bbc2ef5a42c7100f16ec0637123f16a845ddfb2.vk": {
"cid": "QmRVtTtiFzHJTHurYzaCvetGAchux9cktixT4aGHthN6Zt", "cid": "QmUJsvoCuQ4LszPmeRVAkMYb5qY95ctz3UXKhu8xLzyFKo",
"digest": "62c366405404e60f171e661492740b1c", "digest": "576b292938c6c9d0a0e721bd867a543b",
"sector_size": 68719476736 "sector_size": 68719476736
} }
} }
+2 -2
View File
@@ -25,7 +25,7 @@ func buildType() string {
} }
// BuildVersion is the local build version, set by build system // BuildVersion is the local build version, set by build system
const BuildVersion = "0.4.1" const BuildVersion = "0.4.0"
func UserVersion() string { func UserVersion() string {
return BuildVersion + buildType() + CurrentCommit return BuildVersion + buildType() + CurrentCommit
@@ -53,7 +53,7 @@ func (ve Version) EqMajorMinor(v2 Version) bool {
} }
// APIVersion is a semver version of the rpc api exposed // APIVersion is a semver version of the rpc api exposed
var APIVersion Version = newVer(0, 5, 0) var APIVersion Version = newVer(0, 3, 0)
//nolint:varcheck,deadcode //nolint:varcheck,deadcode
const ( const (
-4
View File
@@ -17,10 +17,6 @@ type Response struct {
Err error Err error
} }
// RandomBeacon represents a system that provides randomness to Lotus.
// Other components interrogate the RandomBeacon to acquire randomness that's
// valid for a specific chain epoch. Also to verify beacon entries that have
// been posted on chain.
type RandomBeacon interface { type RandomBeacon interface {
Entry(context.Context, uint64) <-chan Response Entry(context.Context, uint64) <-chan Response
VerifyEntry(types.BeaconEntry, types.BeaconEntry) error VerifyEntry(types.BeaconEntry, types.BeaconEntry) error
+31 -31
View File
@@ -8,9 +8,8 @@ import (
dchain "github.com/drand/drand/chain" dchain "github.com/drand/drand/chain"
dclient "github.com/drand/drand/client" dclient "github.com/drand/drand/client"
hclient "github.com/drand/drand/client/http" gclient "github.com/drand/drand/cmd/relay-gossip/client"
dlog "github.com/drand/drand/log" dlog "github.com/drand/drand/log"
gclient "github.com/drand/drand/lp2p/client"
"github.com/drand/kyber" "github.com/drand/kyber"
kzap "github.com/go-kit/kit/log/zap" kzap "github.com/go-kit/kit/log/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
@@ -19,15 +18,31 @@ import (
logging "github.com/ipfs/go-log" logging "github.com/ipfs/go-log"
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/beacon"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/specs-actors/actors/abi"
) )
var log = logging.Logger("drand") var log = logging.Logger("drand")
var drandServers = []string{
"https://pl-eu.testnet.drand.sh",
"https://pl-us.testnet.drand.sh",
"https://pl-sin.testnet.drand.sh",
}
var drandChain *dchain.Info
func init() {
var err error
drandChain, err = dchain.InfoFromJSON(bytes.NewReader([]byte(build.DrandChain)))
if err != nil {
panic("could not unmarshal chain info: " + err.Error())
}
}
type drandPeer struct { type drandPeer struct {
addr string addr string
tls bool tls bool
@@ -41,13 +56,6 @@ func (dp *drandPeer) IsTLS() bool {
return dp.tls return dp.tls
} }
// DrandBeacon connects Lotus with a drand network in order to provide
// randomness to the system in a way that's aligned with Filecoin rounds/epochs.
//
// We connect to drand peers via their public HTTP endpoints. The peers are
// enumerated in the drandServers variable.
//
// The root trust for the Drand chain is configured from build.DrandChain.
type DrandBeacon struct { type DrandBeacon struct {
client dclient.Client client dclient.Client
@@ -64,34 +72,18 @@ type DrandBeacon struct {
localCache map[uint64]types.BeaconEntry localCache map[uint64]types.BeaconEntry
} }
func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub, config dtypes.DrandConfig) (*DrandBeacon, error) { func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub) (*DrandBeacon, error) {
if genesisTs == 0 { if genesisTs == 0 {
panic("what are you doing this cant be zero") panic("what are you doing this cant be zero")
} }
drandChain, err := dchain.InfoFromJSON(bytes.NewReader([]byte(config.ChainInfoJSON)))
if err != nil {
return nil, xerrors.Errorf("unable to unmarshal drand chain info: %w", err)
}
dlogger := dlog.NewKitLoggerFrom(kzap.NewZapSugarLogger( dlogger := dlog.NewKitLoggerFrom(kzap.NewZapSugarLogger(
log.SugaredLogger.Desugar(), zapcore.InfoLevel)) log.SugaredLogger.Desugar(), zapcore.InfoLevel))
var clients []dclient.Client
for _, url := range config.Servers {
hc, err := hclient.NewWithInfo(url, drandChain, nil)
if err != nil {
return nil, xerrors.Errorf("could not create http drand client: %w", err)
}
clients = append(clients, hc)
}
opts := []dclient.Option{ opts := []dclient.Option{
dclient.WithChainInfo(drandChain), dclient.WithChainInfo(drandChain),
dclient.WithHTTPEndpoints(drandServers),
dclient.WithCacheSize(1024), dclient.WithCacheSize(1024),
dclient.WithLogger(dlogger), dclient.WithLogger(dlogger),
dclient.WithAutoWatch(),
} }
if ps != nil { if ps != nil {
@@ -100,11 +92,19 @@ func NewDrandBeacon(genesisTs, interval uint64, ps *pubsub.PubSub, config dtypes
log.Info("drand beacon without pubsub") log.Info("drand beacon without pubsub")
} }
client, err := dclient.Wrap(clients, opts...) client, err := dclient.New(opts...)
if err != nil { if err != nil {
return nil, xerrors.Errorf("creating drand client") return nil, xerrors.Errorf("creating drand client")
} }
go func() {
// Explicitly Watch until that is fixed in drand
ch := client.Watch(context.Background())
for range ch {
}
log.Error("dranch Watch bork")
}()
db := &DrandBeacon{ db := &DrandBeacon{
client: client, client: client,
localCache: make(map[uint64]types.BeaconEntry), localCache: make(map[uint64]types.BeaconEntry),
+2 -5
View File
@@ -5,15 +5,12 @@ import (
"testing" "testing"
dchain "github.com/drand/drand/chain" dchain "github.com/drand/drand/chain"
hclient "github.com/drand/drand/client/http" dclient "github.com/drand/drand/client"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/filecoin-project/lotus/build"
) )
func TestPrintGroupInfo(t *testing.T) { func TestPrintGroupInfo(t *testing.T) {
server := build.DrandConfig.Servers[0] c, err := dclient.NewHTTPClient(drandServers[0], nil, nil)
c, err := hclient.New(server, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
cg := c.(interface { cg := c.(interface {
FetchChainInfo(groupHash []byte) (*dchain.Info, error) FetchChainInfo(groupHash []byte) (*dchain.Info, error)
-19
View File
@@ -10,7 +10,6 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
cborutil "github.com/filecoin-project/go-cbor-util" cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
@@ -28,24 +27,6 @@ const BlockSyncProtocolID = "/fil/sync/blk/0.0.1"
const BlockSyncMaxRequestLength = 800 const BlockSyncMaxRequestLength = 800
// BlockSyncService is the component that services BlockSync requests from
// peers.
//
// BlockSync is the basic chain synchronization protocol of Filecoin. BlockSync
// is an RPC-oriented protocol, with a single operation to request blocks.
//
// A request contains a start anchor block (referred to with a CID), and a
// amount of blocks requested beyond the anchor (including the anchor itself).
//
// A client can also pass options, encoded as a 64-bit bitfield. Lotus supports
// two options at the moment:
//
// - include block contents
// - include block messages
//
// The response will include a status code, an optional message, and the
// response payload in case of success. The payload is a slice of serialized
// tipsets.
type BlockSyncService struct { type BlockSyncService struct {
cs *store.ChainStore cs *store.ChainStore
} }
-8
View File
@@ -64,11 +64,6 @@ func (bs *BlockSync) processStatus(req *BlockSyncRequest, res *BlockSyncResponse
} }
} }
// GetBlocks fetches count blocks from the network, from the provided tipset
// *backwards*, returning as many tipsets as count.
//
// {hint/usage}: This is used by the Syncer during normal chain syncing and when
// resolving forks.
func (bs *BlockSync) GetBlocks(ctx context.Context, tsk types.TipSetKey, count int) ([]*types.TipSet, error) { func (bs *BlockSync) GetBlocks(ctx context.Context, tsk types.TipSetKey, count int) ([]*types.TipSet, error) {
ctx, span := trace.StartSpan(ctx, "bsync.GetBlocks") ctx, span := trace.StartSpan(ctx, "bsync.GetBlocks")
defer span.End() defer span.End()
@@ -85,9 +80,7 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tsk types.TipSetKey, count i
Options: BSOptBlocks, Options: BSOptBlocks,
} }
// this peerset is sorted by latency and failure counting.
peers := bs.getPeers() peers := bs.getPeers()
// randomize the first few peers so we don't always pick the same peer // randomize the first few peers so we don't always pick the same peer
shufflePrefix(peers) shufflePrefix(peers)
@@ -363,7 +356,6 @@ func (bs *BlockSync) RemovePeer(p peer.ID) {
bs.syncPeers.removePeer(p) bs.syncPeers.removePeer(p)
} }
// getPeers returns a preference-sorted set of peers to query.
func (bs *BlockSync) getPeers() []peer.ID { func (bs *BlockSync) getPeers() []peer.ID {
return bs.syncPeers.prefSortedPeers() return bs.syncPeers.prefSortedPeers()
} }
+15 -4
View File
@@ -51,7 +51,7 @@ type Events struct {
readyOnce sync.Once readyOnce sync.Once
heightEvents heightEvents
*hcEvents calledEvents
} }
func NewEvents(ctx context.Context, api eventAPI) *Events { func NewEvents(ctx context.Context, api eventAPI) *Events {
@@ -74,7 +74,18 @@ func NewEvents(ctx context.Context, api eventAPI) *Events {
htHeights: map[abi.ChainEpoch][]uint64{}, htHeights: map[abi.ChainEpoch][]uint64{},
}, },
hcEvents: newHCEvents(ctx, api, tsc, uint64(gcConfidence)), calledEvents: calledEvents{
cs: api,
tsc: tsc,
ctx: ctx,
gcConfidence: uint64(gcConfidence),
confQueue: map[triggerH]map[msgH][]*queuedEvent{},
revertQueue: map[msgH][]triggerH{},
triggers: map[triggerID]*callHandler{},
matchers: map[triggerID][]MatchFunc{},
timeouts: map[abi.ChainEpoch]map[triggerID]int{},
},
} }
e.ready.Add(1) e.ready.Add(1)
@@ -132,7 +143,7 @@ func (e *Events) listenHeadChangesOnce(ctx context.Context) error {
} }
e.readyOnce.Do(func() { e.readyOnce.Do(func() {
e.lastTs = cur[0].Val e.at = cur[0].Val.Height()
e.ready.Done() e.ready.Done()
}) })
@@ -175,5 +186,5 @@ func (e *Events) headChange(rev, app []*types.TipSet) error {
return err return err
} }
return e.processHeadChangeEvent(rev, app) return e.headChangeCalled(rev, app)
} }
+107 -361
View File
@@ -13,7 +13,6 @@ import (
) )
const NoTimeout = math.MaxInt64 const NoTimeout = math.MaxInt64
const NoHeight = abi.ChainEpoch(-1)
type triggerID = uint64 type triggerID = uint64
@@ -24,60 +23,54 @@ type msgH = abi.ChainEpoch
// message (msgH+confidence) // message (msgH+confidence)
type triggerH = abi.ChainEpoch type triggerH = abi.ChainEpoch
type eventData interface{} // CalledHandler arguments:
// `ts` is the tipset, in which the `msg` is included.
// EventHandler arguments:
// `prevTs` is the previous tipset, eg the "from" tipset for a state change.
// `ts` is the event tipset, eg the tipset in which the `msg` is included.
// `curH`-`ts.Height` = `confidence` // `curH`-`ts.Height` = `confidence`
type EventHandler func(data eventData, prevTs, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error) type CalledHandler func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error)
// CheckFunc is used for atomicity guarantees. If the condition the callbacks // CheckFunc is used for atomicity guarantees. If the condition the callbacks
// wait for has already happened in tipset `ts` // wait for has already happened in tipset `ts`
// //
// If `done` is true, timeout won't be triggered // If `done` is true, timeout won't be triggered
// If `more` is false, no messages will be sent to EventHandler (RevertHandler // If `more` is false, no messages will be sent to CalledHandler (RevertHandler
// may still be called) // may still be called)
type CheckFunc func(ts *types.TipSet) (done bool, more bool, err error) type CheckFunc func(ts *types.TipSet) (done bool, more bool, err error)
// Keep track of information for an event handler type MatchFunc func(msg *types.Message) (bool, error)
type handlerInfo struct {
type callHandler struct {
confidence int confidence int
timeout abi.ChainEpoch timeout abi.ChainEpoch
disabled bool // TODO: GC after gcConfidence reached disabled bool // TODO: GC after gcConfidence reached
handle EventHandler handle CalledHandler
revert RevertHandler revert RevertHandler
} }
// When a change occurs, a queuedEvent is created and put into a queue
// until the required confidence is reached
type queuedEvent struct { type queuedEvent struct {
trigger triggerID trigger triggerID
prevH abi.ChainEpoch h abi.ChainEpoch
h abi.ChainEpoch msg *types.Message
data eventData
called bool called bool
} }
// Manages chain head change events, which may be forward (new tipset added to type calledEvents struct {
// chain) or backward (chain branch discarded in favour of heavier branch)
type hcEvents struct {
cs eventAPI cs eventAPI
tsc *tipSetCache tsc *tipSetCache
ctx context.Context ctx context.Context
gcConfidence uint64 gcConfidence uint64
lastTs *types.TipSet at abi.ChainEpoch
lk sync.Mutex lk sync.Mutex
ctr triggerID ctr triggerID
triggers map[triggerID]*handlerInfo triggers map[triggerID]*callHandler
matchers map[triggerID][]MatchFunc
// maps block heights to events // maps block heights to events
// [triggerH][msgH][event] // [triggerH][msgH][event]
@@ -88,79 +81,27 @@ type hcEvents struct {
// [timeoutH+confidence][triggerID]{calls} // [timeoutH+confidence][triggerID]{calls}
timeouts map[abi.ChainEpoch]map[triggerID]int timeouts map[abi.ChainEpoch]map[triggerID]int
messageEvents
watcherEvents
} }
func newHCEvents(ctx context.Context, cs eventAPI, tsc *tipSetCache, gcConfidence uint64) *hcEvents { func (e *calledEvents) headChangeCalled(rev, app []*types.TipSet) error {
e := hcEvents{
ctx: ctx,
cs: cs,
tsc: tsc,
gcConfidence: gcConfidence,
confQueue: map[triggerH]map[msgH][]*queuedEvent{},
revertQueue: map[msgH][]triggerH{},
triggers: map[triggerID]*handlerInfo{},
timeouts: map[abi.ChainEpoch]map[triggerID]int{},
}
e.messageEvents = newMessageEvents(ctx, &e, cs)
e.watcherEvents = newWatcherEvents(ctx, &e, cs)
return &e
}
// Called when there is a change to the head with tipsets to be
// reverted / applied
func (e *hcEvents) processHeadChangeEvent(rev, app []*types.TipSet) error {
e.lk.Lock()
defer e.lk.Unlock()
for _, ts := range rev { for _, ts := range rev {
e.handleReverts(ts) e.handleReverts(ts)
e.lastTs = ts e.at = ts.Height()
} }
for _, ts := range app { for _, ts := range app {
// Check if the head change caused any state changes that we were // called triggers
// waiting for e.checkNewCalls(ts)
stateChanges := e.watcherEvents.checkStateChanges(e.lastTs, ts) for ; e.at <= ts.Height(); e.at++ {
e.applyWithConfidence(ts, e.at)
// Queue up calls until there have been enough blocks to reach
// confidence on the state changes
for tid, data := range stateChanges {
e.queueForConfidence(tid, data, e.lastTs, ts)
}
// Check if the head change included any new message calls
newCalls, err := e.messageEvents.checkNewCalls(ts)
if err != nil {
return err
}
// Queue up calls until there have been enough blocks to reach
// confidence on the message calls
for tid, data := range newCalls {
e.queueForConfidence(tid, data, nil, ts)
}
for at := e.lastTs.Height(); at <= ts.Height(); at++ {
// Apply any queued events and timeouts that were targeted at the
// current chain height
e.applyWithConfidence(ts, at)
e.applyTimeouts(ts) e.applyTimeouts(ts)
} }
// Update the latest known tipset
e.lastTs = ts
} }
return nil return nil
} }
func (e *hcEvents) handleReverts(ts *types.TipSet) { func (e *calledEvents) handleReverts(ts *types.TipSet) {
reverts, ok := e.revertQueue[ts.Height()] reverts, ok := e.revertQueue[ts.Height()]
if !ok { if !ok {
return // nothing to do return // nothing to do
@@ -176,7 +117,7 @@ func (e *hcEvents) handleReverts(ts *types.TipSet) {
trigger := e.triggers[event.trigger] trigger := e.triggers[event.trigger]
if err := trigger.revert(e.ctx, ts); err != nil { if err := trigger.revert(e.ctx, ts); err != nil {
log.Errorf("reverting chain trigger (@H %d, triggered @ %d) failed: %s", ts.Height(), triggerH, err) log.Errorf("reverting chain trigger (call %s.%d() @H %d, called @ %d) failed: %s", event.msg.To, event.msg.Method, ts.Height(), triggerH, err)
} }
} }
delete(e.confQueue[triggerH], ts.Height()) delete(e.confQueue[triggerH], ts.Height())
@@ -184,15 +125,42 @@ func (e *hcEvents) handleReverts(ts *types.TipSet) {
delete(e.revertQueue, ts.Height()) delete(e.revertQueue, ts.Height())
} }
// Queue up events until the chain has reached a height that reflects the func (e *calledEvents) checkNewCalls(ts *types.TipSet) {
// desired confidence pts, err := e.cs.ChainGetTipSet(e.ctx, ts.Parents()) // we actually care about messages in the parent tipset here
func (e *hcEvents) queueForConfidence(trigID uint64, data eventData, prevTs, ts *types.TipSet) { if err != nil {
log.Errorf("getting parent tipset in checkNewCalls: %s", err)
return
}
e.messagesForTs(pts, func(msg *types.Message) {
// TODO: provide receipts
for tid, matchFns := range e.matchers {
var matched bool
for _, matchFn := range matchFns {
ok, err := matchFn(msg)
if err != nil {
log.Errorf("event matcher failed: %s", err)
continue
}
matched = ok
if matched {
break
}
}
if matched {
e.queueForConfidence(tid, msg, ts)
break
}
}
})
}
func (e *calledEvents) queueForConfidence(trigID uint64, msg *types.Message, ts *types.TipSet) {
trigger := e.triggers[trigID] trigger := e.triggers[trigID]
prevH := NoHeight
if prevTs != nil {
prevH = prevTs.Height()
}
appliedH := ts.Height() appliedH := ts.Height()
triggerH := appliedH + abi.ChainEpoch(trigger.confidence) triggerH := appliedH + abi.ChainEpoch(trigger.confidence)
@@ -205,19 +173,17 @@ func (e *hcEvents) queueForConfidence(trigID uint64, data eventData, prevTs, ts
byOrigH[appliedH] = append(byOrigH[appliedH], &queuedEvent{ byOrigH[appliedH] = append(byOrigH[appliedH], &queuedEvent{
trigger: trigID, trigger: trigID,
prevH: prevH,
h: appliedH, h: appliedH,
data: data, msg: msg,
}) })
e.revertQueue[appliedH] = append(e.revertQueue[appliedH], triggerH) e.revertQueue[appliedH] = append(e.revertQueue[appliedH], triggerH)
} }
// Apply any events that were waiting for this chain height for confidence func (e *calledEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch) {
func (e *hcEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch) {
byOrigH, ok := e.confQueue[height] byOrigH, ok := e.confQueue[height]
if !ok { if !ok {
return // no triggers at this height return // no triggers at thin height
} }
for origH, events := range byOrigH { for origH, events := range byOrigH {
@@ -236,20 +202,15 @@ func (e *hcEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch)
continue continue
} }
// Previous tipset - this is relevant for example in a state change rec, err := e.cs.StateGetReceipt(e.ctx, event.msg.Cid(), ts.Key())
// from one tipset to another if err != nil {
var prevTs *types.TipSet log.Error(err)
if event.prevH != NoHeight { return
prevTs, err = e.tsc.get(event.prevH)
if err != nil {
log.Errorf("events: applyWithConfidence didn't find tipset for previous event; wanted %d; current %d", event.prevH, height)
continue
}
} }
more, err := trigger.handle(event.data, prevTs, triggerTs, height) more, err := trigger.handle(event.msg, rec, triggerTs, height)
if err != nil { if err != nil {
log.Errorf("chain trigger (@H %d, triggered @ %d) failed: %s", origH, height, err) log.Errorf("chain trigger (call %s.%d() @H %d, called @ %d) failed: %s", event.msg.To, event.msg.Method, origH, height, err)
continue // don't revert failed calls continue // don't revert failed calls
} }
@@ -265,8 +226,7 @@ func (e *hcEvents) applyWithConfidence(ts *types.TipSet, height abi.ChainEpoch)
} }
} }
// Apply any timeouts that expire at this height func (e *calledEvents) applyTimeouts(ts *types.TipSet) {
func (e *hcEvents) applyTimeouts(ts *types.TipSet) {
triggers, ok := e.timeouts[ts.Height()] triggers, ok := e.timeouts[ts.Height()]
if !ok { if !ok {
return // nothing to do return // nothing to do
@@ -296,225 +256,12 @@ func (e *hcEvents) applyTimeouts(ts *types.TipSet) {
} }
} }
// Listen for an event func (e *calledEvents) messagesForTs(ts *types.TipSet, consume func(*types.Message)) {
// - CheckFunc: immediately checks if the event already occurred
// - EventHandler: called when the event has occurred, after confidence tipsets
// - RevertHandler: called if the chain head changes causing the event to revert
// - confidence: wait this many tipsets before calling EventHandler
// - timeout: at this chain height, timeout on waiting for this event
func (e *hcEvents) onHeadChanged(check CheckFunc, hnd EventHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch) (triggerID, error) {
e.lk.Lock()
defer e.lk.Unlock()
// Check if the event has already occurred
ts := e.tsc.best()
done, more, err := check(ts)
if err != nil {
return 0, xerrors.Errorf("called check error (h: %d): %w", ts.Height(), err)
}
if done {
timeout = NoTimeout
}
// Create a trigger for the event
id := e.ctr
e.ctr++
e.triggers[id] = &handlerInfo{
confidence: confidence,
timeout: timeout + abi.ChainEpoch(confidence),
disabled: !more,
handle: hnd,
revert: rev,
}
// If there's a timeout, set up a timeout check at that height
if timeout != NoTimeout {
if e.timeouts[timeout+abi.ChainEpoch(confidence)] == nil {
e.timeouts[timeout+abi.ChainEpoch(confidence)] = map[uint64]int{}
}
e.timeouts[timeout+abi.ChainEpoch(confidence)][id] = 0
}
return id, nil
}
// headChangeAPI is used to allow the composed event APIs to call back to hcEvents
// to listen for changes
type headChangeAPI interface {
onHeadChanged(check CheckFunc, hnd EventHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch) (triggerID, error)
}
// watcherEvents watches for a state change
type watcherEvents struct {
ctx context.Context
cs eventAPI
hcAPI headChangeAPI
lk sync.RWMutex
matchers map[triggerID]StateMatchFunc
}
func newWatcherEvents(ctx context.Context, hcAPI headChangeAPI, cs eventAPI) watcherEvents {
return watcherEvents{
ctx: ctx,
cs: cs,
hcAPI: hcAPI,
matchers: make(map[triggerID]StateMatchFunc),
}
}
// Run each of the matchers against the previous and current state to see if
// there's a change
func (we *watcherEvents) checkStateChanges(oldState, newState *types.TipSet) map[triggerID]eventData {
we.lk.RLock()
defer we.lk.RUnlock()
res := make(map[triggerID]eventData)
for tid, matchFn := range we.matchers {
ok, data, err := matchFn(oldState, newState)
if err != nil {
log.Errorf("event diff fn failed: %s", err)
continue
}
if ok {
res[tid] = data
}
}
return res
}
// StateChange represents a change in state
type StateChange interface{}
// StateChangeHandler arguments:
// `oldTs` is the state "from" tipset
// `newTs` is the state "to" tipset
// `states` is the change in state
// `curH`-`ts.Height` = `confidence`
type StateChangeHandler func(oldTs, newTs *types.TipSet, states StateChange, curH abi.ChainEpoch) (more bool, err error)
type StateMatchFunc func(oldTs, newTs *types.TipSet) (bool, StateChange, error)
// StateChanged registers a callback which is triggered when a specified state
// change occurs or a timeout is reached.
//
// * `CheckFunc` callback is invoked immediately with a recent tipset, it
// returns two booleans - `done`, and `more`.
//
// * `done` should be true when some on-chain state change we are waiting
// for has happened. When `done` is set to true, timeout trigger is disabled.
//
// * `more` should be false when we don't want to receive new notifications
// through StateChangeHandler. Note that notifications may still be delivered to
// RevertHandler
//
// * `StateChangeHandler` is called when the specified state change was observed
// on-chain, and a confidence threshold was reached, or the specified `timeout`
// height was reached with no state change observed. When this callback is
// invoked on a timeout, `oldState` and `newState` are set to nil.
// This callback returns a boolean specifying whether further notifications
// should be sent, like `more` return param from `CheckFunc` above.
//
// * `RevertHandler` is called after apply handler, when we drop the tipset
// containing the message. The tipset passed as the argument is the tipset
// that is being dropped. Note that the event dropped may be re-applied
// in a different tipset in small amount of time.
//
// * `StateMatchFunc` is called against each tipset state. If there is a match,
// the state change is queued up until the confidence interval has elapsed (and
// `StateChangeHandler` is called)
func (we *watcherEvents) StateChanged(check CheckFunc, scHnd StateChangeHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf StateMatchFunc) error {
hnd := func(data eventData, prevTs, ts *types.TipSet, height abi.ChainEpoch) (bool, error) {
states, ok := data.(StateChange)
if data != nil && !ok {
panic("expected StateChange")
}
return scHnd(prevTs, ts, states, height)
}
id, err := we.hcAPI.onHeadChanged(check, hnd, rev, confidence, timeout)
if err != nil {
return err
}
we.lk.Lock()
defer we.lk.Unlock()
we.matchers[id] = mf
return nil
}
// messageEvents watches for message calls to actors
type messageEvents struct {
ctx context.Context
cs eventAPI
hcAPI headChangeAPI
lk sync.RWMutex
matchers map[triggerID][]MsgMatchFunc
}
func newMessageEvents(ctx context.Context, hcAPI headChangeAPI, cs eventAPI) messageEvents {
return messageEvents{
ctx: ctx,
cs: cs,
hcAPI: hcAPI,
matchers: map[triggerID][]MsgMatchFunc{},
}
}
// Check if there are any new actor calls
func (me *messageEvents) checkNewCalls(ts *types.TipSet) (map[triggerID]eventData, error) {
pts, err := me.cs.ChainGetTipSet(me.ctx, ts.Parents()) // we actually care about messages in the parent tipset here
if err != nil {
log.Errorf("getting parent tipset in checkNewCalls: %s", err)
return nil, err
}
me.lk.RLock()
defer me.lk.RUnlock()
res := make(map[triggerID]eventData)
me.messagesForTs(pts, func(msg *types.Message) {
// TODO: provide receipts
for tid, matchFns := range me.matchers {
var matched bool
for _, matchFn := range matchFns {
ok, err := matchFn(msg)
if err != nil {
log.Errorf("event matcher failed: %s", err)
continue
}
matched = ok
if matched {
break
}
}
if matched {
res[tid] = msg
break
}
}
})
return res, nil
}
// Get the messages in a tipset
func (me *messageEvents) messagesForTs(ts *types.TipSet, consume func(*types.Message)) {
seen := map[cid.Cid]struct{}{} seen := map[cid.Cid]struct{}{}
for _, tsb := range ts.Blocks() { for _, tsb := range ts.Blocks() {
msgs, err := me.cs.ChainGetBlockMessages(context.TODO(), tsb.Cid()) msgs, err := e.cs.ChainGetBlockMessages(context.TODO(), tsb.Cid())
if err != nil { if err != nil {
log.Errorf("messagesForTs MessagesForBlock failed (ts.H=%d, Bcid:%s, B.Mcid:%s): %s", ts.Height(), tsb.Cid(), tsb.Messages, err) log.Errorf("messagesForTs MessagesForBlock failed (ts.H=%d, Bcid:%s, B.Mcid:%s): %s", ts.Height(), tsb.Cid(), tsb.Messages, err)
// this is quite bad, but probably better than missing all the other updates // this is quite bad, but probably better than missing all the other updates
@@ -543,27 +290,20 @@ func (me *messageEvents) messagesForTs(ts *types.TipSet, consume func(*types.Mes
} }
} }
// MsgHandler arguments: // Called registers a callbacks which are triggered when a specified method is
// `ts` is the tipset, in which the `msg` is included.
// `curH`-`ts.Height` = `confidence`
type MsgHandler func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (more bool, err error)
type MsgMatchFunc func(msg *types.Message) (bool, error)
// Called registers a callback which is triggered when a specified method is
// called on an actor, or a timeout is reached. // called on an actor, or a timeout is reached.
// //
// * `CheckFunc` callback is invoked immediately with a recent tipset, it // * `CheckFunc` callback is invoked immediately with a recent tipset, it
// returns two booleans - `done`, and `more`. // returns two booleans - `done`, and `more`.
// //
// * `done` should be true when some on-chain action we are waiting for has // * `done` should be true when some on-chain action we are waiting for has
// happened. When `done` is set to true, timeout trigger is disabled. // happened. When `done` is set to true, timeout trigger is disabled.
// //
// * `more` should be false when we don't want to receive new notifications // * `more` should be false when we don't want to receive new notifications
// through MsgHandler. Note that notifications may still be delivered to // through CalledHandler. Note that notifications may still be delivered to
// RevertHandler // RevertHandler
// //
// * `MsgHandler` is called when the specified event was observed on-chain, // * `CalledHandler` is called when the specified event was observed on-chain,
// and a confidence threshold was reached, or the specified `timeout` height // and a confidence threshold was reached, or the specified `timeout` height
// was reached with no events observed. When this callback is invoked on a // was reached with no events observed. When this callback is invoked on a
// timeout, `msg` is set to nil. This callback returns a boolean specifying // timeout, `msg` is set to nil. This callback returns a boolean specifying
@@ -574,38 +314,44 @@ type MsgMatchFunc func(msg *types.Message) (bool, error)
// containing the message. The tipset passed as the argument is the tipset // containing the message. The tipset passed as the argument is the tipset
// that is being dropped. Note that the message dropped may be re-applied // that is being dropped. Note that the message dropped may be re-applied
// in a different tipset in small amount of time. // in a different tipset in small amount of time.
// func (e *calledEvents) Called(check CheckFunc, hnd CalledHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf MatchFunc) error {
// * `MsgMatchFunc` is called against each message. If there is a match, the e.lk.Lock()
// message is queued up until the confidence interval has elapsed (and defer e.lk.Unlock()
// `MsgHandler` is called)
func (me *messageEvents) Called(check CheckFunc, msgHnd MsgHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, mf MsgMatchFunc) error {
hnd := func(data eventData, prevTs, ts *types.TipSet, height abi.ChainEpoch) (bool, error) {
msg, ok := data.(*types.Message)
if data != nil && !ok {
panic("expected msg")
}
rec, err := me.cs.StateGetReceipt(me.ctx, msg.Cid(), ts.Key()) ts := e.tsc.best()
if err != nil { done, more, err := check(ts)
return false, err
}
return msgHnd(msg, rec, ts, height)
}
id, err := me.hcAPI.onHeadChanged(check, hnd, rev, confidence, timeout)
if err != nil { if err != nil {
return err return xerrors.Errorf("called check error (h: %d): %w", ts.Height(), err)
}
if done {
timeout = NoTimeout
} }
me.lk.Lock() id := e.ctr
defer me.lk.Unlock() e.ctr++
me.matchers[id] = append(me.matchers[id], mf)
e.triggers[id] = &callHandler{
confidence: confidence,
timeout: timeout + abi.ChainEpoch(confidence),
disabled: !more,
handle: hnd,
revert: rev,
}
e.matchers[id] = append(e.matchers[id], mf)
if timeout != NoTimeout {
if e.timeouts[timeout+abi.ChainEpoch(confidence)] == nil {
e.timeouts[timeout+abi.ChainEpoch(confidence)] = map[uint64]int{}
}
e.timeouts[timeout+abi.ChainEpoch(confidence)][id] = 0
}
return nil return nil
} }
// Convenience function for checking and matching messages func (e *calledEvents) CalledMsg(ctx context.Context, hnd CalledHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, msg types.ChainMsg) error {
func (me *messageEvents) CalledMsg(ctx context.Context, hnd MsgHandler, rev RevertHandler, confidence int, timeout abi.ChainEpoch, msg types.ChainMsg) error { return e.Called(e.CheckMsg(ctx, msg, hnd), hnd, rev, confidence, timeout, e.MatchMsg(msg.VMMessage()))
return me.Called(me.CheckMsg(ctx, msg, hnd), hnd, rev, confidence, timeout, me.MatchMsg(msg.VMMessage()))
} }
+3 -11
View File
@@ -26,15 +26,12 @@ type heightEvents struct {
} }
func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error { func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
ctx, span := trace.StartSpan(e.ctx, "events.HeightHeadChange") ctx, span := trace.StartSpan(e.ctx, "events.HeightHeadChange")
defer span.End() defer span.End()
span.AddAttributes(trace.Int64Attribute("endHeight", int64(app[0].Height()))) span.AddAttributes(trace.Int64Attribute("endHeight", int64(app[0].Height())))
span.AddAttributes(trace.Int64Attribute("reverts", int64(len(rev)))) span.AddAttributes(trace.Int64Attribute("reverts", int64(len(rev))))
span.AddAttributes(trace.Int64Attribute("applies", int64(len(app)))) span.AddAttributes(trace.Int64Attribute("applies", int64(len(app))))
e.lk.Lock()
defer e.lk.Unlock()
for _, ts := range rev { for _, ts := range rev {
// TODO: log error if h below gcconfidence // TODO: log error if h below gcconfidence
// revert height-based triggers // revert height-based triggers
@@ -43,10 +40,7 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
for _, tid := range e.htHeights[h] { for _, tid := range e.htHeights[h] {
ctx, span := trace.StartSpan(ctx, "events.HeightRevert") ctx, span := trace.StartSpan(ctx, "events.HeightRevert")
rev := e.heightTriggers[tid].revert err := e.heightTriggers[tid].revert(ctx, ts)
e.lk.Unlock()
err := rev(ctx, ts)
e.lk.Lock()
e.heightTriggers[tid].called = false e.heightTriggers[tid].called = false
span.End() span.End()
@@ -104,10 +98,8 @@ func (e *heightEvents) headChangeAt(rev, app []*types.TipSet) error {
ctx, span := trace.StartSpan(ctx, "events.HeightApply") ctx, span := trace.StartSpan(ctx, "events.HeightApply")
span.AddAttributes(trace.BoolAttribute("immediate", false)) span.AddAttributes(trace.BoolAttribute("immediate", false))
handle := hnd.handle
e.lk.Unlock() err = hnd.handle(ctx, incTs, h)
err = handle(ctx, incTs, h)
e.lk.Lock()
span.End() span.End()
if err != nil { if err != nil {
+2 -247
View File
@@ -1004,6 +1004,8 @@ func TestRemoveTriggersOnMessage(t *testing.T) {
return false, true, nil return false, true, nil
}, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) { }, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) {
require.Equal(t, false, applied) require.Equal(t, false, applied)
fmt.Println(msg == nil)
fmt.Println(curH)
applied = true applied = true
return more, nil return more, nil
}, func(_ context.Context, ts *types.TipSet) error { }, func(_ context.Context, ts *types.TipSet) error {
@@ -1065,250 +1067,3 @@ func TestRemoveTriggersOnMessage(t *testing.T) {
require.Equal(t, true, applied) require.Equal(t, true, applied)
require.Equal(t, false, reverted) require.Equal(t, false, reverted)
} }
type testStateChange struct {
from string
to string
}
func TestStateChanged(t *testing.T) {
fcs := &fakeCS{
t: t,
h: 1,
msgs: map[cid.Cid]fakeMsg{},
blkMsgs: map[cid.Cid]cid.Cid{},
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
events := NewEvents(context.Background(), fcs)
more := true
var applied, reverted bool
var appliedData StateChange
var appliedOldTs *types.TipSet
var appliedNewTs *types.TipSet
var appliedH abi.ChainEpoch
var matchData StateChange
confidence := 3
timeout := abi.ChainEpoch(20)
err := events.StateChanged(func(ts *types.TipSet) (d bool, m bool, e error) {
return false, true, nil
}, func(oldTs, newTs *types.TipSet, data StateChange, curH abi.ChainEpoch) (bool, error) {
require.Equal(t, false, applied)
applied = true
appliedData = data
appliedOldTs = oldTs
appliedNewTs = newTs
appliedH = curH
return more, nil
}, func(_ context.Context, ts *types.TipSet) error {
reverted = true
return nil
}, confidence, timeout, func(oldTs, newTs *types.TipSet) (bool, StateChange, error) {
if matchData == nil {
return false, matchData, nil
}
d := matchData
matchData = nil
return true, d, nil
})
require.NoError(t, err)
// create few blocks to make sure nothing get's randomly called
fcs.advance(0, 4, nil) // H=5
require.Equal(t, false, applied)
require.Equal(t, false, reverted)
// create state change (but below confidence threshold)
matchData = testStateChange{from: "a", to: "b"}
fcs.advance(0, 3, nil)
require.Equal(t, false, applied)
require.Equal(t, false, reverted)
// create additional block so we are above confidence threshold
fcs.advance(0, 2, nil) // H=10 (confidence=3, apply)
require.Equal(t, true, applied)
require.Equal(t, false, reverted)
applied = false
// dip below confidence (should not apply again)
fcs.advance(2, 2, nil) // H=10 (confidence=3, apply)
require.Equal(t, false, applied)
require.Equal(t, false, reverted)
// Change happens from 5 -> 6
require.Equal(t, abi.ChainEpoch(5), appliedOldTs.Height())
require.Equal(t, abi.ChainEpoch(6), appliedNewTs.Height())
// Actually applied (with confidence) at 9
require.Equal(t, abi.ChainEpoch(9), appliedH)
// Make sure the state change was correctly passed through
rcvd := appliedData.(testStateChange)
require.Equal(t, "a", rcvd.from)
require.Equal(t, "b", rcvd.to)
}
func TestStateChangedRevert(t *testing.T) {
fcs := &fakeCS{
t: t,
h: 1,
msgs: map[cid.Cid]fakeMsg{},
blkMsgs: map[cid.Cid]cid.Cid{},
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
events := NewEvents(context.Background(), fcs)
more := true
var applied, reverted bool
var matchData StateChange
confidence := 1
timeout := abi.ChainEpoch(20)
err := events.StateChanged(func(ts *types.TipSet) (d bool, m bool, e error) {
return false, true, nil
}, func(oldTs, newTs *types.TipSet, data StateChange, curH abi.ChainEpoch) (bool, error) {
require.Equal(t, false, applied)
applied = true
return more, nil
}, func(_ context.Context, ts *types.TipSet) error {
reverted = true
return nil
}, confidence, timeout, func(oldTs, newTs *types.TipSet) (bool, StateChange, error) {
if matchData == nil {
return false, matchData, nil
}
d := matchData
matchData = nil
return true, d, nil
})
require.NoError(t, err)
fcs.advance(0, 2, nil) // H=3
// Make a state change from TS at height 3 to TS at height 4
matchData = testStateChange{from: "a", to: "b"}
fcs.advance(0, 1, nil) // H=4
// Haven't yet reached confidence
require.Equal(t, false, applied)
require.Equal(t, false, reverted)
// Advance to reach confidence level
fcs.advance(0, 1, nil) // H=5
// Should now have called the handler
require.Equal(t, true, applied)
require.Equal(t, false, reverted)
applied = false
// Advance 3 more TS
fcs.advance(0, 3, nil) // H=8
require.Equal(t, false, applied)
require.Equal(t, false, reverted)
// Regress but not so far as to cause a revert
fcs.advance(3, 1, nil) // H=6
require.Equal(t, false, applied)
require.Equal(t, false, reverted)
// Regress back to state where change happened
fcs.advance(3, 1, nil) // H=4
// Expect revert to have happened
require.Equal(t, false, applied)
require.Equal(t, true, reverted)
}
func TestStateChangedTimeout(t *testing.T) {
fcs := &fakeCS{
t: t,
h: 1,
msgs: map[cid.Cid]fakeMsg{},
blkMsgs: map[cid.Cid]cid.Cid{},
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
events := NewEvents(context.Background(), fcs)
called := false
err := events.StateChanged(func(ts *types.TipSet) (d bool, m bool, e error) {
return false, true, nil
}, func(oldTs, newTs *types.TipSet, data StateChange, curH abi.ChainEpoch) (bool, error) {
called = true
require.Nil(t, data)
require.Equal(t, abi.ChainEpoch(20), newTs.Height())
require.Equal(t, abi.ChainEpoch(23), curH)
return false, nil
}, func(_ context.Context, ts *types.TipSet) error {
t.Fatal("revert on timeout")
return nil
}, 3, 20, func(oldTs, newTs *types.TipSet) (bool, StateChange, error) {
return false, nil, nil
})
require.NoError(t, err)
fcs.advance(0, 21, nil)
require.False(t, called)
fcs.advance(0, 5, nil)
require.True(t, called)
called = false
// with check func reporting done
fcs = &fakeCS{
t: t,
h: 1,
msgs: map[cid.Cid]fakeMsg{},
blkMsgs: map[cid.Cid]cid.Cid{},
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid)))
events = NewEvents(context.Background(), fcs)
err = events.StateChanged(func(ts *types.TipSet) (d bool, m bool, e error) {
return true, true, nil
}, func(oldTs, newTs *types.TipSet, data StateChange, curH abi.ChainEpoch) (bool, error) {
called = true
require.Nil(t, data)
require.Equal(t, abi.ChainEpoch(20), newTs.Height())
require.Equal(t, abi.ChainEpoch(23), curH)
return false, nil
}, func(_ context.Context, ts *types.TipSet) error {
t.Fatal("revert on timeout")
return nil
}, 3, 20, func(oldTs, newTs *types.TipSet) (bool, StateChange, error) {
return false, nil, nil
})
require.NoError(t, err)
fcs.advance(0, 21, nil)
require.False(t, called)
fcs.advance(0, 5, nil)
require.False(t, called)
}
-137
View File
@@ -1,137 +0,0 @@
package state
import (
"context"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-amt-ipld/v2"
"github.com/filecoin-project/lotus/api/apibstore"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
)
// UserData is the data returned from the DiffFunc
type UserData interface{}
// ChainAPI abstracts out calls made by this class to external APIs
type ChainAPI interface {
apibstore.ChainIO
StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error)
}
// StatePredicates has common predicates for responding to state changes
type StatePredicates struct {
api ChainAPI
cst *cbor.BasicIpldStore
}
func NewStatePredicates(api ChainAPI) *StatePredicates {
return &StatePredicates{
api: api,
cst: cbor.NewCborStore(apibstore.NewAPIBlockstore(api)),
}
}
// DiffFunc check if there's a change form oldState to newState, and returns
// - changed: was there a change
// - user: user-defined data representing the state change
// - err
type DiffFunc func(ctx context.Context, oldState, newState *types.TipSet) (changed bool, user UserData, err error)
type DiffStateFunc func(ctx context.Context, oldActorStateHead, newActorStateHead cid.Cid) (changed bool, user UserData, err error)
// OnActorStateChanged calls diffStateFunc when the state changes for the given actor
func (sp *StatePredicates) OnActorStateChanged(addr address.Address, diffStateFunc DiffStateFunc) DiffFunc {
return func(ctx context.Context, oldState, newState *types.TipSet) (changed bool, user UserData, err error) {
oldActor, err := sp.api.StateGetActor(ctx, addr, oldState.Key())
if err != nil {
return false, nil, err
}
newActor, err := sp.api.StateGetActor(ctx, addr, newState.Key())
if err != nil {
return false, nil, err
}
if oldActor.Head.Equals(newActor.Head) {
return false, nil, nil
}
return diffStateFunc(ctx, oldActor.Head, newActor.Head)
}
}
type DiffStorageMarketStateFunc func(ctx context.Context, oldState *market.State, newState *market.State) (changed bool, user UserData, err error)
// OnStorageMarketActorChanged calls diffStorageMarketState when the state changes for the market actor
func (sp *StatePredicates) OnStorageMarketActorChanged(diffStorageMarketState DiffStorageMarketStateFunc) DiffFunc {
return sp.OnActorStateChanged(builtin.StorageMarketActorAddr, func(ctx context.Context, oldActorStateHead, newActorStateHead cid.Cid) (changed bool, user UserData, err error) {
var oldState market.State
if err := sp.cst.Get(ctx, oldActorStateHead, &oldState); err != nil {
return false, nil, err
}
var newState market.State
if err := sp.cst.Get(ctx, newActorStateHead, &newState); err != nil {
return false, nil, err
}
return diffStorageMarketState(ctx, &oldState, &newState)
})
}
type DiffDealStatesFunc func(ctx context.Context, oldDealStateRoot *amt.Root, newDealStateRoot *amt.Root) (changed bool, user UserData, err error)
// OnDealStateChanged calls diffDealStates when the market state changes
func (sp *StatePredicates) OnDealStateChanged(diffDealStates DiffDealStatesFunc) DiffStorageMarketStateFunc {
return func(ctx context.Context, oldState *market.State, newState *market.State) (changed bool, user UserData, err error) {
if oldState.States.Equals(newState.States) {
return false, nil, nil
}
oldRoot, err := amt.LoadAMT(ctx, sp.cst, oldState.States)
if err != nil {
return false, nil, err
}
newRoot, err := amt.LoadAMT(ctx, sp.cst, newState.States)
if err != nil {
return false, nil, err
}
return diffDealStates(ctx, oldRoot, newRoot)
}
}
// ChangedDeals is a set of changes to deal state
type ChangedDeals map[abi.DealID]DealStateChange
// DealStateChange is a change in deal state from -> to
type DealStateChange struct {
From market.DealState
To market.DealState
}
// DealStateChangedForIDs detects changes in the deal state AMT for the given deal IDs
func (sp *StatePredicates) DealStateChangedForIDs(dealIds []abi.DealID) DiffDealStatesFunc {
return func(ctx context.Context, oldDealStateRoot *amt.Root, newDealStateRoot *amt.Root) (changed bool, user UserData, err error) {
changedDeals := make(ChangedDeals)
for _, dealID := range dealIds {
var oldDeal, newDeal market.DealState
err := oldDealStateRoot.Get(ctx, uint64(dealID), &oldDeal)
if err != nil {
return false, nil, err
}
err = newDealStateRoot.Get(ctx, uint64(dealID), &newDeal)
if err != nil {
return false, nil, err
}
if oldDeal != newDeal {
changedDeals[dealID] = DealStateChange{oldDeal, newDeal}
}
}
if len(changedDeals) > 0 {
return true, changedDeals, nil
}
return false, nil, nil
}
}
-201
View File
@@ -1,201 +0,0 @@
package state
import (
"context"
"testing"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/ipfs/go-hamt-ipld"
"github.com/filecoin-project/go-amt-ipld/v2"
"github.com/filecoin-project/specs-actors/actors/builtin/market"
ds "github.com/ipfs/go-datastore"
ds_sync "github.com/ipfs/go-datastore/sync"
bstore "github.com/ipfs/go-ipfs-blockstore"
cbornode "github.com/ipfs/go-ipld-cbor"
"golang.org/x/xerrors"
"github.com/ipfs/go-cid"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/specs-actors/actors/abi"
)
var dummyCid cid.Cid
func init() {
dummyCid, _ = cid.Parse("bafkqaaa")
}
type mockAPI struct {
ts map[types.TipSetKey]*types.Actor
bs bstore.Blockstore
}
func newMockAPI(bs bstore.Blockstore) *mockAPI {
return &mockAPI{
bs: bs,
ts: make(map[types.TipSetKey]*types.Actor),
}
}
func (m mockAPI) ChainHasObj(ctx context.Context, c cid.Cid) (bool, error) {
return m.bs.Has(c)
}
func (m mockAPI) ChainReadObj(ctx context.Context, c cid.Cid) ([]byte, error) {
blk, err := m.bs.Get(c)
if err != nil {
return nil, xerrors.Errorf("blockstore get: %w", err)
}
return blk.RawData(), nil
}
func (m mockAPI) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) {
return m.ts[tsk], nil
}
func (m mockAPI) setActor(tsk types.TipSetKey, act *types.Actor) {
m.ts[tsk] = act
}
func TestPredicates(t *testing.T) {
ctx := context.Background()
bs := bstore.NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore()))
store := cbornode.NewCborStore(bs)
oldDeals := map[abi.DealID]*market.DealState{
abi.DealID(1): {
SectorStartEpoch: 1,
LastUpdatedEpoch: 2,
SlashEpoch: 0,
},
abi.DealID(2): {
SectorStartEpoch: 4,
LastUpdatedEpoch: 5,
SlashEpoch: 0,
},
}
oldStateC := createMarketState(ctx, t, store, oldDeals)
newDeals := map[abi.DealID]*market.DealState{
abi.DealID(1): {
SectorStartEpoch: 1,
LastUpdatedEpoch: 3,
SlashEpoch: 0,
},
abi.DealID(2): {
SectorStartEpoch: 4,
LastUpdatedEpoch: 6,
SlashEpoch: 6,
},
}
newStateC := createMarketState(ctx, t, store, newDeals)
miner, err := address.NewFromString("t00")
require.NoError(t, err)
oldState, err := mockTipset(miner, 1)
require.NoError(t, err)
newState, err := mockTipset(miner, 2)
require.NoError(t, err)
api := newMockAPI(bs)
api.setActor(oldState.Key(), &types.Actor{Head: oldStateC})
api.setActor(newState.Key(), &types.Actor{Head: newStateC})
preds := NewStatePredicates(api)
dealIds := []abi.DealID{abi.DealID(1), abi.DealID(2)}
diffFn := preds.OnStorageMarketActorChanged(preds.OnDealStateChanged(preds.DealStateChangedForIDs(dealIds)))
// Diff a state against itself: expect no change
changed, _, err := diffFn(ctx, oldState, oldState)
require.NoError(t, err)
require.False(t, changed)
// Diff old state against new state
changed, val, err := diffFn(ctx, oldState, newState)
require.NoError(t, err)
require.True(t, changed)
changedDeals, ok := val.(ChangedDeals)
require.True(t, ok)
require.Len(t, changedDeals, 2)
require.Contains(t, changedDeals, abi.DealID(1))
require.Contains(t, changedDeals, abi.DealID(2))
deal1 := changedDeals[abi.DealID(1)]
if deal1.From.LastUpdatedEpoch != 2 || deal1.To.LastUpdatedEpoch != 3 {
t.Fatal("Unexpected change to LastUpdatedEpoch")
}
deal2 := changedDeals[abi.DealID(2)]
if deal2.From.SlashEpoch != 0 || deal2.To.SlashEpoch != 6 {
t.Fatal("Unexpected change to SlashEpoch")
}
// Test that OnActorStateChanged does not call the callback if the state has not changed
mockAddr, err := address.NewFromString("t01")
require.NoError(t, err)
actorDiffFn := preds.OnActorStateChanged(mockAddr, func(context.Context, cid.Cid, cid.Cid) (bool, UserData, error) {
t.Fatal("No state change so this should not be called")
return false, nil, nil
})
changed, _, err = actorDiffFn(ctx, oldState, oldState)
require.NoError(t, err)
require.False(t, changed)
// Test that OnDealStateChanged does not call the callback if the state has not changed
diffDealStateFn := preds.OnDealStateChanged(func(context.Context, *amt.Root, *amt.Root) (bool, UserData, error) {
t.Fatal("No state change so this should not be called")
return false, nil, nil
})
marketState := createEmptyMarketState(t, store)
changed, _, err = diffDealStateFn(ctx, marketState, marketState)
require.NoError(t, err)
require.False(t, changed)
}
func mockTipset(miner address.Address, timestamp uint64) (*types.TipSet, error) {
return types.NewTipSet([]*types.BlockHeader{{
Miner: miner,
Height: 5,
ParentStateRoot: dummyCid,
Messages: dummyCid,
ParentMessageReceipts: dummyCid,
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS},
BLSAggregate: &crypto.Signature{Type: crypto.SigTypeBLS},
Timestamp: timestamp,
}})
}
func createMarketState(ctx context.Context, t *testing.T, store *cbornode.BasicIpldStore, deals map[abi.DealID]*market.DealState) cid.Cid {
rootCid := createAMT(ctx, t, store, deals)
state := createEmptyMarketState(t, store)
state.States = rootCid
stateC, err := store.Put(ctx, state)
require.NoError(t, err)
return stateC
}
func createEmptyMarketState(t *testing.T, store *cbornode.BasicIpldStore) *market.State {
emptyArrayCid, err := amt.NewAMT(store).Flush(context.TODO())
require.NoError(t, err)
emptyMap, err := store.Put(context.TODO(), hamt.NewNode(store, hamt.UseTreeBitWidth(5)))
require.NoError(t, err)
return market.ConstructState(emptyArrayCid, emptyMap, emptyMap)
}
func createAMT(ctx context.Context, t *testing.T, store *cbornode.BasicIpldStore, deals map[abi.DealID]*market.DealState) cid.Cid {
root := amt.NewAMT(store)
for dealID, dealState := range deals {
err := root.Set(ctx, uint64(dealID), dealState)
require.NoError(t, err)
}
rootCid, err := root.Flush(ctx)
require.NoError(t, err)
return rootCid
}
+4 -4
View File
@@ -8,11 +8,11 @@ import (
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
func (me *messageEvents) CheckMsg(ctx context.Context, smsg types.ChainMsg, hnd MsgHandler) CheckFunc { func (e *calledEvents) CheckMsg(ctx context.Context, smsg types.ChainMsg, hnd CalledHandler) CheckFunc {
msg := smsg.VMMessage() msg := smsg.VMMessage()
return func(ts *types.TipSet) (done bool, more bool, err error) { return func(ts *types.TipSet) (done bool, more bool, err error) {
fa, err := me.cs.StateGetActor(ctx, msg.From, ts.Key()) fa, err := e.cs.StateGetActor(ctx, msg.From, ts.Key())
if err != nil { if err != nil {
return false, true, err return false, true, err
} }
@@ -22,7 +22,7 @@ func (me *messageEvents) CheckMsg(ctx context.Context, smsg types.ChainMsg, hnd
return false, true, nil return false, true, nil
} }
rec, err := me.cs.StateGetReceipt(ctx, smsg.VMMessage().Cid(), ts.Key()) rec, err := e.cs.StateGetReceipt(ctx, smsg.VMMessage().Cid(), ts.Key())
if err != nil { if err != nil {
return false, true, xerrors.Errorf("getting receipt in CheckMsg: %w", err) return false, true, xerrors.Errorf("getting receipt in CheckMsg: %w", err)
} }
@@ -33,7 +33,7 @@ func (me *messageEvents) CheckMsg(ctx context.Context, smsg types.ChainMsg, hnd
} }
} }
func (me *messageEvents) MatchMsg(inmsg *types.Message) MsgMatchFunc { func (e *calledEvents) MatchMsg(inmsg *types.Message) MatchFunc {
return func(msg *types.Message) (bool, error) { return func(msg *types.Message) (bool, error) {
if msg.From == inmsg.From && msg.Nonce == inmsg.Nonce && !inmsg.Equals(msg) { if msg.From == inmsg.From && msg.Nonce == inmsg.Nonce && !inmsg.Equals(msg) {
return false, xerrors.Errorf("matching msg %s from %s, nonce %d: got duplicate origin/nonce msg %d", inmsg.Cid(), inmsg.From, inmsg.Nonce, msg.Nonce) return false, xerrors.Errorf("matching msg %s from %s, nonce %d: got duplicate origin/nonce msg %d", inmsg.Cid(), inmsg.From, inmsg.Nonce, msg.Nonce)
+11 -13
View File
@@ -45,10 +45,6 @@ var log = logging.Logger("gen")
const msgsPerBlock = 20 const msgsPerBlock = 20
var ValidWpostForTesting = []abi.PoStProof{{
ProofBytes: []byte("valid proof"),
}}
type ChainGen struct { type ChainGen struct {
msgsPerBlock int msgsPerBlock int
@@ -93,8 +89,8 @@ func (m mybs) Get(c cid.Cid) (block.Block, error) {
} }
func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) { func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
saminer.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{ saminer.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {}, abi.RegisteredProof_StackedDRG2KiBSeal: {},
} }
mr := repo.NewMemory(nil) mr := repo.NewMemory(nil)
@@ -145,7 +141,7 @@ func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
return nil, err return nil, err
} }
genm1, k1, err := seed.PreSeal(maddr1, abi.RegisteredSealProof_StackedDrg2KiBV1, 0, numSectors, m1temp, []byte("some randomness"), nil) genm1, k1, err := seed.PreSeal(maddr1, abi.RegisteredProof_StackedDRG2KiBPoSt, 0, numSectors, m1temp, []byte("some randomness"), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -157,7 +153,7 @@ func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
return nil, err return nil, err
} }
genm2, k2, err := seed.PreSeal(maddr2, abi.RegisteredSealProof_StackedDrg2KiBV1, 0, numSectors, m2temp, []byte("some randomness"), nil) genm2, k2, err := seed.PreSeal(maddr2, abi.RegisteredProof_StackedDRG2KiBPoSt, 0, numSectors, m2temp, []byte("some randomness"), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -196,7 +192,7 @@ func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
*genm2, *genm2,
}, },
NetworkName: "", NetworkName: "",
Timestamp: uint64(time.Now().Add(-500 * time.Duration(build.BlockDelaySecs) * time.Second).Unix()), Timestamp: uint64(time.Now().Add(-500 * build.BlockDelay * time.Second).Unix()),
} }
genb, err := genesis2.MakeGenesisBlock(context.TODO(), bs, sys, tpl) genb, err := genesis2.MakeGenesisBlock(context.TODO(), bs, sys, tpl)
@@ -223,7 +219,7 @@ func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) {
miners := []address.Address{maddr1, maddr2} miners := []address.Address{maddr1, maddr2}
beac := beacon.NewMockBeacon(time.Second) beac := beacon.NewMockBeacon(time.Second)
//beac, err := drand.NewDrandBeacon(tpl.Timestamp, build.BlockDelaySecs) //beac, err := drand.NewDrandBeacon(tpl.Timestamp, build.BlockDelay)
//if err != nil { //if err != nil {
//return nil, xerrors.Errorf("creating drand beacon: %w", err) //return nil, xerrors.Errorf("creating drand beacon: %w", err)
//} //}
@@ -414,7 +410,7 @@ func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, vrfticke
if cg.Timestamper != nil { if cg.Timestamper != nil {
ts = cg.Timestamper(parents, height-parents.Height()) ts = cg.Timestamper(parents, height-parents.Height())
} else { } else {
ts = parents.MinTimestamp() + uint64(height-parents.Height())*build.BlockDelaySecs ts = parents.MinTimestamp() + uint64((height-parents.Height())*build.BlockDelay)
} }
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, &api.BlockTemplate{ fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, &api.BlockTemplate{
@@ -537,7 +533,9 @@ func (wpp *wppProvider) GenerateCandidates(ctx context.Context, _ abi.PoStRandom
} }
func (wpp *wppProvider) ComputeProof(context.Context, []abi.SectorInfo, abi.PoStRandomness) ([]abi.PoStProof, error) { func (wpp *wppProvider) ComputeProof(context.Context, []abi.SectorInfo, abi.PoStRandomness) ([]abi.PoStProof, error) {
return ValidWpostForTesting, nil return []abi.PoStProof{{
ProofBytes: []byte("valid proof"),
}}, nil
} }
func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch, func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch,
@@ -613,6 +611,6 @@ func (m genFakeVerifier) VerifyWindowPoSt(ctx context.Context, info abi.WindowPo
panic("not supported") panic("not supported")
} }
func (m genFakeVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, proof abi.RegisteredPoStProof, id abi.ActorID, randomness abi.PoStRandomness, u uint64) ([]uint64, error) { func (m genFakeVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, proof abi.RegisteredProof, id abi.ActorID, randomness abi.PoStRandomness, u uint64) ([]uint64, error) {
panic("not supported") panic("not supported")
} }
+2 -2
View File
@@ -14,8 +14,8 @@ import (
) )
func init() { func init() {
miner.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{ miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {}, abi.RegisteredProof_StackedDRG2KiBSeal: {},
} }
power.ConsensusMinerMinPower = big.NewInt(2048) power.ConsensusMinerMinPower = big.NewInt(2048)
verifreg.MinVerifiedDealSize = big.NewInt(256) verifreg.MinVerifiedDealSize = big.NewInt(256)
+6 -9
View File
@@ -204,12 +204,12 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
{ {
newSectorInfo := &miner.SectorOnChainInfo{ newSectorInfo := &miner.SectorOnChainInfo{
Info: miner.SectorPreCommitInfo{ Info: miner.SectorPreCommitInfo{
SealProof: preseal.ProofType, RegisteredProof: preseal.ProofType,
SectorNumber: preseal.SectorID, SectorNumber: preseal.SectorID,
SealedCID: preseal.CommR, SealedCID: preseal.CommR,
SealRandEpoch: 0, SealRandEpoch: 0,
DealIDs: []abi.DealID{dealIDs[pi]}, DealIDs: []abi.DealID{dealIDs[pi]},
Expiration: preseal.Deal.EndEpoch, Expiration: preseal.Deal.EndEpoch,
}, },
ActivationEpoch: 0, ActivationEpoch: 0,
DealWeight: dealWeight.DealWeight, DealWeight: dealWeight.DealWeight,
@@ -242,9 +242,6 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
st.TotalQualityAdjPower = big.Sub(st.TotalQualityAdjPower, big.NewInt(1)) st.TotalQualityAdjPower = big.Sub(st.TotalQualityAdjPower, big.NewInt(1))
return nil return nil
}) })
if err != nil {
return cid.Undef, xerrors.Errorf("mutating state: %w", err)
}
c, err := vm.Flush(ctx) c, err := vm.Flush(ctx)
if err != nil { if err != nil {
-11
View File
@@ -150,17 +150,6 @@ func aggregateSignatures(sigs []crypto.Signature) (*crypto.Signature, error) {
} }
aggSig := bls.Aggregate(blsSigs) aggSig := bls.Aggregate(blsSigs)
if aggSig == nil {
if len(sigs) > 0 {
return nil, xerrors.Errorf("bls.Aggregate returned nil with %d signatures", len(sigs))
}
return &crypto.Signature{
Type: crypto.SigTypeBLS,
Data: new(bls.Signature)[:],
}, nil
}
return &crypto.Signature{ return &crypto.Signature{
Type: crypto.SigTypeBLS, Type: crypto.SigTypeBLS,
Data: aggSig[:], Data: aggSig[:],
+1 -1
View File
@@ -187,7 +187,7 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
mp := &MessagePool{ mp := &MessagePool{
closer: make(chan struct{}), closer: make(chan struct{}),
repubTk: time.NewTicker(time.Duration(build.BlockDelaySecs) * 10 * time.Second), repubTk: time.NewTicker(build.BlockDelay * 10 * time.Second),
localAddrs: make(map[address.Address]struct{}), localAddrs: make(map[address.Address]struct{}),
pending: make(map[address.Address]*msgSet), pending: make(map[address.Address]*msgSet),
minGasPrice: types.NewInt(0), minGasPrice: types.NewInt(0),
+5 -5
View File
@@ -62,11 +62,11 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate
} }
return &api.InvocResult{ return &api.InvocResult{
Msg: msg, Msg: msg,
MsgRct: &ret.MessageReceipt, MsgRct: &ret.MessageReceipt,
ExecutionTrace: ret.ExecutionTrace, InternalExecutions: ret.InternalExecutions,
Error: errs, Error: errs,
Duration: ret.Duration, Duration: ret.Duration,
}, nil }, nil
} }
+2 -2
View File
@@ -37,8 +37,8 @@ import (
) )
func init() { func init() {
miner.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{ miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {}, abi.RegisteredProof_StackedDRG2KiBSeal: {},
} }
power.ConsensusMinerMinPower = big.NewInt(2048) power.ConsensusMinerMinPower = big.NewInt(2048)
verifreg.MinVerifiedDealSize = big.NewInt(256) verifreg.MinVerifiedDealSize = big.NewInt(256)
+8 -7
View File
@@ -121,10 +121,10 @@ func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (c
var trace []*api.InvocResult var trace []*api.InvocResult
st, _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error { st, _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(mcid cid.Cid, msg *types.Message, ret *vm.ApplyRet) error {
ir := &api.InvocResult{ ir := &api.InvocResult{
Msg: msg, Msg: msg,
MsgRct: &ret.MessageReceipt, MsgRct: &ret.MessageReceipt,
ExecutionTrace: ret.ExecutionTrace, InternalExecutions: ret.InternalExecutions,
Duration: ret.Duration, Duration: ret.Duration,
} }
if ret.ActorErr != nil { if ret.ActorErr != nil {
ir.Error = ret.ActorErr.Error() ir.Error = ret.ActorErr.Error()
@@ -184,9 +184,10 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, pstate cid.Cid, bms []B
var err error var err error
params, err := actors.SerializeParams(&reward.AwardBlockRewardParams{ params, err := actors.SerializeParams(&reward.AwardBlockRewardParams{
Miner: b.Miner, Miner: b.Miner,
Penalty: penalty, Penalty: penalty,
GasReward: gasReward, GasReward: gasReward,
TicketCount: 1, // TODO: no longer need ticket count here.
}) })
if err != nil { if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("failed to serialize award params: %w", err) return cid.Undef, cid.Undef, xerrors.Errorf("failed to serialize award params: %w", err)
+8 -47
View File
@@ -13,7 +13,6 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
amt "github.com/filecoin-project/go-amt-ipld/v2" amt "github.com/filecoin-project/go-amt-ipld/v2"
"github.com/filecoin-project/go-bitfield"
"github.com/filecoin-project/sector-storage/ffiwrapper" "github.com/filecoin-project/sector-storage/ffiwrapper"
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
@@ -174,34 +173,13 @@ func GetSectorsForWinningPoSt(ctx context.Context, pv ffiwrapper.Verifier, sm *S
return nil, xerrors.Errorf("(get sectors) failed to load miner actor state: %w", err) return nil, xerrors.Errorf("(get sectors) failed to load miner actor state: %w", err)
} }
cst := cbor.NewCborStore(sm.cs.Blockstore()) // TODO: Optimization: we could avoid loaditg the whole proving set here if we had AMT.GetNth with bitfield filtering
var deadlines miner.Deadlines sectorSet, err := GetProvingSetRaw(ctx, sm, mas)
if err := cst.Get(ctx, mas.Deadlines, &deadlines); err != nil {
return nil, xerrors.Errorf("failed to load deadlines: %w", err)
}
notProving, err := abi.BitFieldUnion(mas.Faults, mas.Recoveries)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to union faults and recoveries: %w", err) return nil, xerrors.Errorf("getting proving set: %w", err)
} }
allSectors, err := bitfield.MultiMerge(append(deadlines.Due[:], mas.NewSectors)...) if len(sectorSet) == 0 {
if err != nil {
return nil, xerrors.Errorf("merging deadline bitfields failed: %w", err)
}
provingSectors, err := bitfield.SubtractBitField(allSectors, notProving)
if err != nil {
return nil, xerrors.Errorf("failed to subtract non-proving sectors from set: %w", err)
}
numProvSect, err := provingSectors.Count()
if err != nil {
return nil, xerrors.Errorf("failed to count bits: %w", err)
}
// TODO(review): is this right? feels fishy to me
if numProvSect == 0 {
return nil, nil return nil, nil
} }
@@ -220,34 +198,17 @@ func GetSectorsForWinningPoSt(ctx context.Context, pv ffiwrapper.Verifier, sm *S
return nil, xerrors.Errorf("getting miner ID: %w", err) return nil, xerrors.Errorf("getting miner ID: %w", err)
} }
ids, err := pv.GenerateWinningPoStSectorChallenge(ctx, wpt, abi.ActorID(mid), rand, numProvSect) ids, err := pv.GenerateWinningPoStSectorChallenge(ctx, wpt, abi.ActorID(mid), rand, uint64(len(sectorSet)))
if err != nil { if err != nil {
return nil, xerrors.Errorf("generating winning post challenges: %w", err) return nil, xerrors.Errorf("generating winning post challenges: %w", err)
} }
sectors, err := provingSectors.All(miner.SectorsMax)
if err != nil {
return nil, xerrors.Errorf("failed to enumerate all sector IDs: %w", err)
}
sectorAmt, err := amt.LoadAMT(ctx, cst, mas.Sectors)
if err != nil {
return nil, xerrors.Errorf("failed to load sectors amt: %w", err)
}
out := make([]abi.SectorInfo, len(ids)) out := make([]abi.SectorInfo, len(ids))
for i, n := range ids { for i, n := range ids {
sid := sectors[n]
var sinfo miner.SectorOnChainInfo
if err := sectorAmt.Get(ctx, sid, &sinfo); err != nil {
return nil, xerrors.Errorf("failed to get sector %d: %w", sid, err)
}
out[i] = abi.SectorInfo{ out[i] = abi.SectorInfo{
SealProof: spt, RegisteredProof: wpt,
SectorNumber: sinfo.Info.SectorNumber, SectorNumber: sectorSet[n].ID,
SealedCID: sinfo.Info.SealedCID, SealedCID: sectorSet[n].Info.Info.SealedCID,
} }
} }
-3
View File
@@ -32,11 +32,8 @@ func (fts *FullTipSet) Cids() []cid.Cid {
return cids return cids
} }
// TipSet returns a narrower view of this FullTipSet elliding the block
// messages.
func (fts *FullTipSet) TipSet() *types.TipSet { func (fts *FullTipSet) TipSet() *types.TipSet {
if fts.tipset != nil { if fts.tipset != nil {
// FIXME: fts.tipset is actually never set. Should it memoize?
return fts.tipset return fts.tipset
} }
+9 -13
View File
@@ -34,7 +34,7 @@ type lbEntry struct {
target types.TipSetKey target types.TipSetKey
} }
func (ci *ChainIndex) GetTipsetByHeight(_ context.Context, from *types.TipSet, to abi.ChainEpoch) (*types.TipSet, error) { func (ci *ChainIndex) GetTipsetByHeight(ctx context.Context, from *types.TipSet, to abi.ChainEpoch) (*types.TipSet, error) {
if from.Height()-to <= ci.skipLength { if from.Height()-to <= ci.skipLength {
return ci.walkBack(from, to) return ci.walkBack(from, to)
} }
@@ -91,16 +91,15 @@ func (ci *ChainIndex) fillCache(tsk types.TipSetKey) (*lbEntry, error) {
return nil, err return nil, err
} }
if parent.Height() > rheight {
return nil, xerrors.Errorf("cache is inconsistent")
}
rheight -= ci.skipLength rheight -= ci.skipLength
var skipTarget *types.TipSet skipTarget, err := ci.walkBack(parent, rheight)
if parent.Height() < rheight { if err != nil {
skipTarget = parent return nil, err
} else {
skipTarget, err = ci.walkBack(parent, rheight)
if err != nil {
return nil, xerrors.Errorf("fillCache walkback: %w", err)
}
} }
lbe := &lbEntry{ lbe := &lbEntry{
@@ -114,9 +113,8 @@ func (ci *ChainIndex) fillCache(tsk types.TipSetKey) (*lbEntry, error) {
return lbe, nil return lbe, nil
} }
// floors to nearest skipLength multiple
func (ci *ChainIndex) roundHeight(h abi.ChainEpoch) abi.ChainEpoch { func (ci *ChainIndex) roundHeight(h abi.ChainEpoch) abi.ChainEpoch {
return (h / ci.skipLength) * ci.skipLength return abi.ChainEpoch(h/ci.skipLength) * ci.skipLength
} }
func (ci *ChainIndex) roundDown(ts *types.TipSet) (*types.TipSet, error) { func (ci *ChainIndex) roundDown(ts *types.TipSet) (*types.TipSet, error) {
@@ -148,8 +146,6 @@ func (ci *ChainIndex) walkBack(from *types.TipSet, to abi.ChainEpoch) (*types.Ti
} }
if to > pts.Height() { if to > pts.Height() {
// in case pts is lower than the epoch we're looking for (null blocks)
// return a tipset above that height
return ts, nil return ts, nil
} }
if to == pts.Height() { if to == pts.Height() {
-80
View File
@@ -1,80 +0,0 @@
package store_test
import (
"bytes"
"context"
"testing"
"github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/specs-actors/actors/abi"
datastore "github.com/ipfs/go-datastore"
syncds "github.com/ipfs/go-datastore/sync"
blockstore "github.com/ipfs/go-ipfs-blockstore"
"github.com/stretchr/testify/assert"
)
func TestIndexSeeks(t *testing.T) {
cg, err := gen.NewGenerator()
if err != nil {
t.Fatal(err)
}
gencar, err := cg.GenesisCar()
if err != nil {
t.Fatal(err)
}
gen := cg.Genesis()
ctx := context.TODO()
nbs := blockstore.NewBlockstore(syncds.MutexWrap(datastore.NewMapDatastore()))
cs := store.NewChainStore(nbs, syncds.MutexWrap(datastore.NewMapDatastore()), nil)
_, err = cs.Import(bytes.NewReader(gencar))
if err != nil {
t.Fatal(err)
}
cur := mock.TipSet(gen)
if err := cs.PutTipSet(ctx, mock.TipSet(gen)); err != nil {
t.Fatal(err)
}
cs.SetGenesis(gen)
// Put 113 blocks from genesis
for i := 0; i < 113; i++ {
nextts := mock.TipSet(mock.MkBlock(cur, 1, 1))
if err := cs.PutTipSet(ctx, nextts); err != nil {
t.Fatal(err)
}
cur = nextts
}
// Put 50 null epochs + 1 block
skip := mock.MkBlock(cur, 1, 1)
skip.Height += 50
skipts := mock.TipSet(skip)
if err := cs.PutTipSet(ctx, skipts); err != nil {
t.Fatal(err)
}
ts, err := cs.GetTipsetByHeight(ctx, skip.Height-10, skipts, false)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, abi.ChainEpoch(164), ts.Height())
for i := 0; i <= 113; i++ {
ts3, err := cs.GetTipsetByHeight(ctx, abi.ChainEpoch(i), skipts, false)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, abi.ChainEpoch(i), ts3.Height())
}
}
+14 -53
View File
@@ -18,6 +18,7 @@ import (
"github.com/filecoin-project/specs-actors/actors/util/adt" "github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/metrics"
@@ -47,20 +48,7 @@ import (
var log = logging.Logger("chainstore") var log = logging.Logger("chainstore")
var chainHeadKey = dstore.NewKey("head") var chainHeadKey = dstore.NewKey("head")
var blockValidationCacheKeyPrefix = dstore.NewKey("blockValidation")
// ReorgNotifee represents a callback that gets called upon reorgs.
type ReorgNotifee func(rev, app []*types.TipSet) error
// ChainStore is the main point of access to chain data.
//
// Raw chain data is stored in the Blockstore, with relevant markers (genesis,
// latest head tipset references) being tracked in the Datastore (key-value
// store).
//
// To alleviate disk access, the ChainStore has two ARC caches:
// 1. a tipset cache
// 2. a block => messages references cache.
type ChainStore struct { type ChainStore struct {
bs bstore.Blockstore bs bstore.Blockstore
ds dstore.Datastore ds dstore.Datastore
@@ -76,8 +64,8 @@ type ChainStore struct {
cindex *ChainIndex cindex *ChainIndex
reorgCh chan<- reorg reorgCh chan<- reorg
reorgNotifeeCh chan ReorgNotifee headChangeNotifs []func(rev, app []*types.TipSet) error
mmCache *lru.ARCCache mmCache *lru.ARCCache
tsCache *lru.ARCCache tsCache *lru.ARCCache
@@ -102,6 +90,8 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls runtime.Sys
cs.cindex = ci cs.cindex = ci
cs.reorgCh = cs.reorgWorker(context.TODO())
hcnf := func(rev, app []*types.TipSet) error { hcnf := func(rev, app []*types.TipSet) error {
cs.pubLk.Lock() cs.pubLk.Lock()
defer cs.pubLk.Unlock() defer cs.pubLk.Unlock()
@@ -133,8 +123,7 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching, vmcalls runtime.Sys
return nil return nil
} }
cs.reorgNotifeeCh = make(chan ReorgNotifee) cs.headChangeNotifs = append(cs.headChangeNotifs, hcnf, hcmetric)
cs.reorgCh = cs.reorgWorker(context.TODO(), []ReorgNotifee{hcnf, hcmetric})
return cs return cs
} }
@@ -223,24 +212,8 @@ func (cs *ChainStore) SubHeadChanges(ctx context.Context) chan []*api.HeadChange
return out return out
} }
func (cs *ChainStore) SubscribeHeadChanges(f ReorgNotifee) { func (cs *ChainStore) SubscribeHeadChanges(f func(rev, app []*types.TipSet) error) {
cs.reorgNotifeeCh <- f cs.headChangeNotifs = append(cs.headChangeNotifs, f)
}
func (cs *ChainStore) IsBlockValidated(ctx context.Context, blkid cid.Cid) (bool, error) {
key := blockValidationCacheKeyPrefix.Instance(blkid.String())
return cs.ds.Has(key)
}
func (cs *ChainStore) MarkBlockAsValidated(ctx context.Context, blkid cid.Cid) error {
key := blockValidationCacheKeyPrefix.Instance(blkid.String())
if err := cs.ds.Put(key, []byte{0}); err != nil {
return xerrors.Errorf("cache block validation: %w", err)
}
return nil
} }
func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error { func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error {
@@ -275,9 +248,6 @@ func (cs *ChainStore) PutTipSet(ctx context.Context, ts *types.TipSet) error {
return nil return nil
} }
// MaybeTakeHeavierTipSet evaluates the incoming tipset and locks it in our
// internal state as our new head, if and only if it is heavier than the current
// head.
func (cs *ChainStore) MaybeTakeHeavierTipSet(ctx context.Context, ts *types.TipSet) error { func (cs *ChainStore) MaybeTakeHeavierTipSet(ctx context.Context, ts *types.TipSet) error {
cs.heaviestLk.Lock() cs.heaviestLk.Lock()
defer cs.heaviestLk.Unlock() defer cs.heaviestLk.Unlock()
@@ -304,19 +274,13 @@ type reorg struct {
new *types.TipSet new *types.TipSet
} }
func (cs *ChainStore) reorgWorker(ctx context.Context, initialNotifees []ReorgNotifee) chan<- reorg { func (cs *ChainStore) reorgWorker(ctx context.Context) chan<- reorg {
out := make(chan reorg, 32) out := make(chan reorg, 32)
notifees := make([]ReorgNotifee, len(initialNotifees))
copy(notifees, initialNotifees)
go func() { go func() {
defer log.Warn("reorgWorker quit") defer log.Warn("reorgWorker quit")
for { for {
select { select {
case n := <-cs.reorgNotifeeCh:
notifees = append(notifees, n)
case r := <-out: case r := <-out:
revert, apply, err := cs.ReorgOps(r.old, r.new) revert, apply, err := cs.ReorgOps(r.old, r.new)
if err != nil { if err != nil {
@@ -330,7 +294,7 @@ func (cs *ChainStore) reorgWorker(ctx context.Context, initialNotifees []ReorgNo
apply[i], apply[opp] = apply[opp], apply[i] apply[i], apply[opp] = apply[opp], apply[i]
} }
for _, hcf := range notifees { for _, hcf := range cs.headChangeNotifs {
if err := hcf(revert, apply); err != nil { if err := hcf(revert, apply); err != nil {
log.Error("head change func errored (BAD): ", err) log.Error("head change func errored (BAD): ", err)
} }
@@ -343,9 +307,6 @@ func (cs *ChainStore) reorgWorker(ctx context.Context, initialNotifees []ReorgNo
return out return out
} }
// takeHeaviestTipSet actually sets the incoming tipset as our head both in
// memory and in the ChainStore. It also sends a notification to deliver to
// ReorgNotifees.
func (cs *ChainStore) takeHeaviestTipSet(ctx context.Context, ts *types.TipSet) error { func (cs *ChainStore) takeHeaviestTipSet(ctx context.Context, ts *types.TipSet) error {
_, span := trace.StartSpan(ctx, "takeHeaviestTipSet") _, span := trace.StartSpan(ctx, "takeHeaviestTipSet")
defer span.End() defer span.End()
@@ -383,7 +344,6 @@ func (cs *ChainStore) SetHead(ts *types.TipSet) error {
return cs.takeHeaviestTipSet(context.TODO(), ts) return cs.takeHeaviestTipSet(context.TODO(), ts)
} }
// Contains returns whether our BlockStore has all blocks in the supplied TipSet.
func (cs *ChainStore) Contains(ts *types.TipSet) (bool, error) { func (cs *ChainStore) Contains(ts *types.TipSet) (bool, error) {
for _, c := range ts.Cids() { for _, c := range ts.Cids() {
has, err := cs.bs.Has(c) has, err := cs.bs.Has(c)
@@ -398,8 +358,6 @@ func (cs *ChainStore) Contains(ts *types.TipSet) (bool, error) {
return true, nil return true, nil
} }
// GetBlock fetches a BlockHeader with the supplied CID. It returns
// blockstore.ErrNotFound if the block was not found in the BlockStore.
func (cs *ChainStore) GetBlock(c cid.Cid) (*types.BlockHeader, error) { func (cs *ChainStore) GetBlock(c cid.Cid) (*types.BlockHeader, error) {
sb, err := cs.bs.Get(c) sb, err := cs.bs.Get(c)
if err != nil { if err != nil {
@@ -492,7 +450,6 @@ func (cs *ChainStore) ReorgOps(a, b *types.TipSet) ([]*types.TipSet, []*types.Ti
return leftChain, rightChain, nil return leftChain, rightChain, nil
} }
// GetHeaviestTipSet returns the current heaviest tipset known (i.e. our head).
func (cs *ChainStore) GetHeaviestTipSet() *types.TipSet { func (cs *ChainStore) GetHeaviestTipSet() *types.TipSet {
cs.heaviestLk.Lock() cs.heaviestLk.Lock()
defer cs.heaviestLk.Unlock() defer cs.heaviestLk.Unlock()
@@ -1007,6 +964,10 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h abi.ChainEpoch, t
return ts, nil return ts, nil
} }
if ts.Height()-h > build.ForkLengthThreshold {
log.Warnf("expensive call to GetTipsetByHeight, seeking %d levels", ts.Height()-h)
}
lbts, err := cs.cindex.GetTipsetByHeight(ctx, ts, h) lbts, err := cs.cindex.GetTipsetByHeight(ctx, ts, h)
if err != nil { if err != nil {
return nil, err return nil, err
+2 -2
View File
@@ -22,8 +22,8 @@ import (
) )
func init() { func init() {
miner.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{ miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {}, abi.RegisteredProof_StackedDRG2KiBSeal: {},
} }
power.ConsensusMinerMinPower = big.NewInt(2048) power.ConsensusMinerMinPower = big.NewInt(2048)
verifreg.MinVerifiedDealSize = big.NewInt(256) verifreg.MinVerifiedDealSize = big.NewInt(256)
+4 -1
View File
@@ -60,6 +60,8 @@ func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *cha
src := msg.GetFrom() src := msg.GetFrom()
go func() { go func() {
log.Infof("New block over pubsub: %s", blk.Cid())
start := time.Now() start := time.Now()
log.Debug("about to fetch messages for block from pubsub") log.Debug("about to fetch messages for block from pubsub")
bmsgs, err := s.Bsync.FetchMessagesByCids(context.TODO(), blk.BlsMessages) bmsgs, err := s.Bsync.FetchMessagesByCids(context.TODO(), blk.BlsMessages)
@@ -143,7 +145,8 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
// track validation time // track validation time
begin := time.Now() begin := time.Now()
defer func() { defer func() {
log.Debugf("block validation time: %s", time.Since(begin)) end := time.Now()
log.Infof("block validation time: %s", end.Sub(begin))
}() }()
stats.Record(ctx, metrics.BlockReceived.M(1)) stats.Record(ctx, metrics.BlockReceived.M(1))
+39 -206
View File
@@ -8,6 +8,7 @@ import (
"os" "os"
"sort" "sort"
"strings" "strings"
"sync"
"time" "time"
"github.com/Gurpartap/async" "github.com/Gurpartap/async"
@@ -49,37 +50,10 @@ import (
"github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/metrics"
) )
// Blocks that are more than MaxHeightDrift epochs above
//the theoretical max height based on systime are quickly rejected
const MaxHeightDrift = 5
var log = logging.Logger("chain") var log = logging.Logger("chain")
var LocalIncoming = "incoming" var LocalIncoming = "incoming"
// Syncer is in charge of running the chain synchronization logic. As such, it
// is tasked with these functions, amongst others:
//
// * Fast-forwards the chain as it learns of new TipSets from the network via
// the SyncManager.
// * Applies the fork choice rule to select the correct side when confronted
// with a fork in the network.
// * Requests block headers and messages from other peers when not available
// in our BlockStore.
// * Tracks blocks marked as bad in a cache.
// * Keeps the BlockStore and ChainStore consistent with our view of the world,
// the latter of which in turn informs other components when a reorg has been
// committed.
//
// The Syncer does not run workers itself. It's mainly concerned with
// ensuring a consistent state of chain consensus. The reactive and network-
// interfacing processes are part of other components, such as the SyncManager
// (which owns the sync scheduler and sync workers), BlockSync, the HELLO
// protocol, and the gossipsub block propagation layer.
//
// {hint/concept} The fork-choice rule as it currently stands is: "pick the
// chain with the heaviest weight, so long as it hasnt deviated one finality
// threshold from our head (900 epochs, parameter determined by spec-actors)".
type Syncer struct { type Syncer struct {
// The interface for accessing and putting tipsets into local storage // The interface for accessing and putting tipsets into local storage
store *store.ChainStore store *store.ChainStore
@@ -112,7 +86,6 @@ type Syncer struct {
verifier ffiwrapper.Verifier verifier ffiwrapper.Verifier
} }
// NewSyncer creates a new Syncer object.
func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connmgr.ConnManager, self peer.ID, beacon beacon.RandomBeacon, verifier ffiwrapper.Verifier) (*Syncer, error) { func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connmgr.ConnManager, self peer.ID, beacon beacon.RandomBeacon, verifier ffiwrapper.Verifier) (*Syncer, error) {
gen, err := sm.ChainStore().GetGenesis() gen, err := sm.ChainStore().GetGenesis()
if err != nil { if err != nil {
@@ -161,11 +134,6 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool {
return false return false
} }
if syncer.IsEpochBeyondCurrMax(fts.TipSet().Height()) {
log.Errorf("Received block with impossibly large height %d", fts.TipSet().Height())
return false
}
for _, b := range fts.Blocks { for _, b := range fts.Blocks {
if reason, ok := syncer.bad.Has(b.Cid()); ok { if reason, ok := syncer.bad.Has(b.Cid()); ok {
log.Warnf("InformNewHead called on block marked as bad: %s (reason: %s)", b.Cid(), reason) log.Warnf("InformNewHead called on block marked as bad: %s (reason: %s)", b.Cid(), reason)
@@ -215,11 +183,6 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool {
return true return true
} }
// IncomingBlocks spawns a goroutine that subscribes to the local eventbus to
// receive new block headers as they arrive from the network, and sends them to
// the returned channel.
//
// These blocks have not necessarily been incorporated to our view of the chain.
func (syncer *Syncer) IncomingBlocks(ctx context.Context) (<-chan *types.BlockHeader, error) { func (syncer *Syncer) IncomingBlocks(ctx context.Context) (<-chan *types.BlockHeader, error) {
sub := syncer.incoming.Sub(LocalIncoming) sub := syncer.incoming.Sub(LocalIncoming)
out := make(chan *types.BlockHeader, 10) out := make(chan *types.BlockHeader, 10)
@@ -247,15 +210,11 @@ func (syncer *Syncer) IncomingBlocks(ctx context.Context) (<-chan *types.BlockHe
return out, nil return out, nil
} }
// ValidateMsgMeta performs structural and content hash validation of the
// messages within this block. If validation passes, it stores the messages in
// the underlying IPLD block store.
func (syncer *Syncer) ValidateMsgMeta(fblk *types.FullBlock) error { func (syncer *Syncer) ValidateMsgMeta(fblk *types.FullBlock) error {
if msgc := len(fblk.BlsMessages) + len(fblk.SecpkMessages); msgc > build.BlockMessageLimit { if msgc := len(fblk.BlsMessages) + len(fblk.SecpkMessages); msgc > build.BlockMessageLimit {
return xerrors.Errorf("block %s has too many messages (%d)", fblk.Header.Cid(), msgc) return xerrors.Errorf("block %s has too many messages (%d)", fblk.Header.Cid(), msgc)
} }
// Collect the CIDs of both types of messages separately: BLS and Secpk.
var bcids, scids []cbg.CBORMarshaler var bcids, scids []cbg.CBORMarshaler
for _, m := range fblk.BlsMessages { for _, m := range fblk.BlsMessages {
c := cbg.CborCid(m.Cid()) c := cbg.CborCid(m.Cid())
@@ -273,14 +232,11 @@ func (syncer *Syncer) ValidateMsgMeta(fblk *types.FullBlock) error {
blockstore := syncer.store.Blockstore() blockstore := syncer.store.Blockstore()
bs := cbor.NewCborStore(blockstore) bs := cbor.NewCborStore(blockstore)
// Compute the root CID of the combined message trie.
smroot, err := computeMsgMeta(bs, bcids, scids) smroot, err := computeMsgMeta(bs, bcids, scids)
if err != nil { if err != nil {
return xerrors.Errorf("validating msgmeta, compute failed: %w", err) return xerrors.Errorf("validating msgmeta, compute failed: %w", err)
} }
// Check that the message trie root matches with what's in the block.
if fblk.Header.Messages != smroot { if fblk.Header.Messages != smroot {
return xerrors.Errorf("messages in full block did not match msgmeta root in header (%s != %s)", fblk.Header.Messages, smroot) return xerrors.Errorf("messages in full block did not match msgmeta root in header (%s != %s)", fblk.Header.Messages, smroot)
} }
@@ -390,8 +346,6 @@ func zipTipSetAndMessages(bs cbor.IpldStore, ts *types.TipSet, allbmsgs []*types
return fts, nil return fts, nil
} }
// computeMsgMeta computes the root CID of the combined arrays of message CIDs
// of both types (BLS and Secpk).
func computeMsgMeta(bs cbor.IpldStore, bmsgCids, smsgCids []cbg.CBORMarshaler) (cid.Cid, error) { func computeMsgMeta(bs cbor.IpldStore, bmsgCids, smsgCids []cbg.CBORMarshaler) (cid.Cid, error) {
ctx := context.TODO() ctx := context.TODO()
bmroot, err := amt.FromArray(ctx, bs, bmsgCids) bmroot, err := amt.FromArray(ctx, bs, bmsgCids)
@@ -415,24 +369,14 @@ func computeMsgMeta(bs cbor.IpldStore, bmsgCids, smsgCids []cbg.CBORMarshaler) (
return mrcid, nil return mrcid, nil
} }
// FetchTipSet tries to load the provided tipset from the store, and falls back
// to the network (BlockSync) by querying the supplied peer if not found
// locally.
//
// {hint/usage} This is used from the HELLO protocol, to fetch the greeting
// peer's heaviest tipset if we don't have it.
func (syncer *Syncer) FetchTipSet(ctx context.Context, p peer.ID, tsk types.TipSetKey) (*store.FullTipSet, error) { func (syncer *Syncer) FetchTipSet(ctx context.Context, p peer.ID, tsk types.TipSetKey) (*store.FullTipSet, error) {
if fts, err := syncer.tryLoadFullTipSet(tsk); err == nil { if fts, err := syncer.tryLoadFullTipSet(tsk); err == nil {
return fts, nil return fts, nil
} }
// fall back to the network.
return syncer.Bsync.GetFullTipSet(ctx, p, tsk) return syncer.Bsync.GetFullTipSet(ctx, p, tsk)
} }
// tryLoadFullTipSet queries the tipset in the ChainStore, and returns a full
// representation of it containing FullBlocks. If ALL blocks are not found
// locally, it errors entirely with blockstore.ErrNotFound.
func (syncer *Syncer) tryLoadFullTipSet(tsk types.TipSetKey) (*store.FullTipSet, error) { func (syncer *Syncer) tryLoadFullTipSet(tsk types.TipSetKey) (*store.FullTipSet, error) {
ts, err := syncer.store.LoadTipSet(tsk) ts, err := syncer.store.LoadTipSet(tsk)
if err != nil { if err != nil {
@@ -457,12 +401,6 @@ func (syncer *Syncer) tryLoadFullTipSet(tsk types.TipSetKey) (*store.FullTipSet,
return fts, nil return fts, nil
} }
// Sync tries to advance our view of the chain to `maybeHead`. It does nothing
// if our current head is heavier than the requested tipset, or if we're already
// at the requested head, or if the head is the genesis.
//
// Most of the heavy-lifting logic happens in syncer#collectChain. Refer to the
// godocs on that method for a more detailed view.
func (syncer *Syncer) Sync(ctx context.Context, maybeHead *types.TipSet) error { func (syncer *Syncer) Sync(ctx context.Context, maybeHead *types.TipSet) error {
ctx, span := trace.StartSpan(ctx, "chain.Sync") ctx, span := trace.StartSpan(ctx, "chain.Sync")
defer span.End() defer span.End()
@@ -491,8 +429,6 @@ func (syncer *Syncer) Sync(ctx context.Context, maybeHead *types.TipSet) error {
return xerrors.Errorf("collectChain failed: %w", err) return xerrors.Errorf("collectChain failed: %w", err)
} }
// At this point we have accepted and synced to the new `maybeHead`
// (`StageSyncComplete`).
if err := syncer.store.PutTipSet(ctx, maybeHead); err != nil { if err := syncer.store.PutTipSet(ctx, maybeHead); err != nil {
span.AddAttributes(trace.StringAttribute("put_error", err.Error())) span.AddAttributes(trace.StringAttribute("put_error", err.Error()))
span.SetStatus(trace.Status{ span.SetStatus(trace.Status{
@@ -529,27 +465,16 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet)
return nil return nil
} }
var futures []async.ErrorFuture
for _, b := range fts.Blocks { for _, b := range fts.Blocks {
b := b // rebind to a scoped variable if err := syncer.ValidateBlock(ctx, b); err != nil {
if isPermanent(err) {
futures = append(futures, async.Err(func() error { syncer.bad.Add(b.Cid(), err.Error())
if err := syncer.ValidateBlock(ctx, b); err != nil {
if isPermanent(err) {
syncer.bad.Add(b.Cid(), err.Error())
}
return xerrors.Errorf("validating block %s: %w", b.Cid(), err)
} }
return xerrors.Errorf("validating block %s: %w", b.Cid(), err)
}
if err := syncer.sm.ChainStore().AddToTipSetTracker(b.Header); err != nil { if err := syncer.sm.ChainStore().AddToTipSetTracker(b.Header); err != nil {
return xerrors.Errorf("failed to add validated header to tipset tracker: %w", err) return xerrors.Errorf("failed to add validated header to tipset tracker: %w", err)
}
return nil
}))
}
for _, f := range futures {
if err := f.AwaitContext(ctx); err != nil {
return err
} }
} }
return nil return nil
@@ -602,35 +527,9 @@ func blockSanityChecks(h *types.BlockHeader) error {
} }
// ValidateBlock should match up with 'Semantical Validation' in validation.md in the spec // ValidateBlock should match up with 'Semantical Validation' in validation.md in the spec
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (err error) { func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error {
defer func() {
// b.Cid() could panic for empty blocks that are used in tests.
if rerr := recover(); rerr != nil {
err = xerrors.Errorf("validate block panic: %w", rerr)
return
}
}()
isValidated, err := syncer.store.IsBlockValidated(ctx, b.Cid())
if err != nil {
return xerrors.Errorf("check block validation cache %s: %w", b.Cid(), err)
}
if isValidated {
return nil
}
validationStart := time.Now()
defer func() {
dur := time.Since(validationStart)
durMilli := dur.Seconds() * float64(1000)
stats.Record(ctx, metrics.BlockValidationDurationMilliseconds.M(durMilli))
log.Infow("block validation", "took", dur, "height", b.Header.Height)
}()
ctx, span := trace.StartSpan(ctx, "validateBlock") ctx, span := trace.StartSpan(ctx, "validateBlock")
defer span.End() defer span.End()
if build.InsecurePoStValidation { if build.InsecurePoStValidation {
log.Warn("insecure test validation is enabled, if you see this outside of a test, it is a severe bug!") log.Warn("insecure test validation is enabled, if you see this outside of a test, it is a severe bug!")
} }
@@ -666,18 +565,18 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
// fast checks first // fast checks first
now := uint64(time.Now().Unix()) now := uint64(time.Now().Unix())
if h.Timestamp > now+build.AllowableClockDriftSecs { if h.Timestamp > now+build.AllowableClockDrift {
return xerrors.Errorf("block was from the future (now=%d, blk=%d): %w", now, h.Timestamp, ErrTemporal) return xerrors.Errorf("block was from the future (now=%d, blk=%d): %w", now, h.Timestamp, ErrTemporal)
} }
if h.Timestamp > now { if h.Timestamp > now {
log.Warn("Got block from the future, but within threshold", h.Timestamp, time.Now().Unix()) log.Warn("Got block from the future, but within threshold", h.Timestamp, time.Now().Unix())
} }
if h.Timestamp < baseTs.MinTimestamp()+(build.BlockDelaySecs*uint64(h.Height-baseTs.Height())) { if h.Timestamp < baseTs.MinTimestamp()+(build.BlockDelay*uint64(h.Height-baseTs.Height())) {
log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), h.Height, baseTs.Height()) log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), h.Height, baseTs.Height())
diff := (baseTs.MinTimestamp() + (build.BlockDelaySecs * uint64(h.Height-baseTs.Height()))) - h.Timestamp diff := (baseTs.MinTimestamp() + (build.BlockDelay * uint64(h.Height-baseTs.Height()))) - h.Timestamp
return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * deltaH:%d; diff %d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelaySecs, h.Height-baseTs.Height(), diff) return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * deltaH:%d; diff %d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, h.Height-baseTs.Height(), diff)
} }
msgsCheck := async.Err(func() error { msgsCheck := async.Err(func() error {
@@ -740,7 +639,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
return xerrors.Errorf("could not draw randomness: %w", err) return xerrors.Errorf("could not draw randomness: %w", err)
} }
if err := VerifyElectionPoStVRF(ctx, waddr, vrfBase, h.ElectionProof.VRFProof); err != nil { if err := gen.VerifyVRF(ctx, waddr, vrfBase, h.ElectionProof.VRFProof); err != nil {
return xerrors.Errorf("validating block election proof failed: %w", err) return xerrors.Errorf("validating block election proof failed: %w", err)
} }
@@ -801,7 +700,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
return xerrors.Errorf("failed to compute vrf base for ticket: %w", err) return xerrors.Errorf("failed to compute vrf base for ticket: %w", err)
} }
err = VerifyElectionPoStVRF(ctx, waddr, vrfBase, h.Ticket.VRFProof) err = gen.VerifyVRF(ctx, waddr, vrfBase, h.Ticket.VRFProof)
if err != nil { if err != nil {
return xerrors.Errorf("validating block tickets failed: %w", err) return xerrors.Errorf("validating block tickets failed: %w", err)
} }
@@ -849,11 +748,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
} }
} }
if err := syncer.store.MarkBlockAsValidated(ctx, b.Cid()); err != nil { return merr
return xerrors.Errorf("caching block validation %s: %w", b.Cid(), err)
}
return nil
} }
func (syncer *Syncer) VerifyWinningPoStProof(ctx context.Context, h *types.BlockHeader, prevBeacon types.BeaconEntry, lbst cid.Cid, waddr address.Address) error { func (syncer *Syncer) VerifyWinningPoStProof(ctx context.Context, h *types.BlockHeader, prevBeacon types.BeaconEntry, lbst cid.Cid, waddr address.Address) error {
@@ -951,7 +846,7 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
// Phase 1: syntactic validation, as defined in the spec // Phase 1: syntactic validation, as defined in the spec
minGas := vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(msg.ChainLength()) minGas := vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(msg.ChainLength())
if err := m.ValidForBlockInclusion(minGas.Total()); err != nil { if err := m.ValidForBlockInclusion(minGas); err != nil {
return err return err
} }
@@ -1043,14 +938,23 @@ func (syncer *Syncer) verifyBlsAggregate(ctx context.Context, sig *crypto.Signat
trace.Int64Attribute("msgCount", int64(len(msgs))), trace.Int64Attribute("msgCount", int64(len(msgs))),
) )
bmsgs := make([]bls.Message, len(msgs)) var wg sync.WaitGroup
for i, m := range msgs {
bmsgs[i] = m.Bytes() digests := make([]bls.Digest, len(msgs))
for i := 0; i < 10; i++ {
wg.Add(1)
go func(w int) {
defer wg.Done()
for j := 0; (j*10)+w < len(msgs); j++ {
digests[j*10+w] = bls.Hash(bls.Message(msgs[j*10+w].Bytes()))
}
}(i)
} }
wg.Wait()
var bsig bls.Signature var bsig bls.Signature
copy(bsig[:], sig.Data) copy(bsig[:], sig.Data)
if !bls.HashVerify(&bsig, bmsgs, pubks) { if !bls.Verify(&bsig, digests, pubks) {
return xerrors.New("bls aggregate signature failed to verify") return xerrors.New("bls aggregate signature failed to verify")
} }
@@ -1067,39 +971,6 @@ func extractSyncState(ctx context.Context) *SyncerState {
return nil return nil
} }
// collectHeaders collects the headers from the blocks between any two tipsets.
//
// `from` is the heaviest/projected/target tipset we have learned about, and
// `to` is usually an anchor tipset we already have in our view of the chain
// (which could be the genesis).
//
// collectHeaders checks if portions of the chain are in our ChainStore; falling
// down to the network to retrieve the missing parts. If during the process, any
// portion we receive is in our denylist (bad list), we short-circuit.
//
// {hint/naming}: `from` and `to` is in inverse order. `from` is the highest,
// and `to` is the lowest. This method traverses the chain backwards.
//
// {hint/usage}: This is used by collectChain, which is in turn called from the
// main Sync method (Syncer#Sync), so it's a pretty central method.
//
// {hint/logic}: The logic of this method is as follows:
//
// 1. Check that the from tipset is not linked to a parent block known to be
// bad.
// 2. Check the consistency of beacon entries in the from tipset. We check
// total equality of the BeaconEntries in each block.
// 3. Travers the chain backwards, for each tipset:
// 3a. Load it from the chainstore; if found, it move on to its parent.
// 3b. Query our peers via BlockSync in batches, requesting up to a
// maximum of 500 tipsets every time.
//
// Once we've concluded, if we find a mismatching tipset at the height where the
// anchor tipset should be, we are facing a fork, and we invoke Syncer#syncFork
// to resolve it. Refer to the godocs there.
//
// All throughout the process, we keep checking if the received blocks are in
// the deny list, and short-circuit the process if so.
func (syncer *Syncer) collectHeaders(ctx context.Context, from *types.TipSet, to *types.TipSet) ([]*types.TipSet, error) { func (syncer *Syncer) collectHeaders(ctx context.Context, from *types.TipSet, to *types.TipSet) ([]*types.TipSet, error) {
ctx, span := trace.StartSpan(ctx, "collectHeaders") ctx, span := trace.StartSpan(ctx, "collectHeaders")
defer span.End() defer span.End()
@@ -1116,8 +987,6 @@ func (syncer *Syncer) collectHeaders(ctx context.Context, from *types.TipSet, to
} }
} }
// Check if the parents of the from block are in the denylist.
// i.e. if a fork of the chain has been requested that we know to be bad.
for _, pcid := range from.Parents().Cids() { for _, pcid := range from.Parents().Cids() {
if reason, ok := syncer.bad.Has(pcid); ok { if reason, ok := syncer.bad.Has(pcid); ok {
markBad("linked to %s", pcid) markBad("linked to %s", pcid)
@@ -1188,8 +1057,8 @@ loop:
} }
// NB: GetBlocks validates that the blocks are in-fact the ones we // NB: GetBlocks validates that the blocks are in-fact the ones we
// requested, and that they are correctly linked to one another. It does // requested, and that they are correctly linked to eachother. It does
// not validate any state transitions. // not validate any state transitions
window := 500 window := 500
if gap := int(blockSet[len(blockSet)-1].Height() - untilHeight); gap < window { if gap := int(blockSet[len(blockSet)-1].Height() - untilHeight); gap < window {
window = gap window = gap
@@ -1230,6 +1099,7 @@ loop:
at = blks[len(blks)-1].Parents() at = blks[len(blks)-1].Parents()
} }
// We have now ascertained that this is *not* a 'fast forward'
if !types.CidArrsEqual(blockSet[len(blockSet)-1].Parents().Cids(), to.Cids()) { if !types.CidArrsEqual(blockSet[len(blockSet)-1].Parents().Cids(), to.Cids()) {
last := blockSet[len(blockSet)-1] last := blockSet[len(blockSet)-1]
if last.Parents() == to.Parents() { if last.Parents() == to.Parents() {
@@ -1237,8 +1107,6 @@ loop:
return blockSet, nil return blockSet, nil
} }
// We have now ascertained that this is *not* a 'fast forward'
log.Warnf("(fork detected) synced header chain (%s - %d) does not link to our best block (%s - %d)", from.Cids(), from.Height(), to.Cids(), to.Height()) log.Warnf("(fork detected) synced header chain (%s - %d) does not link to our best block (%s - %d)", from.Cids(), from.Height(), to.Cids(), to.Height())
fork, err := syncer.syncFork(ctx, last, to) fork, err := syncer.syncFork(ctx, last, to)
if err != nil { if err != nil {
@@ -1260,12 +1128,6 @@ loop:
var ErrForkTooLong = fmt.Errorf("fork longer than threshold") var ErrForkTooLong = fmt.Errorf("fork longer than threshold")
// syncFork tries to obtain the chain fragment that links a fork into a common
// ancestor in our view of the chain.
//
// If the fork is too long (build.ForkLengthThreshold), we add the entire subchain to the
// denylist. Else, we find the common ancestor, and add the missing chain
// fragment until the fork point to the returned []TipSet.
func (syncer *Syncer) syncFork(ctx context.Context, from *types.TipSet, to *types.TipSet) ([]*types.TipSet, error) { func (syncer *Syncer) syncFork(ctx context.Context, from *types.TipSet, to *types.TipSet) ([]*types.TipSet, error) {
tips, err := syncer.Bsync.GetBlocks(ctx, from.Parents(), int(build.ForkLengthThreshold)) tips, err := syncer.Bsync.GetBlocks(ctx, from.Parents(), int(build.ForkLengthThreshold))
if err != nil { if err != nil {
@@ -1417,25 +1279,6 @@ func persistMessages(bs bstore.Blockstore, bst *blocksync.BSTipSet) error {
return nil return nil
} }
// collectChain tries to advance our view of the chain to the purported head.
//
// It goes through various stages:
//
// 1. StageHeaders: we proceed in the sync process by requesting block headers
// from our peers, moving back from their heads, until we reach a tipset
// that we have in common (such a common tipset must exist, thought it may
// simply be the genesis block).
//
// If the common tipset is our head, we treat the sync as a "fast-forward",
// else we must drop part of our chain to connect to the peer's head
// (referred to as "forking").
//
// 2. StagePersistHeaders: now that we've collected the missing headers,
// augmented by those on the other side of a fork, we persist them to the
// BlockStore.
//
// 3. StageMessages: having acquired the headers and found a common tipset,
// we then move forward, requesting the full blocks, including the messages.
func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet) error { func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet) error {
ctx, span := trace.StartSpan(ctx, "collectChain") ctx, span := trace.StartSpan(ctx, "collectChain")
defer span.End() defer span.End()
@@ -1482,11 +1325,12 @@ func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet) error
return nil return nil
} }
func VerifyElectionPoStVRF(ctx context.Context, worker address.Address, rand []byte, evrf []byte) error { func VerifyElectionPoStVRF(ctx context.Context, evrf []byte, rand []byte, worker address.Address) error {
if build.InsecurePoStValidation { if err := gen.VerifyVRF(ctx, worker, rand, evrf); err != nil {
return nil return xerrors.Errorf("failed to verify post_randomness vrf: %w", err)
} }
return gen.VerifyVRF(ctx, worker, rand, evrf)
return nil
} }
func (syncer *Syncer) State() []SyncerState { func (syncer *Syncer) State() []SyncerState {
@@ -1497,7 +1341,6 @@ func (syncer *Syncer) State() []SyncerState {
return out return out
} }
// MarkBad manually adds a block to the "bad blocks" cache.
func (syncer *Syncer) MarkBad(blk cid.Cid) { func (syncer *Syncer) MarkBad(blk cid.Cid) {
syncer.bad.Add(blk, "manually marked bad") syncer.bad.Add(blk, "manually marked bad")
} }
@@ -1505,7 +1348,7 @@ func (syncer *Syncer) MarkBad(blk cid.Cid) {
func (syncer *Syncer) CheckBadBlockCache(blk cid.Cid) (string, bool) { func (syncer *Syncer) CheckBadBlockCache(blk cid.Cid) (string, bool) {
return syncer.bad.Has(blk) return syncer.bad.Has(blk)
} }
func (syncer *Syncer) getLatestBeaconEntry(_ context.Context, ts *types.TipSet) (*types.BeaconEntry, error) { func (syncer *Syncer) getLatestBeaconEntry(ctx context.Context, ts *types.TipSet) (*types.BeaconEntry, error) {
cur := ts cur := ts
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
cbe := cur.Blocks()[0].BeaconEntries cbe := cur.Blocks()[0].BeaconEntries
@@ -1526,13 +1369,3 @@ func (syncer *Syncer) getLatestBeaconEntry(_ context.Context, ts *types.TipSet)
return nil, xerrors.Errorf("found NO beacon entries in the 20 blocks prior to given tipset") return nil, xerrors.Errorf("found NO beacon entries in the 20 blocks prior to given tipset")
} }
func (syncer *Syncer) IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool {
g, err := syncer.store.GetGenesis()
if err != nil {
return false
}
now := uint64(time.Now().Unix())
return epoch > (abi.ChainEpoch((now-g.Timestamp)/build.BlockDelaySecs) + MaxHeightDrift)
}
+1 -2
View File
@@ -35,7 +35,6 @@ type SyncManager struct {
syncStates []*SyncerState syncStates []*SyncerState
// Normally this handler is set to `(*Syncer).Sync()`.
doSync func(context.Context, *types.TipSet) error doSync func(context.Context, *types.TipSet) error
stop chan struct{} stop chan struct{}
@@ -281,7 +280,7 @@ func (sm *SyncManager) syncScheduler() {
} }
func (sm *SyncManager) scheduleIncoming(ts *types.TipSet) { func (sm *SyncManager) scheduleIncoming(ts *types.TipSet) {
log.Debug("scheduling incoming tipset sync: ", ts.Cids()) log.Info("scheduling incoming tipset sync: ", ts.Cids())
if sm.getBootstrapState() == BSStateSelected { if sm.getBootstrapState() == BSStateSelected {
sm.setBootstrapState(BSStateScheduled) sm.setBootstrapState(BSStateScheduled)
sm.syncTargets <- ts sm.syncTargets <- ts
+3 -3
View File
@@ -34,8 +34,8 @@ import (
func init() { func init() {
build.InsecurePoStValidation = true build.InsecurePoStValidation = true
os.Setenv("TRUST_PARAMS", "1") os.Setenv("TRUST_PARAMS", "1")
miner.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{ miner.SupportedProofTypes = map[abi.RegisteredProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {}, abi.RegisteredProof_StackedDRG2KiBSeal: {},
} }
power.ConsensusMinerMinPower = big.NewInt(2048) power.ConsensusMinerMinPower = big.NewInt(2048)
verifreg.MinVerifiedDealSize = big.NewInt(256) verifreg.MinVerifiedDealSize = big.NewInt(256)
@@ -408,7 +408,7 @@ func TestSyncBadTimestamp(t *testing.T) {
base := tu.g.CurTipset base := tu.g.CurTipset
tu.g.Timestamper = func(pts *types.TipSet, tl abi.ChainEpoch) uint64 { tu.g.Timestamper = func(pts *types.TipSet, tl abi.ChainEpoch) uint64 {
return pts.MinTimestamp() + (build.BlockDelaySecs / 2) return pts.MinTimestamp() + (build.BlockDelay / 2)
} }
fmt.Println("BASE: ", base.Cids()) fmt.Println("BASE: ", base.Cids())
+1 -1
View File
@@ -76,7 +76,7 @@ func SizeStr(bi BigInt) string {
} }
f, _ := r.Float64() f, _ := r.Float64()
return fmt.Sprintf("%.4g %s", f, byteSizeUnits[i]) return fmt.Sprintf("%.3g %s", f, byteSizeUnits[i])
} }
var deciUnits = []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} var deciUnits = []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"}
+1 -24
View File
@@ -3,12 +3,7 @@ package types
import ( import (
"bytes" "bytes"
"math/big" "math/big"
"math/rand"
"strings"
"testing" "testing"
"time"
"github.com/docker/go-units"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -65,10 +60,8 @@ func TestSizeStr(t *testing.T) {
}{ }{
{0, "0 B"}, {0, "0 B"},
{1, "1 B"}, {1, "1 B"},
{1016, "1016 B"},
{1024, "1 KiB"}, {1024, "1 KiB"},
{1000 * 1024, "1000 KiB"}, {2000, "1.95 KiB"},
{2000, "1.953 KiB"},
{5 << 20, "5 MiB"}, {5 << 20, "5 MiB"},
{11 << 60, "11 EiB"}, {11 << 60, "11 EiB"},
} }
@@ -78,22 +71,6 @@ func TestSizeStr(t *testing.T) {
} }
} }
func TestSizeStrUnitsSymmetry(t *testing.T) {
s := rand.NewSource(time.Now().UnixNano())
r := rand.New(s)
for i := 0; i < 1000000; i++ {
n := r.Uint64()
l := strings.ReplaceAll(units.BytesSize(float64(n)), " ", "")
r := strings.ReplaceAll(SizeStr(NewInt(n)), " ", "")
assert.NotContains(t, l, "e+")
assert.NotContains(t, r, "e+")
assert.Equal(t, l, r, "wrong formatting for %d", n)
}
}
func TestSizeStrBig(t *testing.T) { func TestSizeStrBig(t *testing.T) {
ZiB := big.NewInt(50000) ZiB := big.NewInt(50000)
ZiB = ZiB.Lsh(ZiB, 70) ZiB = ZiB.Lsh(ZiB, 70)
+5 -4
View File
@@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/stretchr/testify/require"
"reflect" "reflect"
"testing" "testing"
@@ -80,7 +79,7 @@ func TestInteropBH(t *testing.T) {
} }
posts := []abi.PoStProof{ posts := []abi.PoStProof{
{abi.RegisteredPoStProof_StackedDrgWinning2KiBV1, []byte{0x07}}, {abi.RegisteredProof_StackedDRG2KiBWinningPoSt, []byte{0x07}},
} }
bh := &BlockHeader{ bh := &BlockHeader{
@@ -117,8 +116,10 @@ func TestInteropBH(t *testing.T) {
} }
// acquired from go-filecoin // acquired from go-filecoin
gfc := "8f5501d04cb15021bf6bd003073d79e2238d4e61f1ad22814301020381420a0b818205410c818200410781d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc430003e802d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc410001f603" gfc := "8f5501d04cb15021bf6bd003073d79e2238d4e61f1ad22814301020381420a0b818205410c818209410781d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc430003e802d82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619ccd82a5827000171a0e402202f84fef0d7cc2d7f9f00d22445f7bf7539fdd685fd9f284aa37f3822b57619cc410001f603"
require.Equal(t, gfc, hex.EncodeToString(bhsb)) if gfc != hex.EncodeToString(bhsb) {
t.Fatal("not equal!")
}
} }
func BenchmarkBlockHeaderMarshal(b *testing.B) { func BenchmarkBlockHeaderMarshal(b *testing.B) {
+7 -98
View File
@@ -1,103 +1,12 @@
package types package types
import ( import "time"
"encoding/json"
"fmt"
"runtime"
"strings"
"time"
)
type ExecutionTrace struct { type ExecutionResult struct {
Msg *Message Msg *Message
MsgRct *MessageReceipt MsgRct *MessageReceipt
Error string Error string
Duration time.Duration Duration time.Duration
GasCharges []*GasTrace
Subcalls []ExecutionTrace Subcalls []*ExecutionResult
}
type GasTrace struct {
Name string
Location []Loc `json:"loc"`
TotalGas int64 `json:"tg"`
ComputeGas int64 `json:"cg"`
StorageGas int64 `json:"sg"`
TotalVirtualGas int64 `json:"vtg"`
VirtualComputeGas int64 `json:"vcg"`
VirtualStorageGas int64 `json:"vsg"`
TimeTaken time.Duration `json:"tt"`
Extra interface{} `json:"ex,omitempty"`
Callers []uintptr `json:"-"`
}
type Loc struct {
File string
Line int
Function string
}
func (l Loc) Show() bool {
ignorePrefix := []string{
"reflect.",
"github.com/filecoin-project/lotus/chain/vm.(*Invoker).transform",
"github.com/filecoin-project/go-amt-ipld/",
}
for _, pre := range ignorePrefix {
if strings.HasPrefix(l.Function, pre) {
return false
}
}
return true
}
func (l Loc) String() string {
file := strings.Split(l.File, "/")
fn := strings.Split(l.Function, "/")
var fnpkg string
if len(fn) > 2 {
fnpkg = strings.Join(fn[len(fn)-2:], "/")
} else {
fnpkg = l.Function
}
return fmt.Sprintf("%s@%s:%d", fnpkg, file[len(file)-1], l.Line)
}
func (l Loc) Important() bool {
if strings.HasPrefix(l.Function, "github.com/filecoin-project/specs-actors/actors/builtin") {
return true
}
return false
}
func (gt *GasTrace) MarshalJSON() ([]byte, error) {
type GasTraceCopy GasTrace
if len(gt.Location) == 0 {
if len(gt.Callers) != 0 {
frames := runtime.CallersFrames(gt.Callers)
for {
frame, more := frames.Next()
if frame.Function == "github.com/filecoin-project/lotus/chain/vm.(*VM).ApplyMessage" {
break
}
l := Loc{
File: frame.File,
Line: frame.Line,
Function: frame.Function,
}
gt.Location = append(gt.Location, l)
if !more {
break
}
}
}
}
cpy := (*GasTraceCopy)(gt)
return json.Marshal(cpy)
} }
+2 -2
View File
@@ -11,7 +11,7 @@ import (
type FIL BigInt type FIL BigInt
func (f FIL) String() string { func (f FIL) String() string {
r := new(big.Rat).SetFrac(f.Int, big.NewInt(int64(build.FilecoinPrecision))) r := new(big.Rat).SetFrac(f.Int, big.NewInt(build.FilecoinPrecision))
if r.Sign() == 0 { if r.Sign() == 0 {
return "0" return "0"
} }
@@ -33,7 +33,7 @@ func ParseFIL(s string) (FIL, error) {
return FIL{}, fmt.Errorf("failed to parse %q as a decimal number", s) return FIL{}, fmt.Errorf("failed to parse %q as a decimal number", s)
} }
r = r.Mul(r, big.NewRat(int64(build.FilecoinPrecision), 1)) r = r.Mul(r, big.NewRat(build.FilecoinPrecision, 1))
if !r.IsInt() { if !r.IsInt() {
return FIL{}, fmt.Errorf("invalid FIL value: %q", s) return FIL{}, fmt.Errorf("invalid FIL value: %q", s)
} }
-1
View File
@@ -21,7 +21,6 @@ type ChainMsg interface {
Cid() cid.Cid Cid() cid.Cid
VMMessage() *Message VMMessage() *Message
ToStorageBlock() (block.Block, error) ToStorageBlock() (block.Block, error)
// FIXME: This is the *message* length, this name is misleading.
ChainLength() int ChainLength() int
} }
+1 -6
View File
@@ -49,11 +49,6 @@ func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types
panic(err) panic(err)
} }
pstateRoot := c
if parents != nil {
pstateRoot = parents.Blocks()[0].ParentStateRoot
}
var pcids []cid.Cid var pcids []cid.Cid
var height abi.ChainEpoch var height abi.ChainEpoch
weight := types.NewInt(weightInc) weight := types.NewInt(weightInc)
@@ -77,7 +72,7 @@ func MkBlock(parents *types.TipSet, weightInc uint64, ticketNonce uint64) *types
ParentWeight: weight, ParentWeight: weight,
Messages: c, Messages: c,
Height: height, Height: height,
ParentStateRoot: pstateRoot, ParentStateRoot: c,
BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("boo! im a signature")}, BlockSig: &crypto.Signature{Type: crypto.SigTypeBLS, Data: []byte("boo! im a signature")},
} }
} }
+16 -77
View File
@@ -12,74 +12,34 @@ import (
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
) )
const (
GasStorageMulti = 1
GasComputeMulti = 1
)
type GasCharge struct {
Name string
Extra interface{}
ComputeGas int64
StorageGas int64
VirtualCompute int64
VirtualStorage int64
}
func (g GasCharge) Total() int64 {
return g.ComputeGas*GasComputeMulti + g.StorageGas*GasStorageMulti
}
func (g GasCharge) WithVirtual(compute, storage int64) GasCharge {
out := g
out.VirtualCompute = compute
out.VirtualStorage = storage
return out
}
func (g GasCharge) WithExtra(extra interface{}) GasCharge {
out := g
out.Extra = extra
return out
}
func newGasCharge(name string, computeGas int64, storageGas int64) GasCharge {
return GasCharge{
Name: name,
ComputeGas: computeGas,
StorageGas: storageGas,
}
}
// Pricelist provides prices for operations in the VM. // Pricelist provides prices for operations in the VM.
// //
// Note: this interface should be APPEND ONLY since last chain checkpoint // Note: this interface should be APPEND ONLY since last chain checkpoint
type Pricelist interface { type Pricelist interface {
// OnChainMessage returns the gas used for storing a message of a given size in the chain. // OnChainMessage returns the gas used for storing a message of a given size in the chain.
OnChainMessage(msgSize int) GasCharge OnChainMessage(msgSize int) int64
// OnChainReturnValue returns the gas used for storing the response of a message in the chain. // OnChainReturnValue returns the gas used for storing the response of a message in the chain.
OnChainReturnValue(dataSize int) GasCharge OnChainReturnValue(dataSize int) int64
// OnMethodInvocation returns the gas used when invoking a method. // OnMethodInvocation returns the gas used when invoking a method.
OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64
// OnIpldGet returns the gas used for storing an object // OnIpldGet returns the gas used for storing an object
OnIpldGet(dataSize int) GasCharge OnIpldGet(dataSize int) int64
// OnIpldPut returns the gas used for storing an object // OnIpldPut returns the gas used for storing an object
OnIpldPut(dataSize int) GasCharge OnIpldPut(dataSize int) int64
// OnCreateActor returns the gas used for creating an actor // OnCreateActor returns the gas used for creating an actor
OnCreateActor() GasCharge OnCreateActor() int64
// OnDeleteActor returns the gas used for deleting an actor // OnDeleteActor returns the gas used for deleting an actor
OnDeleteActor() GasCharge OnDeleteActor() int64
OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error) OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error)
OnHashing(dataSize int) GasCharge OnHashing(dataSize int) int64
OnComputeUnsealedSectorCid(proofType abi.RegisteredSealProof, pieces []abi.PieceInfo) GasCharge OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64
OnVerifySeal(info abi.SealVerifyInfo) GasCharge OnVerifySeal(info abi.SealVerifyInfo) int64
OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge OnVerifyPost(info abi.WindowPoStVerifyInfo) int64
OnVerifyConsensusFault() GasCharge OnVerifyConsensusFault() int64
} }
var prices = map[abi.ChainEpoch]Pricelist{ var prices = map[abi.ChainEpoch]Pricelist{
@@ -133,7 +93,7 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist {
type pricedSyscalls struct { type pricedSyscalls struct {
under vmr.Syscalls under vmr.Syscalls
pl Pricelist pl Pricelist
chargeGas func(GasCharge) chargeGas func(int64)
} }
// Verifies that a signature is valid for an address and plaintext. // Verifies that a signature is valid for an address and plaintext.
@@ -143,40 +103,30 @@ func (ps pricedSyscalls) VerifySignature(signature crypto.Signature, signer addr
return err return err
} }
ps.chargeGas(c) ps.chargeGas(c)
defer ps.chargeGas(gasOnActorExec)
return ps.under.VerifySignature(signature, signer, plaintext) return ps.under.VerifySignature(signature, signer, plaintext)
} }
// Hashes input data using blake2b with 256 bit output. // Hashes input data using blake2b with 256 bit output.
func (ps pricedSyscalls) HashBlake2b(data []byte) [32]byte { func (ps pricedSyscalls) HashBlake2b(data []byte) [32]byte {
ps.chargeGas(ps.pl.OnHashing(len(data))) ps.chargeGas(ps.pl.OnHashing(len(data)))
defer ps.chargeGas(gasOnActorExec)
return ps.under.HashBlake2b(data) return ps.under.HashBlake2b(data)
} }
// Computes an unsealed sector CID (CommD) from its constituent piece CIDs (CommPs) and sizes. // Computes an unsealed sector CID (CommD) from its constituent piece CIDs (CommPs) and sizes.
func (ps pricedSyscalls) ComputeUnsealedSectorCID(reg abi.RegisteredSealProof, pieces []abi.PieceInfo) (cid.Cid, error) { func (ps pricedSyscalls) ComputeUnsealedSectorCID(reg abi.RegisteredProof, pieces []abi.PieceInfo) (cid.Cid, error) {
ps.chargeGas(ps.pl.OnComputeUnsealedSectorCid(reg, pieces)) ps.chargeGas(ps.pl.OnComputeUnsealedSectorCid(reg, pieces))
defer ps.chargeGas(gasOnActorExec)
return ps.under.ComputeUnsealedSectorCID(reg, pieces) return ps.under.ComputeUnsealedSectorCID(reg, pieces)
} }
// Verifies a sector seal proof. // Verifies a sector seal proof.
func (ps pricedSyscalls) VerifySeal(vi abi.SealVerifyInfo) error { func (ps pricedSyscalls) VerifySeal(vi abi.SealVerifyInfo) error {
ps.chargeGas(ps.pl.OnVerifySeal(vi)) ps.chargeGas(ps.pl.OnVerifySeal(vi))
defer ps.chargeGas(gasOnActorExec)
return ps.under.VerifySeal(vi) return ps.under.VerifySeal(vi)
} }
// Verifies a proof of spacetime. // Verifies a proof of spacetime.
func (ps pricedSyscalls) VerifyPoSt(vi abi.WindowPoStVerifyInfo) error { func (ps pricedSyscalls) VerifyPoSt(vi abi.WindowPoStVerifyInfo) error {
ps.chargeGas(ps.pl.OnVerifyPost(vi)) ps.chargeGas(ps.pl.OnVerifyPost(vi))
defer ps.chargeGas(gasOnActorExec)
return ps.under.VerifyPoSt(vi) return ps.under.VerifyPoSt(vi)
} }
@@ -192,21 +142,10 @@ func (ps pricedSyscalls) VerifyPoSt(vi abi.WindowPoStVerifyInfo) error {
// Returns nil and an error if the headers don't prove a fault. // Returns nil and an error if the headers don't prove a fault.
func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte) (*runtime.ConsensusFault, error) { func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte) (*runtime.ConsensusFault, error) {
ps.chargeGas(ps.pl.OnVerifyConsensusFault()) ps.chargeGas(ps.pl.OnVerifyConsensusFault())
defer ps.chargeGas(gasOnActorExec)
return ps.under.VerifyConsensusFault(h1, h2, extra) return ps.under.VerifyConsensusFault(h1, h2, extra)
} }
func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) { func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) {
var gasChargeSum GasCharge ps.chargeGas(0) // TODO: this is only called by the cron actor. Should we even charge gas?
gasChargeSum.Name = "BatchVerifySeals"
count := int64(0)
for _, svis := range inp {
count += int64(len(svis))
}
gasChargeSum = gasChargeSum.WithExtra(count).WithVirtual(129778623*count+716683250, 0)
ps.chargeGas(gasChargeSum) // TODO: this is only called by the cron actor. Should we even charge gas?
defer ps.chargeGas(gasOnActorExec)
return ps.under.BatchVerifySeals(inp) return ps.under.BatchVerifySeals(inp)
} }
+27 -61
View File
@@ -2,9 +2,7 @@ package vm
import ( import (
"fmt" "fmt"
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/crypto" "github.com/filecoin-project/specs-actors/actors/crypto"
) )
@@ -86,112 +84,80 @@ type pricelistV0 struct {
var _ Pricelist = (*pricelistV0)(nil) var _ Pricelist = (*pricelistV0)(nil)
// OnChainMessage returns the gas used for storing a message of a given size in the chain. // OnChainMessage returns the gas used for storing a message of a given size in the chain.
func (pl *pricelistV0) OnChainMessage(msgSize int) GasCharge { func (pl *pricelistV0) OnChainMessage(msgSize int) int64 {
return newGasCharge("OnChainMessage", 0, pl.onChainMessageBase+pl.onChainMessagePerByte*int64(msgSize)).WithVirtual(77302, 0) return pl.onChainMessageBase + pl.onChainMessagePerByte*int64(msgSize)
} }
// OnChainReturnValue returns the gas used for storing the response of a message in the chain. // OnChainReturnValue returns the gas used for storing the response of a message in the chain.
func (pl *pricelistV0) OnChainReturnValue(dataSize int) GasCharge { func (pl *pricelistV0) OnChainReturnValue(dataSize int) int64 {
return newGasCharge("OnChainReturnValue", 0, int64(dataSize)*pl.onChainReturnValuePerByte).WithVirtual(107294, 0) return int64(dataSize) * pl.onChainReturnValuePerByte
} }
// OnMethodInvocation returns the gas used when invoking a method. // OnMethodInvocation returns the gas used when invoking a method.
func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge { func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64 {
ret := pl.sendBase ret := pl.sendBase
extra := ""
virtGas := int64(1072944)
if value != abi.NewTokenAmount(0) { if value != abi.NewTokenAmount(0) {
// TODO: fix this, it is comparing pointers instead of values
// see vv
ret += pl.sendTransferFunds ret += pl.sendTransferFunds
} }
if big.Cmp(value, abi.NewTokenAmount(0)) != 0 {
virtGas += 497495
if methodNum == builtin.MethodSend {
// transfer only
virtGas += 973940
}
extra += "t"
}
if methodNum != builtin.MethodSend { if methodNum != builtin.MethodSend {
ret += pl.sendInvokeMethod ret += pl.sendInvokeMethod
extra += "i"
// running actors is cheaper becase we hand over to actors
virtGas += -295779
} }
return newGasCharge("OnMethodInvocation", ret, 0).WithVirtual(virtGas, 0).WithExtra(extra) return ret
} }
// OnIpldGet returns the gas used for storing an object // OnIpldGet returns the gas used for storing an object
func (pl *pricelistV0) OnIpldGet(dataSize int) GasCharge { func (pl *pricelistV0) OnIpldGet(dataSize int) int64 {
return newGasCharge("OnIpldGet", pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0). return pl.ipldGetBase + int64(dataSize)*pl.ipldGetPerByte
WithExtra(dataSize).WithVirtual(433685, 0)
} }
// OnIpldPut returns the gas used for storing an object // OnIpldPut returns the gas used for storing an object
func (pl *pricelistV0) OnIpldPut(dataSize int) GasCharge { func (pl *pricelistV0) OnIpldPut(dataSize int) int64 {
return newGasCharge("OnIpldPut", pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte). return pl.ipldPutBase + int64(dataSize)*pl.ipldPutPerByte
WithExtra(dataSize).WithVirtual(88970, 0)
} }
// OnCreateActor returns the gas used for creating an actor // OnCreateActor returns the gas used for creating an actor
func (pl *pricelistV0) OnCreateActor() GasCharge { func (pl *pricelistV0) OnCreateActor() int64 {
return newGasCharge("OnCreateActor", pl.createActorBase, pl.createActorExtra).WithVirtual(65636, 0) return pl.createActorBase + pl.createActorExtra
} }
// OnDeleteActor returns the gas used for deleting an actor // OnDeleteActor returns the gas used for deleting an actor
func (pl *pricelistV0) OnDeleteActor() GasCharge { func (pl *pricelistV0) OnDeleteActor() int64 {
return newGasCharge("OnDeleteActor", 0, pl.deleteActor) return pl.deleteActor
} }
// OnVerifySignature // OnVerifySignature
func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error) {
func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error) {
costFn, ok := pl.verifySignature[sigType] costFn, ok := pl.verifySignature[sigType]
if !ok { if !ok {
return GasCharge{}, fmt.Errorf("cost function for signature type %d not supported", sigType) return 0, fmt.Errorf("cost function for signature type %d not supported", sigType)
} }
sigName, _ := sigType.Name() return costFn(int64(planTextSize)), nil
virtGas := int64(0)
switch sigType {
case crypto.SigTypeBLS:
virtGas = 220138570
case crypto.SigTypeSecp256k1:
virtGas = 7053730
}
return newGasCharge("OnVerifySignature", costFn(int64(planTextSize)), 0).
WithExtra(map[string]interface{}{
"type": sigName,
"size": planTextSize,
}).WithVirtual(virtGas, 0), nil
} }
// OnHashing // OnHashing
func (pl *pricelistV0) OnHashing(dataSize int) GasCharge { func (pl *pricelistV0) OnHashing(dataSize int) int64 {
return newGasCharge("OnHashing", pl.hashingBase+int64(dataSize)*pl.hashingPerByte, 0).WithExtra(dataSize).WithVirtual(77300, 0) return pl.hashingBase + int64(dataSize)*pl.hashingPerByte
} }
// OnComputeUnsealedSectorCid // OnComputeUnsealedSectorCid
func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredSealProof, pieces []abi.PieceInfo) GasCharge { func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64 {
// TODO: this needs more cost tunning, check with @lotus // TODO: this needs more cost tunning, check with @lotus
return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0).WithVirtual(382370, 0) return pl.computeUnsealedSectorCidBase
} }
// OnVerifySeal // OnVerifySeal
func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) GasCharge { func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) int64 {
// TODO: this needs more cost tunning, check with @lotus // TODO: this needs more cost tunning, check with @lotus
return newGasCharge("OnVerifySeal", pl.verifySealBase, 0).WithVirtual(199954003, 0) return pl.verifySealBase
} }
// OnVerifyPost // OnVerifyPost
func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge { func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) int64 {
// TODO: this needs more cost tunning, check with @lotus // TODO: this needs more cost tunning, check with @lotus
return newGasCharge("OnVerifyPost", pl.verifyPostBase, 0).WithVirtual(2629471704, 0).WithExtra(len(info.ChallengedSectors)) return pl.verifyPostBase
} }
// OnVerifyConsensusFault // OnVerifyConsensusFault
func (pl *pricelistV0) OnVerifyConsensusFault() GasCharge { func (pl *pricelistV0) OnVerifyConsensusFault() int64 {
return newGasCharge("OnVerifyConsensusFault", pl.verifyConsensusFault, 0).WithVirtual(551935, 0) return pl.verifyConsensusFault
} }
+32 -77
View File
@@ -5,7 +5,6 @@ import (
"context" "context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
gruntime "runtime"
"time" "time"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
@@ -52,12 +51,10 @@ type Runtime struct {
origin address.Address origin address.Address
originNonce uint64 originNonce uint64
executionTrace types.ExecutionTrace internalExecutions []*types.ExecutionResult
numActorsCreated uint64 numActorsCreated uint64
allowInternal bool allowInternal bool
callerValidated bool callerValidated bool
lastGasChargeTime time.Time
lastGasCharge *types.GasTrace
} }
func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount { func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount {
@@ -142,7 +139,7 @@ func (rt *Runtime) shimCall(f func() interface{}) (rval []byte, aerr aerrors.Act
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
if ar, ok := r.(aerrors.ActorError); ok { if ar, ok := r.(aerrors.ActorError); ok {
log.Warnf("VM.Call failure: %+v", ar) log.Errorf("VM.Call failure: %+v", ar)
aerr = ar aerr = ar
return return
} }
@@ -253,7 +250,7 @@ func (rt *Runtime) CreateActor(codeID cid.Cid, address address.Address) {
rt.Abortf(exitcode.SysErrorIllegalArgument, "Actor address already exists") rt.Abortf(exitcode.SysErrorIllegalArgument, "Actor address already exists")
} }
rt.chargeGas(rt.Pricelist().OnCreateActor()) rt.ChargeGas(rt.Pricelist().OnCreateActor())
err = rt.state.SetActor(address, &types.Actor{ err = rt.state.SetActor(address, &types.Actor{
Code: codeID, Code: codeID,
@@ -264,11 +261,10 @@ func (rt *Runtime) CreateActor(codeID cid.Cid, address address.Address) {
if err != nil { if err != nil {
panic(aerrors.Fatalf("creating actor entry: %v", err)) panic(aerrors.Fatalf("creating actor entry: %v", err))
} }
_ = rt.chargeGasSafe(gasOnActorExec)
} }
func (rt *Runtime) DeleteActor(addr address.Address) { func (rt *Runtime) DeleteActor(addr address.Address) {
rt.chargeGas(rt.Pricelist().OnDeleteActor()) rt.ChargeGas(rt.Pricelist().OnDeleteActor())
act, err := rt.state.GetActor(rt.Message().Receiver()) act, err := rt.state.GetActor(rt.Message().Receiver())
if err != nil { if err != nil {
if xerrors.Is(err, types.ErrActorNotFound) { if xerrors.Is(err, types.ErrActorNotFound) {
@@ -285,10 +281,10 @@ func (rt *Runtime) DeleteActor(addr address.Address) {
if err := rt.state.DeleteActor(rt.Message().Receiver()); err != nil { if err := rt.state.DeleteActor(rt.Message().Receiver()); err != nil {
panic(aerrors.Fatalf("failed to delete actor: %s", err)) panic(aerrors.Fatalf("failed to delete actor: %s", err))
} }
_ = rt.chargeGasSafe(gasOnActorExec)
} }
func (rt *Runtime) Syscalls() vmr.Syscalls { func (rt *Runtime) Syscalls() vmr.Syscalls {
// TODO: Make sure this is wrapped in something that charges gas for each of the calls
return rt.sys return rt.sys
} }
@@ -313,7 +309,7 @@ func (rt *Runtime) Context() context.Context {
} }
func (rt *Runtime) Abortf(code exitcode.ExitCode, msg string, args ...interface{}) { func (rt *Runtime) Abortf(code exitcode.ExitCode, msg string, args ...interface{}) {
log.Warnf("Abortf: " + fmt.Sprintf(msg, args...)) log.Error("Abortf: ", fmt.Sprintf(msg, args...))
panic(aerrors.NewfSkip(2, code, msg, args...)) panic(aerrors.NewfSkip(2, code, msg, args...))
} }
@@ -368,12 +364,10 @@ func (rt *Runtime) Send(to address.Address, method abi.MethodNum, m vmr.CBORMars
log.Warnf("vmctx send failed: to: %s, method: %d: ret: %d, err: %s", to, method, ret, err) log.Warnf("vmctx send failed: to: %s, method: %d: ret: %d, err: %s", to, method, ret, err)
return nil, err.RetCode() return nil, err.RetCode()
} }
_ = rt.chargeGasSafe(gasOnActorExec)
return &dumbWrapperType{ret}, 0 return &dumbWrapperType{ret}, 0
} }
func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) { func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) {
start := time.Now() start := time.Now()
ctx, span := trace.StartSpan(rt.ctx, "vmc.Send") ctx, span := trace.StartSpan(rt.ctx, "vmc.Send")
defer span.End() defer span.End()
@@ -400,17 +394,34 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
} }
defer st.ClearSnapshot() defer st.ClearSnapshot()
ret, errSend, subrt := rt.vm.send(ctx, msg, rt, nil, start) ret, errSend, subrt := rt.vm.send(ctx, msg, rt, 0)
if errSend != nil { if errSend != nil {
if errRevert := st.Revert(); errRevert != nil { if errRevert := st.Revert(); errRevert != nil {
return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall") return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall")
} }
} }
mr := types.MessageReceipt{
ExitCode: aerrors.RetCode(errSend),
Return: ret,
GasUsed: 0,
}
er := types.ExecutionResult{
Msg: msg,
MsgRct: &mr,
Duration: time.Since(start),
}
if errSend != nil {
er.Error = errSend.Error()
}
if subrt != nil { if subrt != nil {
er.Subcalls = subrt.internalExecutions
rt.numActorsCreated = subrt.numActorsCreated rt.numActorsCreated = subrt.numActorsCreated
} }
rt.executionTrace.Subcalls = append(rt.executionTrace.Subcalls, subrt.executionTrace) rt.internalExecutions = append(rt.internalExecutions, &er)
return ret, errSend return ret, errSend
} }
@@ -492,78 +503,22 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
return nil return nil
} }
func (rt *Runtime) finilizeGasTracing() { func (rt *Runtime) ChargeGas(toUse int64) {
if rt.lastGasCharge != nil { err := rt.chargeGasSafe(toUse)
rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime)
}
}
// ChargeGas is spec actors function
func (rt *Runtime) ChargeGas(name string, compute int64, virtual int64) {
err := rt.chargeGasInternal(newGasCharge(name, compute, 0).WithVirtual(virtual, 0), 1)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
func (rt *Runtime) chargeGas(gas GasCharge) { func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError {
err := rt.chargeGasInternal(gas, 1)
if err != nil {
panic(err)
}
}
func (rt *Runtime) chargeGasFunc(skip int) func(GasCharge) {
return func(gas GasCharge) {
err := rt.chargeGasInternal(gas, 1+skip)
if err != nil {
panic(err)
}
}
}
func (rt *Runtime) chargeGasInternal(gas GasCharge, skip int) aerrors.ActorError {
toUse := gas.Total()
var callers [10]uintptr
cout := gruntime.Callers(2+skip, callers[:])
now := time.Now()
if rt.lastGasCharge != nil {
rt.lastGasCharge.TimeTaken = now.Sub(rt.lastGasChargeTime)
}
gasTrace := types.GasTrace{
Name: gas.Name,
Extra: gas.Extra,
TotalGas: toUse,
ComputeGas: gas.ComputeGas,
StorageGas: gas.StorageGas,
TotalVirtualGas: gas.VirtualCompute*GasComputeMulti + gas.VirtualStorage*GasStorageMulti,
VirtualComputeGas: gas.VirtualCompute,
VirtualStorageGas: gas.VirtualStorage,
Callers: callers[:cout],
}
rt.executionTrace.GasCharges = append(rt.executionTrace.GasCharges, &gasTrace)
rt.lastGasChargeTime = now
rt.lastGasCharge = &gasTrace
if rt.gasUsed+toUse > rt.gasAvailable { if rt.gasUsed+toUse > rt.gasAvailable {
rt.gasUsed = rt.gasAvailable rt.gasUsed = rt.gasAvailable
return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", rt.gasUsed, rt.gasAvailable)
rt.gasUsed, rt.gasAvailable)
} }
rt.gasUsed += toUse rt.gasUsed += toUse
return nil return nil
} }
func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError {
return rt.chargeGasInternal(gas, 1)
}
func (rt *Runtime) Pricelist() Pricelist { func (rt *Runtime) Pricelist() Pricelist {
return rt.pricelist return rt.pricelist
} }
+4 -6
View File
@@ -43,7 +43,7 @@ type syscallShim struct {
verifier ffiwrapper.Verifier verifier ffiwrapper.Verifier
} }
func (ss *syscallShim) ComputeUnsealedSectorCID(st abi.RegisteredSealProof, pieces []abi.PieceInfo) (cid.Cid, error) { func (ss *syscallShim) ComputeUnsealedSectorCID(st abi.RegisteredProof, pieces []abi.PieceInfo) (cid.Cid, error) {
var sum abi.PaddedPieceSize var sum abi.PaddedPieceSize
for _, p := range pieces { for _, p := range pieces {
sum += p.Size sum += p.Size
@@ -115,7 +115,7 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime.Consen
// (b) time-offset mining fault // (b) time-offset mining fault
// strictly speaking no need to compare heights based on double fork mining check above, // strictly speaking no need to compare heights based on double fork mining check above,
// but at same height this would be a different fault. // but at same height this would be a different fault.
if types.CidArrsEqual(blockA.Parents, blockB.Parents) && blockA.Height != blockB.Height { if !types.CidArrsEqual(blockA.Parents, blockB.Parents) && blockA.Height != blockB.Height {
consensusFault = &runtime.ConsensusFault{ consensusFault = &runtime.ConsensusFault{
Target: blockA.Miner, Target: blockA.Miner,
Epoch: blockB.Height, Epoch: blockB.Height,
@@ -159,7 +159,7 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime.Consen
} }
if sigErr := ss.VerifyBlockSig(&blockB); sigErr != nil { if sigErr := ss.VerifyBlockSig(&blockB); sigErr != nil {
return nil, xerrors.Errorf("cannot verify second block sig: %w", sigErr) return nil, xerrors.Errorf("cannot verify first block sig: %w", sigErr)
} }
return consensusFault, nil return consensusFault, nil
@@ -241,12 +241,10 @@ func (ss *syscallShim) VerifySignature(sig crypto.Signature, addr address.Addres
return sigs.Verify(&sig, kaddr, input) return sigs.Verify(&sig, kaddr, input)
} }
var BatchSealVerifyParallelism = goruntime.NumCPU()
func (ss *syscallShim) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) { func (ss *syscallShim) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) {
out := make(map[address.Address][]bool) out := make(map[address.Address][]bool)
sema := make(chan struct{}, BatchSealVerifyParallelism) sema := make(chan struct{}, goruntime.NumCPU())
var wg sync.WaitGroup var wg sync.WaitGroup
for addr, seals := range inp { for addr, seals := range inp {
+45 -84
View File
@@ -35,7 +35,6 @@ import (
) )
var log = logging.Logger("vm") var log = logging.Logger("vm")
var gasOnActorExec = newGasCharge("OnActorExec", 0, 0)
// ResolveToKeyAddr returns the public key type of address (`BLS`/`SECP256K1`) of an account actor identified by `addr`. // ResolveToKeyAddr returns the public key type of address (`BLS`/`SECP256K1`) of an account actor identified by `addr`.
func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Address) (address.Address, aerrors.ActorError) { func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Address) (address.Address, aerrors.ActorError) {
@@ -63,19 +62,17 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil) var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
type gasChargingBlocks struct { type gasChargingBlocks struct {
chargeGas func(GasCharge) chargeGas func(int64)
pricelist Pricelist pricelist Pricelist
under cbor.IpldBlockstore under cbor.IpldBlockstore
} }
func (bs *gasChargingBlocks) Get(c cid.Cid) (block.Block, error) { func (bs *gasChargingBlocks) Get(c cid.Cid) (block.Block, error) {
bs.chargeGas(newGasCharge("OnIpldGetStart", 0, 0))
blk, err := bs.under.Get(c) blk, err := bs.under.Get(c)
if err != nil { if err != nil {
return nil, aerrors.Escalate(err, "failed to get block from blockstore") return nil, aerrors.Escalate(err, "failed to get block from blockstore")
} }
bs.chargeGas(bs.pricelist.OnIpldGet(len(blk.RawData()))) bs.chargeGas(bs.pricelist.OnIpldGet(len(blk.RawData())))
bs.chargeGas(gasOnActorExec)
return blk, nil return blk, nil
} }
@@ -86,7 +83,6 @@ func (bs *gasChargingBlocks) Put(blk block.Block) error {
if err := bs.under.Put(blk); err != nil { if err := bs.under.Put(blk); err != nil {
return aerrors.Escalate(err, "failed to write data to disk") return aerrors.Escalate(err, "failed to write data to disk")
} }
bs.chargeGas(gasOnActorExec)
return nil return nil
} }
@@ -106,16 +102,15 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
pricelist: PricelistByEpoch(vm.blockHeight), pricelist: PricelistByEpoch(vm.blockHeight),
allowInternal: true, allowInternal: true,
callerValidated: false, callerValidated: false,
executionTrace: types.ExecutionTrace{Msg: msg},
} }
rt.cst = &cbor.BasicIpldStore{ rt.cst = &cbor.BasicIpldStore{
Blocks: &gasChargingBlocks{rt.chargeGasFunc(2), rt.pricelist, vm.cst.Blocks}, Blocks: &gasChargingBlocks{rt.ChargeGas, rt.pricelist, vm.cst.Blocks},
Atlas: vm.cst.Atlas, Atlas: vm.cst.Atlas,
} }
rt.sys = pricedSyscalls{ rt.sys = pricedSyscalls{
under: vm.Syscalls, under: vm.Syscalls,
chargeGas: rt.chargeGasFunc(1), chargeGas: rt.ChargeGas,
pl: rt.pricelist, pl: rt.pricelist,
} }
@@ -168,93 +163,63 @@ type Rand interface {
type ApplyRet struct { type ApplyRet struct {
types.MessageReceipt types.MessageReceipt
ActorErr aerrors.ActorError ActorErr aerrors.ActorError
Penalty types.BigInt Penalty types.BigInt
ExecutionTrace types.ExecutionTrace InternalExecutions []*types.ExecutionResult
Duration time.Duration Duration time.Duration
} }
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime, func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
gasCharge *GasCharge, start time.Time) ([]byte, aerrors.ActorError, *Runtime) { gasCharge int64) ([]byte, aerrors.ActorError, *Runtime) {
st := vm.cstate st := vm.cstate
gasUsed := gasCharge
origin := msg.From origin := msg.From
on := msg.Nonce on := msg.Nonce
var nac uint64 = 0 var nac uint64 = 0
var gasUsed int64
if parent != nil { if parent != nil {
gasUsed = parent.gasUsed gasUsed = parent.gasUsed + gasUsed
origin = parent.origin origin = parent.origin
on = parent.originNonce on = parent.originNonce
nac = parent.numActorsCreated nac = parent.numActorsCreated
} }
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac) rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
rt.lastGasChargeTime = start
if parent != nil { if parent != nil {
rt.lastGasChargeTime = parent.lastGasChargeTime
rt.lastGasCharge = parent.lastGasCharge
defer func() { defer func() {
parent.gasUsed = rt.gasUsed parent.gasUsed = rt.gasUsed
parent.lastGasChargeTime = rt.lastGasChargeTime
parent.lastGasCharge = rt.lastGasCharge
}() }()
} }
if gasCharge != nil { if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
if err := rt.chargeGasSafe(*gasCharge); err != nil { return nil, aerrors.Wrap(aerr, "not enough gas for method invocation"), rt
// this should never happen
return nil, aerrors.Wrap(err, "not enough gas for initial message charge, this should not happen"), rt
}
} }
ret, err := func() ([]byte, aerrors.ActorError) { toActor, err := st.GetActor(msg.To)
if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
return nil, aerrors.Wrap(aerr, "not enough gas for method invocation")
}
toActor, err := st.GetActor(msg.To)
if err != nil {
if xerrors.Is(err, init_.ErrAddressNotFound) {
a, err := TryCreateAccountActor(rt, msg.To)
if err != nil {
return nil, aerrors.Wrapf(err, "could not create account")
}
toActor = a
} else {
return nil, aerrors.Escalate(err, "getting actor")
}
}
if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
if err := vm.transfer(msg.From, msg.To, msg.Value); err != nil {
return nil, aerrors.Wrap(err, "failed to transfer funds")
}
}
if msg.Method != 0 {
var ret []byte
_ = rt.chargeGasSafe(gasOnActorExec)
ret, err := vm.Invoke(toActor, rt, msg.Method, msg.Params)
_ = rt.chargeGasSafe(newGasCharge("OnActorExecDone", 0, 0))
return ret, err
}
return nil, nil
}()
mr := types.MessageReceipt{
ExitCode: aerrors.RetCode(err),
Return: ret,
GasUsed: rt.gasUsed,
}
rt.executionTrace.MsgRct = &mr
rt.executionTrace.Duration = time.Since(start)
if err != nil { if err != nil {
rt.executionTrace.Error = err.Error() if xerrors.Is(err, init_.ErrAddressNotFound) {
a, err := TryCreateAccountActor(rt, msg.To)
if err != nil {
return nil, aerrors.Wrapf(err, "could not create account"), rt
}
toActor = a
} else {
return nil, aerrors.Escalate(err, "getting actor"), rt
}
} }
return ret, err, rt if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
if err := vm.transfer(msg.From, msg.To, msg.Value); err != nil {
return nil, aerrors.Wrap(err, "failed to transfer funds"), nil
}
}
if msg.Method != 0 {
ret, err := vm.Invoke(toActor, rt, msg.Method, msg.Params)
return ret, err, rt
}
return nil, nil, rt
} }
func checkMessage(msg *types.Message) error { func checkMessage(msg *types.Message) error {
@@ -278,18 +243,17 @@ func checkMessage(msg *types.Message) error {
func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) { func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) {
start := time.Now() start := time.Now()
ret, actorErr, rt := vm.send(ctx, msg, nil, nil, start) ret, actorErr, rt := vm.send(ctx, msg, nil, 0)
rt.finilizeGasTracing()
return &ApplyRet{ return &ApplyRet{
MessageReceipt: types.MessageReceipt{ MessageReceipt: types.MessageReceipt{
ExitCode: aerrors.RetCode(actorErr), ExitCode: aerrors.RetCode(actorErr),
Return: ret, Return: ret,
GasUsed: 0, GasUsed: 0,
}, },
ActorErr: actorErr, ActorErr: actorErr,
ExecutionTrace: rt.executionTrace, InternalExecutions: rt.internalExecutions,
Penalty: types.NewInt(0), Penalty: types.NewInt(0),
Duration: time.Since(start), Duration: time.Since(start),
}, actorErr }, actorErr
} }
@@ -312,8 +276,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
pl := PricelistByEpoch(vm.blockHeight) pl := PricelistByEpoch(vm.blockHeight)
msgGas := pl.OnChainMessage(cmsg.ChainLength()) msgGasCost := pl.OnChainMessage(cmsg.ChainLength())
msgGasCost := msgGas.Total()
// this should never happen, but is currently still exercised by some tests // this should never happen, but is currently still exercised by some tests
if msgGasCost > msg.GasLimit { if msgGasCost > msg.GasLimit {
return &ApplyRet{ return &ApplyRet{
@@ -396,7 +359,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
} }
defer st.ClearSnapshot() defer st.ClearSnapshot()
ret, actorErr, rt := vm.send(ctx, msg, nil, &msgGas, start) ret, actorErr, rt := vm.send(ctx, msg, nil, msgGasCost)
if aerrors.IsFatal(actorErr) { if aerrors.IsFatal(actorErr) {
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr) return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
} }
@@ -450,18 +413,16 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
return nil, xerrors.Errorf("gas handling math is wrong") return nil, xerrors.Errorf("gas handling math is wrong")
} }
rt.finilizeGasTracing()
return &ApplyRet{ return &ApplyRet{
MessageReceipt: types.MessageReceipt{ MessageReceipt: types.MessageReceipt{
ExitCode: errcode, ExitCode: errcode,
Return: ret, Return: ret,
GasUsed: gasUsed, GasUsed: gasUsed,
}, },
ActorErr: actorErr, ActorErr: actorErr,
ExecutionTrace: rt.executionTrace, InternalExecutions: rt.internalExecutions,
Penalty: types.NewInt(0), Penalty: types.NewInt(0),
Duration: time.Since(start), Duration: time.Since(start),
}, nil }, nil
} }
+5 -35
View File
@@ -12,6 +12,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/docker/go-units"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
cborutil "github.com/filecoin-project/go-cbor-util" cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
@@ -221,9 +222,6 @@ var chainStatObjCmd = &cli.Command{
base := cid.Undef base := cid.Undef
if cctx.IsSet("base") { if cctx.IsSet("base") {
base, err = cid.Decode(cctx.String("base")) base, err = cid.Decode(cctx.String("base"))
if err != nil {
return err
}
} }
stats, err := api.ChainStatObj(ctx, obj, base) stats, err := api.ChainStatObj(ctx, obj, base)
@@ -232,7 +230,7 @@ var chainStatObjCmd = &cli.Command{
} }
fmt.Printf("Links: %d\n", stats.Links) fmt.Printf("Links: %d\n", stats.Links)
fmt.Printf("Size: %s (%d)\n", types.SizeStr(types.NewInt(stats.Size)), stats.Size) fmt.Printf("Size: %s (%d)\n", units.BytesSize(float64(stats.Size)), stats.Size)
return nil return nil
}, },
} }
@@ -844,10 +842,6 @@ var slashConsensusFault = &cli.Command{
Name: "miner", Name: "miner",
Usage: "Miner address", Usage: "Miner address",
}, },
&cli.StringFlag{
Name: "extra",
Usage: "Extra block cid",
},
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx) api, closer, err := GetFullNodeAPI(cctx)
@@ -892,34 +886,10 @@ var slashConsensusFault = &cli.Command{
return err return err
} }
params := miner.ReportConsensusFaultParams{ params, err := actors.SerializeParams(&miner.ReportConsensusFaultParams{
BlockHeader1: bh1, BlockHeader1: bh1,
BlockHeader2: bh2, BlockHeader2: bh2,
} })
if cctx.String("extra") != "" {
cExtra, err := cid.Parse(cctx.String("extra"))
if err != nil {
return xerrors.Errorf("parsing cid extra: %w", err)
}
bExtra, err := api.ChainGetBlock(ctx, cExtra)
if err != nil {
return xerrors.Errorf("getting block extra: %w", err)
}
be, err := cborutil.Dump(bExtra)
if err != nil {
return err
}
params.BlockHeaderExtra = be
}
enc, err := actors.SerializeParams(&params)
if err != nil {
return err
}
if cctx.String("miner") == "" { if cctx.String("miner") == "" {
return xerrors.Errorf("--miner flag is required") return xerrors.Errorf("--miner flag is required")
@@ -937,7 +907,7 @@ var slashConsensusFault = &cli.Command{
GasPrice: types.NewInt(1), GasPrice: types.NewInt(1),
GasLimit: 10000000, GasLimit: 10000000,
Method: builtin.MethodsMiner.ReportConsensusFault, Method: builtin.MethodsMiner.ReportConsensusFault,
Params: enc, Params: params,
} }
smsg, err := api.MpoolPushMessage(ctx, msg) smsg, err := api.MpoolPushMessage(ctx, msg)
+17 -38
View File
@@ -19,7 +19,6 @@ import (
"github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/market" "github.com/filecoin-project/specs-actors/actors/builtin/market"
"github.com/filecoin-project/lotus/api"
lapi "github.com/filecoin-project/lotus/api" lapi "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
@@ -144,7 +143,7 @@ var clientCommPCmd = &cli.Command{
} }
fmt.Println("CID: ", encoder.Encode(ret.Root)) fmt.Println("CID: ", encoder.Encode(ret.Root))
fmt.Println("Piece size: ", types.SizeStr(types.NewInt(uint64(ret.Size)))) fmt.Println("Piece size: ", ret.Size)
return nil return nil
}, },
} }
@@ -204,7 +203,7 @@ var clientLocalCmd = &cli.Command{
} }
for _, v := range list { for _, v := range list {
fmt.Printf("%s %s %s %s\n", encoder.Encode(v.Key), v.FilePath, types.SizeStr(types.NewInt(v.Size)), v.Status) fmt.Printf("%s %s %d %s\n", encoder.Encode(v.Key), v.FilePath, v.Size, v.Status)
} }
return nil return nil
}, },
@@ -372,7 +371,7 @@ var clientFindCmd = &cli.Command{
fmt.Printf("ERR %s@%s: %s\n", offer.Miner, offer.MinerPeerID, offer.Err) fmt.Printf("ERR %s@%s: %s\n", offer.Miner, offer.MinerPeerID, offer.Err)
continue continue
} }
fmt.Printf("RETRIEVAL %s@%s-%sfil-%s\n", offer.Miner, offer.MinerPeerID, types.FIL(offer.MinPrice), types.SizeStr(types.NewInt(offer.Size))) fmt.Printf("RETRIEVAL %s@%s-%sfil-%db\n", offer.Miner, offer.MinerPeerID, types.FIL(offer.MinPrice), offer.Size)
} }
return nil return nil
@@ -392,10 +391,6 @@ var clientRetrieveCmd = &cli.Command{
Name: "car", Name: "car",
Usage: "export to a car file instead of a regular file", Usage: "export to a car file instead of a regular file",
}, },
&cli.StringFlag{
Name: "miner",
Usage: "miner address for retrieval, if not present it'll use local discovery",
},
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
if cctx.NArg() != 2 { if cctx.NArg() != 2 {
@@ -403,7 +398,7 @@ var clientRetrieveCmd = &cli.Command{
return nil return nil
} }
fapi, closer, err := GetFullNodeAPI(cctx) api, closer, err := GetFullNodeAPI(cctx)
if err != nil { if err != nil {
return err return err
} }
@@ -414,7 +409,7 @@ var clientRetrieveCmd = &cli.Command{
if cctx.String("address") != "" { if cctx.String("address") != "" {
payer, err = address.NewFromString(cctx.String("address")) payer, err = address.NewFromString(cctx.String("address"))
} else { } else {
payer, err = fapi.WalletDefaultAddress(ctx) payer, err = api.WalletDefaultAddress(ctx)
} }
if err != nil { if err != nil {
return err return err
@@ -437,39 +432,23 @@ var clientRetrieveCmd = &cli.Command{
return nil return nil
}*/ // TODO: fix }*/ // TODO: fix
var offer api.QueryOffer offers, err := api.ClientFindData(ctx, file)
minerStrAddr := cctx.String("miner") if err != nil {
if minerStrAddr == "" { // Local discovery return err
offers, err := fapi.ClientFindData(ctx, file)
if err != nil {
return err
}
// TODO: parse offer strings from `client find`, make this smarter
if len(offers) < 1 {
fmt.Println("Failed to find file")
return nil
}
offer = offers[0]
} else { // Directed retrieval
minerAddr, err := address.NewFromString(minerStrAddr)
if err != nil {
return err
}
offer, err = fapi.ClientMinerQueryOffer(ctx, file, minerAddr)
if err != nil {
return err
}
} }
if offer.Err != "" {
return fmt.Errorf("The received offer errored: %s", offer.Err) // TODO: parse offer strings from `client find`, make this smarter
if len(offers) < 1 {
fmt.Println("Failed to find file")
return nil
} }
ref := &lapi.FileRef{ ref := &lapi.FileRef{
Path: cctx.Args().Get(1), Path: cctx.Args().Get(1),
IsCAR: cctx.Bool("car"), IsCAR: cctx.Bool("car"),
} }
if err := fapi.ClientRetrieve(ctx, offer.Order(payer), ref); err != nil { if err := api.ClientRetrieve(ctx, offers[0].Order(payer), ref); err != nil {
return xerrors.Errorf("Retrieval Failed: %w", err) return xerrors.Errorf("Retrieval Failed: %w", err)
} }
@@ -541,7 +520,7 @@ var clientQueryAskCmd = &cli.Command{
fmt.Printf("Ask: %s\n", maddr) fmt.Printf("Ask: %s\n", maddr)
fmt.Printf("Price per GiB: %s\n", types.FIL(ask.Ask.Price)) fmt.Printf("Price per GiB: %s\n", types.FIL(ask.Ask.Price))
fmt.Printf("Max Piece size: %s\n", types.SizeStr(types.NewInt(uint64(ask.Ask.MaxPieceSize)))) fmt.Printf("Max Piece size: %d\n", ask.Ask.MaxPieceSize)
size := cctx.Int64("size") size := cctx.Int64("size")
if size == 0 { if size == 0 {
@@ -618,7 +597,7 @@ var clientListDeals = &cli.Command{
slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch) slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch)
} }
fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\n", d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, storagemarket.DealStates[d.LocalDeal.State], onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), d.LocalDeal.PricePerEpoch, d.LocalDeal.Duration, d.LocalDeal.Message) fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\t%s\t%d\t%s\t%d\t%s\n", d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, storagemarket.DealStates[d.LocalDeal.State], onChain, slashed, d.LocalDeal.PieceCID, d.LocalDeal.Size, d.LocalDeal.PricePerEpoch, d.LocalDeal.Duration, d.LocalDeal.Message)
} }
return w.Flush() return w.Flush()
}, },
+10 -8
View File
@@ -10,16 +10,18 @@ import (
) )
var fetchParamCmd = &cli.Command{ var fetchParamCmd = &cli.Command{
Name: "fetch-params", Name: "fetch-params",
Usage: "Fetch proving parameters", Usage: "Fetch proving parameters",
ArgsUsage: "[sectorSize]", Flags: []cli.Flag{
&cli.StringFlag{
Name: "proving-params",
Usage: "download params used creating proofs for given size, i.e. 32GiB",
},
},
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() { sectorSizeInt, err := units.RAMInBytes(cctx.String("proving-params"))
return xerrors.Errorf("must pass sector size to fetch params for (specify as \"32GiB\", for instance)")
}
sectorSizeInt, err := units.RAMInBytes(cctx.Args().First())
if err != nil { if err != nil {
return xerrors.Errorf("error parsing sector size (specify as \"32GiB\", for instance): %w", err) return err
} }
sectorSize := uint64(sectorSizeInt) sectorSize := uint64(sectorSizeInt)
+136 -248
View File
@@ -5,14 +5,13 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"html/template"
"os"
"reflect" "reflect"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/docker/go-units"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peer"
"github.com/multiformats/go-multihash" "github.com/multiformats/go-multihash"
@@ -33,7 +32,6 @@ import (
"github.com/filecoin-project/specs-actors/actors/builtin/power" "github.com/filecoin-project/specs-actors/actors/builtin/power"
"github.com/filecoin-project/specs-actors/actors/builtin/reward" "github.com/filecoin-project/specs-actors/actors/builtin/reward"
"github.com/filecoin-project/specs-actors/actors/builtin/verifreg" "github.com/filecoin-project/specs-actors/actors/builtin/verifreg"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
"github.com/filecoin-project/specs-actors/actors/util/adt" "github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
@@ -43,7 +41,7 @@ import (
) )
type methodMeta struct { type methodMeta struct {
Name string name string
params reflect.Type params reflect.Type
ret reflect.Type ret reflect.Type
@@ -71,7 +69,7 @@ func init() {
nf := rt.NumField() nf := rt.NumField()
methods[c] = append(methods[c], methodMeta{ methods[c] = append(methods[c], methodMeta{
Name: "Send", name: "Send",
params: reflect.TypeOf(new(adt.EmptyValue)), params: reflect.TypeOf(new(adt.EmptyValue)),
ret: reflect.TypeOf(new(adt.EmptyValue)), ret: reflect.TypeOf(new(adt.EmptyValue)),
}) })
@@ -81,7 +79,7 @@ func init() {
export := reflect.TypeOf(exports[i+1]) export := reflect.TypeOf(exports[i+1])
methods[c] = append(methods[c], methodMeta{ methods[c] = append(methods[c], methodMeta{
Name: rt.Field(i).Name, name: rt.Field(i).Name,
params: export.In(1), params: export.In(1),
ret: export.Out(0), ret: export.Out(0),
}) })
@@ -147,15 +145,27 @@ var stateMinerInfo = &cli.Command{
return err return err
} }
mi, err := api.StateMinerInfo(ctx, addr, ts.Key()) act, err := api.StateGetActor(ctx, addr, ts.Key())
if err != nil { if err != nil {
return err return err
} }
aso, err := api.ChainReadObj(ctx, act.Head)
if err != nil {
return err
}
var mst miner2.State
if err := mst.UnmarshalCBOR(bytes.NewReader(aso)); err != nil {
return err
}
mi := mst.Info
fmt.Printf("Owner:\t%s\n", mi.Owner) fmt.Printf("Owner:\t%s\n", mi.Owner)
fmt.Printf("Worker:\t%s\n", mi.Worker) fmt.Printf("Worker:\t%s\n", mi.Worker)
fmt.Printf("PeerID:\t%s\n", mi.PeerId) fmt.Printf("PeerID:\t%s\n", mi.PeerId)
fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize) fmt.Printf("SectorSize:\t%s (%d)\n", units.BytesSize(float64(mi.SectorSize)), mi.SectorSize)
return nil return nil
}, },
@@ -351,7 +361,7 @@ var stateReplaySetCmd = &cli.Command{
return fmt.Errorf("message cid was invalid: %s", err) return fmt.Errorf("message cid was invalid: %s", err)
} }
fapi, closer, err := GetFullNodeAPI(cctx) api, closer, err := GetFullNodeAPI(cctx)
if err != nil { if err != nil {
return err return err
} }
@@ -373,7 +383,7 @@ var stateReplaySetCmd = &cli.Command{
if len(tscids) > 0 { if len(tscids) > 0 {
var headers []*types.BlockHeader var headers []*types.BlockHeader
for _, c := range tscids { for _, c := range tscids {
h, err := fapi.ChainGetBlock(ctx, c) h, err := api.ChainGetBlock(ctx, c)
if err != nil { if err != nil {
return err return err
} }
@@ -383,13 +393,12 @@ var stateReplaySetCmd = &cli.Command{
ts, err = types.NewTipSet(headers) ts, err = types.NewTipSet(headers)
} else { } else {
var r *api.MsgLookup r, err := api.StateWaitMsg(ctx, mcid, build.MessageConfidence)
r, err = fapi.StateWaitMsg(ctx, mcid, build.MessageConfidence)
if err != nil { if err != nil {
return xerrors.Errorf("finding message in chain: %w", err) return xerrors.Errorf("finding message in chain: %w", err)
} }
ts, err = fapi.ChainGetTipSet(ctx, r.TipSet.Parents()) ts, err = api.ChainGetTipSet(ctx, r.TipSet.Parents())
} }
if err != nil { if err != nil {
return err return err
@@ -397,7 +406,7 @@ var stateReplaySetCmd = &cli.Command{
} }
res, err := fapi.StateReplay(ctx, ts.Key(), mcid) res, err := api.StateReplay(ctx, ts.Key(), mcid)
if err != nil { if err != nil {
return xerrors.Errorf("replay call failed: %w", err) return xerrors.Errorf("replay call failed: %w", err)
} }
@@ -710,7 +719,7 @@ var stateSectorSizeCmd = &cli.Command{
return err return err
} }
fmt.Printf("%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize) fmt.Printf("%d\n", mi.SectorSize)
return nil return nil
}, },
} }
@@ -742,7 +751,12 @@ var stateReadStateCmd = &cli.Command{
return err return err
} }
as, err := api.StateReadState(ctx, addr, ts.Key()) act, err := api.StateGetActor(ctx, addr, ts.Key())
if err != nil {
return err
}
as, err := api.StateReadState(ctx, act, ts.Key())
if err != nil { if err != nil {
return err return err
} }
@@ -922,29 +936,37 @@ var stateComputeStateCmd = &cli.Command{
return c.Code, nil return c.Code, nil
} }
return computeStateHTMLTempl(ts, stout, getCode) return computeStateHtml(ts, stout, getCode)
} }
fmt.Println("computed state cid: ", stout.Root) fmt.Println("computed state cid: ", stout.Root)
if cctx.Bool("show-trace") { if cctx.Bool("show-trace") {
for _, ir := range stout.Trace { for _, ir := range stout.Trace {
fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", ir.Msg.From, ir.Msg.To, ir.Msg.Value, ir.Msg.Method, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.Return) fmt.Printf("%s\t%s\t%s\t%d\t%x\t%d\t%x\n", ir.Msg.From, ir.Msg.To, ir.Msg.Value, ir.Msg.Method, ir.Msg.Params, ir.MsgRct.ExitCode, ir.MsgRct.Return)
printInternalExecutions("\t", ir.ExecutionTrace.Subcalls) printInternalExecutions("\t", ir.InternalExecutions)
} }
} }
return nil return nil
}, },
} }
func printInternalExecutions(prefix string, trace []types.ExecutionTrace) { func printInternalExecutions(prefix string, trace []*types.ExecutionResult) {
for _, im := range trace { for _, im := range trace {
fmt.Printf("%s%s\t%s\t%s\t%d\t%x\t%d\t%x\n", prefix, im.Msg.From, im.Msg.To, im.Msg.Value, im.Msg.Method, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.Return) fmt.Printf("%s%s\t%s\t%s\t%d\t%x\t%d\t%x\n", prefix, im.Msg.From, im.Msg.To, im.Msg.Value, im.Msg.Method, im.Msg.Params, im.MsgRct.ExitCode, im.MsgRct.Return)
printInternalExecutions(prefix+"\t", im.Subcalls) printInternalExecutions(prefix+"\t", im.Subcalls)
} }
} }
var compStateTemplate = ` func codeStr(c cid.Cid) string {
<html> cmh, err := multihash.Decode(c.Hash())
if err != nil {
panic(err)
}
return string(cmh.Digest)
}
func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
fmt.Printf(`<html>
<head> <head>
<style> <style>
html, body { font-family: monospace; } html, body { font-family: monospace; }
@@ -966,259 +988,125 @@ var compStateTemplate = `
} }
.slow-true-false { color: #660; } .slow-true-false { color: #660; }
.slow-true-true { color: #f80; } .slow-true-true { color: #f80; }
.deemp { color: #444; }
table {
font-size: 12px;
border-collapse: collapse;
}
tr {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
tr.sum { border-top: 2px solid black; }
tr:first-child { border-top: none; }
tr:last-child { border-bottom: none; }
.ellipsis-content,
.ellipsis-toggle input {
display: none;
}
.ellipsis-toggle {
cursor: pointer;
}
/**
Checked State
**/
.ellipsis-toggle input:checked + .ellipsis {
display: none;
}
.ellipsis-toggle input:checked ~ .ellipsis-content {
display: inline;
background-color: #ddd;
}
hr {
border: none;
height: 1px;
background-color: black;
margin: 0;
}
</style> </style>
</head> </head>
<body> <body>
<div>Tipset: <b>{{.TipSet.Key}}</b></div> <div>Tipset: <b>%s</b></div>
<div>Epoch: {{.TipSet.Height}}</div> <div>Height: %d</div>
<div>State CID: <b>{{.Comp.Root}}</b></div> <div>State CID: <b>%s</b></div>
<div>Calls</div> <div>Calls</div>`, ts.Key(), ts.Height(), o.Root)
{{range .Comp.Trace}}
{{template "message" (Call .ExecutionTrace false .Msg.Cid.String)}}
{{end}}
</body>
</html>
`
var compStateMsg = ` for _, ir := range o.Trace {
<div class="exec" id="{{.Hash}}"> toCode, err := getCode(ir.Msg.To)
{{$code := GetCode .Msg.To}} if err != nil {
<div> return xerrors.Errorf("getting code for %s: %w", toCode, err)
<a href="#{{.Hash}}"> }
{{if not .Subcall}}
<h2 class="call">
{{else}}
<h4 class="call">
{{end}}
{{- CodeStr $code}}:{{GetMethod ($code) (.Msg.Method)}}
{{if not .Subcall}}
</h2>
{{else}}
</h4>
{{end}}
</a>
</div>
<div><b>{{.Msg.From}}</b> -&gt; <b>{{.Msg.To}}</b> ({{ToFil .Msg.Value}} FIL), M{{.Msg.Method}}</div> params, err := jsonParams(toCode, ir.Msg.Method, ir.Msg.Params)
{{if not .Subcall}}<div><small>Msg CID: {{.Msg.Cid}}</small></div>{{end}} if err != nil {
{{if gt (len .Msg.Params) 0}} return xerrors.Errorf("decoding params: %w", err)
<div><pre class="params">{{JsonParams ($code) (.Msg.Method) (.Msg.Params) | html}}</pre></div> }
{{end}}
<div><span class="slow-{{IsSlow .Duration}}-{{IsVerySlow .Duration}}">Took {{.Duration}}</span>, <span class="exit{{IntExit .MsgRct.ExitCode}}">Exit: <b>{{.MsgRct.ExitCode}}</b></span>{{if gt (len .MsgRct.Return) 0}}, Return{{end}}</div>
{{if gt (len .MsgRct.Return) 0}} if len(ir.Msg.Params) != 0 {
<div><pre class="ret">{{JsonReturn ($code) (.Msg.Method) (.MsgRct.Return) | html}}</pre></div> params = `<div><pre class="params">` + params + `</pre></div>`
{{end}} } else {
params = ""
}
{{if ne .MsgRct.ExitCode 0}} ret, err := jsonReturn(toCode, ir.Msg.Method, ir.MsgRct.Return)
<div class="error">Error: <pre>{{.Error}}</pre></div> if err != nil {
{{end}} return xerrors.Errorf("decoding return value: %w", err)
}
<details> if len(ir.MsgRct.Return) == 0 {
<summary>Gas Trace</summary> ret = "</div>"
<table> } else {
<tr><th>Name</th><th>Total/Compute/Storage</th><th>Time Taken</th><th>Location</th></tr> ret = `, Return</div><div><pre class="ret">` + ret + `</pre></div>`
{{define "virt" -}} }
{{- if . -}}
<span class="deemp">+({{.}})</span>
{{- end -}}
{{- end}}
{{define "gasC" -}} slow := ir.Duration > 10*time.Millisecond
<td>{{.TotalGas}}{{template "virt" .TotalVirtualGas }}/{{.ComputeGas}}{{template "virt" .VirtualComputeGas}}/{{.StorageGas}}{{template "virt" .VirtualStorageGas}}</td> veryslow := ir.Duration > 50*time.Millisecond
{{- end}}
{{range .GasCharges}} cid := ir.Msg.Cid()
<tr><td>{{.Name}}{{if .Extra}}:{{.Extra}}{{end}}</td>
{{template "gasC" .}}
<td>{{.TimeTaken}}</td>
<td>
{{ $fImp := FirstImportant .Location }}
{{ if $fImp }}
<details>
<summary>{{ $fImp }}</summary><hr />
{{ $elipOn := false }}
{{ range $index, $ele := .Location -}}
{{- if $index }}<br />{{end -}}
{{- if .Show -}}
{{ if $elipOn }}
{{ $elipOn = false }}
</span></label>
{{end}}
{{- if .Important }}<b>{{end -}} fmt.Printf(`<div class="exec" id="%s">
{{- . -}} <div><a href="#%s"><h2 class="call">%s:%s</h2></a></div>
{{if .Important }}</b>{{end}} <div><b>%s</b> -&gt; <b>%s</b> (%s FIL), M%d</div>
{{else}} <div><small>Msg CID: %s</small></div>
{{ if not $elipOn }} %s
{{ $elipOn = true }} <div><span class="slow-%t-%t">Took %s</span>, <span class="exit%d">Exit: <b>%d</b></span>%s
<label class="ellipsis-toggle"><input type="checkbox" /><span class="ellipsis">[]<br /></span> `, cid, cid, codeStr(toCode), methods[toCode][ir.Msg.Method].name, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, cid, params, slow, veryslow, ir.Duration, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ret)
<span class="ellipsis-content"> if ir.MsgRct.ExitCode != 0 {
{{end}} fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, ir.Error)
{{- "" -}} }
{{- . -}}
{{end}}
{{end}}
{{ if $elipOn }}
{{ $elipOn = false }}
</span></label>
{{end}}
</details>
{{end}}
</td></tr>
{{end}}
{{with SumGas .GasCharges}}
<tr class="sum"><td><b>Sum</b></td>
{{template "gasC" .}}
<td>{{.TimeTaken}}</td>
<td></td></tr>
{{end}}
</table>
</details>
if len(ir.InternalExecutions) > 0 {
{{if gt (len .Subcalls) 0}} fmt.Println("<div>Internal executions:</div>")
<div>Subcalls:</div> if err := printInternalExecutionsHtml(cid.String(), ir.InternalExecutions, getCode); err != nil {
{{$hash := .Hash}} return err
{{range .Subcalls}}
{{template "message" (Call . true (printf "%s-%s" $hash .Msg.Cid.String))}}
{{end}}
{{end}}
</div>`
type compStateHTMLIn struct {
TipSet *types.TipSet
Comp *api.ComputeStateOutput
}
func computeStateHTMLTempl(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error {
t, err := template.New("compute_state").Funcs(map[string]interface{}{
"GetCode": getCode,
"GetMethod": getMethod,
"ToFil": toFil,
"JsonParams": jsonParams,
"JsonReturn": jsonReturn,
"IsSlow": isSlow,
"IsVerySlow": isVerySlow,
"IntExit": func(i exitcode.ExitCode) int64 { return int64(i) },
"SumGas": sumGas,
"CodeStr": codeStr,
"Call": call,
"FirstImportant": func(locs []types.Loc) *types.Loc {
if len(locs) != 0 {
for _, l := range locs {
if l.Important() {
return &l
}
}
return &locs[0]
} }
return nil }
}, fmt.Println("</div>")
}).Parse(compStateTemplate)
if err != nil {
return err
}
t, err = t.New("message").Parse(compStateMsg)
if err != nil {
return err
} }
return t.ExecuteTemplate(os.Stdout, "compute_state", &compStateHTMLIn{ fmt.Printf(`</body>
TipSet: ts, </html>`)
Comp: o, return nil
})
} }
type callMeta struct { func printInternalExecutionsHtml(hashName string, trace []*types.ExecutionResult, getCode func(addr address.Address) (cid.Cid, error)) error {
types.ExecutionTrace for i, im := range trace {
Subcall bool hashName := fmt.Sprintf("%s-r%d", hashName, i)
Hash string
}
func call(e types.ExecutionTrace, subcall bool, hash string) callMeta { toCode, err := getCode(im.Msg.To)
return callMeta{ if err != nil {
ExecutionTrace: e, return xerrors.Errorf("getting code for %s: %w", toCode, err)
Subcall: subcall, }
Hash: hash,
}
}
func codeStr(c cid.Cid) string { params, err := jsonParams(toCode, im.Msg.Method, im.Msg.Params)
cmh, err := multihash.Decode(c.Hash()) if err != nil {
if err != nil { return xerrors.Errorf("decoding params: %w", err)
panic(err) }
}
return string(cmh.Digest)
}
func getMethod(code cid.Cid, method abi.MethodNum) string { if len(im.Msg.Params) != 0 {
return methods[code][method].Name params = `<div><pre class="params">` + params + `</pre></div>`
} } else {
params = ""
}
func toFil(f types.BigInt) types.FIL { ret, err := jsonReturn(toCode, im.Msg.Method, im.MsgRct.Return)
return types.FIL(f) if err != nil {
} return xerrors.Errorf("decoding return value: %w", err)
}
func isSlow(t time.Duration) bool { if len(im.MsgRct.Return) == 0 {
return t > 10*time.Millisecond ret = "</div>"
} } else {
ret = `, Return</div><div><pre class="ret">` + ret + `</pre></div>`
}
func isVerySlow(t time.Duration) bool { slow := im.Duration > 10*time.Millisecond
return t > 50*time.Millisecond veryslow := im.Duration > 50*time.Millisecond
}
func sumGas(changes []*types.GasTrace) types.GasTrace { fmt.Printf(`<div class="exec" id="%s">
var out types.GasTrace <div><a href="#%s"><h4 class="call">%s:%s</h4></a></div>
for _, gc := range changes { <div><b>%s</b> -&gt; <b>%s</b> (%s FIL), M%d</div>
out.TotalGas += gc.TotalGas %s
out.ComputeGas += gc.ComputeGas <div><span class="slow-%t-%t">Took %s</span>, <span class="exit%d">Exit: <b>%d</b></span>%s
out.StorageGas += gc.StorageGas `, hashName, hashName, 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 {
out.TotalVirtualGas += gc.TotalVirtualGas fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, im.Error)
out.VirtualComputeGas += gc.VirtualComputeGas }
out.VirtualStorageGas += gc.VirtualStorageGas if len(im.Subcalls) > 0 {
fmt.Println("<div>Subcalls:</div>")
if err := printInternalExecutionsHtml(hashName, im.Subcalls, getCode); err != nil {
return err
}
}
fmt.Println("</div>")
} }
return out return nil
} }
func jsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error) { func jsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error) {
+1 -1
View File
@@ -186,7 +186,7 @@ func SyncWait(ctx context.Context, napi api.FullNode) error {
fmt.Printf("\r\x1b[2KWorker %d: Target: %s\tState: %s\tHeight: %d", working, target, chain.SyncStageString(ss.Stage), ss.Height) fmt.Printf("\r\x1b[2KWorker %d: Target: %s\tState: %s\tHeight: %d", working, target, chain.SyncStageString(ss.Stage), ss.Height)
if time.Now().Unix()-int64(head.MinTimestamp()) < int64(build.BlockDelaySecs) { if time.Now().Unix()-int64(head.MinTimestamp()) < build.BlockDelay {
fmt.Println("\nDone!") fmt.Println("\nDone!")
return nil return nil
} }
+1 -6
View File
@@ -113,12 +113,7 @@ var walletBalance = &cli.Command{
return err return err
} }
if balance.Equals(types.NewInt(0)) { fmt.Printf("%s\n", types.FIL(balance))
fmt.Printf("%s (warning: may display 0 if chain sync in progress)\n", types.FIL(balance))
} else {
fmt.Printf("%s\n", types.FIL(balance))
}
return nil return nil
}, },
} }
+26 -437
View File
@@ -1,17 +1,11 @@
package main package main
import ( import (
"bufio"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"math"
"net/http"
_ "net/http/pprof"
"os" "os"
"runtime"
"runtime/pprof" "runtime/pprof"
"sort" "sort"
"time" "time"
@@ -50,14 +44,8 @@ var importBenchCmd = &cli.Command{
Name: "height", Name: "height",
Usage: "halt validation after given height", Usage: "halt validation after given height",
}, },
&cli.IntFlag{
Name: "batch-seal-verify-threads",
Usage: "set the parallelism factor for batch seal verification",
Value: runtime.NumCPU(),
},
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
vm.BatchSealVerifyParallelism = cctx.Int("batch-seal-verify-threads")
if !cctx.Args().Present() { if !cctx.Args().Present() {
fmt.Println("must pass car file of chain to benchmark importing") fmt.Println("must pass car file of chain to benchmark importing")
return nil return nil
@@ -123,22 +111,14 @@ var importBenchCmd = &cli.Command{
ts = next ts = next
} }
ibj, err := os.Create("import-bench.json") out := make([]TipSetExec, 0, len(tschain))
if err != nil {
return err
}
defer ibj.Close() //nolint:errcheck
enc := json.NewEncoder(ibj)
var lastTse *TipSetExec
lastState := tschain[len(tschain)-1].ParentState() lastState := tschain[len(tschain)-1].ParentState()
for i := len(tschain) - 2; i >= 0; i-- { for i := len(tschain) - 2; i >= 0; i-- {
cur := tschain[i] cur := tschain[i]
log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids()) log.Infof("computing state (height: %d, ts=%s)", cur.Height(), cur.Cids())
if cur.ParentState() != lastState { if cur.ParentState() != lastState {
lastTrace := lastTse.Trace lastTrace := out[len(out)-1].Trace
d, err := json.MarshalIndent(lastTrace, "", " ") d, err := json.MarshalIndent(lastTrace, "", " ")
if err != nil { if err != nil {
panic(err) panic(err)
@@ -152,301 +132,36 @@ var importBenchCmd = &cli.Command{
if err != nil { if err != nil {
return err return err
} }
stripCallers(trace) out = append(out, TipSetExec{
lastTse = &TipSetExec{
TipSet: cur.Key(), TipSet: cur.Key(),
Trace: trace, Trace: trace,
Duration: time.Since(start), Duration: time.Since(start),
} })
lastState = st lastState = st
if err := enc.Encode(lastTse); err != nil {
return xerrors.Errorf("failed to write out tipsetexec: %w", err)
}
} }
pprof.StopCPUProfile() pprof.StopCPUProfile()
ibj, err := os.Create("import-bench.json")
if err != nil {
return err
}
defer ibj.Close() //nolint:errcheck
if err := json.NewEncoder(ibj).Encode(out); err != nil {
return err
}
return nil return nil
}, },
} }
func walkExecutionTrace(et *types.ExecutionTrace) {
for _, gc := range et.GasCharges {
gc.Callers = nil
}
for _, sub := range et.Subcalls {
walkExecutionTrace(&sub) //nolint:scopelint,gosec
}
}
func stripCallers(trace []*api.InvocResult) {
for _, t := range trace {
walkExecutionTrace(&t.ExecutionTrace)
}
}
type Invocation struct { type Invocation struct {
TipSet types.TipSetKey TipSet types.TipSetKey
Invoc *api.InvocResult Invoc *api.InvocResult
} }
const GasPerNs = 10
func countGasCosts(et *types.ExecutionTrace) (int64, int64) {
var cgas, vgas int64
for _, gc := range et.GasCharges {
cgas += gc.ComputeGas
vgas += gc.VirtualComputeGas
}
for _, sub := range et.Subcalls {
c, v := countGasCosts(&sub)
cgas += c
vgas += v
}
return cgas, vgas
}
func compStats(vals []float64) (float64, float64) {
var sum float64
for _, v := range vals {
sum += v
}
av := sum / float64(len(vals))
var varsum float64
for _, v := range vals {
delta := av - v
varsum += delta * delta
}
return av, math.Sqrt(varsum / float64(len(vals)))
}
type stats struct {
timeTaken meanVar
gasRatio meanVar
extraCovar *covar
}
type covar struct {
meanX float64
meanY float64
c float64
n float64
m2x float64
m2y float64
}
func (cov1 *covar) Covariance() float64 {
return cov1.c / (cov1.n - 1)
}
func (cov1 *covar) VarianceX() float64 {
return cov1.m2x / (cov1.n - 1)
}
func (v1 *covar) StddevX() float64 {
return math.Sqrt(v1.VarianceX())
}
func (cov1 *covar) VarianceY() float64 {
return cov1.m2y / (cov1.n - 1)
}
func (v1 *covar) StddevY() float64 {
return math.Sqrt(v1.VarianceY())
}
func (cov1 *covar) AddPoint(x, y float64) {
cov1.n += 1
dx := x - cov1.meanX
cov1.meanX += dx / cov1.n
dx2 := x - cov1.meanX
cov1.m2x += dx * dx2
dy := y - cov1.meanY
cov1.meanY += dy / cov1.n
dy2 := y - cov1.meanY
cov1.m2y += dy * dy2
cov1.c += dx * dy
}
func (cov1 *covar) Combine(cov2 *covar) {
if cov1.n == 0 {
*cov1 = *cov2
return
}
if cov2.n == 0 {
return
}
if cov1.n == 1 {
cpy := *cov2
cpy.AddPoint(cov2.meanX, cov2.meanY)
*cov1 = cpy
return
}
if cov2.n == 1 {
cov1.AddPoint(cov2.meanX, cov2.meanY)
}
out := covar{}
out.n = cov1.n + cov2.n
dx := cov1.meanX - cov2.meanX
out.meanX = cov1.meanX - dx*cov2.n/out.n
out.m2x = cov1.m2x + cov2.m2x + dx*dx*cov1.n*cov2.n/out.n
dy := cov1.meanY - cov2.meanY
out.meanY = cov1.meanY - dy*cov2.n/out.n
out.m2y = cov1.m2y + cov2.m2y + dy*dy*cov1.n*cov2.n/out.n
out.c = cov1.c + cov2.c + dx*dy*cov1.n*cov2.n/out.n
*cov1 = out
}
func (cov1 *covar) A() float64 {
return cov1.Covariance() / cov1.VarianceX()
}
func (cov1 *covar) B() float64 {
return cov1.meanY - cov1.meanX*cov1.A()
}
func (cov1 *covar) Correl() float64 {
return cov1.Covariance() / cov1.StddevX() / cov1.StddevY()
}
type meanVar struct {
n float64
mean float64
m2 float64
}
func (v1 *meanVar) AddPoint(value float64) {
// based on https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
v1.n += 1
delta := value - v1.mean
v1.mean += delta / v1.n
delta2 := value - v1.mean
v1.m2 += delta * delta2
}
func (v1 *meanVar) Variance() float64 {
return v1.m2 / (v1.n - 1)
}
func (v1 *meanVar) Mean() float64 {
return v1.mean
}
func (v1 *meanVar) Stddev() float64 {
return math.Sqrt(v1.Variance())
}
func (v1 *meanVar) Combine(v2 *meanVar) {
if v1.n == 0 {
*v1 = *v2
return
}
if v2.n == 0 {
return
}
if v1.n == 1 {
cpy := *v2
cpy.AddPoint(v1.mean)
*v1 = cpy
return
}
if v2.n == 1 {
v1.AddPoint(v2.mean)
return
}
newCount := v1.n + v2.n
delta := v2.mean - v1.mean
meanDelta := delta * v2.n / newCount
m2 := v1.m2 + v2.m2 + delta*meanDelta*v1.n
v1.n = newCount
v1.mean += meanDelta
v1.m2 = m2
}
func getExtras(ex interface{}) (*string, *float64) {
if t, ok := ex.(string); ok {
return &t, nil
}
if size, ok := ex.(float64); ok {
return nil, &size
}
if exMap, ok := ex.(map[string]interface{}); ok {
t, tok := exMap["type"].(string)
size, sok := exMap["size"].(float64)
if tok && sok {
return &t, &size
}
if tok {
return &t, nil
}
if sok {
return nil, &size
}
return nil, nil
}
return nil, nil
}
func tallyGasCharges(charges map[string]*stats, et types.ExecutionTrace) {
for i, gc := range et.GasCharges {
name := gc.Name
if name == "OnIpldGetStart" {
continue
}
tt := float64(gc.TimeTaken.Nanoseconds())
if name == "OnIpldGet" {
prev := et.GasCharges[i-1]
if prev.Name != "OnIpldGetStart" {
log.Warn("OnIpldGet without OnIpldGetStart")
}
tt += float64(prev.TimeTaken.Nanoseconds())
}
eType, eSize := getExtras(gc.Extra)
if eType != nil {
name += "-" + *eType
}
compGas := gc.VirtualComputeGas
if compGas == 0 {
compGas = 1
}
s := charges[name]
if s == nil {
s = new(stats)
charges[name] = s
}
if eSize != nil {
if s.extraCovar == nil {
s.extraCovar = &covar{}
}
s.extraCovar.AddPoint(*eSize, tt)
}
s.timeTaken.AddPoint(tt)
ratio := tt / float64(compGas) * GasPerNs
s.gasRatio.AddPoint(ratio)
}
for _, sub := range et.Subcalls {
tallyGasCharges(charges, sub)
}
}
var importAnalyzeCmd = &cli.Command{ var importAnalyzeCmd = &cli.Command{
Name: "analyze", Name: "analyze",
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
@@ -455,142 +170,27 @@ var importAnalyzeCmd = &cli.Command{
return nil return nil
} }
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
fi, err := os.Open(cctx.Args().First()) fi, err := os.Open(cctx.Args().First())
if err != nil { if err != nil {
return err return err
} }
const nWorkers = 16 var results []TipSetExec
jsonIn := make(chan []byte, 2*nWorkers) if err := json.NewDecoder(fi).Decode(&results); err != nil {
type result struct { return err
totalTime time.Duration
chargeStats map[string]*stats
expensiveInvocs []Invocation
} }
results := make(chan result, nWorkers)
for i := 0; i < nWorkers; i++ {
go func() {
chargeStats := make(map[string]*stats)
var totalTime time.Duration
const invocsKeep = 32
var expensiveInvocs = make([]Invocation, 0, 8*invocsKeep)
var leastExpensiveInvoc = time.Duration(0)
for {
b, ok := <-jsonIn
if !ok {
results <- result{
totalTime: totalTime,
chargeStats: chargeStats,
expensiveInvocs: expensiveInvocs,
}
return
}
var tse TipSetExec
err := json.Unmarshal(b, &tse)
if err != nil {
log.Warnf("error unmarshaling tipset: %+v", err)
continue
}
totalTime += tse.Duration
for _, inv := range tse.Trace {
if inv.Duration > leastExpensiveInvoc {
expensiveInvocs = append(expensiveInvocs, Invocation{
TipSet: tse.TipSet,
Invoc: inv,
})
}
tallyGasCharges(chargeStats, inv.ExecutionTrace)
}
if len(expensiveInvocs) > 4*invocsKeep {
sort.Slice(expensiveInvocs, func(i, j int) bool {
return expensiveInvocs[i].Invoc.Duration > expensiveInvocs[j].Invoc.Duration
})
leastExpensiveInvoc = expensiveInvocs[len(expensiveInvocs)-1].Invoc.Duration
n := 30
if len(expensiveInvocs) < n {
n = len(expensiveInvocs)
}
expensiveInvocs = expensiveInvocs[:n]
}
}
}()
}
var totalTipsets int64
reader := bufio.NewReader(fi)
for {
b, err := reader.ReadBytes('\n')
if err != nil && err != io.EOF {
if e, ok := err.(*json.SyntaxError); ok {
log.Warnf("syntax error at byte offset %d", e.Offset)
}
return err
}
totalTipsets++
jsonIn <- b
fmt.Fprintf(os.Stderr, "\rProcessed %d tipsets", totalTipsets)
if err == io.EOF {
break
}
}
close(jsonIn)
fmt.Fprintf(os.Stderr, "\n")
fmt.Fprintf(os.Stderr, "Collecting results\n")
var invocs []Invocation var invocs []Invocation
var totalTime time.Duration var totalTime time.Duration
var keys []string for i, r := range results {
var charges = make(map[string]*stats) _ = i
for i := 0; i < nWorkers; i++ { totalTime += r.Duration
fmt.Fprintf(os.Stderr, "\rProcessing results from worker %d/%d", i+1, nWorkers)
res := <-results
invocs = append(invocs, res.expensiveInvocs...)
for k, v := range res.chargeStats {
s := charges[k]
if s == nil {
s = new(stats)
charges[k] = s
}
s.timeTaken.Combine(&v.timeTaken)
s.gasRatio.Combine(&v.gasRatio)
if v.extraCovar != nil { for _, inv := range r.Trace {
if s.extraCovar == nil { invocs = append(invocs, Invocation{
s.extraCovar = &covar{} TipSet: r.TipSet,
} Invoc: inv,
s.extraCovar.Combine(v.extraCovar) })
}
}
totalTime += res.totalTime
}
fmt.Fprintf(os.Stderr, "\nCollecting gas keys\n")
for k := range charges {
keys = append(keys, k)
}
fmt.Println("Gas Price Deltas")
sort.Strings(keys)
for _, k := range keys {
s := charges[k]
fmt.Printf("%s: incr by %.4f~%.4f; tt %.4f~%.4f\n", k, s.gasRatio.Mean(), s.gasRatio.Stddev(),
s.timeTaken.Mean(), s.timeTaken.Stddev())
if s.extraCovar != nil {
fmt.Printf("\t correll: %.2f, tt = %.2f * extra + %.2f\n", s.extraCovar.Correl(),
s.extraCovar.A(), s.extraCovar.B())
fmt.Printf("\t covar: %.2f, extra: %.2f~%.2f, tt2: %.2f~%.2f, count %.0f\n",
s.extraCovar.Covariance(), s.extraCovar.meanX, s.extraCovar.StddevX(),
s.extraCovar.meanY, s.extraCovar.StddevY(), s.extraCovar.n)
} }
} }
@@ -599,20 +199,9 @@ var importAnalyzeCmd = &cli.Command{
}) })
fmt.Println("Total time: ", totalTime) fmt.Println("Total time: ", totalTime)
fmt.Println("Average time per epoch: ", totalTime/time.Duration(totalTipsets)) fmt.Println("Average time per epoch: ", totalTime/time.Duration(len(results)))
if actorExec, ok := charges["OnActorExec"]; ok {
timeInActors := actorExec.timeTaken.Mean() * actorExec.timeTaken.n
fmt.Printf("Avarage time per epoch in actors: %s (%.1f%%)\n", time.Duration(timeInActors)/time.Duration(totalTipsets), timeInActors/float64(totalTime)*100)
}
if actorExecDone, ok := charges["OnActorExecDone"]; ok {
timeInActors := actorExecDone.timeTaken.Mean() * actorExecDone.timeTaken.n
fmt.Printf("Avarage time per epoch in OnActorExecDone %s (%.1f%%)\n", time.Duration(timeInActors)/time.Duration(totalTipsets), timeInActors/float64(totalTime)*100)
}
n := 30 n := 30
if len(invocs) < n {
n = len(invocs)
}
fmt.Printf("Top %d most expensive calls:\n", n) fmt.Printf("Top %d most expensive calls:\n", n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
inv := invocs[i].Invoc inv := invocs[i].Invoc
+136 -208
View File
@@ -74,7 +74,7 @@ func main() {
log.Info("Starting lotus-bench") log.Info("Starting lotus-bench")
miner.SupportedProofTypes[abi.RegisteredSealProof_StackedDrg2KiBV1] = struct{}{} miner.SupportedProofTypes[abi.RegisteredProof_StackedDRG2KiBSeal] = struct{}{}
app := &cli.App{ app := &cli.App{
Name: "lotus-bench", Name: "lotus-bench",
@@ -139,10 +139,6 @@ var sealBenchCmd = &cli.Command{
Name: "num-sectors", Name: "num-sectors",
Value: 1, Value: 1,
}, },
&cli.IntFlag{
Name: "parallel",
Value: 1,
},
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.Bool("no-gpu") { if c.Bool("no-gpu") {
@@ -239,12 +235,7 @@ var sealBenchCmd = &cli.Command{
if robench == "" { if robench == "" {
var err error var err error
parCfg := ParCfg{ sealTimings, sealedSectors, err = runSeals(sb, sbfs, c.Int("num-sectors"), mid, sectorSize, []byte(c.String("ticket-preimage")), c.String("save-commit2-input"), c.Bool("skip-commit2"), c.Bool("skip-unseal"))
PreCommit1: c.Int("parallel"),
PreCommit2: 1,
Commit: 1,
}
sealTimings, sealedSectors, err = runSeals(sb, sbfs, c.Int("num-sectors"), parCfg, mid, sectorSize, []byte(c.String("ticket-preimage")), c.String("save-commit2-input"), c.Bool("skip-commit2"), c.Bool("skip-unseal"))
if err != nil { if err != nil {
return xerrors.Errorf("failed to run seals: %w", err) return xerrors.Errorf("failed to run seals: %w", err)
} }
@@ -279,9 +270,9 @@ var sealBenchCmd = &cli.Command{
for _, s := range genm.Sectors { for _, s := range genm.Sectors {
sealedSectors = append(sealedSectors, abi.SectorInfo{ sealedSectors = append(sealedSectors, abi.SectorInfo{
SealedCID: s.CommR, SealedCID: s.CommR,
SectorNumber: s.SectorID, SectorNumber: s.SectorID,
SealProof: s.ProofType, RegisteredProof: s.ProofType,
}) })
} }
} }
@@ -293,12 +284,7 @@ var sealBenchCmd = &cli.Command{
if !c.Bool("skip-commit2") { if !c.Bool("skip-commit2") {
log.Info("generating winning post candidates") log.Info("generating winning post candidates")
wipt, err := spt.RegisteredWinningPoStProof() fcandidates, err := ffiwrapper.ProofVerifier.GenerateWinningPoStSectorChallenge(context.TODO(), spt, mid, challenge[:], uint64(len(sealedSectors)))
if err != nil {
return err
}
fcandidates, err := ffiwrapper.ProofVerifier.GenerateWinningPoStSectorChallenge(context.TODO(), wipt, mid, challenge[:], uint64(len(sealedSectors)))
if err != nil { if err != nil {
return err return err
} }
@@ -316,7 +302,7 @@ var sealBenchCmd = &cli.Command{
return err return err
} }
winningpost1 := time.Now() winnnigpost1 := time.Now()
log.Info("computing winning post snark (hot)") log.Info("computing winning post snark (hot)")
proof2, err := sb.GenerateWinningPoSt(context.TODO(), mid, candidates, challenge[:]) proof2, err := sb.GenerateWinningPoSt(context.TODO(), mid, candidates, challenge[:])
@@ -340,7 +326,7 @@ var sealBenchCmd = &cli.Command{
log.Error("post verification failed") log.Error("post verification failed")
} }
verifyWinningPost1 := time.Now() verifyWinnnigPost1 := time.Now()
pvi2 := abi.WinningPoStVerifyInfo{ pvi2 := abi.WinningPoStVerifyInfo{
Randomness: abi.PoStRandomness(challenge[:]), Randomness: abi.PoStRandomness(challenge[:]),
@@ -359,7 +345,7 @@ var sealBenchCmd = &cli.Command{
verifyWinningPost2 := time.Now() verifyWinningPost2 := time.Now()
log.Info("computing window post snark (cold)") log.Info("computing window post snark (cold)")
wproof1, _, err := sb.GenerateWindowPoSt(context.TODO(), mid, sealedSectors, challenge[:]) wproof1, err := sb.GenerateWindowPoSt(context.TODO(), mid, sealedSectors, challenge[:])
if err != nil { if err != nil {
return err return err
} }
@@ -367,7 +353,7 @@ var sealBenchCmd = &cli.Command{
windowpost1 := time.Now() windowpost1 := time.Now()
log.Info("computing window post snark (hot)") log.Info("computing window post snark (hot)")
wproof2, _, err := sb.GenerateWindowPoSt(context.TODO(), mid, sealedSectors, challenge[:]) wproof2, err := sb.GenerateWindowPoSt(context.TODO(), mid, sealedSectors, challenge[:])
if err != nil { if err != nil {
return err return err
} }
@@ -407,10 +393,10 @@ var sealBenchCmd = &cli.Command{
verifyWindowpost2 := time.Now() verifyWindowpost2 := time.Now()
bo.PostGenerateCandidates = gencandidates.Sub(beforePost) bo.PostGenerateCandidates = gencandidates.Sub(beforePost)
bo.PostWinningProofCold = winningpost1.Sub(gencandidates) bo.PostWinningProofCold = winnnigpost1.Sub(gencandidates)
bo.PostWinningProofHot = winnningpost2.Sub(winningpost1) bo.PostWinningProofHot = winnningpost2.Sub(winnnigpost1)
bo.VerifyWinningPostCold = verifyWinningPost1.Sub(winnningpost2) bo.VerifyWinningPostCold = verifyWinnnigPost1.Sub(winnningpost2)
bo.VerifyWinningPostHot = verifyWinningPost2.Sub(verifyWinningPost1) bo.VerifyWinningPostHot = verifyWinningPost2.Sub(verifyWinnnigPost1)
bo.PostWindowProofCold = windowpost1.Sub(verifyWinningPost2) bo.PostWindowProofCold = windowpost1.Sub(verifyWinningPost2)
bo.PostWindowProofHot = windowpost2.Sub(windowpost1) bo.PostWindowProofHot = windowpost2.Sub(windowpost1)
@@ -426,7 +412,7 @@ var sealBenchCmd = &cli.Command{
fmt.Println(string(data)) fmt.Println(string(data))
} else { } else {
fmt.Printf("----\nresults (v27) (%d)\n", sectorSize) fmt.Printf("----\nresults (v26) (%d)\n", sectorSize)
if robench == "" { if robench == "" {
fmt.Printf("seal: addPiece: %s (%s)\n", bo.SealingResults[0].AddPiece, bps(bo.SectorSize, bo.SealingResults[0].AddPiece)) // TODO: average across multiple sealings fmt.Printf("seal: addPiece: %s (%s)\n", bo.SealingResults[0].AddPiece, bps(bo.SectorSize, bo.SealingResults[0].AddPiece)) // TODO: average across multiple sealings
fmt.Printf("seal: preCommit phase 1: %s (%s)\n", bo.SealingResults[0].PreCommit1, bps(bo.SectorSize, bo.SealingResults[0].PreCommit1)) fmt.Printf("seal: preCommit phase 1: %s (%s)\n", bo.SealingResults[0].PreCommit1, bps(bo.SectorSize, bo.SealingResults[0].PreCommit1))
@@ -441,10 +427,10 @@ var sealBenchCmd = &cli.Command{
} }
if !c.Bool("skip-commit2") { if !c.Bool("skip-commit2") {
fmt.Printf("generate candidates: %s (%s)\n", bo.PostGenerateCandidates, bps(bo.SectorSize*abi.SectorSize(len(bo.SealingResults)), bo.PostGenerateCandidates)) fmt.Printf("generate candidates: %s (%s)\n", bo.PostGenerateCandidates, bps(bo.SectorSize*abi.SectorSize(len(bo.SealingResults)), bo.PostGenerateCandidates))
fmt.Printf("compute winning post proof (cold): %s\n", bo.PostWinningProofCold) fmt.Printf("compute winnnig post proof (cold): %s\n", bo.PostWinningProofCold)
fmt.Printf("compute winning post proof (hot): %s\n", bo.PostWinningProofHot) fmt.Printf("compute winnnig post proof (hot): %s\n", bo.PostWinningProofHot)
fmt.Printf("verify winning post proof (cold): %s\n", bo.VerifyWinningPostCold) fmt.Printf("verify winnnig post proof (cold): %s\n", bo.VerifyWinningPostCold)
fmt.Printf("verify winning post proof (hot): %s\n\n", bo.VerifyWinningPostHot) fmt.Printf("verify winnnig post proof (hot): %s\n\n", bo.VerifyWinningPostHot)
fmt.Printf("compute window post proof (cold): %s\n", bo.PostWindowProofCold) fmt.Printf("compute window post proof (cold): %s\n", bo.PostWindowProofCold)
fmt.Printf("compute window post proof (hot): %s\n", bo.PostWindowProofHot) fmt.Printf("compute window post proof (hot): %s\n", bo.PostWindowProofHot)
@@ -456,23 +442,9 @@ var sealBenchCmd = &cli.Command{
}, },
} }
type ParCfg struct { func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, mid abi.ActorID, sectorSize abi.SectorSize, ticketPreimage []byte, saveC2inp string, skipc2, skipunseal bool) ([]SealingResult, []abi.SectorInfo, error) {
PreCommit1 int var sealTimings []SealingResult
PreCommit2 int var sealedSectors []abi.SectorInfo
Commit int
}
func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par ParCfg, mid abi.ActorID, sectorSize abi.SectorSize, ticketPreimage []byte, saveC2inp string, skipc2, skipunseal bool) ([]SealingResult, []abi.SectorInfo, error) {
var pieces []abi.PieceInfo
sealTimings := make([]SealingResult, numSectors)
sealedSectors := make([]abi.SectorInfo, numSectors)
preCommit2Sema := make(chan struct{}, par.PreCommit2)
commitSema := make(chan struct{}, par.Commit)
if numSectors%par.PreCommit1 != 0 {
return nil, nil, fmt.Errorf("parallelism factor must cleanly divide numSectors")
}
for i := abi.SectorNumber(1); i <= abi.SectorNumber(numSectors); i++ { for i := abi.SectorNumber(1); i <= abi.SectorNumber(numSectors); i++ {
sid := abi.SectorID{ sid := abi.SectorID{
@@ -481,7 +453,7 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
} }
start := time.Now() start := time.Now()
log.Infof("[%d] Writing piece into sector...", i) log.Info("Writing piece into sector...")
r := rand.New(rand.NewSource(100 + int64(i))) r := rand.New(rand.NewSource(100 + int64(i)))
@@ -490,168 +462,129 @@ func runSeals(sb *ffiwrapper.Sealer, sbfs *basicfs.Provider, numSectors int, par
return nil, nil, err return nil, nil, err
} }
pieces = append(pieces, pi) addpiece := time.Now()
sealTimings[i-1].AddPiece = time.Since(start) trand := blake2b.Sum256(ticketPreimage)
} ticket := abi.SealRandomness(trand[:])
sectorsPerWorker := numSectors / par.PreCommit1 log.Info("Running replication(1)...")
pieces := []abi.PieceInfo{pi}
pc1o, err := sb.SealPreCommit1(context.TODO(), sid, ticket, pieces)
if err != nil {
return nil, nil, xerrors.Errorf("commit: %w", err)
}
errs := make(chan error, par.PreCommit1) precommit1 := time.Now()
for wid := 0; wid < par.PreCommit1; wid++ {
go func(worker int) {
sealerr := func() error {
start := 1 + (worker * sectorsPerWorker)
end := start + sectorsPerWorker
for i := abi.SectorNumber(start); i < abi.SectorNumber(end); i++ {
ix := int(i - 1)
sid := abi.SectorID{
Miner: mid,
Number: i,
}
start := time.Now() log.Info("Running replication(2)...")
cids, err := sb.SealPreCommit2(context.TODO(), sid, pc1o)
if err != nil {
return nil, nil, xerrors.Errorf("commit: %w", err)
}
trand := blake2b.Sum256(ticketPreimage) precommit2 := time.Now()
ticket := abi.SealRandomness(trand[:])
log.Infof("[%d] Running replication(1)...", i) sealedSectors = append(sealedSectors, abi.SectorInfo{
pieces := []abi.PieceInfo{pieces[ix]} RegisteredProof: sb.SealProofType(),
pc1o, err := sb.SealPreCommit1(context.TODO(), sid, ticket, pieces) SectorNumber: i,
if err != nil { SealedCID: cids.Sealed,
return xerrors.Errorf("commit: %w", err) })
}
precommit1 := time.Now() seed := lapi.SealSeed{
Epoch: 101,
Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 255},
}
preCommit2Sema <- struct{}{} log.Info("Generating PoRep for sector (1)")
pc2Start := time.Now() c1o, err := sb.SealCommit1(context.TODO(), sid, ticket, seed.Value, pieces, cids)
log.Infof("[%d] Running replication(2)...", i)
cids, err := sb.SealPreCommit2(context.TODO(), sid, pc1o)
if err != nil {
return xerrors.Errorf("commit: %w", err)
}
precommit2 := time.Now()
<-preCommit2Sema
sealedSectors[ix] = abi.SectorInfo{
SealProof: sb.SealProofType(),
SectorNumber: i,
SealedCID: cids.Sealed,
}
seed := lapi.SealSeed{
Epoch: 101,
Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 255},
}
commitSema <- struct{}{}
commitStart := time.Now()
log.Infof("[%d] Generating PoRep for sector (1)", i)
c1o, err := sb.SealCommit1(context.TODO(), sid, ticket, seed.Value, pieces, cids)
if err != nil {
return err
}
sealcommit1 := time.Now()
log.Infof("[%d] Generating PoRep for sector (2)", i)
if saveC2inp != "" {
c2in := Commit2In{
SectorNum: int64(i),
Phase1Out: c1o,
SectorSize: uint64(sectorSize),
}
b, err := json.Marshal(&c2in)
if err != nil {
return err
}
if err := ioutil.WriteFile(saveC2inp, b, 0664); err != nil {
log.Warnf("%+v", err)
}
}
var proof storage.Proof
if !skipc2 {
proof, err = sb.SealCommit2(context.TODO(), sid, c1o)
if err != nil {
return err
}
}
sealcommit2 := time.Now()
<-commitSema
if !skipc2 {
svi := abi.SealVerifyInfo{
SectorID: abi.SectorID{Miner: mid, Number: i},
SealedCID: cids.Sealed,
SealProof: sb.SealProofType(),
Proof: proof,
DealIDs: nil,
Randomness: ticket,
InteractiveRandomness: seed.Value,
UnsealedCID: cids.Unsealed,
}
ok, err := ffiwrapper.ProofVerifier.VerifySeal(svi)
if err != nil {
return err
}
if !ok {
return xerrors.Errorf("porep proof for sector %d was invalid", i)
}
}
verifySeal := time.Now()
if !skipunseal {
log.Infof("[%d] Unsealing sector", i)
{
p, done, err := sbfs.AcquireSector(context.TODO(), abi.SectorID{Miner: mid, Number: 1}, stores.FTUnsealed, stores.FTNone, true)
if err != nil {
return xerrors.Errorf("acquire unsealed sector for removing: %w", err)
}
done()
if err := os.Remove(p.Unsealed); err != nil {
return xerrors.Errorf("removing unsealed sector: %w", err)
}
}
err := sb.UnsealPiece(context.TODO(), abi.SectorID{Miner: mid, Number: 1}, 0, abi.PaddedPieceSize(sectorSize).Unpadded(), ticket, cids.Unsealed)
if err != nil {
return err
}
}
unseal := time.Now()
sealTimings[ix].PreCommit1 = precommit1.Sub(start)
sealTimings[ix].PreCommit2 = precommit2.Sub(pc2Start)
sealTimings[ix].Commit1 = sealcommit1.Sub(commitStart)
sealTimings[ix].Commit2 = sealcommit2.Sub(sealcommit1)
sealTimings[ix].Verify = verifySeal.Sub(sealcommit2)
sealTimings[ix].Unseal = unseal.Sub(verifySeal)
}
return nil
}()
if sealerr != nil {
errs <- sealerr
return
}
errs <- nil
}(wid)
}
for i := 0; i < par.PreCommit1; i++ {
err := <-errs
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
sealcommit1 := time.Now()
log.Info("Generating PoRep for sector (2)")
if saveC2inp != "" {
c2in := Commit2In{
SectorNum: int64(i),
Phase1Out: c1o,
SectorSize: uint64(sectorSize),
}
b, err := json.Marshal(&c2in)
if err != nil {
return nil, nil, err
}
if err := ioutil.WriteFile(saveC2inp, b, 0664); err != nil {
log.Warnf("%+v", err)
}
}
var proof storage.Proof
if !skipc2 {
proof, err = sb.SealCommit2(context.TODO(), sid, c1o)
if err != nil {
return nil, nil, err
}
}
sealcommit2 := time.Now()
if !skipc2 {
svi := abi.SealVerifyInfo{
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,
}
ok, err := ffiwrapper.ProofVerifier.VerifySeal(svi)
if err != nil {
return nil, nil, err
}
if !ok {
return nil, nil, xerrors.Errorf("porep proof for sector %d was invalid", i)
}
}
verifySeal := time.Now()
if !skipunseal {
log.Info("Unsealing sector")
{
p, done, err := sbfs.AcquireSector(context.TODO(), abi.SectorID{Miner: mid, Number: 1}, stores.FTUnsealed, stores.FTNone, true)
if err != nil {
return nil, nil, xerrors.Errorf("acquire unsealed sector for removing: %w", err)
}
done()
if err := os.Remove(p.Unsealed); err != nil {
return nil, nil, xerrors.Errorf("removing unsealed sector: %w", err)
}
}
err := sb.UnsealPiece(context.TODO(), abi.SectorID{Miner: mid, Number: 1}, 0, abi.UnpaddedPieceSize(sectorSize), ticket, cids.Unsealed)
if err != nil {
return nil, nil, err
}
}
unseal := time.Now()
sealTimings = append(sealTimings, SealingResult{
AddPiece: addpiece.Sub(start),
PreCommit1: precommit1.Sub(addpiece),
PreCommit2: precommit2.Sub(precommit1),
Commit1: sealcommit1.Sub(precommit2),
Commit2: sealcommit2.Sub(sealcommit1),
Verify: verifySeal.Sub(sealcommit2),
Unseal: unseal.Sub(verifySeal),
})
} }
return sealTimings, sealedSectors, nil return sealTimings, sealedSectors, nil
@@ -665,11 +598,6 @@ var proveCmd = &cli.Command{
Name: "no-gpu", Name: "no-gpu",
Usage: "disable gpu usage for the benchmark run", Usage: "disable gpu usage for the benchmark run",
}, },
&cli.StringFlag{
Name: "miner-addr",
Usage: "pass miner address (only necessary if using existing sectorbuilder)",
Value: "t01000",
},
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.Bool("no-gpu") { if c.Bool("no-gpu") {
@@ -731,7 +659,7 @@ var proveCmd = &cli.Command{
fmt.Printf("proof: %x\n", proof) fmt.Printf("proof: %x\n", proof)
fmt.Printf("----\nresults (v27) (%d)\n", c2in.SectorSize) fmt.Printf("----\nresults (v23) (%d)\n", c2in.SectorSize)
dur := sealCommit2.Sub(start) dur := sealCommit2.Sub(start)
fmt.Printf("seal: commit phase 2: %s (%s)\n", dur, bps(abi.SectorSize(c2in.SectorSize), dur)) fmt.Printf("seal: commit phase 2: %s (%s)\n", dur, bps(abi.SectorSize(c2in.SectorSize), dur))
-47
View File
@@ -1,47 +0,0 @@
package main
import (
"math/rand"
"testing"
)
func TestMeanVar(t *testing.T) {
N := 16
ss := make([]*meanVar, N)
rng := rand.New(rand.NewSource(1))
for i := 0; i < N; i++ {
ss[i] = &meanVar{}
maxJ := rng.Intn(1000)
for j := 0; j < maxJ; j++ {
ss[i].AddPoint(rng.NormFloat64()*5 + 500)
}
t.Logf("mean: %f, stddev: %f, count %f", ss[i].mean, ss[i].Stddev(), ss[i].n)
}
out := &meanVar{}
for i := 0; i < N; i++ {
out.Combine(ss[i])
t.Logf("combine: mean: %f, stddev: %f", out.mean, out.Stddev())
}
}
func TestCovar(t *testing.T) {
N := 16
ss := make([]*covar, N)
rng := rand.New(rand.NewSource(1))
for i := 0; i < N; i++ {
ss[i] = &covar{}
maxJ := rng.Intn(1000) + 500
for j := 0; j < maxJ; j++ {
x := rng.NormFloat64()*5 + 500
ss[i].AddPoint(x, x*2-1000)
}
t.Logf("corell: %f, y = %f*x+%f @%.0f", ss[i].Correl(), ss[i].A(), ss[i].B(), ss[i].n)
t.Logf("\txVar: %f yVar: %f covar: %f", ss[i].StddevX(), ss[i].StddevY(), ss[i].Covariance())
}
out := &covar{}
for i := 0; i < N; i++ {
out.Combine(ss[i])
t.Logf("combine: corell: %f, y = %f*x+%f", out.Correl(), out.A(), out.B())
t.Logf("\txVar: %f yVar: %f covar: %f", out.StddevX(), out.StddevY(), out.Covariance())
}
}
-3
View File
@@ -18,9 +18,6 @@ var log = logging.Logger("chainwatch")
func main() { func main() {
_ = logging.SetLogLevel("*", "INFO") _ = logging.SetLogLevel("*", "INFO")
if err := logging.SetLogLevel("rpc", "error"); err != nil {
panic(err)
}
log.Info("Starting chainwatch") log.Info("Starting chainwatch")
+1 -1
View File
@@ -46,7 +46,7 @@ func subMpool(ctx context.Context, api aapi.FullNode, st *storage) {
msgs[v.Message.Message.Cid()] = &v.Message.Message msgs[v.Message.Message.Cid()] = &v.Message.Message
} }
log.Debugf("Processing %d mpool updates", len(msgs)) log.Infof("Processing %d mpool updates", len(msgs))
err := st.storeMessages(msgs) err := st.storeMessages(msgs)
if err != nil { if err != nil {
+58 -326
View File
@@ -1,17 +1,11 @@
package main package main
import ( import (
"context"
"database/sql" "database/sql"
"fmt"
"github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/libp2p/go-libp2p-core/peer"
"sync" "sync"
"time" "time"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
miner_spec "github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"golang.org/x/xerrors" "golang.org/x/xerrors"
@@ -24,9 +18,6 @@ type storage struct {
db *sql.DB db *sql.DB
headerLk sync.Mutex headerLk sync.Mutex
// stateful miner data
minerSectors map[cid.Cid]struct{}
} }
func openStorage(dbSource string) (*storage, error) { func openStorage(dbSource string) (*storage, error) {
@@ -37,10 +28,7 @@ func openStorage(dbSource string) (*storage, error) {
db.SetMaxOpenConns(1350) db.SetMaxOpenConns(1350)
ms := make(map[cid.Cid]struct{}) st := &storage{db: db}
ms[cid.Undef] = struct{}{}
st := &storage{db: db, minerSectors: ms}
return st, st.setup() return st, st.setup()
} }
@@ -130,7 +118,7 @@ create unique index if not exists block_cid_uindex
create materialized view if not exists state_heights create materialized view if not exists state_heights
as select distinct height, parentstateroot from blocks; as select distinct height, parentstateroot from blocks;
create index if not exists state_heights_index create unique index if not exists state_heights_uindex
on state_heights (height); on state_heights (height);
create index if not exists state_heights_height_index create index if not exists state_heights_height_index
@@ -264,56 +252,31 @@ create table if not exists receipts
create index if not exists receipts_msg_state_index create index if not exists receipts_msg_state_index
on receipts (msg, state); on receipts (msg, state);
create table if not exists miner_sectors
(
miner_id text not null,
sector_id bigint not null,
activation_epoch bigint not null,
expiration_epoch bigint not null,
termination_epoch bigint,
deal_weight text not null,
verified_deal_weight text not null,
seal_cid text not null,
seal_rand_epoch bigint not null,
constraint miner_sectors_pk
primary key (miner_id, sector_id)
);
create index if not exists miner_sectors_miner_sectorid_index
on miner_sectors (miner_id, sector_id);
create table if not exists miner_info
(
miner_id text not null,
owner_addr text not null,
worker_addr text not null,
peer_id text,
sector_size text not null,
precommit_deposits text not null,
locked_funds text not null,
next_deadline_process_faults bigint not null,
constraint miner_info_pk
primary key (miner_id)
);
/* used to tell when a miners sectors (proven-not-yet-expired) changed if the miner_sectors_cid's are different a new sector was added or removed (terminated/expired) */
create table if not exists miner_sectors_heads
(
miner_id text not null,
miner_sectors_cid text not null,
state_root text not null,
constraint miner_sectors_heads_pk
primary key (miner_id,miner_sectors_cid)
);
/* /*
create table if not exists miner_heads
(
head text not null,
addr text not null,
stateroot text not null,
sectorset text not null,
setsize decimal not null,
provingset text not null,
provingsize decimal not null,
owner text not null,
worker text not null,
peerid text not null,
sectorsize bigint not null,
power decimal not null,
active bool,
ppe bigint not null,
slashed_at bigint not null,
constraint miner_heads_pk
primary key (head, addr)
);
create index if not exists miner_heads_stateroot_index
on miner_heads (stateroot);
create or replace function miner_tips(epoch bigint) create or replace function miner_tips(epoch bigint)
returns table (head text, returns table (head text,
addr text, addr text,
@@ -493,261 +456,54 @@ func (st *storage) storeActors(actors map[address.Address]map[types.Actor]actorI
return nil return nil
} }
type storeSectorsAPI interface { func (st *storage) storeMiners(miners map[minerKey]*minerInfo) error {
StateMinerSectors(context.Context, address.Address, *abi.BitField, bool, types.TipSetKey) ([]*api.ChainSectorInfo, error) /*tx, err := st.db.Begin()
} if err != nil {
return err
func (st *storage) storeSectors(minerTips map[types.TipSetKey][]*minerStateInfo, sectorApi storeSectorsAPI) error {
tx, err := st.db.Begin()
if err != nil {
return err
}
if _, err := tx.Exec(`create temp table ms (like miner_sectors excluding constraints) on commit drop;`); err != nil {
return xerrors.Errorf("prep temp: %w", err)
}
stmt, err := tx.Prepare(`copy ms (miner_id, sector_id, activation_epoch, expiration_epoch, deal_weight, verified_deal_weight, seal_cid, seal_rand_epoch) from STDIN`)
if err != nil {
return err
}
for tipset, miners := range minerTips {
for _, miner := range miners {
sectors, err := sectorApi.StateMinerSectors(context.TODO(), miner.addr, nil, true, tipset)
if err != nil {
log.Debugw("Failed to load sectors", "tipset", tipset.String(), "miner", miner.addr.String(), "error", err)
}
for _, sector := range sectors {
if _, err := stmt.Exec(
miner.addr.String(),
uint64(sector.ID),
int64(sector.Info.ActivationEpoch),
int64(sector.Info.Info.Expiration),
sector.Info.DealWeight.String(),
sector.Info.VerifiedDealWeight.String(),
sector.Info.Info.SealedCID.String(),
int64(sector.Info.Info.SealRandEpoch),
); err != nil {
return err
}
}
} }
}
if err := stmt.Close(); err != nil { if _, err := tx.Exec(`
return err
}
if _, err := tx.Exec(`insert into miner_sectors select * from ms on conflict do nothing `); err != nil { create temp table mh (like miner_heads excluding constraints) on commit drop;
return xerrors.Errorf("actor put: %w", err)
}
return tx.Commit()
}
func (st *storage) storeMiners(minerTips map[types.TipSetKey][]*minerStateInfo) error { `); err != nil {
tx, err := st.db.Begin() return xerrors.Errorf("prep temp: %w", err)
if err != nil { }
return err
}
if _, err := tx.Exec(`create temp table mi (like miner_info excluding constraints) on commit drop;`); err != nil { stmt, err := tx.Prepare(`copy mh (head, addr, stateroot, sectorset, setsize, provingset, provingsize, owner, worker, peerid, sectorsize, power, ppe) from STDIN`)
return xerrors.Errorf("prep temp: %w", err) if err != nil {
} return err
}
stmt, err := tx.Prepare(`copy mi (miner_id, owner_addr, worker_addr, peer_id, sector_size, precommit_deposits, locked_funds, next_deadline_process_faults) from STDIN`) for k, i := range miners {
if err != nil {
return err
}
for ts, miners := range minerTips {
for _, miner := range miners {
var pid string
if len(miner.info.PeerId) != 0 {
peerid, err := peer.IDFromBytes(miner.info.PeerId)
if err != nil {
// this should "never happen", but if it does we should still store info about the miner.
log.Warnw("failed to decode peerID", "peerID (bytes)", miner.info.PeerId, "miner", miner.addr, "tipset", ts.String())
} else {
pid = peerid.String()
}
}
if _, err := stmt.Exec( if _, err := stmt.Exec(
miner.addr.String(), k.act.Head.String(),
miner.info.Owner.String(), k.addr.String(),
miner.info.Worker.String(), k.stateroot.String(),
pid, i.state.Sectors.String(),
miner.info.SectorSize.ShortString(), fmt.Sprint(i.ssize),
miner.state.PreCommitDeposits.String(), i.state.ProvingSet.String(),
miner.state.LockedFunds.String(), fmt.Sprint(i.psize),
miner.state.NextDeadlineToProcessFaults, i.info.Owner.String(),
i.info.Worker.String(),
i.info.PeerId.String(),
i.info.SectorSize,
i.power.String(), // TODO: SPA
i.state.PoStState.ProvingPeriodStart,
); err != nil { ); err != nil {
log.Errorw("failed to store miner state", "state", miner.state, "info", miner.info, "error", err)
return err return err
} }
} }
} if err := stmt.Close(); err != nil {
if err := stmt.Close(); err != nil {
return err
}
if _, err := tx.Exec(`insert into miner_info select * from mi on conflict do nothing `); err != nil {
return xerrors.Errorf("actor put: %w", err)
}
return tx.Commit()
}
type minerSectorUpdate struct {
minerState *minerStateInfo
tskey types.TipSetKey
oldSector cid.Cid
}
func (st *storage) storeMinerSectorsHeads(minerTips map[types.TipSetKey][]*minerStateInfo, api api.FullNode) error {
tx, err := st.db.Begin()
if err != nil {
return err
}
if _, err := tx.Exec(`create temp table msh (like miner_sectors_heads excluding constraints) on commit drop;`); err != nil {
return xerrors.Errorf("prep temp: %w", err)
}
stmt, err := tx.Prepare(`copy msh (miner_id, miner_sectors_cid, state_root) from STDIN`)
if err != nil {
return err
}
var updateMiners []*minerSectorUpdate
for tsk, miners := range minerTips {
for _, miner := range miners {
sectorCID, err := st.getLatestMinerSectorCID(context.TODO(), miner.addr)
if err != nil {
panic(err)
}
if sectorCID == cid.Undef {
continue
}
if _, found := st.minerSectors[sectorCID]; !found {
// schedule miner table update
updateMiners = append(updateMiners, &minerSectorUpdate{
minerState: miner,
tskey: tsk,
oldSector: sectorCID,
})
}
st.minerSectors[sectorCID] = struct{}{}
log.Debugw("got sector CID", "miner", miner.addr, "cid", sectorCID.String())
if _, err := stmt.Exec(
miner.addr.String(),
miner.state.Sectors.String(),
miner.stateroot.String(),
); err != nil {
log.Errorw("failed to store miners sectors head", "state", miner.state, "info", miner.info, "error", err)
return err
}
}
}
if err := stmt.Close(); err != nil {
return err
}
if _, err := tx.Exec(`insert into miner_sectors_heads select * from msh on conflict do nothing `); err != nil {
return xerrors.Errorf("actor put: %w", err)
}
if err := tx.Commit(); err != nil {
return err
}
return st.updateMinerSectors(updateMiners, api)
}
type deletedSector struct {
deletedSector miner_spec.SectorOnChainInfo
miner address.Address
tskey types.TipSetKey
}
func (st *storage) updateMinerSectors(miners []*minerSectorUpdate, api api.FullNode) error {
log.Info("updating miners constant sector table")
var deletedSectors []*deletedSector
for _, miner := range miners {
s := &apiIpldStore{context.TODO(), api}
newSectors, err := adt.AsArray(s, miner.minerState.state.Sectors)
if err != nil {
log.Warnw("new sectors as array", "error", err, "cid", miner.minerState.state.Sectors)
return err return err
} }
oldSectors, err := adt.AsArray(s, miner.oldSector) if _, err := tx.Exec(`insert into miner_heads select * from mh on conflict do nothing `); err != nil {
if err != nil { return xerrors.Errorf("actor put: %w", err)
log.Warnw("old sectors as array", "error", err, "cid", miner.oldSector.String())
return err
} }
var oldSecInfo miner_spec.SectorOnChainInfo return tx.Commit()*/
var newSecInfo miner_spec.SectorOnChainInfo return nil
// if we cannot find an old sector in the new list then it was removed.
if err := oldSectors.ForEach(&oldSecInfo, func(i int64) error {
found, err := newSectors.Get(uint64(oldSecInfo.Info.SectorNumber), &newSecInfo)
if err != nil {
log.Warnw("new sectors get", "error", err)
return err
}
if !found {
log.Infow("MINER DELETED SECTOR", "miner", miner.minerState.addr.String(), "sector", oldSecInfo.Info.SectorNumber, "tipset", miner.tskey.String())
deletedSectors = append(deletedSectors, &deletedSector{
deletedSector: oldSecInfo,
miner: miner.minerState.addr,
tskey: miner.tskey,
})
}
return nil
}); err != nil {
log.Warnw("old sectors foreach", "error", err)
return err
}
if len(deletedSectors) > 0 {
log.Infow("Calculated updates", "miner", miner.minerState.addr, "deleted sectors", len(deletedSectors))
}
}
// now we have all the sectors that were removed, update the database
tx, err := st.db.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare(`UPDATE miner_sectors SET termination_epoch=$1 WHERE miner_id=$2 AND sector_id=$3`)
if err != nil {
return err
}
for _, ds := range deletedSectors {
ts, err := api.ChainGetTipSet(context.TODO(), ds.tskey)
if err != nil {
log.Warnw("get tipset", "error", err)
return err
}
// TODO validate this shits right
if ts.Height() >= ds.deletedSector.Info.Expiration {
// means it expired, do nothing
log.Infow("expired sector", "miner", ds.miner.String(), "sector", ds.deletedSector.Info.SectorNumber)
continue
}
log.Infow("terminated sector", "miner", ds.miner.String(), "sector", ds.deletedSector.Info.SectorNumber)
// means it was terminated.
if _, err := stmt.Exec(int64(ts.Height()), ds.miner.String(), int64(ds.deletedSector.Info.SectorNumber)); err != nil {
return err
}
}
if err := stmt.Close(); err != nil {
return err
}
defer log.Info("update miner sectors complete")
return tx.Commit()
} }
func (st *storage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool) error { func (st *storage) storeHeaders(bhs map[cid.Cid]*types.BlockHeader, sync bool) error {
@@ -1252,27 +1008,3 @@ func (st *storage) refreshViews() error {
func (st *storage) close() error { func (st *storage) close() error {
return st.db.Close() return st.db.Close()
} }
func (st *storage) getLatestMinerSectorCID(ctx context.Context, miner address.Address) (cid.Cid, error) {
queryStr := fmt.Sprintf(`
SELECT miner_sectors_cid
FROM miner_sectors_heads
LEFT JOIN blocks ON miner_sectors_heads.state_root = blocks.parentstateroot
WHERE miner_id = '%s'
ORDER BY blocks.height DESC
LIMIT 1;
`,
miner.String())
var cidstr string
err := st.db.QueryRowContext(ctx, queryStr).Scan(&cidstr)
switch {
case err == sql.ErrNoRows:
log.Warnf("no miner with miner_id: %s in table", miner)
return cid.Undef, nil
case err != nil:
return cid.Undef, err
default:
return cid.Decode(cidstr)
}
}
+61 -204
View File
@@ -5,21 +5,17 @@ import (
"container/list" "container/list"
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"math" "math"
"sync" "sync"
"time"
"github.com/filecoin-project/go-address"
"github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/miner" "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" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/store"
@@ -53,30 +49,27 @@ func runSyncer(ctx context.Context, api api.FullNode, st *storage, maxBatch int)
}() }()
} }
type minerStateInfo struct { type minerKey struct {
// common
addr address.Address addr address.Address
act types.Actor act types.Actor
stateroot cid.Cid stateroot cid.Cid
}
// miner specific type minerInfo struct {
state miner.State state miner.State
info miner.MinerInfo info miner.MinerInfo
// tracked by power actor power big.Int
rawPower big.Int ssize uint64
qalPower big.Int psize uint64
ssize uint64
psize uint64
} }
type actorInfo struct { type actorInfo struct {
stateroot cid.Cid stateroot cid.Cid
tsKey types.TipSetKey
state string state string
} }
func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.TipSet, maxBatch int) { func syncHead(ctx context.Context, api api.FullNode, st *storage, ts *types.TipSet, maxBatch int) {
var alk sync.Mutex var alk sync.Mutex
log.Infof("Getting synced block list") log.Infof("Getting synced block list")
@@ -85,28 +78,25 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
log.Infof("Getting headers / actors") log.Infof("Getting headers / actors")
// global list of all blocks that need to be synced
allToSync := map[cid.Cid]*types.BlockHeader{} allToSync := map[cid.Cid]*types.BlockHeader{}
// a stack
toVisit := list.New() toVisit := list.New()
for _, header := range headTs.Blocks() { for _, header := range ts.Blocks() {
toVisit.PushBack(header) toVisit.PushBack(header)
} }
// TODO consider making a db query to check where syncing left off at in the case of a restart and avoid reprocessing
// those entries, or write value to file on shutdown
// walk the entire chain starting from headTS
for toVisit.Len() > 0 { for toVisit.Len() > 0 {
bh := toVisit.Remove(toVisit.Back()).(*types.BlockHeader) bh := toVisit.Remove(toVisit.Back()).(*types.BlockHeader)
_, has := hazlist[bh.Cid()] _, has := hazlist[bh.Cid()]
if _, seen := allToSync[bh.Cid()]; seen || has { if _, seen := allToSync[bh.Cid()]; seen || has {
continue continue
} }
allToSync[bh.Cid()] = bh allToSync[bh.Cid()] = bh
if len(allToSync)%500 == 10 { if len(allToSync)%500 == 10 {
log.Debugf("to visit: (%d) %s @%d", len(allToSync), bh.Cid(), bh.Height) log.Infof("todo: (%d) %s @%d", len(allToSync), bh.Cid(), bh.Height)
} }
if len(bh.Parents) == 0 { if len(bh.Parents) == 0 {
@@ -124,42 +114,30 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
} }
} }
// Main worker loop, this loop runs until all tipse from headTS to genesis have been processed.
for len(allToSync) > 0 { for len(allToSync) > 0 {
// first map is addresses -> common actors states (head, code, balance, nonce)
// second map common actor states -> chain state (tipset, stateroot) & unique actor state (deserialization of their head CID) represented as json.
actors := map[address.Address]map[types.Actor]actorInfo{} actors := map[address.Address]map[types.Actor]actorInfo{}
addresses := map[address.Address]address.Address{}
// map of actor public key address to ID address
addressToID := map[address.Address]address.Address{}
minH := abi.ChainEpoch(math.MaxInt64) minH := abi.ChainEpoch(math.MaxInt64)
// find the blockheader with the lowest height
for _, header := range allToSync { for _, header := range allToSync {
if header.Height < minH { if header.Height < minH {
minH = header.Height minH = header.Height
} }
} }
// toSync maps block cids to their headers and contains all block headers that will be synced in this batch
// `maxBatch` is a tunable parameter to control how many blocks we sync per iteration.
toSync := map[cid.Cid]*types.BlockHeader{} toSync := map[cid.Cid]*types.BlockHeader{}
for c, header := range allToSync { for c, header := range allToSync {
if header.Height < minH+abi.ChainEpoch(maxBatch) { if header.Height < minH+abi.ChainEpoch(maxBatch) {
toSync[c] = header toSync[c] = header
addressToID[header.Miner] = address.Undef addresses[header.Miner] = address.Undef
} }
} }
// remove everything we are syncing this round from the global list of blocks to sync
for c := range toSync { for c := range toSync {
delete(allToSync, c) delete(allToSync, c)
} }
log.Infow("Starting Sync", "height", minH, "numBlocks", len(toSync), "maxBatch", maxBatch) log.Infof("Syncing %d blocks", len(toSync))
// map of addresses to changed actors
var changes map[string]types.Actor
// collect all actor state that has changes between block headers
paDone := 0 paDone := 0
parmap.Par(50, parmap.MapArr(toSync), func(bh *types.BlockHeader) { parmap.Par(50, parmap.MapArr(toSync), func(bh *types.BlockHeader) {
paDone++ paDone++
@@ -168,23 +146,20 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
} }
if len(bh.Parents) == 0 { // genesis case if len(bh.Parents) == 0 { // genesis case
genesisTs, _ := types.NewTipSet([]*types.BlockHeader{bh}) ts, _ := types.NewTipSet([]*types.BlockHeader{bh})
aadrs, err := api.StateListActors(ctx, genesisTs.Key()) aadrs, err := api.StateListActors(ctx, ts.Key())
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return
} }
// TODO suspicious there is not a lot to be gained by doing this in parallel since the genesis state
// is unlikely to contain a lot of actors, why not for loop here?
parmap.Par(50, aadrs, func(addr address.Address) { parmap.Par(50, aadrs, func(addr address.Address) {
act, err := api.StateGetActor(ctx, addr, genesisTs.Key()) act, err := api.StateGetActor(ctx, addr, ts.Key())
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return
} }
ast, err := api.StateReadState(ctx, act, ts.Key())
ast, err := api.StateReadState(ctx, addr, genesisTs.Key())
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return
@@ -202,10 +177,9 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
} }
actors[addr][*act] = actorInfo{ actors[addr][*act] = actorInfo{
stateroot: bh.ParentStateRoot, stateroot: bh.ParentStateRoot,
tsKey: genesisTs.Key(),
state: string(state), state: string(state),
} }
addressToID[addr] = address.Undef addresses[addr] = address.Undef
alk.Unlock() alk.Unlock()
}) })
@@ -218,15 +192,12 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
return return
} }
// TODO Does this return actors that have been deleted between states? changes, err := api.StateChangedActors(ctx, pts.ParentState(), bh.ParentStateRoot)
// collect all actors that had state changes between the blockheader parent-state and its grandparent-state.
changes, err = api.StateChangedActors(ctx, pts.ParentState(), bh.ParentStateRoot)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return
} }
// record the state of all actors that have changed
for a, act := range changes { for a, act := range changes {
act := act act := act
@@ -235,14 +206,11 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
log.Error(err) log.Error(err)
return return
} }
ast, err := api.StateReadState(ctx, &act, pts.Key())
ast, err := api.StateReadState(ctx, addr, pts.Key())
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return
} }
state, err := json.Marshal(ast.State) state, err := json.Marshal(ast.State)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@@ -254,22 +222,15 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
if !ok { if !ok {
actors[addr] = map[types.Actor]actorInfo{} actors[addr] = map[types.Actor]actorInfo{}
} }
// a change occurred for the actor with address `addr` and state `act` at tipset `pts`.
actors[addr][act] = actorInfo{ actors[addr][act] = actorInfo{
stateroot: bh.ParentStateRoot, stateroot: bh.ParentStateRoot,
state: string(state), state: string(state),
tsKey: pts.Key(),
} }
addressToID[addr] = address.Undef addresses[addr] = address.Undef
alk.Unlock() alk.Unlock()
} }
}) })
// map of tipset to all miners that had a head-change at that tipset.
minerTips := make(map[types.TipSetKey][]*minerStateInfo, len(changes))
// heads we've seen, im being paranoid
headsSeen := make(map[cid.Cid]struct{}, len(actors))
log.Infof("Getting messages") log.Infof("Getting messages")
msgs, incls := fetchMessages(ctx, api, toSync) msgs, incls := fetchMessages(ctx, api, toSync)
@@ -277,109 +238,70 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
log.Infof("Resolving addresses") log.Infof("Resolving addresses")
for _, message := range msgs { for _, message := range msgs {
addressToID[message.To] = address.Undef addresses[message.To] = address.Undef
addressToID[message.From] = address.Undef addresses[message.From] = address.Undef
} }
parmap.Par(50, parmap.KMapArr(addressToID), func(addr address.Address) { parmap.Par(50, parmap.KMapArr(addresses), func(addr address.Address) {
// FIXME: cannot use EmptyTSK here since actorID's can change during reorgs, need to use the corresponding tipset.
// TODO: figure out a way to get the corresponding tipset...
raddr, err := api.StateLookupID(ctx, addr, types.EmptyTSK) raddr, err := api.StateLookupID(ctx, addr, types.EmptyTSK)
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
return return
} }
alk.Lock() alk.Lock()
addressToID[addr] = raddr addresses[addr] = raddr
alk.Unlock() alk.Unlock()
}) })
log.Infof("Getting miner info") log.Infof("Getting miner info")
minerChanges := 0 miners := map[minerKey]*minerInfo{}
for addr, m := range actors { for addr, m := range actors {
for actor, c := range m { for actor, c := range m {
if actor.Code != builtin.StorageMinerActorCodeID { if actor.Code != builtin.StorageMinerActorCodeID {
continue continue
} }
// only want miner actors with head change events miners[minerKey{
if _, found := headsSeen[actor.Head]; found {
continue
}
minerChanges++
minerTips[c.tsKey] = append(minerTips[c.tsKey], &minerStateInfo{
addr: addr, addr: addr,
act: actor, act: actor,
stateroot: c.stateroot, stateroot: c.stateroot,
}] = &minerInfo{}
state: miner.State{},
info: miner.MinerInfo{},
rawPower: big.Zero(),
qalPower: big.Zero(),
})
headsSeen[actor.Head] = struct{}{}
} }
} }
minerProcessingStartedAt := time.Now() parmap.Par(50, parmap.KVMapArr(miners), func(it func() (minerKey, *minerInfo)) {
log.Infow("Processing miners", "numTips", len(minerTips), "numMinerChanges", minerChanges) k, info := it()
// extract the power actor state at each tipset, loop over all miners that changed at said tipset and extract their
// claims from the power actor state. This ensures we only fetch the power actors state once for each tipset.
parmap.Par(50, parmap.KVMapArr(minerTips), func(it func() (types.TipSetKey, []*minerStateInfo)) {
tsKey, minerInfo := it()
// get the power actors claims map pow, err := api.StateMinerPower(ctx, k.addr, types.EmptyTSK)
mp, err := getPowerActorClaimsMap(ctx, api, tsKey) if err != nil {
log.Error(err)
// Not sure why this would fail, but its probably worth continuing
}
info.power = pow.MinerPower.QualityAdjPower
sszs, err := api.StateMinerSectorCount(ctx, k.addr, types.EmptyTSK)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return
} }
// Get miner raw and quality power info.psize = sszs.Pset
for _, mi := range minerInfo { info.ssize = sszs.Sset
var claim power.Claim
// get miner claim from power actors claim map and store if found, else the miner had no claim at
// this tipset
found, err := mp.Get(adt.AddrKey(mi.addr), &claim)
if err != nil {
log.Error(err)
}
if found {
mi.qalPower = claim.QualityAdjPower
mi.rawPower = claim.RawBytePower
}
// Get the miner state info astb, err := api.ChainReadObj(ctx, k.act.Head)
astb, err := api.ChainReadObj(ctx, mi.act.Head) if err != nil {
if err != nil { log.Error(err)
log.Error(err) return
return
}
if err := mi.state.UnmarshalCBOR(bytes.NewReader(astb)); err != nil {
log.Error(err)
return
}
mi.info = mi.state.Info
} }
// TODO Get the Sector Count if err := info.state.UnmarshalCBOR(bytes.NewReader(astb)); err != nil {
// FIXME this is returning a lot of "address not found" errors, which is strange given that StateChangedActors log.Error(err)
// retruns all actors that had a state change at tipset `k.tsKey`, maybe its returning deleted miners too?? return
/* }
sszs, err := api.StateMinerSectorCount(ctx, k.addr, k.tsKey)
if err != nil { info.info = info.state.Info
info.psize = 0
info.ssize = 0
} else {
info.psize = sszs.Pset
info.ssize = sszs.Sset
}
*/
}) })
log.Infow("Completed Miner Processing", "duration", time.Since(minerProcessingStartedAt).String(), "processed", minerChanges)
log.Info("Getting receipts") log.Info("Getting receipts")
@@ -394,7 +316,7 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
log.Info("Storing address mapping") log.Info("Storing address mapping")
if err := st.storeAddressMap(addressToID); err != nil { if err := st.storeAddressMap(addresses); err != nil {
log.Error(err) log.Error(err)
return return
} }
@@ -407,21 +329,8 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
} }
log.Info("Storing miners") log.Info("Storing miners")
if err := st.storeMiners(minerTips); err != nil {
log.Error(err)
return
}
log.Info("Storing miner sectors") if err := st.storeMiners(miners); err != nil {
sectorStart := time.Now()
if err := st.storeSectors(minerTips, api); err != nil {
log.Error(err)
return
}
log.Infow("Finished storing miner sectors", "duration", time.Since(sectorStart).String())
log.Info("Storing miner sectors heads")
if err := st.storeMinerSectorsHeads(minerTips, api); err != nil {
log.Error(err) log.Error(err)
return return
} }
@@ -452,7 +361,7 @@ func syncHead(ctx context.Context, api api.FullNode, st *storage, headTs *types.
log.Infof("Get deals") log.Infof("Get deals")
// TODO: incremental, gather expired // TODO: incremental, gather expired
deals, err := api.StateMarketDeals(ctx, headTs.Key()) deals, err := api.StateMarketDeals(ctx, ts.Key())
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return
@@ -542,55 +451,3 @@ func fetchParentReceipts(ctx context.Context, api api.FullNode, toSync map[cid.C
return out return out
} }
// load the power actor state clam as an adt.Map at the tipset `ts`.
func getPowerActorClaimsMap(ctx context.Context, api api.FullNode, ts types.TipSetKey) (*adt.Map, error) {
powerActor, err := api.StateGetActor(ctx, builtin.StoragePowerActorAddr, ts)
if err != nil {
return nil, err
}
powerRaw, err := api.ChainReadObj(ctx, powerActor.Head)
if err != nil {
return nil, err
}
var powerActorState power.State
if err := powerActorState.UnmarshalCBOR(bytes.NewReader(powerRaw)); err != nil {
return nil, fmt.Errorf("failed to unmarshal power actor state: %w", err)
}
s := &apiIpldStore{ctx, api}
return adt.AsMap(s, powerActorState.Claims)
}
// require for AMT and HAMT access
// TODO extract this to a common location in lotus and reuse the code
type apiIpldStore struct {
ctx context.Context
api api.FullNode
}
func (ht *apiIpldStore) Context() context.Context {
return ht.ctx
}
func (ht *apiIpldStore) Get(ctx context.Context, c cid.Cid, out interface{}) error {
raw, err := ht.api.ChainReadObj(ctx, c)
if err != nil {
return err
}
cu, ok := out.(cbg.CBORUnmarshaler)
if ok {
if err := cu.UnmarshalCBOR(bytes.NewReader(raw)); err != nil {
return err
}
return nil
}
return fmt.Errorf("Object does not implement CBORUnmarshaler: %T", out)
}
func (ht *apiIpldStore) Put(ctx context.Context, v interface{}) (cid.Cid, error) {
return cid.Undef, fmt.Errorf("Put is not implemented on apiIpldStore")
}
+4 -5
View File
@@ -63,7 +63,7 @@ var watchHeadCmd = &cli.Command{
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "interval", Name: "interval",
Value: int(build.BlockDelaySecs), Value: build.BlockDelay,
Usage: "interval in seconds between chain head checks", Usage: "interval in seconds between chain head checks",
}, },
&cli.StringFlag{ &cli.StringFlag{
@@ -72,9 +72,8 @@ var watchHeadCmd = &cli.Command{
Usage: "systemd unit name to restart on health check failure", Usage: "systemd unit name to restart on health check failure",
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "api-timeout", Name: "api-timeout",
// TODO: this default value seems spurious. Value: build.BlockDelay,
Value: int(build.BlockDelaySecs),
Usage: "timeout between API retries", Usage: "timeout between API retries",
}, },
&cli.IntFlag{ &cli.IntFlag{
@@ -237,7 +236,7 @@ func waitForSyncComplete(ctx context.Context, a api.FullNode, r int, t time.Dura
return err return err
} }
if time.Now().Unix()-int64(head.MinTimestamp()) < int64(build.BlockDelaySecs) { if time.Now().Unix()-int64(head.MinTimestamp()) < build.BlockDelay {
return nil return nil
} }
} }
+4 -56
View File
@@ -3,14 +3,11 @@ package main
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"syscall"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@@ -120,19 +117,11 @@ var runCmd = &cli.Command{
} }
// Connect to storage-miner // Connect to storage-miner
var nodeApi api.StorageMiner
var closer func()
var err error
for {
nodeApi, closer, err = lcli.GetStorageMinerAPI(cctx)
if err == nil {
break
}
fmt.Printf("\r\x1b[0KConnecting to miner API... (%s)", err)
time.Sleep(time.Second)
continue
}
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return xerrors.Errorf("getting miner api: %w", err)
}
defer closer() defer closer()
ctx := lcli.ReqContext(cctx) ctx := lcli.ReqContext(cctx)
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
@@ -147,8 +136,6 @@ var runCmd = &cli.Command{
} }
log.Infof("Remote version %s", v) log.Infof("Remote version %s", v)
watchMinerConn(ctx, cctx, nodeApi)
// Check params // Check params
act, err := nodeApi.ActorAddress(ctx) act, err := nodeApi.ActorAddress(ctx)
@@ -330,42 +317,3 @@ var runCmd = &cli.Command{
return srv.Serve(nl) return srv.Serve(nl)
}, },
} }
func watchMinerConn(ctx context.Context, cctx *cli.Context, nodeApi api.StorageMiner) {
go func() {
closing, err := nodeApi.Closing(ctx)
if err != nil {
log.Errorf("failed to get remote closing channel: %+v", err)
}
select {
case <-closing:
case <-ctx.Done():
}
if ctx.Err() != nil {
return // graceful shutdown
}
log.Warnf("Connection with miner node lost, restarting")
exe, err := os.Executable()
if err != nil {
log.Errorf("getting executable for auto-restart: %+v", err)
}
log.Sync()
// TODO: there are probably cleaner/more graceful ways to restart,
// but this is good enough for now (FSM can recover from the mess this creates)
if err := syscall.Exec(exe, []string{exe, "run",
fmt.Sprintf("--address=%s", cctx.String("address")),
fmt.Sprintf("--no-local-storage=%t", cctx.Bool("no-local-storage")),
fmt.Sprintf("--precommit1=%t", cctx.Bool("precommit1")),
fmt.Sprintf("--precommit2=%t", cctx.Bool("precommit2")),
fmt.Sprintf("--commit=%t", cctx.Bool("commit")),
}, os.Environ()); err != nil {
fmt.Println(err)
}
}()
}
+1 -1
View File
@@ -124,7 +124,7 @@ var genesisAddMinerCmd = &cli.Command{
log.Infof("Giving %s some initial balance", miner.Owner) log.Infof("Giving %s some initial balance", miner.Owner)
template.Accounts = append(template.Accounts, genesis.Actor{ template.Accounts = append(template.Accounts, genesis.Actor{
Type: genesis.TAccount, Type: genesis.TAccount,
Balance: big.Mul(big.NewInt(50_000_000), big.NewInt(int64(build.FilecoinPrecision))), Balance: big.Mul(big.NewInt(50_000_000), big.NewInt(build.FilecoinPrecision)),
Meta: (&genesis.AccountMeta{Owner: miner.Owner}).ActorMeta(), Meta: (&genesis.AccountMeta{Owner: miner.Owner}).ActorMeta(),
}) })
} }
+9 -18
View File
@@ -33,7 +33,12 @@ import (
var log = logging.Logger("preseal") var log = logging.Logger("preseal")
func PreSeal(maddr address.Address, spt abi.RegisteredSealProof, offset abi.SectorNumber, sectors int, sbroot string, preimage []byte, key *types.KeyInfo) (*genesis.Miner, *types.KeyInfo, error) { func PreSeal(maddr address.Address, pt abi.RegisteredProof, offset abi.SectorNumber, sectors int, sbroot string, preimage []byte, key *types.KeyInfo) (*genesis.Miner, *types.KeyInfo, error) {
spt, err := pt.RegisteredSealProof()
if err != nil {
return nil, nil, err
}
mid, err := address.IDFromAddress(maddr) mid, err := address.IDFromAddress(maddr)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -58,7 +63,7 @@ func PreSeal(maddr address.Address, spt abi.RegisteredSealProof, offset abi.Sect
return nil, nil, err return nil, nil, err
} }
ssize, err := spt.SectorSize() ssize, err := pt.SectorSize()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -88,20 +93,16 @@ func PreSeal(maddr address.Address, spt abi.RegisteredSealProof, offset abi.Sect
return nil, nil, xerrors.Errorf("commit: %w", err) return nil, nil, xerrors.Errorf("commit: %w", err)
} }
if err := sb.FinalizeSector(context.TODO(), sid, nil); err != nil { if err := sb.FinalizeSector(context.TODO(), sid); err != nil {
return nil, nil, xerrors.Errorf("trim cache: %w", err) return nil, nil, xerrors.Errorf("trim cache: %w", err)
} }
if err := cleanupUnsealed(sbfs, sid); err != nil {
return nil, nil, xerrors.Errorf("remove unsealed file: %w", err)
}
log.Warn("PreCommitOutput: ", sid, cids.Sealed, cids.Unsealed) log.Warn("PreCommitOutput: ", sid, cids.Sealed, cids.Unsealed)
sealedSectors = append(sealedSectors, &genesis.PreSeal{ sealedSectors = append(sealedSectors, &genesis.PreSeal{
CommR: cids.Sealed, CommR: cids.Sealed,
CommD: cids.Unsealed, CommD: cids.Unsealed,
SectorID: sid.Number, SectorID: sid.Number,
ProofType: spt, ProofType: pt,
}) })
} }
@@ -165,16 +166,6 @@ func PreSeal(maddr address.Address, spt abi.RegisteredSealProof, offset abi.Sect
return miner, &minerAddr.KeyInfo, nil return miner, &minerAddr.KeyInfo, nil
} }
func cleanupUnsealed(sbfs *basicfs.Provider, sid abi.SectorID) error {
paths, done, err := sbfs.AcquireSector(context.TODO(), sid, stores.FTUnsealed, stores.FTNone, stores.PathSealing)
if err != nil {
return err
}
defer done()
return os.Remove(paths.Unsealed)
}
func WriteGenesisMiner(maddr address.Address, sbroot string, gm *genesis.Miner, key *types.KeyInfo) error { func WriteGenesisMiner(maddr address.Address, sbroot string, gm *genesis.Miner, key *types.KeyInfo) error {
output := map[string]genesis.Miner{ output := map[string]genesis.Miner{
maddr.String(): *gm, maddr.String(): *gm,
+35 -316
View File
@@ -1,10 +1,8 @@
package main package main
import ( import (
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -13,356 +11,77 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/libp2p/go-libp2p-core/crypto" _ "github.com/filecoin-project/lotus/lib/sigs/bls"
"github.com/libp2p/go-libp2p-core/peer" _ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet" "github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/node/modules/lp2p"
"github.com/filecoin-project/lotus/node/repo"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
) )
var validTypes = []string{wallet.KTBLS, wallet.KTSecp256k1, lp2p.KTLibp2pHost} type walletInfo struct {
type keyInfoOutput struct {
Type string Type string
Address string Address string
PublicKey string PublicKey string
} }
func (wi walletInfo) String() string {
bs, _ := json.Marshal(wi)
return string(bs)
}
var keyinfoCmd = &cli.Command{ var keyinfoCmd = &cli.Command{
Name: "keyinfo", Name: "keyinfo",
Usage: "work with lotus keyinfo files (wallets and libp2p host keys)", Description: "decode a keyinfo",
Description: `The subcommands of keyinfo provide helpful tools for working with keyinfo files without
having to run the lotus daemon.`,
Subcommands: []*cli.Command{
keyinfoNewCmd,
keyinfoInfoCmd,
keyinfoImportCmd,
},
}
var keyinfoImportCmd = &cli.Command{
Name: "import",
Usage: "import a keyinfo file into a lotus repository",
Description: `The import command provides a way to import keyfiles into a lotus repository
without running the daemon.
Note: The LOTUS_PATH directory must be created. This command will not create this directory for you.
Examples
env LOTUS_PATH=/var/lib/lotus lotus-shed keyinfo import libp2p-host.keyinfo`,
Action: func(cctx *cli.Context) error {
flagRepo := cctx.String("repo")
var input io.Reader
if cctx.Args().Len() == 0 {
input = os.Stdin
} else {
var err error
input, err = os.Open(cctx.Args().First())
if err != nil {
return err
}
}
encoded, err := ioutil.ReadAll(input)
if err != nil {
return err
}
decoded, err := hex.DecodeString(strings.TrimSpace(string(encoded)))
if err != nil {
return err
}
var keyInfo types.KeyInfo
if err := json.Unmarshal(decoded, &keyInfo); err != nil {
return err
}
fsrepo, err := repo.NewFS(flagRepo)
if err != nil {
return err
}
lkrepo, err := fsrepo.Lock(repo.FullNode)
if err != nil {
return err
}
defer lkrepo.Close()
keystore, err := lkrepo.KeyStore()
if err != nil {
return err
}
switch keyInfo.Type {
case lp2p.KTLibp2pHost:
if err := keystore.Put(lp2p.KLibp2pHost, keyInfo); err != nil {
return err
}
sk, err := crypto.UnmarshalPrivateKey(keyInfo.PrivateKey)
if err != nil {
return err
}
peerid, err := peer.IDFromPrivateKey(sk)
if err != nil {
return err
}
fmt.Printf("%s\n", peerid.String())
break
case wallet.KTSecp256k1:
case wallet.KTBLS:
w, err := wallet.NewWallet(keystore)
if err != nil {
return err
}
addr, err := w.Import(&keyInfo)
if err != nil {
return err
}
fmt.Printf("%s\n", addr.String())
}
return nil
},
}
var keyinfoInfoCmd = &cli.Command{
Name: "info",
Usage: "print information about a keyinfo file",
Description: `The info command prints additional information about a key which can't easily
be retrieved by inspecting the file itself.
The 'format' flag takes a golang text/template template as its value.
The following fields can be retrived through this command
Type
Address
PublicKey
The PublicKey value will be printed base64 encoded using golangs StdEncoding
Examples
Retreive the address of a lotus wallet
lotus-shed keyinfo info --format '{{ .Address }}' wallet.keyinfo
`,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "format", Name: "format",
Value: "{{ .Type }} {{ .Address }}", Value: "{{.Address}}",
Usage: "specify which output columns to print", Usage: "Format to output",
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
format := cctx.String("format") format := cctx.String("format")
var input io.Reader var input io.Reader
if cctx.Args().Len() == 0 { if cctx.Args().Len() == 0 {
input = os.Stdin input = os.Stdin
} else { } else {
var err error input = strings.NewReader(cctx.Args().First())
input, err = os.Open(cctx.Args().First())
if err != nil {
return err
}
} }
encoded, err := ioutil.ReadAll(input) bytes, err := ioutil.ReadAll(input)
data, err := hex.DecodeString(strings.TrimSpace(string(bytes)))
if err != nil { if err != nil {
return err return err
} }
decoded, err := hex.DecodeString(strings.TrimSpace(string(encoded))) var ki types.KeyInfo
if err := json.Unmarshal(data, &ki); err != nil {
return err
}
key, err := wallet.NewKey(ki)
if err != nil { if err != nil {
return err return err
} }
var keyInfo types.KeyInfo bs, err := json.Marshal(key)
if err := json.Unmarshal(decoded, &keyInfo); err != nil {
return err
}
var kio keyInfoOutput
switch keyInfo.Type {
case lp2p.KTLibp2pHost:
kio.Type = keyInfo.Type
sk, err := crypto.UnmarshalPrivateKey(keyInfo.PrivateKey)
if err != nil {
return err
}
pk := sk.GetPublic()
peerid, err := peer.IDFromPrivateKey(sk)
if err != nil {
return err
}
pkBytes, err := pk.Raw()
if err != nil {
return err
}
kio.Address = peerid.String()
kio.PublicKey = base64.StdEncoding.EncodeToString(pkBytes)
break
case wallet.KTSecp256k1:
case wallet.KTBLS:
kio.Type = keyInfo.Type
key, err := wallet.NewKey(keyInfo)
if err != nil {
return err
}
kio.Address = key.Address.String()
kio.PublicKey = base64.StdEncoding.EncodeToString(key.PublicKey)
}
tmpl, err := template.New("output").Parse(format)
if err != nil { if err != nil {
return err return err
} }
return tmpl.Execute(os.Stdout, kio) var wi walletInfo
if err := json.Unmarshal(bs, &wi); err != nil {
return err
}
tmpl, err := template.New("").Parse(format)
if err != nil {
return err
}
return tmpl.Execute(os.Stdout, wi)
}, },
} }
var keyinfoNewCmd = &cli.Command{
Name: "new",
Usage: "create a new keyinfo file of the provided type",
ArgsUsage: "[bls|secp256k1|libp2p-host]",
Description: `Keyinfo files are base16 encoded json structures containing a type
string value, and a base64 encoded private key.
Both the bls and secp256k1 keyfiles can be imported into a running lotus daemon using
the 'lotus wallet import' command. Or imported to a non-running / unitialized repo using
the 'lotus-shed keyinfo import' command. Libp2p host keys can only be imported using lotus-shed
as lotus itself does not provide this functionality at the moment.`,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Value: "<type>-<addr>.keyinfo",
Usage: "output file formt",
},
&cli.BoolFlag{
Name: "silent",
Value: false,
Usage: "do not print the address to stdout",
},
},
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("please specify a type to generate")
}
keyType := cctx.Args().First()
flagOutput := cctx.String("output")
if i := SliceIndex(len(validTypes), func(i int) bool {
if keyType == validTypes[i] {
return true
}
return false
}); i == -1 {
return fmt.Errorf("invalid key type argument provided '%s'", keyType)
}
keystore := wallet.NewMemKeyStore()
var keyAddr string
var keyInfo types.KeyInfo
switch keyType {
case lp2p.KTLibp2pHost:
sk, err := lp2p.PrivKey(keystore)
if err != nil {
return err
}
ki, err := keystore.Get(lp2p.KLibp2pHost)
if err != nil {
return err
}
peerid, err := peer.IDFromPrivateKey(sk)
if err != nil {
return err
}
keyAddr = peerid.String()
keyInfo = ki
break
case wallet.KTSecp256k1:
case wallet.KTBLS:
key, err := wallet.GenerateKey(wallet.ActSigType(keyType))
if err != nil {
return err
}
keyAddr = key.Address.String()
keyInfo = key.KeyInfo
break
}
filename := flagOutput
filename = strings.ReplaceAll(filename, "<addr>", keyAddr)
filename = strings.ReplaceAll(filename, "<type>", keyType)
file, err := os.Create(filename)
if err != nil {
return err
}
defer func() {
if err := file.Close(); err != nil {
log.Warnf("failed to close output file: %w", err)
}
}()
bytes, err := json.Marshal(keyInfo)
if err != nil {
return err
}
encoded := hex.EncodeToString(bytes)
if _, err := file.Write([]byte(encoded)); err != nil {
return err
}
if !cctx.Bool("silent") {
fmt.Println(keyAddr)
}
return nil
},
}
func SliceIndex(length int, fn func(i int) bool) int {
for i := 0; i < length; i++ {
if fn(i) {
return i
}
}
return -1
}
+1
View File
@@ -19,6 +19,7 @@ func main() {
base16Cmd, base16Cmd,
bitFieldCmd, bitFieldCmd,
keyinfoCmd, keyinfoCmd,
peerkeyCmd,
noncefix, noncefix,
bigIntParseCmd, bigIntParseCmd,
staterootStatsCmd, staterootStatsCmd,
+101
View File
@@ -0,0 +1,101 @@
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/lp2p"
"github.com/libp2p/go-libp2p-core/peer"
)
type keystore struct {
set bool
info types.KeyInfo
}
func (ks *keystore) Put(name string, info types.KeyInfo) error {
ks.info = info
ks.set = true
return nil
}
func (ks *keystore) Get(name string) (types.KeyInfo, error) {
if !ks.set {
return types.KeyInfo{}, types.ErrKeyInfoNotFound
}
return ks.info, nil
}
func (ks *keystore) Delete(name string) error {
panic("Implement me")
}
func (ks *keystore) List() ([]string, error) {
panic("Implement me")
}
var peerkeyCmd = &cli.Command{
Name: "peerkey",
Description: "create libp2p host key",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Value: "<peerid>.peerkey",
Usage: "Output file format",
},
&cli.BoolFlag{
Name: "silent",
Value: false,
Usage: "Do not print peerid at end",
},
},
Action: func(cctx *cli.Context) error {
output := cctx.String("output")
ks := keystore{}
sk, err := lp2p.PrivKey(&ks)
if err != nil {
return err
}
bs, err := json.Marshal(ks.info)
if err != nil {
return err
}
peerid, err := peer.IDFromPrivateKey(sk)
if err != nil {
return err
}
output = strings.ReplaceAll(output, "<peerid>", peerid.String())
f, err := os.Create(output)
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
log.Warnf("failed to close output file: %w", err)
}
}()
if _, err := f.Write(bs); err != nil {
return err
}
if !cctx.Bool("silent") {
fmt.Println(peerid.String())
}
return nil
},
}
+1 -1
View File
@@ -88,7 +88,7 @@ var verifySealProofCmd = &cli.Command{
Number: snum, Number: snum,
}, },
SealedCID: commr, SealedCID: commr,
SealProof: abi.RegisteredSealProof(cctx.Int64("proof-type")), RegisteredProof: abi.RegisteredProof(cctx.Int64("proof-type")),
Proof: proof, Proof: proof,
DealIDs: nil, DealIDs: nil,
Randomness: abi.SealRandomness(ticket), Randomness: abi.SealRandomness(ticket),
-94
View File
@@ -1,94 +0,0 @@
package main
import (
"fmt"
ma "github.com/multiformats/go-multiaddr"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
)
var actorCmd = &cli.Command{
Name: "actor",
Usage: "manipulate the miner actor",
Subcommands: []*cli.Command{
actorSetAddrsCmd,
},
}
var actorSetAddrsCmd = &cli.Command{
Name: "set-addrs",
Usage: "set addresses that your miner can be publically dialed on",
Flags: []cli.Flag{
&cli.Int64Flag{
Name: "gas-limit",
Usage: "set gas limit",
Value: 100000,
},
},
Action: func(cctx *cli.Context) error {
nodeAPI, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
api, acloser, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer acloser()
ctx := lcli.ReqContext(cctx)
var addrs []abi.Multiaddrs
for _, a := range cctx.Args().Slice() {
maddr, err := ma.NewMultiaddr(a)
if err != nil {
return fmt.Errorf("failed to parse %q as a multiaddr: %w", a, err)
}
addrs = append(addrs, maddr.Bytes())
}
maddr, err := nodeAPI.ActorAddress(ctx)
if err != nil {
return err
}
minfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK)
if err != nil {
return err
}
params, err := actors.SerializeParams(&miner.ChangeMultiaddrsParams{NewMultiaddrs: addrs})
if err != nil {
return err
}
gasLimit := cctx.Int64("gas-limit")
smsg, err := api.MpoolPushMessage(ctx, &types.Message{
To: maddr,
From: minfo.Worker,
Value: types.NewInt(0),
GasPrice: types.NewInt(1),
GasLimit: gasLimit,
Method: 18,
Params: params,
})
if err != nil {
return err
}
fmt.Printf("Requested multiaddrs change in message %s\n", smsg.Cid())
return nil
},
}
+9 -14
View File
@@ -12,7 +12,6 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/specs-actors/actors/builtin/miner" "github.com/filecoin-project/specs-actors/actors/builtin/miner"
"github.com/filecoin-project/specs-actors/actors/builtin/power"
sealing "github.com/filecoin-project/storage-fsm" sealing "github.com/filecoin-project/storage-fsm"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
@@ -120,20 +119,16 @@ var infoCmd = &cli.Command{
faultyPercentage) faultyPercentage)
} }
if pow.MinerPower.RawBytePower.LessThan(power.ConsensusMinerMinPower) { expWinChance := float64(types.BigMul(qpercI, types.NewInt(build.BlocksPerEpoch)).Int64()) / 1000000
fmt.Print("Below minimum power threshold, no blocks will be won") if expWinChance > 0 {
} else { if expWinChance > 1 {
expWinChance := float64(types.BigMul(qpercI, types.NewInt(build.BlocksPerEpoch)).Int64()) / 1000000 expWinChance = 1
if expWinChance > 0 {
if expWinChance > 1 {
expWinChance = 1
}
winRate := time.Duration(float64(time.Second*time.Duration(build.BlockDelaySecs)) / expWinChance)
winPerDay := float64(time.Hour*24) / float64(winRate)
fmt.Print("Expected block win rate: ")
color.Blue("%.4f/day (every %s)", winPerDay, winRate.Truncate(time.Second))
} }
winRate := time.Duration(float64(time.Second*build.BlockDelay) / expWinChance)
winPerDay := float64(time.Hour*24) / float64(winRate)
fmt.Print("Expected block win rate: ")
color.Blue("%.4f/day (every %s)", winPerDay, winRate.Truncate(time.Second))
} }
fmt.Println() fmt.Println()
+1 -1
View File
@@ -460,7 +460,7 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api lapi.FullNode,
} }
if cerr != nil { if cerr != nil {
return xerrors.Errorf("failed to configure storage miner: %w", cerr) return xerrors.Errorf("failed to configure storage miner: %w", err)
} }
} }
+2 -3
View File
@@ -22,9 +22,7 @@ func main() {
lotuslog.SetupLogLevels() lotuslog.SetupLogLevels()
local := []*cli.Command{ local := []*cli.Command{
actorCmd, dealsCmd,
storageDealsCmd,
retrievalDealsCmd,
infoCmd, infoCmd,
initCmd, initCmd,
rewardsCmd, rewardsCmd,
@@ -32,6 +30,7 @@ func main() {
stopCmd, stopCmd,
sectorsCmd, sectorsCmd,
storageCmd, storageCmd,
setPriceCmd,
workersCmd, workersCmd,
provingCmd, provingCmd,
} }
+13 -360
View File
@@ -1,301 +1,47 @@
package main package main
import ( import (
"bufio"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"path/filepath"
"text/tabwriter"
"time"
"github.com/docker/go-units"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-cidutil/cidenc"
"github.com/multiformats/go-multibase"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli" lcli "github.com/filecoin-project/lotus/cli"
"github.com/ipfs/go-cid"
"github.com/urfave/cli/v2"
) )
var CidBaseFlag = cli.StringFlag{ var setPriceCmd = &cli.Command{
Name: "cid-base", Name: "set-price",
Hidden: true, Usage: "Set price that miner will accept storage deals at (FIL / GiB / Epoch)",
Value: "base32", Flags: []cli.Flag{},
Usage: "Multibase encoding used for version 1 CIDs in output.",
DefaultText: "base32",
}
// GetCidEncoder returns an encoder using the `cid-base` flag if provided, or
// the default (Base32) encoder if not.
func GetCidEncoder(cctx *cli.Context) (cidenc.Encoder, error) {
val := cctx.String("cid-base")
e := cidenc.Encoder{Base: multibase.MustNewEncoder(multibase.Base32)}
if val != "" {
var err error
e.Base, err = multibase.EncoderByName(val)
if err != nil {
return e, err
}
}
return e, nil
}
var storageDealSelectionCmd = &cli.Command{
Name: "selection",
Usage: "Configure acceptance criteria for storage deal proposals",
Subcommands: []*cli.Command{
storageDealSelectionShowCmd,
storageDealSelectionResetCmd,
storageDealSelectionRejectCmd,
},
}
var storageDealSelectionShowCmd = &cli.Command{
Name: "list",
Usage: "List storage deal proposal selection criteria",
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
smapi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
onlineOk, err := smapi.DealsConsiderOnlineStorageDeals(lcli.DaemonContext(cctx))
if err != nil {
return err
}
offlineOk, err := smapi.DealsConsiderOfflineStorageDeals(lcli.DaemonContext(cctx))
if err != nil {
return err
}
fmt.Printf("considering online storage deals: %t\n", onlineOk)
fmt.Printf("considering offline storage deals: %t\n", offlineOk)
return nil
},
}
var storageDealSelectionResetCmd = &cli.Command{
Name: "reset",
Usage: "Reset storage deal proposal selection criteria to default values",
Action: func(cctx *cli.Context) error {
smapi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
err = smapi.DealsSetConsiderOnlineStorageDeals(lcli.DaemonContext(cctx), true)
if err != nil {
return err
}
err = smapi.DealsSetConsiderOfflineStorageDeals(lcli.DaemonContext(cctx), true)
if err != nil {
return err
}
return nil
},
}
var storageDealSelectionRejectCmd = &cli.Command{
Name: "reject",
Usage: "Configure criteria which necessitate automatic rejection",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "online",
},
&cli.BoolFlag{
Name: "offline",
},
},
Action: func(cctx *cli.Context) error {
smapi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
if cctx.Bool("online") {
err = smapi.DealsSetConsiderOnlineStorageDeals(lcli.DaemonContext(cctx), false)
if err != nil {
return err
}
}
if cctx.Bool("offline") {
err = smapi.DealsSetConsiderOfflineStorageDeals(lcli.DaemonContext(cctx), false)
if err != nil {
return err
}
}
return nil
},
}
var setAskCmd = &cli.Command{
Name: "set-ask",
Usage: "Configure the miner's ask",
Flags: []cli.Flag{
&cli.Uint64Flag{
Name: "price",
Usage: "Set the price of the ask (specified as FIL / GiB / Epoch) to `PRICE`",
Required: true,
},
&cli.StringFlag{
Name: "duration",
Usage: "Set duration of ask (a quantity of time after which the ask expires) `DURATION`",
DefaultText: "720h0m0s",
Value: "720h0m0s",
},
&cli.StringFlag{
Name: "min-piece-size",
Usage: "Set minimum piece size (w/bit-padding, in bytes) in ask to `SIZE`",
DefaultText: "256B",
Value: "256B",
},
&cli.StringFlag{
Name: "max-piece-size",
Usage: "Set maximum piece size (w/bit-padding, in bytes) in ask to `SIZE`",
DefaultText: "miner sector size",
},
},
Action: func(cctx *cli.Context) error {
ctx := lcli.DaemonContext(cctx)
api, closer, err := lcli.GetStorageMinerAPI(cctx) api, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil { if err != nil {
return err return err
} }
defer closer() defer closer()
pri := types.NewInt(cctx.Uint64("price"))
dur, err := time.ParseDuration(cctx.String("duration"))
if err != nil {
return xerrors.Errorf("cannot parse duration: %w", err)
}
qty := dur.Seconds() / float64(build.BlockDelaySecs)
min, err := units.RAMInBytes(cctx.String("min-piece-size"))
if err != nil {
return xerrors.Errorf("cannot parse min-piece-size to quantity of bytes: %w", err)
}
if min < 256 {
return xerrors.New("minimum piece size (w/bit-padding) is 256B")
}
max, err := units.RAMInBytes(cctx.String("max-piece-size"))
if err != nil {
return xerrors.Errorf("cannot parse max-piece-size to quantity of bytes: %w", err)
}
maddr, err := api.ActorAddress(ctx)
if err != nil {
return err
}
ssize, err := api.ActorSectorSize(ctx, maddr)
if err != nil {
return err
}
smax := int64(ssize)
if max == 0 {
max = smax
}
if max > smax {
return xerrors.Errorf("max piece size (w/bit-padding) %s cannot exceed miner sector size %s", types.SizeStr(types.NewInt(uint64(max))), types.SizeStr(types.NewInt(uint64(smax))))
}
return api.MarketSetAsk(ctx, pri, abi.ChainEpoch(qty), abi.PaddedPieceSize(min), abi.PaddedPieceSize(max))
},
}
var getAskCmd = &cli.Command{
Name: "get-ask",
Usage: "Print the miner's ask",
Flags: []cli.Flag{},
Action: func(cctx *cli.Context) error {
ctx := lcli.DaemonContext(cctx) ctx := lcli.DaemonContext(cctx)
fnapi, closer, err := lcli.GetFullNodeAPI(cctx) if !cctx.Args().Present() {
if err != nil { return fmt.Errorf("must specify price to set")
return err
} }
defer closer()
smapi, closer, err := lcli.GetStorageMinerAPI(cctx) fp, err := types.ParseFIL(cctx.Args().First())
if err != nil {
return err
}
defer closer()
sask, err := smapi.MarketGetAsk(ctx)
if err != nil { if err != nil {
return err return err
} }
var ask *storagemarket.StorageAsk return api.MarketSetPrice(ctx, types.BigInt(fp))
if sask != nil && sask.Ask != nil {
ask = sask.Ask
}
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
fmt.Fprintf(w, "Price per GiB / Epoch\tMin. Piece Size (w/bit-padding)\tMax. Piece Size (w/bit-padding)\tExpiry (Epoch)\tExpiry (Appx. Rem. Time)\tSeq. No.\n")
if ask == nil {
fmt.Fprintf(w, "<miner does not have an ask>\n")
return w.Flush()
}
head, err := fnapi.ChainHead(ctx)
if err != nil {
return err
}
dlt := ask.Expiry - head.Height()
rem := "<expired>"
if dlt > 0 {
rem = (time.Second * time.Duration(int64(dlt)*int64(build.BlockDelaySecs))).String()
}
fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%s\t%d\n", ask.Price, types.SizeStr(types.NewInt(uint64(ask.MinPieceSize))), types.SizeStr(types.NewInt(uint64(ask.MaxPieceSize))), ask.Expiry, rem, ask.SeqNo)
return w.Flush()
}, },
} }
var storageDealsCmd = &cli.Command{ var dealsCmd = &cli.Command{
Name: "storage-deals", Name: "deals",
Usage: "Manage storage deals and related configuration", Usage: "interact with your deals",
Subcommands: []*cli.Command{ Subcommands: []*cli.Command{
dealsImportDataCmd, dealsImportDataCmd,
dealsListCmd, dealsListCmd,
storageDealSelectionCmd,
setAskCmd,
getAskCmd,
setBlocklistCmd,
getBlocklistCmd,
resetBlocklistCmd,
}, },
} }
@@ -354,96 +100,3 @@ var dealsListCmd = &cli.Command{
return nil return nil
}, },
} }
var getBlocklistCmd = &cli.Command{
Name: "get-blocklist",
Usage: "List the contents of the storage miner's piece CID blocklist",
Flags: []cli.Flag{
&CidBaseFlag,
},
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
blocklist, err := api.DealsPieceCidBlocklist(lcli.DaemonContext(cctx))
if err != nil {
return err
}
encoder, err := GetCidEncoder(cctx)
if err != nil {
return err
}
for idx := range blocklist {
fmt.Println(encoder.Encode(blocklist[idx]))
}
return nil
},
}
var setBlocklistCmd = &cli.Command{
Name: "set-blocklist",
Usage: "Set the storage miner's list of blocklisted piece CIDs",
ArgsUsage: "[<path-of-file-containing-newline-delimited-piece-CIDs> (optional, will read from stdin if omitted)]",
Flags: []cli.Flag{},
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
scanner := bufio.NewScanner(os.Stdin)
if cctx.Args().Present() && cctx.Args().First() != "-" {
absPath, err := filepath.Abs(cctx.Args().First())
if err != nil {
return err
}
file, err := os.Open(absPath)
if err != nil {
log.Fatal(err)
}
defer file.Close() //nolint:errcheck
scanner = bufio.NewScanner(file)
}
var blocklist []cid.Cid
for scanner.Scan() {
decoded, err := cid.Decode(scanner.Text())
if err != nil {
return err
}
blocklist = append(blocklist, decoded)
}
err = scanner.Err()
if err != nil {
return err
}
return api.DealsSetPieceCidBlocklist(lcli.DaemonContext(cctx), blocklist)
},
}
var resetBlocklistCmd = &cli.Command{
Name: "reset-blocklist",
Usage: "Remove all entries from the storage miner's piece CID blocklist",
Flags: []cli.Flag{},
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
return api.DealsSetPieceCidBlocklist(lcli.DaemonContext(cctx), []cid.Cid{})
},
}
+5 -77
View File
@@ -21,86 +21,15 @@ import (
) )
var provingCmd = &cli.Command{ var provingCmd = &cli.Command{
Name: "proving", Name: "proving",
Usage: "View proving information",
Subcommands: []*cli.Command{ Subcommands: []*cli.Command{
provingInfoCmd, provingInfoCmd,
provingDeadlinesCmd, provingDeadlinesCmd,
provingFaultsCmd,
},
}
var provingFaultsCmd = &cli.Command{
Name: "faults",
Usage: "View the currently known proving faulty sectors information",
Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
api, acloser, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer acloser()
ctx := lcli.ReqContext(cctx)
maddr, err := nodeApi.ActorAddress(ctx)
if err != nil {
return xerrors.Errorf("getting actor address: %w", err)
}
var mas miner.State
{
mact, err := api.StateGetActor(ctx, maddr, types.EmptyTSK)
if err != nil {
return err
}
rmas, err := api.ChainReadObj(ctx, mact.Head)
if err != nil {
return err
}
if err := mas.UnmarshalCBOR(bytes.NewReader(rmas)); err != nil {
return err
}
}
faults, err := mas.Faults.All(100000000000)
if err != nil {
return err
}
if len(faults) == 0 {
fmt.Println("no faulty sectors")
}
head, err := api.ChainHead(ctx)
if err != nil {
return xerrors.Errorf("getting chain head: %w", err)
}
deadlines, err := api.StateMinerDeadlines(ctx, maddr, head.Key())
if err != nil {
return xerrors.Errorf("getting miner deadlines: %w", err)
}
tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
_, _ = fmt.Fprintln(tw, "deadline\tsectors")
for deadline, sectors := range deadlines.Due {
intersectSectors, _ := bitfield.IntersectBitField(sectors, mas.Faults)
if intersectSectors != nil {
allSectors, _ := intersectSectors.All(100000000000)
for _, num := range allSectors {
_, _ = fmt.Fprintf(tw, "%d\t%d\n", deadline, num)
}
}
}
return tw.Flush()
}, },
} }
var provingInfoCmd = &cli.Command{ var provingInfoCmd = &cli.Command{
Name: "info", Name: "info",
Usage: "View current state information",
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil { if err != nil {
@@ -211,19 +140,18 @@ var provingInfoCmd = &cli.Command{
func epochTime(curr, e abi.ChainEpoch) string { func epochTime(curr, e abi.ChainEpoch) string {
switch { switch {
case curr > e: case curr > e:
return fmt.Sprintf("%d (%s ago)", e, time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e))) return fmt.Sprintf("%d (%s ago)", e, time.Second*time.Duration(build.BlockDelay*(curr-e)))
case curr == e: case curr == e:
return fmt.Sprintf("%d (now)", e) return fmt.Sprintf("%d (now)", e)
case curr < e: case curr < e:
return fmt.Sprintf("%d (in %s)", e, time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr))) return fmt.Sprintf("%d (in %s)", e, time.Second*time.Duration(build.BlockDelay*(e-curr)))
} }
panic("math broke") panic("math broke")
} }
var provingDeadlinesCmd = &cli.Command{ var provingDeadlinesCmd = &cli.Command{
Name: "deadlines", Name: "deadlines",
Usage: "View the current proving period deadlines information",
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil { if err != nil {
-113
View File
@@ -1,113 +0,0 @@
package main
import (
"fmt"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/urfave/cli/v2"
)
var retrievalDealsCmd = &cli.Command{
Name: "retrieval-deals",
Usage: "Manage retrieval deals and related configuration",
Subcommands: []*cli.Command{
retrievalDealSelectionCmd,
},
}
var retrievalDealSelectionCmd = &cli.Command{
Name: "selection",
Usage: "Configure acceptance criteria for retrieval deal proposals",
Subcommands: []*cli.Command{
retrievalDealSelectionShowCmd,
retrievalDealSelectionResetCmd,
retrievalDealSelectionRejectCmd,
},
}
var retrievalDealSelectionShowCmd = &cli.Command{
Name: "list",
Usage: "List retrieval deal proposal selection criteria",
Action: func(cctx *cli.Context) error {
smapi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
onlineOk, err := smapi.DealsConsiderOnlineRetrievalDeals(lcli.DaemonContext(cctx))
if err != nil {
return err
}
offlineOk, err := smapi.DealsConsiderOfflineRetrievalDeals(lcli.DaemonContext(cctx))
if err != nil {
return err
}
fmt.Printf("considering online retrieval deals: %t\n", onlineOk)
fmt.Printf("considering offline retrieval deals: %t\n", offlineOk)
return nil
},
}
var retrievalDealSelectionResetCmd = &cli.Command{
Name: "reset",
Usage: "Reset retrieval deal proposal selection criteria to default values",
Action: func(cctx *cli.Context) error {
smapi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
err = smapi.DealsSetConsiderOnlineRetrievalDeals(lcli.DaemonContext(cctx), true)
if err != nil {
return err
}
err = smapi.DealsSetConsiderOfflineRetrievalDeals(lcli.DaemonContext(cctx), true)
if err != nil {
return err
}
return nil
},
}
var retrievalDealSelectionRejectCmd = &cli.Command{
Name: "reject",
Usage: "Configure criteria which necessitate automatic rejection",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "online",
},
&cli.BoolFlag{
Name: "offline",
},
},
Action: func(cctx *cli.Context) error {
smapi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
if cctx.Bool("online") {
err = smapi.DealsSetConsiderOnlineRetrievalDeals(lcli.DaemonContext(cctx), false)
if err != nil {
return err
}
}
if cctx.Bool("offline") {
err = smapi.DealsSetConsiderOfflineRetrievalDeals(lcli.DaemonContext(cctx), false)
if err != nil {
return err
}
}
return nil
},
}
+5 -40
View File
@@ -27,7 +27,6 @@ var sectorsCmd = &cli.Command{
sectorsRefsCmd, sectorsRefsCmd,
sectorsUpdateCmd, sectorsUpdateCmd,
sectorsPledgeCmd, sectorsPledgeCmd,
sectorsRemoveCmd,
}, },
} }
@@ -47,9 +46,8 @@ var sectorsPledgeCmd = &cli.Command{
} }
var sectorsStatusCmd = &cli.Command{ var sectorsStatusCmd = &cli.Command{
Name: "status", Name: "status",
Usage: "Get the seal status of a sector by its number", Usage: "Get the seal status of a sector by its ID",
ArgsUsage: "<sectorNum>",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{ &cli.BoolFlag{
Name: "log", Name: "log",
@@ -65,7 +63,7 @@ var sectorsStatusCmd = &cli.Command{
ctx := lcli.ReqContext(cctx) ctx := lcli.ReqContext(cctx)
if !cctx.Args().Present() { if !cctx.Args().Present() {
return fmt.Errorf("must specify sector number to get status of") return fmt.Errorf("must specify sector ID to get status of")
} }
id, err := strconv.ParseUint(cctx.Args().First(), 10, 64) id, err := strconv.ParseUint(cctx.Args().First(), 10, 64)
@@ -210,39 +208,6 @@ var sectorsRefsCmd = &cli.Command{
}, },
} }
var sectorsRemoveCmd = &cli.Command{
Name: "remove",
Usage: "Forcefully remove a sector (WARNING: This means losing power and collateral for the removed sector)",
ArgsUsage: "<sectorNum>",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "really-do-it",
Usage: "pass this flag if you know what you are doing",
},
},
Action: func(cctx *cli.Context) error {
if !cctx.Bool("really-do-it") {
return xerrors.Errorf("this is a command for advanced users, only use it if you are sure of what you are doing")
}
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
if cctx.Args().Len() != 1 {
return xerrors.Errorf("must pass sector number")
}
id, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64)
if err != nil {
return xerrors.Errorf("could not parse sector number: %w", err)
}
return nodeApi.SectorRemove(ctx, abi.SectorNumber(id))
},
}
var sectorsUpdateCmd = &cli.Command{ var sectorsUpdateCmd = &cli.Command{
Name: "update-state", Name: "update-state",
Usage: "ADVANCED: manually update the state of a sector, this may aid in error recovery", Usage: "ADVANCED: manually update the state of a sector, this may aid in error recovery",
@@ -263,12 +228,12 @@ var sectorsUpdateCmd = &cli.Command{
defer closer() defer closer()
ctx := lcli.ReqContext(cctx) ctx := lcli.ReqContext(cctx)
if cctx.Args().Len() < 2 { if cctx.Args().Len() < 2 {
return xerrors.Errorf("must pass sector number and new state") return xerrors.Errorf("must pass sector ID and new state")
} }
id, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64) id, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64)
if err != nil { if err != nil {
return xerrors.Errorf("could not parse sector number: %w", err) return xerrors.Errorf("could not parse sector ID: %w", err)
} }
return nodeApi.SectorsUpdate(ctx, abi.SectorNumber(id), api.SectorState(cctx.Args().Get(1))) return nodeApi.SectorsUpdate(ctx, abi.SectorNumber(id), api.SectorState(cctx.Args().Get(1)))
-21
View File
@@ -19,7 +19,6 @@ import (
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
"github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multiaddr"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"go.opencensus.io/plugin/runmetrics"
"go.opencensus.io/stats" "go.opencensus.io/stats"
"go.opencensus.io/stats/view" "go.opencensus.io/stats/view"
"go.opencensus.io/tag" "go.opencensus.io/tag"
@@ -32,7 +31,6 @@ import (
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
lcli "github.com/filecoin-project/lotus/cli" lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/lib/peermgr" "github.com/filecoin-project/lotus/lib/peermgr"
"github.com/filecoin-project/lotus/lib/ulimit"
"github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/metrics"
"github.com/filecoin-project/lotus/node" "github.com/filecoin-project/lotus/node"
"github.com/filecoin-project/lotus/node/modules" "github.com/filecoin-project/lotus/node/modules"
@@ -114,27 +112,8 @@ var DaemonCmd = &cli.Command{
Name: "profile", Name: "profile",
Usage: "specify type of node", Usage: "specify type of node",
}, },
&cli.BoolFlag{
Name: "manage-fdlimit",
Usage: "manage open file limit",
Value: true,
},
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
err := runmetrics.Enable(runmetrics.RunMetricOptions{
EnableCPU: true,
EnableMemory: true,
})
if err != nil {
return xerrors.Errorf("enabling runtime metrics: %w", err)
}
if cctx.Bool("manage-fdlimit") {
if _, _, err := ulimit.ManageFdLimit(); err != nil {
log.Errorf("setting file descriptor limit: %s", err)
}
}
if prof := cctx.String("pprof"); prof != "" { if prof := cctx.String("pprof"); prof != "" {
profile, err := os.Create(prof) profile, err := os.Create(prof)
if err != nil { if err != nil {
+2 -2
View File
@@ -69,10 +69,10 @@ func init() {
} }
// TODO: beacon // TODO: beacon
uts := head.MinTimestamp() + uint64(build.BlockDelaySecs) uts := head.MinTimestamp() + uint64(build.BlockDelay)
nheight := head.Height() + 1 nheight := head.Height() + 1
blk, err := api.MinerCreateBlock(ctx, &lapi.BlockTemplate{ blk, err := api.MinerCreateBlock(ctx, &lapi.BlockTemplate{
addr, head.Key(), ticket, &types.ElectionProof{}, nil, msgs, nheight, uts, gen.ValidWpostForTesting, addr, head.Key(), ticket, nil, nil, msgs, nheight, uts, nil,
}) })
if err != nil { if err != nil {
return xerrors.Errorf("creating block: %w", err) return xerrors.Errorf("creating block: %w", err)
+4
View File
@@ -63,6 +63,10 @@
"title": "Proof-of-Spacetime(s)", "title": "Proof-of-Spacetime(s)",
"value": "Filecoin is a protocol token whose blockchain runs on a novel proof, called Proof-of-Spacetime, where blocks are created by miners that are storing data." "value": "Filecoin is a protocol token whose blockchain runs on a novel proof, called Proof-of-Spacetime, where blocks are created by miners that are storing data."
}, },
"lotus-testnet": {
"title": "Filecoin Testnet",
"value": "Until we launch, we are making lots of changes to Lotus. The Testnet is expected to bring a few significant fixes/improvements. During Testnet, you can retrieve test filecoin from our network faucet to use as collateral to start mining. Test filecoin do not have any value the official filecoin tokens will not be released until Mainnet launch."
},
"filecoin-testnet": { "filecoin-testnet": {
"title": "Filecoin Testnet", "title": "Filecoin Testnet",
"value": "Until we launch, we are making lots of changes to Lotus. The Testnet is expected to bring a few significant fixes/improvements. During Testnet, you can retrieve test filecoin from our network faucet to use as collateral to start mining. Test filecoin do not have any value the official filecoin tokens will not be released until Mainnet launch." "value": "Until we launch, we are making lots of changes to Lotus. The Testnet is expected to bring a few significant fixes/improvements. During Testnet, you can retrieve test filecoin from our network faucet to use as collateral to start mining. Test filecoin do not have any value the official filecoin tokens will not be released until Mainnet launch."
+2 -8
View File
@@ -58,8 +58,8 @@
}, },
{ {
"title": "Use Lotus with systemd", "title": "Use Lotus with systemd",
"slug": "en+install-systemd-services", "slug": "en+install-system-services",
"github": "en/install-systemd-services.md", "github": "en/install-system-services.md",
"value": null "value": null
}, },
{ {
@@ -71,12 +71,6 @@
] ]
}, },
{ {
"title": "Architecture",
"slug": "en+arch",
"github": "en/architecture.md",
"value": null,
"posts": []
}, {
"title": "Storage Mining", "title": "Storage Mining",
"slug": "en+mining", "slug": "en+mining",
"github": "en/mining.md", "github": "en/mining.md",
-394
View File
@@ -1,394 +0,0 @@
# Lotus
Lotus is an implementation of the [Filecoin Distributed Storage Network](https://filecoin.io/).
A Lotus node syncs blockchains that follow the
Filecoin protocol, validating the blocks and state transitions.
The specification for the Filecoin protocol can be found [here](https://filecoin-project.github.io/specs/).
For information on how to setup and operate a Lotus node,
please follow the instructions [here](https://lotu.sh/en+getting-started).
# Components
At a high level, a Lotus node comprises the following components:
FIXME: No mention of block production here, cross-reference with schomatis's miner doc
- The Syncer, which manages the process of syncing the blockchain
- The State Manager, which can compute the state at any given point in the chain
- The Virtual Machine (VM), which executes messages
- The Repository, where all data is stored
- P2P stuff (FIXME missing libp2p listed under other PL dependencies)? allows hello, blocksync, retrieval, storage
- API / CLI (FIXME missing, in scratchpad)
- Other Filecoin dependencies (specs actors, proofs, storage, etc., FIXME missing)
- Is the Builder worth its own component?
- Other PL dependencies (IPFS, libp2p, IPLD? FIXME, missing)
- External libraries used by Lotus and other deps (FIXME, missing)
# Preliminaries
We discuss some key Filecoin concepts here, aiming to explain them by contrasting them with analogous concepts
in other well-known blockchains like Ethereum. We only provide brief descriptions here; elaboration
can be found in the [spec](https://filecoin-project.github.io/specs/).
### Tipsets
Unlike in Ethereum, a block can have multiple parents in Filecoin. We thus refer to the parent set of a block,
instead of a single parent.
A [tipset](https://filecoin-project.github.io/specs/#systems__filecoin_blockchain__struct__tipset)
is any set of blocks that share the same parent set.
There is no concept of "block difficulty" in Filecoin. Instead,
the weight of a tipset is simply the number of blocks in the chain that ends in that tipset. Note that a longer chain
can have less weight than a shorter chain with more blocks per tipset.
We also allow for "null" tipsets, which include zero blocks. This allows miners to "skip" a round, and build on top
of an imaginary empty tipset if they want to.
We call the heaviest tipset in a chain the "head" of the chain.
### Actors and Messages
An [Actor](https://filecoin-project.github.io/specs/#systems__filecoin_vm__actor)
is analogous to a smart contract in Ethereum. Filecoin does not allow users to define their own
actors, but comes with several [builtin actors](https://github.com/filecoin-project/specs-actors),
which can be thought of as pre-compiled contracts.
A [Message](https://filecoin-project.github.io/specs/#systems__filecoin_vm__message)
is analogous to transactions in Ethereum.
# Sync
Sync refers to the process by which a Lotus node synchronizes to the heaviest chain being advertised by its peers.
At a high-level, Lotus syncs in a manner similar to most other blockchains; a Lotus node listens to the various
chains its peers claim to be at, picks the heaviest one, requests the blocks in the chosen chain,
and validates each block in that chain, running all state transitions along the way.
The majority of the sync functionality happens in the [`Syncer`](https://github.com/filecoin-project/lotus/blob/master/chain/sync.go),
internally managed by a [`SyncManager`](https://github.com/filecoin-project/lotus/blob/master/chain/sync_manager.go).
We now discuss the various stages of the sync process.
## Sync setup
When a Lotus node connects to a new peer, we exchange the head of our chain
with the new peer through [the `hello` protocol](https://github.com/filecoin-project/lotus/blob/master/node/hello/hello.go).
If the peer's head is heavier than ours, we try to sync to it. Note
that we do NOT update our chain head at this stage.
## Fetching and Persisting Block Headers
Note: The API refers to these stages as `StageHeaders` and `StagePersistHeaders`.
We proceed in the sync process by requesting block headers from the peer,
moving back from their head, until we reach a tipset that we have in common
(such a common tipset must exist, thought it may simply be the genesis block).
The functionality can be found in `Syncer::collectHeaders()`.
If the common tipset is our head, we treat the sync as a "fast-forward", else we must
drop part of our chain to connect to the peer's head (referred to as "forking").
FIXME: This next para might be best replaced with a link to the validation doc
Some of the possible causes of failure in this stage include:
- The chain is linked to a block that we have previously marked as bad,
and stored in a [`BadBlockCache`](https://github.com/filecoin-project/lotus/blob/master/chain/badtscache.go).
- The beacon entries in a block are inconsistent (FIXME: more details about what is validated here wouldn't be bad).
- Switching to this new chain would involve a chain reorganization beyond the allowed threshold (SPECK-CHECK).
## Fetching and Validating Blocks
Note: The API refers to this stage as `StageMessages`.
Having acquired the headers and found a common tipset, we then move forward, requesting the full blocks, including the messages.
For each block, we first confirm the syntactic validity of the block (SPECK-CHECK),
which includes the syntactic validity of messages included
in the block.
We then apply the messages, running all the state transitions, and compare the state root we calculate with the provided state root.
FIXME: This next para might be best replaced with a link to the validation doc
Some of the possible causes of failure in this stage include:
- a block is syntactically invalid (including potentially containing syntactically invalid messages)
- the computed state root after applying the block doesn't match the block's state root
- FIXME: Check what's covered by syntactic validity, and add anything important that isn't (like proof validity, future checks, etc.)
The core functionality can be found in `Syncer::ValidateTipset()`, with `Syncer::checkBlockMessages()` performing
syntactic validation of messages.
## Setting the head
Note: The API refers to this stage as `StageSyncComplete`.
If all validations pass we will now set that head as our heaviest tipset in
[`ChainStore`](https://github.com/filecoin-project/lotus/blob/master/chain/store/store.go).
We already have the full state, since we calculated
it during the sync process.
FIXME (aayush) I don't fuilly understand the next 2 paragraphs, but it seems important. Confirm and polish.
Relevant issue in IPFS: https://github.com/ipfs/ipfs-docs/issues/264
It is important to note at this point that similar to the IPFS architecture of addressing by content and not by location/address (FIXME: check and link to IPFS docs) the "actual" chain stored in the node repo is *relative* to which CID we look for. We always have stored a series of Filecoin blocks pointing to other blocks, each a potential chain in itself by following its parent's reference, and its parent's parent, and so on up to the genesis block. (FIXME: We need a diagram here, one of the Filecoin blog entries might have something similar to what we are describing here.) It only depends on *where* (location) do we start to look for. The *only* address/location reference we hold of the chain, a relative reference, is the `heaviest` pointer. This is reflected by the fact that we don't store it in the `Blockstore` by a fixed, *absolute*, CID that reflects its contents, as this will change each time we sync to a new head (FIXME: link to the immutability IPFS doc that I need to write).
FIXME: Create a further reading appendix, move this next para to it, along with other
extraneous content
This is one of the few items we store in `Datastore` by key, location, allowing its contents to change on every sync. This is reflected in the `(*ChainStore) writeHead()` function (called by `takeHeaviestTipSet()` above) where we reference the pointer by the explicit `chainHeadKey` address (the string `"head"`, not a hash embedded in a CID), and similarly in `(*ChainStore).Load()` when we start the node and create the `ChainStore`. Compare this to a Filecoin block or message which are immutable, stored in the `Blockstore` by CID, once created they never change.
## Keeping up with the chain
A Lotus node also listens for new blocks broadcast by its peers over the `gossipsub` channel (see FIXME for more).
If we have validated such a block's parent tipset, and adding it to our tipset at its height would lead to a heavier
head, then we validate and add this block. The validation described is identical to that invoked during the sync
process (indeed, it's the same codepath).
# State
In Filecoin, the chain state at any given point is a collection of data stored under a root CID
encapsulated in the [`StateTree`](https://github.com/filecoin-project/lotus/blob/master/chain/state/statetree.go),
and accessed through the
[`StateManager`](https://github.com/filecoin-project/lotus/blob/master/chain/stmgr/stmgr.go).
The state at the chain's head is thus easily tracked and updated in a state root CID.
(FIXME: Talk about CIDs somewhere, we might want to explain some of the modify/flush/update-root mechanism here.))
## Calculating a Tipset State
Recall that a tipset is a set of blocks that have identical parents (that is, that are built on top of the same tipset).
The genesis tipset comprises the genesis block(s), and has some state corresponding to it.
The methods `TipSetState()` and `computeTipSetState()` in
[`StateManager`](https://github.com/filecoin-project/lotus/blob/master/chain/stmgr/stmgr.go)
are responsible for computing
the state that results from applying a tipset. This involves applying all the messages included
in the tipset, and performing implicit operations like awarding block rewards.
Any valid block built on top of a tipset `ts` should have its Parent State Root equal to the result of
calculating the tipset state of `ts`. Note that this means that all blocks in a tipset must have the same Parent
State Root (which is to be expected, since they have the same parent tipset)
### Preparing to apply a tipset
When `StateManager::computeTipsetState()` is called with a tipset, `ts`,
it retrieves the parent state root of the blocks in `ts`. It also creates a list of `BlockMessages`, which wraps the BLS
and SecP messages in a block along with the miner that produced the block.
Control then flows to `StateManager::ApplyBlocks()`, which builds a VM to apply the messages given to it. The VM
is initialized with the parent state root of the blocks in `ts`. We apply the blocks in `ts` in order (see FIXME for
ordering of blocks in a tipset).
### Applying a block
For each block, we prepare to apply the ordered messages (first BLS, then SecP). Before applying a message, we check if
we have already applied a message with that CID within the scope of this method. If so, we simply skip that message;
this is how duplicate messages included in the same tipset are skipped (with only the miner of the "first" block to
include the message getting the reward). For the actual process of message application, see FIXME (need an
internal link here), for now we
simply assume that the outcome of the VM applying a message is either an error, or a
[`MessageReceipt`](https://github.com/filecoin-project/lotus/blob/master/chain/types/message_receipt.go)
and some
other information.
We treat an error from the VM as a showstopper; there is no recovery, and no meaningful state can be computed for `ts`.
Given a successful receipt, we add the rewards and penalties to what the miner has earned so far. Once all the messages
included in a block have been applied (or skipped if they're a duplicate), we use an implicit message to call
the Reward Actor. This awards the miner their reward for having won a block, and also awards / penalizes them based
on the message rewards and penalties we tracked.
We then proceed to apply the next block in `ts`, using the same VM. This means that the state changes that result
from applying a message are visible when applying all subsequent messages, even if they are included in a different block.
### Finishing up
Having applied all the blocks, we send one more implicit message, to the Cron Actor, which handles operations that
must be performed at the end of every epoch (see FIXME for more). The resulting state after calling the Cron Actor
is the computed state of the tipset.
# Virtual Machine
The Virtual Machine (VM) is responsible for executing messages.
The [Lotus Virtual Machine](https://github.com/filecoin-project/lotus/blob/master/chain/vm/vm.go)
invokes the appropriate methods in the builtin actors, and provides
a [`Runtime`](https://github.com/filecoin-project/specs-actors/blob/master/actors/runtime/runtime.go)
interface to the [builtin actors](https://github.com/filecoin-project/specs-actors)
that exposes their state, allows them to take certain actions, and meters
their gas usage. The VM also performs balance transfers, creates new account actors as needed, and tracks the gas reward,
penalty, return value, and exit code.
## Applying a Message
The primary entrypoint of the VM is the `ApplyMessage()` method. This method should not return an error
unless something goes unrecoverably wrong.
The first thing this method does is assess if the message provided meets any of the penalty criteria.
If so, a penalty is issued, and the method returns. Next, the entire gas cost of the message is transferred to
a temporary gas holder account. It is from this gas holder that gas will be deducted; if it runs out of gas, the message
fails. Any unused gas in this holder will be refunded to the message's sender at the end of message execution.
The VM then increments the sender's nonce, takes a snapshot of the state, and invokes `VM::send()`.
The `send()` method creates a [`Runtime`](https://github.com/filecoin-project/lotus/blob/master/chain/vm/runtime.go)
for the subsequent message execution.
It then transfers the message's value to the recipient, creating a new account actor if needed.
### Method Invocation
We use reflection to translate a Filecoin message for the VM to an actual Go function, relying on the VM's
[`invoker`](https://github.com/filecoin-project/lotus/blob/master/chain/vm/invoker.go) structure.
Each actor has its own set of codes defined in `specs-actors/actors/builtin/methods.go`.
The `invoker` structure maps the builtin actors' CIDs
to a list of `invokeFunc` (one per exported method), which each take the `Runtime` (for state manipulation)
and the serialized input parameters.
FIXME (aayush) Polish this next para.
The basic layout (without reflection details) of `(*invoker).transform()` is as follows. From each actor registered in `NewInvoker()` we take its `Exports()` methods converting them to `invokeFunc`s. The actual method is wrapped in another function that takes care of decoding the serialized parameters and the runtime, this function is passed to `shimCall()` that will encapsulate the actors code being run inside a `defer` function to `recover()` from panics (we fail in the actors code with panics to unwrap the stack). The return values will then be (CBOR) marshaled and returned to the VM.
### Returning from the VM
Once method invocation is complete (including any subcalls), we return to `ApplyMessage()`, which receives
the serialized response and the [`ActorError`](https://github.com/filecoin-project/lotus/blob/master/chain/actors/aerrors/error.go).
The sender will be charged the appropriate amount of gas for the returned response, which gets put into the
[`MessageReceipt`](https://github.com/filecoin-project/lotus/blob/master/chain/types/message_receipt.go).
The method then refunds any unused gas to the sender, sets up the gas reward for the miner, and
wraps all of this into an `ApplyRet`, which is returned.
# Building a Lotus node
When we launch a Lotus node with the command `./lotus daemon`
(see [here](https://github.com/filecoin-project/lotus/blob/master/cmd/lotus/daemon.go) for more),
the node is created through [dependency injection](https://godoc.org/go.uber.org/fx).
This relies on reflection, which makes some of the references hard to follow.
The node sets up all of the subsystems it needs to run, such as the repository, the network connections, thechain sync
service, etc.
This setup is orchestrated through calls to the `node.Override` function.
The structure of each call indicates the type of component it will set up
(many defined in [`node/modules/dtypes/`](https://github.com/filecoin-project/lotus/tree/master/node/modules/dtypes)),
and the function that will provide it.
The dependency is implicit in the argument of the provider function.
As an example, consider the `modules.ChainStore()` function that provides the
[`ChainStore`](https://github.com/filecoin-project/lotus/blob/master/chain/store/store.go) structure.
It takes as one of its parameters the [`ChainBlockstore`](https://github.com/filecoin-project/lotus/blob/master/node/modules/dtypes/storage.go)
type, which becomes one of its dependencies.
For the node to be built successfully the `ChainBlockstore` will need to be provided before `ChainStore`, a requirement
that is made explicit in another `Override()` call that sets the provider of that type as the `ChainBlockstore()` function.
## The Repository
The repo is the directory where all of a node's information is stored. The node is entirely defined by its repo, which
makes it easy to port to another location. This one-to-one relationship means we can speak
of the node as the repo it is associated with, instead of the daemon process that runs from that repo.
Only one daemon can run be running with an associated repo at a time.
A process signals that it is running a node associated with a particular repo, by creating and acquiring
a `repo.lock`.
```sh
lsof ~/.lotus/repo.lock
# COMMAND PID
# lotus 52356
```
Trying to launch a second daemon hooked to the same repo leads to a `repo is already locked (lotus daemon already running)`
error.
The `node.Repo()` function (`node/builder.go`) contains most of the dependencies (specified as `Override()` calls)
needed to properly set up the node's repo. We list the most salient ones here.
### Datastore
`Datastore` and `ChainBlockstore`: Data related to the node state is saved in the repo's `Datastore`,
an IPFS interface defined [here](https://github.com/ipfs/go-datastore/blob/master/datastore.go).
Lotus creates this interface from a [Badger DB](https://github.com/dgraph-io/badger) in
[`FsRepo`](https://github.com/filecoin-project/lotus/blob/master/node/repo/fsrepo.go).
Every piece of data is fundamentally a key-value pair in the `datastore` directory of the repo.
There are several abstractions laid on top of it that appear through the code depending on *how* we access it,
but it is important to remember that we're always accessing it from the same place.
FIXME: Maybe mention the `Batching` interface as the developer will stumble upon it before reaching the `Datastore` one.
#### Blocks
FIXME: IPFS blocks vs Filecoin blocks ideally happens before this / here
The [`Blockstore` interface](`github.com/ipfs/go-ipfs-blockstore/blockstore.go`) structures the key-value pair
into the CID format for the key and the [`Block` interface](`github.com/ipfs/go-block-format/blocks.go`) for the value.
The `Block` value is just a raw string of bytes addressed by its hash, which is included in the CID key.
`ChainBlockstore` creates a `Blockstore` in the repo under the `/blocks` namespace.
Every key stored there will have the `blocks` prefix so that it does not collide with other stores that use the same repo.
FIXME: Link to IPFS documentation about DAG, CID, and related, especially we need a diagram that shows how do we wrap each datastore inside the next layer (datastore, batching, block store, gc, etc).
#### Metadata
`modules.Datastore()` creates a `dtypes.MetadataDS`, which is an alias for the basic `Datastore` interface.
Metadata is stored here under the `/metadata` prefix.
(FIXME: Explain *what* is metadata in contrast with the block store, namely we store the pointer to the heaviest chain, we might just link to that unwritten section here later.)
FIXME: Explain the key store related calls (maybe remove, per Schomatis)
### LockedRepo
`LockedRepo()`: This method doesn't create or initialize any new structures, but rather registers an
`OnStop` [hook](https://godoc.org/go.uber.org/fx/internal/lifecycle#Hook)
that will close the locked repository associated with it on shutdown.
### Repo types / Node types
FIXME: This section needs to be clarified / corrected...I don't fully understand the config differences (what do they have in common, if anything?)
At the end of the `Repo()` function we see two mutually exclusive configuration calls based on the `RepoType` (`node/repo/fsrepo.go`).
```Go
ApplyIf(isType(repo.FullNode), ConfigFullNode(c)),
ApplyIf(isType(repo.StorageMiner), ConfigStorageMiner(c)),
```
As we said, the repo fully identifies the node so a repo type is also a *node* type, in this case a full node or a storage miner. (FIXME: What is the difference between the two, does *full* imply miner?) In this case the `daemon` command will create a `FullNode`, this is specified in the command logic itself in `main.DaemonCmd()`, the `FsRepo` created (and passed to `node.Repo()`) will be initiated with that type (see `(*FsRepo).Init(t RepoType)`).
## Online
FIXME: Much of this might need to be subsumed into the p2p section
The `node.Online()` configuration function (`node/builder.go`) initializes components that involve connecting to,
or interacting with, the Filecoin network. These connections are managed through the libp2p stack (FIXME link to this section when it exists).
We discuss some of the components found in the full node type (that is, included in the `ApplyIf(isType(repo.FullNode),` call).
#### Chainstore
`modules.ChainStore()` creates the [`store.ChainStore`](https://github.com/filecoin-project/lotus/blob/master/chain/store/store.go))
that wraps the stores
previously instantiated in `Repo()`. It is the main point of entry for the node to all chain-related data
(FIXME: this is incorrect, we sometimes access its underlying block store directly, and probably shouldn't).
It also holds the crucial `heaviest` pointer, which indicates the current head of the chain.
#### ChainExchange and ChainBlockservice
`ChainExchange()` and `ChainBlockservice()` establish a BitSwap connection (FIXME libp2p link)
to exchange chain information in the form of `blocks.Block`s stored in the repo. (See sync section for more details, the Filecoin blocks and messages are backed by these raw IPFS blocks that together form the different structures that define the state of the current/heaviest chain.)
#### Incoming handlers
`HandleIncomingBlocks()` and `HandleIncomingMessages()` start the services in charge of processing new Filecoin blocks
and messages from the network (see `<undefined>` for more information about the topics the node is subscribed to, FIXME: should that be part of the libp2p section or should we expand on gossipsub separately?).
#### Hello
`RunHello()`: starts the services to both send (`(*Service).SayHello()`) and receive (`(*Service).HandleStream()`, `node/hello/hello.go`)
`hello` messages. When nodes establish a new connection with each other, they exchange these messages
to share chain-related information (namely their genesis block and their heaviest tipset).
#### Syncer
`NewSyncer()` creates the `Syncer` structure and starts the services related to the chain sync process (FIXME link).
### Ordering the dependencies
We can establish the dependency relations by looking at the parameters that each function needs and by understanding
the architecture of the node and how the different components relate to each other (the chief purpose of this document).
As an example, the sync mechanism depends on the node being able to exchange different IPFS blocks with the network,
so as to be able to request the "missing pieces" needed to construct the chain. This dependency is reflected by `NewSyncer()`
having a `blocksync.BlockSync` parameter, which in turn depends on `ChainBlockservice()` and `ChainExchange()`.
The chain exchange service further depends on the chain store to save and retrieve chain data, which is reflected
in `ChainExchange()` having `ChainGCBlockstore` as a parameter (which is just a wrapper around `ChainBlockstore` capable
of garbage collection).
This block store is the same store underlying the chain store, which is an indirect dependency of `NewSyncer()` (through the `StateManager`).
(FIXME: This last line is flaky, we need to resolve the hierarchy better, we sometimes refer to the chain store and sometimes to its underlying block store. We need a diagram to visualize all the different components just mentioned otherwise it is too hard to follow. We probably even need to skip some of the connections mentioned.)

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