Compare commits

..
Author SHA1 Message Date
whyrusleeping e6f454ac33 fix setup of burnt funds actor state 2020-08-24 18:45:09 +00:00
whyrusleeping ee20eec451 randomize ticket in genesis creation 2020-08-24 18:40:51 +00:00
209 changed files with 2410 additions and 9832 deletions

No files matched your search

+6 -91
View File
@@ -113,7 +113,7 @@ jobs:
test: &test
description: |
Run tests with gotestsum.
parameters: &test-params
parameters:
executor:
type: executor
default: golang
@@ -161,7 +161,6 @@ jobs:
name: go test
environment:
LOTUS_TEST_WINDOW_POST: << parameters.winpost-test >>
SKIP_CONFORMANCE: "1"
command: |
mkdir -p /tmp/test-reports/<< parameters.test-suite-name >>
mkdir -p /tmp/test-artifacts
@@ -192,63 +191,6 @@ jobs:
<<: *test
test-window-post:
<<: *test
test-conformance:
description: |
Run tests using a corpus of interoperable test vectors for Filecoin
implementations to test their correctness and compliance with the Filecoin
specifications.
parameters:
<<: *test-params
vectors-branch:
type: string
default: ""
description: |
Branch on github.com/filecoin-project/test-vectors to checkout and
test with. If empty (the default) the commit defined by the git
submodule is used.
executor: << parameters.executor >>
steps:
- install-deps
- prepare
- run:
command: make deps lotus
no_output_timeout: 30m
- download-params
- when:
condition:
not:
equal: [ "", << parameters.vectors-branch >> ]
steps:
- run:
name: checkout vectors branch
command: |
cd extern/test-vectors
git fetch
git checkout origin/<< parameters.vectors-branch >>
- run:
name: go get vectors branch
command: go get github.com/filecoin-project/test-vectors@<< parameters.vectors-branch >>
- go/install-gotestsum:
gobin: $HOME/.local/bin
version: 0.5.2
- run:
name: go test
environment:
SKIP_CONFORMANCE: "0"
command: |
mkdir -p /tmp/test-reports
mkdir -p /tmp/test-artifacts
gotestsum \
--format pkgname-and-test-fails \
--junitfile /tmp/test-reports/junit.xml \
-- \
-v -coverpkg ./chain/vm/,github.com/filecoin-project/specs-actors/... -coverprofile=/tmp/conformance.out ./conformance/
go tool cover -html=/tmp/conformance.out -o /tmp/test-artifacts/conformance-coverage.html
no_output_timeout: 30m
- store_test_results:
path: /tmp/test-reports
- store_artifacts:
path: /tmp/test-artifacts/conformance-coverage.html
build-macos:
description: build darwin lotus binary
@@ -310,27 +252,6 @@ jobs:
- run:
command: "! go fmt ./... 2>&1 | read"
cbor-gen-check:
executor: golang
steps:
- install-deps
- prepare
- run: make deps
- run: go install golang.org/x/tools/cmd/goimports
- run: go install github.com/hannahhoward/cbor-gen-for
- run: go generate ./...
- run: git --no-pager diff
- run: git --no-pager diff --quiet
docs-check:
executor: golang
steps:
- install-deps
- prepare
- run: make docsgen
- run: git --no-pager diff
- run: git --no-pager diff --quiet
lint: &lint
description: |
Run golangci-lint.
@@ -367,6 +288,9 @@ jobs:
command: |
$HOME/.local/bin/golangci-lint run -v --timeout 2m \
--concurrency << parameters.concurrency >> << parameters.args >>
lint-changes:
<<: *lint
lint-all:
<<: *lint
@@ -395,11 +319,10 @@ workflows:
version: 2.1
ci:
jobs:
- lint-all
- lint-changes:
args: "--new-from-rev origin/next"
- mod-tidy-check
- gofmt
- cbor-gen-check
- docs-check
- test:
codecov-upload: true
test-suite-name: full
@@ -414,14 +337,6 @@ workflows:
tags:
only:
- /^v\d+\.\d+\.\d+$/
- test-conformance:
test-suite-name: conformance
packages: "./conformance"
- test-conformance:
name: test-conformance-bleeding-edge
test-suite-name: conformance-bleeding-edge
packages: "./conformance"
vectors-branch: master
- build-debug
- build-all:
requires:
+2 -5
View File
@@ -26,14 +26,11 @@
color: 00A4E2
description: "Area: Chain/VM"
- name: area/chain/sync
color: 00A4E4
color: 00A4E2
description: "Area: Chain/Sync"
- name: area/chain/misc
color: 00A4E6
color: 00A4E2
description: "Area: Chain/Misc"
- name: area/markets
color: 00A4E8
description: "Area: Markets"
- name: area/sealing/fsm
color: 0bb1ed
description: "Area: Sealing/FSM"
-3
View File
@@ -5,6 +5,3 @@
[submodule "extern/serialization-vectors"]
path = extern/serialization-vectors
url = https://github.com/filecoin-project/serialization-vectors
[submodule "extern/test-vectors"]
path = extern/test-vectors
url = https://github.com/filecoin-project/test-vectors.git
-21
View File
@@ -23,14 +23,6 @@ issues:
- "Potential file inclusion via variable"
- "should have( a package)? comment"
- "Error return value of `logging.SetLogLevel` is not checked"
- "comment on exported"
- "(func|method) \\w+ should be \\w+"
- "(type|var|struct field|(method|func) parameter) `\\w+` should be `\\w+`"
- "(G306|G301|G307|G108|G302|G204|G104)"
- "don't use ALL_CAPS in Go names"
- "string .* has .* occurrences, make it a constant"
- "a blank import should be only in a main or test package, or have a comment justifying it"
- "package comment should be of the form"
exclude-use-default: false
exclude-rules:
@@ -54,19 +46,6 @@ issues:
linters:
- gosec
- path: chain/vectors/gen/.*
linters:
- gosec
- path: cmd/lotus-bench/.*
linters:
- gosec
- path: api/test/.*
text: "context.Context should be the first parameter"
linters:
- golint
linters-settings:
goconst:
min-occurrences: 6
+8 -104
View File
@@ -1,108 +1,12 @@
# Lotus changelog
# 0.5.7 / 2020-08-31
This patch release includes some bugfixes and enhancements to the sector lifecycle and message pool logic.
## Changes
- Rebuild unsealed infos on miner restart (https://github.com/filecoin-project/lotus/pull/3401)
- CLI to attach storage paths to workers (https://github.com/filecoin-project/lotus/pull/3405)
- Do not select negative performing message chains for inclusion (https://github.com/filecoin-project/lotus/pull/3392)
- Remove a redundant error-check (https://github.com/filecoin-project/lotus/pull/3421)
- Correctly move unsealed sectors in `FinalizeSectors` (https://github.com/filecoin-project/lotus/pull/3424)
- Improve worker selection logic (https://github.com/filecoin-project/lotus/pull/3425)
- Don't use context to close bitswap (https://github.com/filecoin-project/lotus/pull/3430)
- Correctly estimate gas premium when there is only one message on chain (https://github.com/filecoin-project/lotus/pull/3428)
# 0.5.6 / 2020-08-29
Hotfix release that fixes a panic in the sealing scheduler (https://github.com/filecoin-project/lotus/pull/3389).
# 0.5.5
This patch release introduces a large number of improvements to the sealing process.
It also updates go-fil-markets to
[version 0.5.8](https://github.com/filecoin-project/go-fil-markets/releases/tag/v0.5.8),
and go-libp2p-pubsub to [v0.3.5](https://github.com/libp2p/go-libp2p-pubsub/releases/tag/v0.3.5).
#### Downstream upgrades
- Upgrades markets to v0.5.8 (https://github.com/filecoin-project/lotus/pull/3384)
- Upgrades go-libp2p-pubsub to v0.3.5 (https://github.com/filecoin-project/lotus/pull/3305)
#### Sector sealing
- The following improvements were introduced in https://github.com/filecoin-project/lotus/pull/3350.
- Allow `lotus-miner sectors remove` to remove a sector in any state.
- Create a separate state in the storage FSM dedicated to submitting the Commit message.
- Recovery for when the Deal IDs of deals in a sector get changed in a reorg.
- Auto-retry sending Precommit and Commit messages if they run out of gas
- Auto-retry sector remove tasks when they fail
- Compact worker windows, and allow their tasks to be executed in any order
- Don't simply skip PoSt for bad sectors (https://github.com/filecoin-project/lotus/pull/3323)
#### Message Pool
- Spam Protection: Track required funds for pending messages (https://github.com/filecoin-project/lotus/pull/3313)
#### Chainwatch
- Add more power and reward metrics (https://github.com/filecoin-project/lotus/pull/3367)
- Fix raciness in sector deal table (https://github.com/filecoin-project/lotus/pull/3275)
- Parallelize miner processing (https://github.com/filecoin-project/lotus/pull/3380)
- Accept Lotus API and token (https://github.com/filecoin-project/lotus/pull/3337)
# 0.5.4
A patch release, containing a few nice bugfixes and improvements:
- Fix parsing of peer ID in `lotus-miner actor set-peer-id` (@whyrusleeping)
- Update dependencies, fixing several bugs (@Stebalien)
- Fix remaining linter warnings (@Stebalien)
- Use safe string truncation (@Ingar)
- Allow tweaking of blocksync message window size (@whyrusleeping)
- Add some additional gas stats to metrics (@Kubuxu)
- Fix an edge case bug in message selection, add many tests (@vyzo)
# 0.5.3
Yet another hotfix release.
A lesson for readers, having people who have been awake for 12+ hours review
your hotfix PR is not a good idea. Find someone who has enough slept recently
enough to give you good code review, otherwise you'll end up quickly bumping
versions again.
- Fixed a bug in the mempool that was introduced in v0.5.2
# 0.5.2 / 2020-08-24
This is a hotfix release.
- Fix message selection to not include messages that are invalid for block
inclusion.
- Improve SelectMessage handling of the case where the message pools tipset
differs from our mining base.
# 0.5.1 / 2020-08-24
The Space Race release!
This release contains the genesis car file and bootstrap peers for the space
race network.
Additionally, we included two small fixes to genesis creation:
- Randomize ticket value in genesis generation
- Correctly set t099 (burnt funds actor) to have valid account actor state
# 0.5.0 / 2020-08-20
## 0.5.0 / 2020-08-2020
This version of Lotus will be used for the incentivized testnet Space Race competition,
and can be considered mainnet-ready code. It includes some protocol
changes, upgrades of core dependencies, and various bugfixes and UX/performance improvements.
## Highlights
# Highlights
Among the highlights included in this release are:
@@ -112,7 +16,7 @@ and the packing of multiple deals into a single sector.
- Renamed features: We renamed some of the binaries, environment variables, and default
paths associated with a Lotus node.
### Gas changes
## Gas changes
We made some significant changes to the mechanics of gas in this release.
@@ -169,7 +73,7 @@ data can be quickly served in a retrieval deal, since it will not need to be uns
Miners can now pack multiple deals into a single sector, so long as all the deals
fit into the sector capacity. This should increase the packing efficiency of miners.
### Renamed features
## Renamed features
To improve the user experience, we updated several names to mainatin
standard prefixing, and to better reflect the meaning of the features being referenced.
@@ -179,7 +83,7 @@ path for miner data is now `~/.lotusminer`, and the environment variable
that sets the path for miner data is now `$LOTUS_MINER_PATH`. A full list of renamed
features can be found [here](https://github.com/filecoin-project/lotus/issues/2304).
## Changelog
# Changelog
#### Downstream upgrades
- Upgrades markets to v0.5.6 (https://github.com/filecoin-project/lotus/pull/3058)
@@ -210,7 +114,7 @@ features can be found [here](https://github.com/filecoin-project/lotus/issues/23
- Miners can toggle auto-acceptance of deals (https://github.com/filecoin-project/lotus/pull/1994)
- Miners can maintain a blocklist of piece CIDs (https://github.com/filecoin-project/lotus/pull/2069)
## Contributors
# Contributors
The following contributors had 10 or more commits go into this release.
We are grateful for every contribution!
@@ -237,8 +141,8 @@ We are grateful for every contribution!
| Stebalien | 11 | +1204/-980 |
| RobQuistNL | 10 | +69/-74 |
# 0.1.0 / 2019-12-11
## 0.1.0 / 2019-12-11
We are very excited to release **lotus** 0.1.0. This is our testnet release. To install lotus and join the testnet, please visit [lotu.sh](lotu.sh). Please file bug reports as [issues](https://github.com/filecoin-project/lotus/issues).
A huge thank you to all contributors for this testnet release!
A huge thank you to all contributors for this testnet release!
-3
View File
@@ -279,8 +279,5 @@ method-gen:
gen: type-gen method-gen
docsgen:
go run ./api/docgen > documentation/en/api-methods.md
print-%:
@echo $*=$($*)
+3
View File
@@ -40,6 +40,9 @@ All work is tracked via issues. An attempt at keeping an up-to-date view on rema
The lotus Filecoin implementation unfolds into the following packages:
- [This repo](https://github.com/filecoin-project/lotus)
- [storage-fsm](https://github.com/filecoin-project/storage-fsm)
- [sector-storage](https://github.com/filecoin-project/sector-storage)
- [specs-storage](https://github.com/filecoin-project/specs-storage)
- [go-fil-markets](https://github.com/filecoin-project/go-fil-markets) which has its own [kanban work tracker available here](https://app.zenhub.com/workspaces/markets-shared-components-5daa144a7046a60001c6e253/board)
- [spec-actors](https://github.com/filecoin-project/specs-actors) which has its own [kanban work tracker available here](https://app.zenhub.com/workspaces/actors-5ee6f3aa87591f0016c05685/board)
+2 -4
View File
@@ -243,8 +243,6 @@ type FullNode interface {
ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error)
// ClientListDeals returns information about the deals made by the local client.
ClientListDeals(ctx context.Context) ([]DealInfo, error)
// ClientGetDealUpdates returns the status of updated deals
ClientGetDealUpdates(ctx context.Context) (<-chan DealInfo, error)
// ClientHasLocal indicates whether a certain CID is locally stored.
ClientHasLocal(ctx context.Context, root cid.Cid) (bool, error)
// ClientFindData identifies peers that have a certain file, and returns QueryOffers (one per peer).
@@ -325,7 +323,7 @@ type FullNode interface {
StateMinerAvailableBalance(context.Context, address.Address, types.TipSetKey) (types.BigInt, error)
// StateSectorPreCommitInfo returns the PreCommit info for the specified miner's sector
StateSectorPreCommitInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (miner.SectorPreCommitOnChainInfo, error)
// StateSectorGetInfo returns the on-chain info for the specified miner's sector. Returns null in case the sector info isn't found
// StateSectorGetInfo returns the on-chain info for the specified miner's sector
// NOTE: returned info.Expiration may not be accurate in some cases, use StateSectorExpiration to get accurate
// expiration epoch
StateSectorGetInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorOnChainInfo, error)
@@ -429,7 +427,7 @@ type FullNode interface {
PaychVoucherCreate(context.Context, address.Address, types.BigInt, uint64) (*paych.SignedVoucher, error)
PaychVoucherAdd(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error)
PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error)
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error)
PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher) (cid.Cid, error)
}
type FileRef struct {
+9 -12
View File
@@ -120,18 +120,15 @@ type SectorLog struct {
}
type SectorInfo struct {
SectorID abi.SectorNumber
State SectorState
CommD *cid.Cid
CommR *cid.Cid
Proof []byte
Deals []abi.DealID
Ticket SealTicket
Seed SealSeed
PreCommitMsg *cid.Cid
CommitMsg *cid.Cid
Retries uint64
ToUpgrade bool
SectorID abi.SectorNumber
State SectorState
CommD *cid.Cid
CommR *cid.Cid
Proof []byte
Deals []abi.DealID
Ticket SealTicket
Seed SealSeed
Retries uint64
LastErr string
+1 -3
View File
@@ -27,13 +27,11 @@ type WorkerAPI interface {
storage.Sealer
MoveStorage(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error
MoveStorage(ctx context.Context, sector abi.SectorID) error
UnsealPiece(context.Context, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize, abi.SealRandomness, cid.Cid) error
ReadPiece(context.Context, io.Writer, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) (bool, error)
StorageAddLocal(ctx context.Context, path string) error
Fetch(context.Context, abi.SectorID, stores.SectorFileType, stores.PathType, stores.AcquireMode) error
Closing(context.Context) (<-chan struct{}, error)
+6 -16
View File
@@ -139,7 +139,6 @@ type FullNodeStruct struct {
ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"`
ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"`
ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"`
ClientGetDealUpdates func(ctx context.Context) (<-chan api.DealInfo, error) `perm:"read"`
ClientRetrieve func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error `perm:"admin"`
ClientRetrieveWithEvents func(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) (<-chan marketevents.RetrievalEvent, error) `perm:"admin"`
ClientQueryAsk func(ctx context.Context, p peer.ID, miner address.Address) (*storagemarket.SignedStorageAsk, error) `perm:"read"`
@@ -215,7 +214,7 @@ type FullNodeStruct struct {
PaychVoucherAdd func(context.Context, address.Address, *paych.SignedVoucher, []byte, types.BigInt) (types.BigInt, error) `perm:"write"`
PaychVoucherCreate func(context.Context, address.Address, big.Int, uint64) (*paych.SignedVoucher, error) `perm:"sign"`
PaychVoucherList func(context.Context, address.Address) ([]*paych.SignedVoucher, error) `perm:"write"`
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) `perm:"sign"`
PaychVoucherSubmit func(context.Context, address.Address, *paych.SignedVoucher) (cid.Cid, error) `perm:"sign"`
}
}
@@ -317,8 +316,7 @@ type WorkerStruct struct {
FinalizeSector func(context.Context, abi.SectorID, []storage.Range) error `perm:"admin"`
ReleaseUnsealed func(ctx context.Context, sector abi.SectorID, safeToFree []storage.Range) error `perm:"admin"`
Remove func(ctx context.Context, sector abi.SectorID) error `perm:"admin"`
MoveStorage func(ctx context.Context, sector abi.SectorID, types stores.SectorFileType) error `perm:"admin"`
StorageAddLocal func(ctx context.Context, path string) 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"`
ReadPiece func(context.Context, io.Writer, abi.SectorID, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) (bool, error) `perm:"admin"`
@@ -435,10 +433,6 @@ func (c *FullNodeStruct) ClientListDeals(ctx context.Context) ([]api.DealInfo, e
return c.Internal.ClientListDeals(ctx)
}
func (c *FullNodeStruct) ClientGetDealUpdates(ctx context.Context) (<-chan api.DealInfo, error) {
return c.Internal.ClientGetDealUpdates(ctx)
}
func (c *FullNodeStruct) ClientRetrieve(ctx context.Context, order api.RetrievalOrder, ref *api.FileRef) error {
return c.Internal.ClientRetrieve(ctx, order, ref)
}
@@ -926,8 +920,8 @@ func (c *FullNodeStruct) PaychNewPayment(ctx context.Context, from, to address.A
return c.Internal.PaychNewPayment(ctx, from, to, vouchers)
}
func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *paych.SignedVoucher, secret []byte, proof []byte) (cid.Cid, error) {
return c.Internal.PaychVoucherSubmit(ctx, ch, sv, secret, proof)
func (c *FullNodeStruct) PaychVoucherSubmit(ctx context.Context, ch address.Address, sv *paych.SignedVoucher) (cid.Cid, error) {
return c.Internal.PaychVoucherSubmit(ctx, ch, sv)
}
// StorageMinerStruct
@@ -1220,12 +1214,8 @@ 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, types stores.SectorFileType) error {
return w.Internal.MoveStorage(ctx, sector, types)
}
func (w *WorkerStruct) StorageAddLocal(ctx context.Context, path string) error {
return w.Internal.StorageAddLocal(ctx, path)
func (w *WorkerStruct) MoveStorage(ctx context.Context, sector abi.SectorID) error {
return w.Internal.MoveStorage(ctx, sector)
}
func (w *WorkerStruct) UnsealPiece(ctx context.Context, id abi.SectorID, index storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize, randomness abi.SealRandomness, c cid.Cid) error {
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"fmt"
"io"
abi "github.com/filecoin-project/specs-actors/actors/abi"
paych "github.com/filecoin-project/specs-actors/actors/builtin/paych"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/paych"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
+8 -9
View File
@@ -1,7 +1,6 @@
package client
import (
"context"
"net/http"
"net/url"
"path"
@@ -15,9 +14,9 @@ import (
)
// NewCommonRPC creates a new http jsonrpc client.
func NewCommonRPC(ctx context.Context, addr string, requestHeader http.Header) (api.Common, jsonrpc.ClientCloser, error) {
func NewCommonRPC(addr string, requestHeader http.Header) (api.Common, jsonrpc.ClientCloser, error) {
var res apistruct.CommonStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
[]interface{}{
&res.Internal,
},
@@ -28,9 +27,9 @@ func NewCommonRPC(ctx context.Context, addr string, requestHeader http.Header) (
}
// NewFullNodeRPC creates a new http jsonrpc client.
func NewFullNodeRPC(ctx context.Context, addr string, requestHeader http.Header) (api.FullNode, jsonrpc.ClientCloser, error) {
func NewFullNodeRPC(addr string, requestHeader http.Header) (api.FullNode, jsonrpc.ClientCloser, error) {
var res apistruct.FullNodeStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
[]interface{}{
&res.CommonStruct.Internal,
&res.Internal,
@@ -40,9 +39,9 @@ func NewFullNodeRPC(ctx context.Context, addr string, requestHeader http.Header)
}
// NewStorageMinerRPC creates a new http jsonrpc client for miner
func NewStorageMinerRPC(ctx context.Context, addr string, requestHeader http.Header, opts ...jsonrpc.Option) (api.StorageMiner, jsonrpc.ClientCloser, error) {
func NewStorageMinerRPC(addr string, requestHeader http.Header, opts ...jsonrpc.Option) (api.StorageMiner, jsonrpc.ClientCloser, error) {
var res apistruct.StorageMinerStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
[]interface{}{
&res.CommonStruct.Internal,
&res.Internal,
@@ -54,7 +53,7 @@ func NewStorageMinerRPC(ctx context.Context, addr string, requestHeader http.Hea
return &res, closer, err
}
func NewWorkerRPC(ctx context.Context, addr string, requestHeader http.Header) (api.WorkerAPI, jsonrpc.ClientCloser, error) {
func NewWorkerRPC(addr string, requestHeader http.Header) (api.WorkerAPI, jsonrpc.ClientCloser, error) {
u, err := url.Parse(addr)
if err != nil {
return nil, nil, err
@@ -70,7 +69,7 @@ func NewWorkerRPC(ctx context.Context, addr string, requestHeader http.Header) (
u.Path = path.Join(u.Path, "../streams/v0/push")
var res apistruct.WorkerStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
closer, err := jsonrpc.NewMergeClient(addr, "Filecoin",
[]interface{}{
&res.Internal,
},
+9 -31
View File
@@ -12,29 +12,22 @@ import (
"time"
"unicode"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-filestore"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/multiformats/go-multiaddr"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield"
datatransfer "github.com/filecoin-project/go-data-transfer"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/go-multistore"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-filestore"
"github.com/libp2p/go-libp2p-core/network"
peer "github.com/libp2p/go-libp2p-peer"
"github.com/multiformats/go-multiaddr"
)
var ExampleValues = map[reflect.Type]interface{}{
@@ -73,12 +66,11 @@ func init() {
ExampleValues[reflect.TypeOf(addr)] = addr
pid, err := peer.Decode("12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf")
pid, err := peer.IDB58Decode("12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf")
if err != nil {
panic(err)
}
addExample(pid)
addExample(&pid)
addExample(bitfield.NewFromSet([]uint64{5}))
addExample(abi.RegisteredSealProof_StackedDrg32GiBV1)
@@ -106,12 +98,6 @@ func init() {
addExample(build.APIVersion)
addExample(api.PCHInbound)
addExample(time.Minute)
addExample(datatransfer.TransferID(3))
addExample(datatransfer.Ongoing)
addExample(multistore.StoreID(50))
addExample(retrievalmarket.ClientEventDealAccepted)
addExample(retrievalmarket.DealStatusNew)
addExample(network.ReachabilityPublic)
addExample(&types.ExecutionTrace{
Msg: exampleValue(reflect.TypeOf(&types.Message{}), nil).(*types.Message),
MsgRct: exampleValue(reflect.TypeOf(&types.MessageReceipt{}), nil).(*types.MessageReceipt),
@@ -125,14 +111,6 @@ func init() {
addExample(map[string]api.MarketBalance{
"t026363": exampleValue(reflect.TypeOf(api.MarketBalance{}), nil).(api.MarketBalance),
})
addExample(map[string]*pubsub.TopicScoreSnapshot{
"/blocks": {
TimeInMesh: time.Minute,
FirstMessageDeliveries: 122,
MeshMessageDeliveries: 1234,
InvalidMessageDeliveries: 3,
},
})
maddr, err := multiaddr.NewMultiaddr("/ip4/52.36.61.156/tcp/1347/p2p/12D3KooWFETiESTf1v4PGUvtnxMAcEFMzLZbJGg4tjWfGEimYior")
if err != nil {
+1 -1
View File
@@ -41,7 +41,7 @@ func (bm *BlockMiner) MineBlocks() {
nulls := atomic.SwapInt64(&bm.nulls, 0)
if err := bm.miner.MineOne(bm.ctx, miner.MineReq{
InjectNulls: abi.ChainEpoch(nulls),
Done: func(bool, abi.ChainEpoch, error) {},
Done: func(bool, error) {},
}); err != nil {
bm.t.Error(err)
}
+5 -6
View File
@@ -24,7 +24,6 @@ import (
"github.com/filecoin-project/lotus/build"
sealing "github.com/filecoin-project/lotus/extern/storage-sealing"
"github.com/filecoin-project/lotus/miner"
"github.com/filecoin-project/specs-actors/actors/abi"
dag "github.com/ipfs/go-merkledag"
dstest "github.com/ipfs/go-merkledag/test"
unixfile "github.com/ipfs/go-unixfs/file"
@@ -36,7 +35,7 @@ import (
var MineNext = miner.MineReq{
InjectNulls: 0,
Done: func(bool, abi.ChainEpoch, error) {},
Done: func(bool, error) {},
}
func init() {
@@ -142,7 +141,7 @@ func makeDeal(t *testing.T, ctx context.Context, rseed int, client *impl.FullNod
info, err := client.ClientGetDealInfo(ctx, *deal)
require.NoError(t, err)
testRetrieval(t, ctx, client, fcid, &info.PieceCID, carExport, data)
testRetrieval(t, ctx, err, client, fcid, &info.PieceCID, carExport, data)
}
func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
@@ -194,7 +193,7 @@ func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Durati
info, err := client.ClientGetDealInfo(ctx, *deal)
require.NoError(t, err)
testRetrieval(t, ctx, client, fcid, &info.PieceCID, false, data)
testRetrieval(t, ctx, err, client, fcid, &info.PieceCID, false, data)
atomic.AddInt64(&mine, -1)
fmt.Println("shutting down mining")
<-done
@@ -268,7 +267,7 @@ func TestSenondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration
rf, _ := miner.SectorsRefs(ctx)
fmt.Printf("refs: %+v\n", rf)
testRetrieval(t, ctx, client, fcid2, &info.PieceCID, false, data2)
testRetrieval(t, ctx, err, client, fcid2, &info.PieceCID, false, data2)
}
atomic.AddInt64(&mine, -1)
@@ -374,7 +373,7 @@ func startSealingWaiting(t *testing.T, ctx context.Context, miner TestStorageNod
}
}
func testRetrieval(t *testing.T, ctx context.Context, client *impl.FullNodeAPI, fcid cid.Cid, piece *cid.Cid, carExport bool, data []byte) {
func testRetrieval(t *testing.T, ctx context.Context, err error, client *impl.FullNodeAPI, fcid cid.Cid, piece *cid.Cid, carExport bool, data []byte) {
offers, err := client.ClientFindData(ctx, fcid, piece)
if err != nil {
t.Fatal(err)
+9 -8
View File
@@ -20,7 +20,6 @@ import (
"github.com/filecoin-project/lotus/node/impl"
)
//nolint:deadcode,varcheck
var log = logging.Logger("apitest")
func (ts *testSuite) testMining(t *testing.T) {
@@ -31,20 +30,22 @@ func (ts *testSuite) testMining(t *testing.T) {
newHeads, err := api.ChainNotify(ctx)
require.NoError(t, err)
initHead := (<-newHeads)[0]
baseHeight := initHead.Val.Height()
if initHead.Val.Height() != 2 {
<-newHeads
}
h1, err := api.ChainHead(ctx)
require.NoError(t, err)
require.Equal(t, int64(h1.Height()), int64(baseHeight))
require.Equal(t, abi.ChainEpoch(2), h1.Height())
MineUntilBlock(ctx, t, apis[0], sn[0], nil)
err = sn[0].MineOne(ctx, MineNext)
require.NoError(t, err)
<-newHeads
h2, err := api.ChainHead(ctx)
require.NoError(t, err)
require.Greater(t, int64(h2.Height()), int64(h1.Height()))
require.Equal(t, abi.ChainEpoch(3), h2.Height())
}
func (ts *testSuite) testMiningReal(t *testing.T) {
@@ -68,7 +69,7 @@ func (ts *testSuite) testMiningReal(t *testing.T) {
require.NoError(t, err)
require.Equal(t, abi.ChainEpoch(2), h1.Height())
MineUntilBlock(ctx, t, apis[0], sn[0], nil)
err = sn[0].MineOne(ctx, MineNext)
require.NoError(t, err)
<-newHeads
@@ -77,7 +78,7 @@ func (ts *testSuite) testMiningReal(t *testing.T) {
require.NoError(t, err)
require.Equal(t, abi.ChainEpoch(3), h2.Height())
MineUntilBlock(ctx, t, apis[0], sn[0], nil)
err = sn[0].MineOne(ctx, MineNext)
require.NoError(t, err)
<-newHeads
@@ -142,7 +143,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo
complChan := minedTwo
for atomic.LoadInt32(&mine) != 0 {
wait := make(chan int)
mdone := func(mined bool, _ abi.ChainEpoch, err error) {
mdone := func(mined bool, err error) {
n := 0
if mined {
n = 1
-3
View File
@@ -153,9 +153,6 @@ func TestPaymentChannels(t *testing.T, b APIBuilder, blocktime time.Duration) {
}, int(build.MessageConfidence)+1, build.SealRandomnessLookbackLimit, func(oldTs, newTs *types.TipSet) (bool, events.StateChange, error) {
return preds.OnPaymentChannelActorChanged(channel, preds.OnToSendAmountChanges())(ctx, oldTs.Key(), newTs.Key())
})
if err != nil {
t.Fatal(err)
}
select {
case <-finished:
-50
View File
@@ -3,13 +3,11 @@ package test
import (
"context"
"testing"
"time"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/miner"
)
func SendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address.Address, amount abi.TokenAmount) {
@@ -36,51 +34,3 @@ func SendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address.
t.Fatal("did not successfully send money")
}
}
func MineUntilBlock(ctx context.Context, t *testing.T, fn TestNode, sn TestStorageNode, cb func(abi.ChainEpoch)) {
for i := 0; i < 1000; i++ {
var success bool
var err error
var epoch abi.ChainEpoch
wait := make(chan struct{})
mineErr := sn.MineOne(ctx, miner.MineReq{
Done: func(win bool, ep abi.ChainEpoch, e error) {
success = win
err = e
epoch = ep
wait <- struct{}{}
},
})
if mineErr != nil {
t.Fatal(mineErr)
}
<-wait
if err != nil {
t.Fatal(err)
}
if success {
// Wait until it shows up on the given full nodes ChainHead
nloops := 50
for i := 0; i < nloops; i++ {
ts, err := fn.ChainHead(ctx)
if err != nil {
t.Fatal(err)
}
if ts.Height() == epoch {
break
}
if i == nloops-1 {
t.Fatal("block never managed to sync to node")
}
time.Sleep(time.Millisecond * 10)
}
if cb != nil {
cb(epoch)
}
return
}
t.Log("did not mine block, trying again", i)
}
t.Fatal("failed to mine 1000 times in a row...")
}
+5 -12
View File
@@ -24,14 +24,9 @@ import (
"github.com/filecoin-project/lotus/node/impl"
)
func init() {
err := os.Setenv("BELLMAN_NO_GPU", "1")
if err != nil {
panic(fmt.Sprintf("failed to set BELLMAN_NO_GPU env variable: %s", err))
}
}
func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) {
os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background()
n, sn := b(t, 1, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI)
@@ -53,7 +48,7 @@ func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSect
defer close(done)
for mine {
build.Clock.Sleep(blocktime)
if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) {
if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, error) {
}}); err != nil {
t.Error(err)
@@ -115,6 +110,8 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n,
}
func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) {
os.Setenv("BELLMAN_NO_GPU", "1")
ctx := context.Background()
n, sn := b(t, 1, OneMiner)
client := n[0].FullNode.(*impl.FullNodeAPI)
@@ -216,10 +213,6 @@ func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSector
sn, err := parts[0].Sectors.First()
require.NoError(t, err)
all, err := parts[0].Sectors.All(2)
require.NoError(t, err)
fmt.Println("the sectors", all)
s = abi.SectorID{
Miner: abi.ActorID(mid),
Number: abi.SectorNumber(sn),
Binary file not shown.
+2 -2
View File
@@ -25,7 +25,7 @@ func buildType() string {
}
// BuildVersion is the local build version, set by build system
const BuildVersion = "0.5.7"
const BuildVersion = "0.4.6"
func UserVersion() string {
return BuildVersion + buildType() + CurrentCommit
@@ -53,7 +53,7 @@ func (ve Version) EqMajorMinor(v2 Version) bool {
}
// APIVersion is a semver version of the rpc api exposed
var APIVersion Version = newVer(0, 14, 0)
var APIVersion Version = newVer(0, 11, 0)
//nolint:varcheck,deadcode
const (
-4
View File
@@ -37,10 +37,6 @@ func ValidateBlockValues(b RandomBeacon, h *types.BlockHeader, prevEntry types.B
return nil
}
if len(h.BeaconEntries) == 0 {
return xerrors.Errorf("expected to have beacon entries in this block, but didn't find any")
}
last := h.BeaconEntries[len(h.BeaconEntries)-1]
if last.Round != maxRound {
return xerrors.Errorf("expected final beacon entry in block to be at round %d, got %d", maxRound, last.Round)
+2 -2
View File
@@ -7,8 +7,8 @@ import (
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
lru "github.com/hashicorp/golang-lru"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/hashicorp/golang-lru"
peer "github.com/libp2p/go-libp2p-core/peer"
)
type blockReceiptTracker struct {
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"fmt"
"io"
types "github.com/filecoin-project/lotus/chain/types"
cid "github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/chain/types"
"github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
+2 -8
View File
@@ -172,7 +172,7 @@ func (client *BlockSync) processResponse(
resLength, req.Length)
}
if resLength < int(req.Length) && res.Status != Partial {
return nil, xerrors.Errorf("got less than requested without a proper status: %d", res.Status)
return nil, xerrors.Errorf("got less than requested without a proper status: %s", res.Status)
}
validRes := &validatedResponse{}
@@ -205,7 +205,7 @@ func (client *BlockSync) processResponse(
validRes.messages = make([]*CompactedMessages, resLength)
for i := 0; i < resLength; i++ {
if res.Chain[i].Messages == nil {
return nil, xerrors.Errorf("no messages included for tipset at height (head - %d)", i)
return nil, xerrors.Errorf("no messages included for tipset at height (head - %d): %w", i)
}
validRes.messages[i] = res.Chain[i].Messages
}
@@ -308,12 +308,6 @@ func (client *BlockSync) GetChainMessages(
length uint64,
) ([]*CompactedMessages, error) {
ctx, span := trace.StartSpan(ctx, "GetChainMessages")
if span.IsRecordingEvents() {
span.AddAttributes(
trace.StringAttribute("tipset", fmt.Sprint(head.Cids())),
trace.Int64Attribute("count", int64(length)),
)
}
defer span.End()
req := &Request{
+1 -2
View File
@@ -1,10 +1,9 @@
package blocksync
import (
"time"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/store"
"time"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log"
-1
View File
@@ -2,7 +2,6 @@ package state
import (
"bytes"
"github.com/filecoin-project/specs-actors/actors/util/adt"
typegen "github.com/whyrusleeping/cbor-gen"
)
-1
View File
@@ -3,7 +3,6 @@ package state
import (
"bytes"
"context"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
+8 -10
View File
@@ -114,10 +114,10 @@ func TestMarketPredicates(t *testing.T) {
}
oldBalances := map[address.Address]balance{
tutils.NewIDAddr(t, 1): {abi.NewTokenAmount(1000), abi.NewTokenAmount(1000)},
tutils.NewIDAddr(t, 2): {abi.NewTokenAmount(2000), abi.NewTokenAmount(500)},
tutils.NewIDAddr(t, 3): {abi.NewTokenAmount(3000), abi.NewTokenAmount(2000)},
tutils.NewIDAddr(t, 5): {abi.NewTokenAmount(3000), abi.NewTokenAmount(1000)},
tutils.NewIDAddr(t, 1): balance{abi.NewTokenAmount(1000), abi.NewTokenAmount(1000)},
tutils.NewIDAddr(t, 2): balance{abi.NewTokenAmount(2000), abi.NewTokenAmount(500)},
tutils.NewIDAddr(t, 3): balance{abi.NewTokenAmount(3000), abi.NewTokenAmount(2000)},
tutils.NewIDAddr(t, 5): balance{abi.NewTokenAmount(3000), abi.NewTokenAmount(1000)},
}
oldStateC := createMarketState(ctx, t, store, oldDeals, oldProps, oldBalances)
@@ -162,10 +162,10 @@ func TestMarketPredicates(t *testing.T) {
// NB: DealProposals cannot be modified, so don't test that case.
}
newBalances := map[address.Address]balance{
tutils.NewIDAddr(t, 1): {abi.NewTokenAmount(3000), abi.NewTokenAmount(0)},
tutils.NewIDAddr(t, 2): {abi.NewTokenAmount(2000), abi.NewTokenAmount(500)},
tutils.NewIDAddr(t, 4): {abi.NewTokenAmount(5000), abi.NewTokenAmount(0)},
tutils.NewIDAddr(t, 5): {abi.NewTokenAmount(1000), abi.NewTokenAmount(3000)},
tutils.NewIDAddr(t, 1): balance{abi.NewTokenAmount(3000), abi.NewTokenAmount(0)},
tutils.NewIDAddr(t, 2): balance{abi.NewTokenAmount(2000), abi.NewTokenAmount(500)},
tutils.NewIDAddr(t, 4): balance{abi.NewTokenAmount(5000), abi.NewTokenAmount(0)},
tutils.NewIDAddr(t, 5): balance{abi.NewTokenAmount(1000), abi.NewTokenAmount(3000)},
}
newStateC := createMarketState(ctx, t, store, newDeals, newProps, newBalances)
@@ -505,7 +505,6 @@ func createBalanceTable(ctx context.Context, t *testing.T, store adt.Store, bala
lockedMapRootCid, err := lockedMapRoot.Root()
require.NoError(t, err)
lockedRoot, err := adt.AsBalanceTable(store, lockedMapRootCid)
require.NoError(t, err)
for addr, balance := range balances {
err := escrowRoot.Add(addr, big.Add(balance.available, balance.locked))
@@ -543,7 +542,6 @@ func createEmptyMinerState(ctx context.Context, t *testing.T, store adt.Store, o
emptyVestingFunds := miner.ConstructVestingFunds()
emptyVestingFundsCid, err := store.Put(store.Context(), emptyVestingFunds)
require.NoError(t, err)
emptyDeadlines := miner.ConstructDeadlines(emptyDeadline)
emptyDeadlinesCid, err := store.Put(store.Context(), emptyDeadlines)
+3 -4
View File
@@ -41,11 +41,10 @@ import (
"github.com/filecoin-project/lotus/node/repo"
)
const msgsPerBlock = 20
//nolint:deadcode,varcheck
var log = logging.Logger("gen")
const msgsPerBlock = 20
var ValidWpostForTesting = []abi.PoStProof{{
ProofBytes: []byte("valid proof"),
}}
@@ -606,7 +605,7 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch,
buf := new(bytes.Buffer)
if err := miner.MarshalCBOR(buf); err != nil {
return nil, xerrors.Errorf("failed to cbor marshal address: %w", err)
return nil, xerrors.Errorf("failed to cbor marshal address: %w")
}
electionRand, err := store.DrawRandomness(brand.Data, crypto.DomainSeparationTag_ElectionProofProduction, round, buf.Bytes())
+4 -10
View File
@@ -129,9 +129,6 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
return nil
})
if err != nil {
return cid.Undef, xerrors.Errorf("mutating state: %w", err)
}
}
// Add market funds
@@ -220,12 +217,9 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
}
err = vm.MutateState(ctx, builtin.RewardActorAddr, func(sct cbor.IpldStore, st *reward.State) error {
*st = *reward.ConstructState(qaPow)
st = reward.ConstructState(qaPow)
return nil
})
if err != nil {
return cid.Undef, xerrors.Errorf("mutating state: %w", err)
}
}
for i, m := range miners {
@@ -250,7 +244,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
// we've added fake power for this sector above, remove it now
err = vm.MutateState(ctx, builtin.StoragePowerActorAddr, func(cst cbor.IpldStore, st *power.State) error {
st.TotalQualityAdjPower = types.BigSub(st.TotalQualityAdjPower, sectorWeight) //nolint:scopelint
st.TotalQualityAdjPower = types.BigSub(st.TotalQualityAdjPower, sectorWeight)
st.TotalRawBytePower = types.BigSub(st.TotalRawBytePower, types.NewInt(uint64(m.SectorSize)))
return nil
})
@@ -330,13 +324,13 @@ type fakeRand struct{}
func (fr *fakeRand) GetChainRandomness(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) {
out := make([]byte, 32)
_, _ = rand.New(rand.NewSource(int64(randEpoch * 1000))).Read(out) //nolint
_, _ = rand.New(rand.NewSource(int64(randEpoch * 1000))).Read(out)
return out, nil
}
func (fr *fakeRand) GetBeaconRandomness(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) {
out := make([]byte, 32)
_, _ = rand.New(rand.NewSource(int64(randEpoch))).Read(out) //nolint
_, _ = rand.New(rand.NewSource(int64(randEpoch))).Read(out)
return out, nil
}
+4
View File
@@ -21,6 +21,10 @@ func mustEnc(i cbg.CBORMarshaler) []byte {
return enc
}
func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method abi.MethodNum, params []byte) ([]byte, error) {
return doExecValue(ctx, vm, to, from, types.NewInt(0), method, params)
}
func doExecValue(ctx context.Context, vm *vm.VM, to, from address.Address, value types.BigInt, method abi.MethodNum, params []byte) ([]byte, error) {
act, err := vm.StateTree().GetActor(from)
if err != nil {
-1
View File
@@ -147,7 +147,6 @@ func TestAddFunds(t *testing.T) {
}
for testCase, data := range testCases {
//nolint:scopelint
t.Run(testCase, func(t *testing.T) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
+1 -1
View File
@@ -56,7 +56,7 @@ func binomialCoefficient(n, k float64) float64 {
for d := 1.0; d <= k; d++ {
r *= n
r /= d
n--
n -= 1
}
return r
}
+85 -207
View File
@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"math"
stdbig "math/big"
"sort"
"sync"
"time"
@@ -28,7 +27,6 @@ import (
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/lib/sigs"
@@ -39,7 +37,7 @@ import (
var log = logging.Logger("messagepool")
var futureDebug = false
const futureDebug = false
var rbfNumBig = types.NewInt(uint64((ReplaceByFeeRatioDefault - 1) * RbfDenom))
var rbfDenomBig = types.NewInt(RbfDenom)
@@ -48,10 +46,6 @@ const RbfDenom = 256
var RepublishInterval = pubsub.TimeCacheDuration + time.Duration(5*build.BlockDelaySecs+build.PropagationDelaySecs)*time.Second
var minimumBaseFee = types.NewInt(uint64(build.MinimumBaseFee))
var MaxActorPendingMessages = 1000
var (
ErrMessageTooBig = errors.New("message too big")
@@ -59,15 +53,12 @@ var (
ErrNonceTooLow = errors.New("message nonce too low")
ErrGasFeeCapTooLow = errors.New("gas fee cap too low")
ErrNotEnoughFunds = errors.New("not enough funds to execute transaction")
ErrInvalidToAddr = errors.New("message had invalid to address")
ErrSoftValidationFailure = errors.New("validation failure")
ErrRBFTooLowPremium = errors.New("replace by fee has too low GasPremium")
ErrTooManyPendingMessages = errors.New("too many pending messages for actor")
ErrBroadcastAnyway = errors.New("broadcasting message despite validation fail")
ErrRBFTooLowPremium = errors.New("replace by fee has too low GasPremium")
ErrTryAgain = errors.New("state inconsistency while pushing message; please try again")
)
@@ -126,19 +117,17 @@ type MessagePool struct {
}
type msgSet struct {
msgs map[uint64]*types.SignedMessage
nextNonce uint64
requiredFunds *stdbig.Int
msgs map[uint64]*types.SignedMessage
nextNonce uint64
}
func newMsgSet() *msgSet {
return &msgSet{
msgs: make(map[uint64]*types.SignedMessage),
requiredFunds: stdbig.NewInt(0),
msgs: make(map[uint64]*types.SignedMessage),
}
}
func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool, limit bool) (bool, error) {
func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool) (bool, error) {
if len(ms.msgs) == 0 || m.Message.Nonce >= ms.nextNonce {
ms.nextNonce = m.Message.Nonce + 1
}
@@ -160,51 +149,21 @@ func (ms *msgSet) add(m *types.SignedMessage, mp *MessagePool, limit bool) (bool
ErrRBFTooLowPremium)
}
}
ms.requiredFunds.Sub(ms.requiredFunds, exms.Message.RequiredFunds().Int)
//ms.requiredFunds.Sub(ms.requiredFunds, exms.Message.Value.Int)
}
if !has && limit && len(ms.msgs) > MaxActorPendingMessages {
log.Errorf("too many pending messages from actor %s", m.Message.From)
return false, ErrTooManyPendingMessages
}
ms.msgs[m.Message.Nonce] = m
ms.requiredFunds.Add(ms.requiredFunds, m.Message.RequiredFunds().Int)
//ms.requiredFunds.Add(ms.requiredFunds, m.Message.Value.Int)
return !has, nil
}
func (ms *msgSet) rm(nonce uint64) {
m, has := ms.msgs[nonce]
if has {
ms.requiredFunds.Sub(ms.requiredFunds, m.Message.RequiredFunds().Int)
//ms.requiredFunds.Sub(ms.requiredFunds, m.Message.Value.Int)
delete(ms.msgs, nonce)
}
}
func (ms *msgSet) getRequiredFunds(nonce uint64) types.BigInt {
requiredFunds := new(stdbig.Int).Set(ms.requiredFunds)
m, has := ms.msgs[nonce]
if has {
requiredFunds.Sub(requiredFunds, m.Message.RequiredFunds().Int)
//requiredFunds.Sub(requiredFunds, m.Message.Value.Int)
}
return types.BigInt{Int: requiredFunds}
}
func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*MessagePool, error) {
cache, _ := lru.New2Q(build.BlsSignatureCacheSize)
verifcache, _ := lru.New2Q(build.VerifSigCacheSize)
cfg, err := loadConfig(ds)
if err != nil {
return nil, xerrors.Errorf("error loading mpool config: %w", err)
if err != nil {
return nil, xerrors.Errorf("error loading mpool config: %w", err)
}
}
mp := &MessagePool{
@@ -230,7 +189,12 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
// enable initial prunes
mp.pruneCooldown <- struct{}{}
// load the current tipset and subscribe to head changes _before_ loading local messages
if err := mp.loadLocal(); err != nil {
log.Errorf("loading local messages: %+v", err)
}
go mp.runLoop()
mp.curTs = api.SubscribeHeadChanges(func(rev, app []*types.TipSet) error {
err := mp.HeadChange(rev, app)
if err != nil {
@@ -239,12 +203,6 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName) (*Messa
return err
})
if err := mp.loadLocal(); err != nil {
log.Errorf("loading local messages: %+v", err)
}
go mp.runLoop()
return mp, nil
}
@@ -297,7 +255,7 @@ func (mp *MessagePool) addLocal(m *types.SignedMessage, msgb []byte) error {
return nil
}
func (mp *MessagePool) verifyMsgBeforeAdd(m *types.SignedMessage, epoch abi.ChainEpoch) error {
func (mp *MessagePool) verifyMsgBeforePush(m *types.SignedMessage, epoch abi.ChainEpoch) error {
minGas := vm.PricelistByEpoch(epoch).OnChainMessage(m.ChainLength())
if err := m.VMMessage().ValidForBlockInclusion(minGas.Total()); err != nil {
@@ -318,12 +276,25 @@ func (mp *MessagePool) Push(m *types.SignedMessage) (cid.Cid, error) {
<-mp.addSema
}()
mp.curTsLk.Lock()
curTs := mp.curTs
epoch := curTs.Height()
mp.curTsLk.Unlock()
if err := mp.verifyMsgBeforePush(m, epoch); err != nil {
return cid.Undef, err
}
msgb, err := m.Serialize()
if err != nil {
return cid.Undef, err
}
mp.curTsLk.Lock()
if mp.curTs != curTs {
mp.curTsLk.Unlock()
return cid.Undef, ErrTryAgain
}
if err := mp.addTs(m, mp.curTs); err != nil {
mp.curTsLk.Unlock()
return cid.Undef, err
@@ -346,11 +317,6 @@ func (mp *MessagePool) checkMessage(m *types.SignedMessage) error {
return xerrors.Errorf("mpool message too large (%dB): %w", m.Size(), ErrMessageTooBig)
}
// Perform syntactic validation, minGas=0 as we check the actual mingas before we add it
if err := m.Message.ValidForBlockInclusion(0); err != nil {
return xerrors.Errorf("message not valid for block inclusion: %w", err)
}
if m.Message.To == address.Undef {
return ErrInvalidToAddr
}
@@ -359,12 +325,8 @@ func (mp *MessagePool) checkMessage(m *types.SignedMessage) error {
return ErrMessageValueTooHigh
}
if m.Message.GasFeeCap.LessThan(minimumBaseFee) {
return ErrGasFeeCapTooLow
}
if err := mp.VerifyMsgSig(m); err != nil {
log.Warnf("signature verification failed: %s", err)
log.Warnf("mpooladd signature verification failed: %s", err)
return err
}
@@ -424,71 +386,48 @@ func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error {
return nil
}
func (mp *MessagePool) checkBalance(m *types.SignedMessage, curTs *types.TipSet) error {
balance, err := mp.getStateBalance(m.Message.From, curTs)
if err != nil {
return xerrors.Errorf("failed to check sender balance: %s: %w", err, ErrSoftValidationFailure)
}
requiredFunds := m.Message.RequiredFunds()
if balance.LessThan(requiredFunds) {
return xerrors.Errorf("not enough funds (required: %s, balance: %s): %w", types.FIL(requiredFunds), types.FIL(balance), ErrNotEnoughFunds)
}
// add Value for soft failure check
//requiredFunds = types.BigAdd(requiredFunds, m.Message.Value)
mset, ok := mp.pending[m.Message.From]
if ok {
requiredFunds = types.BigAdd(requiredFunds, mset.getRequiredFunds(m.Message.Nonce))
}
if balance.LessThan(requiredFunds) {
// Note: we fail here for ErrSoftValidationFailure to signal a soft failure because we might
// be out of sync.
return xerrors.Errorf("not enough funds including pending messages (required: %s, balance: %s): %w", types.FIL(requiredFunds), types.FIL(balance), ErrSoftValidationFailure)
}
return nil
}
func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet) error {
snonce, err := mp.getStateNonce(m.Message.From, curTs)
if err != nil {
return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrSoftValidationFailure)
return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrBroadcastAnyway)
}
if snonce > m.Message.Nonce {
return xerrors.Errorf("minimum expected nonce is %d: %w", snonce, ErrNonceTooLow)
}
balance, err := mp.getStateBalance(m.Message.From, curTs)
if err != nil {
return xerrors.Errorf("failed to check sender balance: %s: %w", err, ErrBroadcastAnyway)
}
if balance.LessThan(m.Message.RequiredFunds()) {
return xerrors.Errorf("not enough funds (required: %s, balance: %s): %w", types.FIL(m.Message.RequiredFunds()), types.FIL(balance), ErrNotEnoughFunds)
}
mp.lk.Lock()
defer mp.lk.Unlock()
if err := mp.verifyMsgBeforeAdd(m, curTs.Height()); err != nil {
return err
}
if err := mp.checkBalance(m, curTs); err != nil {
return err
}
return mp.addLocked(m, true)
return mp.addLocked(m)
}
func (mp *MessagePool) addSkipChecks(m *types.SignedMessage) error {
mp.lk.Lock()
defer mp.lk.Unlock()
return mp.addLocked(m, false)
return mp.addLocked(m)
}
func (mp *MessagePool) addLocked(m *types.SignedMessage, limit bool) error {
func (mp *MessagePool) addLocked(m *types.SignedMessage) error {
log.Debugf("mpooladd: %s %d", m.Message.From, m.Message.Nonce)
if m.Signature.Type == crypto.SigTypeBLS {
mp.blsSigCache.Add(m.Cid(), m.Signature)
}
if m.Message.GasLimit > build.BlockGasLimit {
return xerrors.Errorf("given message has too high of a gas limit")
}
if _, err := mp.api.PutMessage(m); err != nil {
log.Warnf("mpooladd cs.PutMessage failed: %s", err)
return err
@@ -505,7 +444,7 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage, limit bool) error {
mp.pending[m.Message.From] = mset
}
incr, err := mset.add(m, mp, limit)
incr, err := mset.add(m, mp)
if err != nil {
log.Info(err)
return err
@@ -560,16 +499,42 @@ func (mp *MessagePool) getNonceLocked(addr address.Address, curTs *types.TipSet)
}
func (mp *MessagePool) getStateNonce(addr address.Address, curTs *types.TipSet) (uint64, error) {
act, err := mp.api.GetActorAfter(addr, curTs)
// TODO: this method probably should be cached
act, err := mp.api.StateGetActor(addr, curTs)
if err != nil {
return 0, err
}
return act.Nonce, nil
baseNonce := act.Nonce
// TODO: the correct thing to do here is probably to set curTs to chain.head
// but since we have an accurate view of the world until a head change occurs,
// this should be fine
if curTs == nil {
return baseNonce, nil
}
msgs, err := mp.api.MessagesForTipset(curTs)
if err != nil {
return 0, xerrors.Errorf("failed to check messages for tipset: %w", err)
}
for _, m := range msgs {
msg := m.VMMessage()
if msg.From == addr {
if msg.Nonce != baseNonce {
return 0, xerrors.Errorf("tipset %s has bad nonce ordering (%d != %d)", curTs.Cids(), msg.Nonce, baseNonce)
}
baseNonce++
}
}
return baseNonce, nil
}
func (mp *MessagePool) getStateBalance(addr address.Address, ts *types.TipSet) (types.BigInt, error) {
act, err := mp.api.GetActorAfter(addr, ts)
act, err := mp.api.StateGetActor(addr, ts)
if err != nil {
return types.EmptyInt, err
}
@@ -616,16 +581,6 @@ func (mp *MessagePool) PushWithNonce(ctx context.Context, addr address.Address,
return nil, err
}
err = mp.checkMessage(msg)
if err != nil {
return nil, err
}
msgb, err := msg.Serialize()
if err != nil {
return nil, err
}
// reacquire the locks and check state for consistency
mp.curTsLk.Lock()
defer mp.curTsLk.Unlock()
@@ -646,15 +601,16 @@ func (mp *MessagePool) PushWithNonce(ctx context.Context, addr address.Address,
return nil, ErrTryAgain
}
if err := mp.verifyMsgBeforeAdd(msg, curTs.Height()); err != nil {
if err := mp.verifyMsgBeforePush(msg, mp.curTs.Height()); err != nil {
return nil, err
}
if err := mp.checkBalance(msg, curTs); err != nil {
msgb, err := msg.Serialize()
if err != nil {
return nil, err
}
if err := mp.addLocked(msg, true); err != nil {
if err := mp.addLocked(msg); err != nil {
return nil, xerrors.Errorf("add locked failed: %w", err)
}
if err := mp.addLocal(msg, msgb); err != nil {
@@ -688,7 +644,7 @@ func (mp *MessagePool) remove(from address.Address, nonce uint64) {
// NB: This deletes any message with the given nonce. This makes sense
// as two messages with the same sender cannot have the same nonce
mset.rm(nonce)
delete(mset.msgs, nonce)
if len(mset.msgs) == 0 {
delete(mp.pending, from)
@@ -714,10 +670,6 @@ func (mp *MessagePool) Pending() ([]*types.SignedMessage, *types.TipSet) {
mp.lk.Lock()
defer mp.lk.Unlock()
return mp.allPending()
}
func (mp *MessagePool) allPending() ([]*types.SignedMessage, *types.TipSet) {
out := make([]*types.SignedMessage, 0)
for a := range mp.pending {
out = append(out, mp.pendingFor(a)...)
@@ -725,7 +677,6 @@ func (mp *MessagePool) allPending() ([]*types.SignedMessage, *types.TipSet) {
return out, mp.curTs
}
func (mp *MessagePool) PendingFor(a address.Address) ([]*types.SignedMessage, *types.TipSet) {
mp.curTsLk.Lock()
defer mp.curTsLk.Unlock()
@@ -858,9 +809,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
}
if len(revert) > 0 && futureDebug {
mp.lk.Lock()
msgs, ts := mp.allPending()
mp.lk.Unlock()
msgs, ts := mp.Pending()
buckets := map[address.Address]*statBucket{}
@@ -877,8 +826,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
}
for a, bkt := range buckets {
// TODO that might not be correct with GatActorAfter but it is only debug code
act, err := mp.api.GetActorAfter(a, ts)
act, err := mp.api.StateGetActor(a, ts)
if err != nil {
log.Debugf("%s, err: %s\n", a, err)
continue
@@ -928,71 +876,6 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
return merr
}
func (mp *MessagePool) runHeadChange(from *types.TipSet, to *types.TipSet, rmsgs map[address.Address]map[uint64]*types.SignedMessage) error {
add := func(m *types.SignedMessage) {
s, ok := rmsgs[m.Message.From]
if !ok {
s = make(map[uint64]*types.SignedMessage)
rmsgs[m.Message.From] = s
}
s[m.Message.Nonce] = m
}
rm := func(from address.Address, nonce uint64) {
s, ok := rmsgs[from]
if !ok {
return
}
if _, ok := s[nonce]; ok {
delete(s, nonce)
return
}
}
revert, apply, err := store.ReorgOps(mp.api.LoadTipSet, from, to)
if err != nil {
return xerrors.Errorf("failed to compute reorg ops for mpool pending messages: %w", err)
}
var merr error
for _, ts := range revert {
msgs, err := mp.MessagesForBlocks(ts.Blocks())
if err != nil {
log.Errorf("error retrieving messages for reverted block: %s", err)
merr = multierror.Append(merr, err)
continue
}
for _, msg := range msgs {
add(msg)
}
}
for _, ts := range apply {
for _, b := range ts.Blocks() {
bmsgs, smsgs, err := mp.api.MessagesForBlock(b)
if err != nil {
xerr := xerrors.Errorf("failed to get messages for apply block %s(height %d) (msgroot = %s): %w", b.Cid(), b.Height, b.Messages, err)
log.Errorf("error retrieving messages for block: %s", xerr)
merr = multierror.Append(merr, xerr)
continue
}
for _, msg := range smsgs {
rm(msg.Message.From, msg.Message.Nonce)
}
for _, msg := range bmsgs {
rm(msg.From, msg.Nonce)
}
}
}
return merr
}
type statBucket struct {
msgs map[uint64]*types.SignedMessage
}
@@ -1043,7 +926,6 @@ func (mp *MessagePool) Updates(ctx context.Context) (<-chan api.MpoolUpdate, err
go func() {
defer mp.changes.Unsub(sub, localUpdates)
defer close(out)
for {
select {
@@ -1052,13 +934,9 @@ func (mp *MessagePool) Updates(ctx context.Context) (<-chan api.MpoolUpdate, err
case out <- u.(api.MpoolUpdate):
case <-ctx.Done():
return
case <-mp.closer:
return
}
case <-ctx.Done():
return
case <-mp.closer:
return
}
}
}()
+9 -355
View File
@@ -3,11 +3,9 @@ package messagepool
import (
"context"
"fmt"
"sort"
"testing"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/chain/messagepool/gasguess"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/lotus/chain/wallet"
@@ -32,25 +30,14 @@ type testMpoolAPI struct {
balance map[address.Address]types.BigInt
tipsets []*types.TipSet
published int
}
func newTestMpoolAPI() *testMpoolAPI {
tma := &testMpoolAPI{
return &testMpoolAPI{
bmsgs: make(map[cid.Cid][]*types.SignedMessage),
statenonce: make(map[address.Address]uint64),
balance: make(map[address.Address]types.BigInt),
}
genesis := mock.MkBlock(nil, 1, 1)
tma.tipsets = append(tma.tipsets, mock.TipSet(genesis))
return tma
}
func (tma *testMpoolAPI) nextBlock() *types.BlockHeader {
newBlk := mock.MkBlock(tma.tipsets[len(tma.tipsets)-1], 1, 1)
tma.tipsets = append(tma.tipsets, mock.TipSet(newBlk))
return newBlk
}
func (tma *testMpoolAPI) applyBlock(t *testing.T, b *types.BlockHeader) {
@@ -81,11 +68,12 @@ func (tma *testMpoolAPI) setBalanceRaw(addr address.Address, v types.BigInt) {
func (tma *testMpoolAPI) setBlockMessages(h *types.BlockHeader, msgs ...*types.SignedMessage) {
tma.bmsgs[h.Cid()] = msgs
tma.tipsets = append(tma.tipsets, mock.TipSet(h))
}
func (tma *testMpoolAPI) SubscribeHeadChanges(cb func(rev, app []*types.TipSet) error) *types.TipSet {
tma.cb = cb
return tma.tipsets[0]
return nil
}
func (tma *testMpoolAPI) PutMessage(m types.ChainMsg) (cid.Cid, error) {
@@ -93,47 +81,18 @@ func (tma *testMpoolAPI) PutMessage(m types.ChainMsg) (cid.Cid, error) {
}
func (tma *testMpoolAPI) PubSubPublish(string, []byte) error {
tma.published++
return nil
}
func (tma *testMpoolAPI) GetActorAfter(addr address.Address, ts *types.TipSet) (*types.Actor, error) {
// regression check for load bug
if ts == nil {
panic("GetActorAfter called with nil tipset")
}
func (tma *testMpoolAPI) StateGetActor(addr address.Address, ts *types.TipSet) (*types.Actor, error) {
balance, ok := tma.balance[addr]
if !ok {
balance = types.NewInt(1000e6)
tma.balance[addr] = balance
}
msgs := make([]*types.SignedMessage, 0)
for _, b := range ts.Blocks() {
for _, m := range tma.bmsgs[b.Cid()] {
if m.Message.From == addr {
msgs = append(msgs, m)
}
}
}
sort.Slice(msgs, func(i, j int) bool {
return msgs[i].Message.Nonce < msgs[j].Message.Nonce
})
nonce := tma.statenonce[addr]
for _, m := range msgs {
if m.Message.Nonce != nonce {
break
}
nonce++
}
return &types.Actor{
Code: builtin.StorageMarketActorCodeID,
Nonce: nonce,
Nonce: tma.statenonce[addr],
Balance: balance,
}, nil
}
@@ -219,7 +178,7 @@ func TestMessagePool(t *testing.T) {
t.Fatal(err)
}
a := tma.nextBlock()
a := mock.MkBlock(nil, 1, 1)
sender, err := w.GenerateKey(crypto.SigTypeBLS)
if err != nil {
@@ -245,56 +204,7 @@ func TestMessagePool(t *testing.T) {
assertNonce(t, mp, sender, 2)
}
func TestMessagePoolMessagesInEachBlock(t *testing.T) {
tma := newTestMpoolAPI()
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest")
if err != nil {
t.Fatal(err)
}
a := tma.nextBlock()
sender, err := w.GenerateKey(crypto.SigTypeBLS)
if err != nil {
t.Fatal(err)
}
target := mock.Address(1001)
var msgs []*types.SignedMessage
for i := 0; i < 5; i++ {
m := mock.MkMessage(sender, target, uint64(i), w)
msgs = append(msgs, m)
mustAdd(t, mp, m)
}
tma.setStateNonce(sender, 0)
tma.setBlockMessages(a, msgs[0], msgs[1])
tma.applyBlock(t, a)
tsa := mock.TipSet(a)
_, _ = mp.Pending()
selm, _ := mp.SelectMessages(tsa, 1)
if len(selm) == 0 {
t.Fatal("should have returned the rest of the messages")
}
}
func TestRevertMessages(t *testing.T) {
futureDebug = true
defer func() {
futureDebug = false
}()
tma := newTestMpoolAPI()
w, err := wallet.NewWallet(wallet.NewMemKeyStore())
@@ -309,8 +219,8 @@ func TestRevertMessages(t *testing.T) {
t.Fatal(err)
}
a := tma.nextBlock()
b := tma.nextBlock()
a := mock.MkBlock(nil, 1, 1)
b := mock.MkBlock(mock.TipSet(a), 1, 1)
sender, err := w.GenerateKey(crypto.SigTypeBLS)
if err != nil {
@@ -344,7 +254,6 @@ func TestRevertMessages(t *testing.T) {
assertNonce(t, mp, sender, 4)
p, _ := mp.Pending()
fmt.Printf("%+v\n", p)
if len(p) != 3 {
t.Fatal("expected three messages in mempool")
}
@@ -366,14 +275,13 @@ func TestPruningSimple(t *testing.T) {
t.Fatal(err)
}
a := tma.nextBlock()
a := mock.MkBlock(nil, 1, 1)
tma.applyBlock(t, a)
sender, err := w.GenerateKey(crypto.SigTypeBLS)
if err != nil {
t.Fatal(err)
}
tma.setBalance(sender, 1) // in FIL
target := mock.Address(1001)
for i := 0; i < 5; i++ {
@@ -400,257 +308,3 @@ func TestPruningSimple(t *testing.T) {
t.Fatal("expected only 5 messages in pool, got: ", len(msgs))
}
}
func TestLoadLocal(t *testing.T) {
tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest")
if err != nil {
t.Fatal(err)
}
// the actors
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
tma.setBalance(a1, 1) // in FIL
tma.setBalance(a2, 1) // in FIL
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
msgs := make(map[cid.Cid]struct{})
for i := 0; i < 10; i++ {
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
cid, err := mp.Push(m)
if err != nil {
t.Fatal(err)
}
msgs[cid] = struct{}{}
}
err = mp.Close()
if err != nil {
t.Fatal(err)
}
mp, err = New(tma, ds, "mptest")
if err != nil {
t.Fatal(err)
}
pmsgs, _ := mp.Pending()
if len(msgs) != len(pmsgs) {
t.Fatalf("expected %d messages, but got %d", len(msgs), len(pmsgs))
}
for _, m := range pmsgs {
cid := m.Cid()
_, ok := msgs[cid]
if !ok {
t.Fatal("unknown message")
}
delete(msgs, cid)
}
if len(msgs) > 0 {
t.Fatalf("not all messages were laoded; missing %d messages", len(msgs))
}
}
func TestClearAll(t *testing.T) {
tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest")
if err != nil {
t.Fatal(err)
}
// the actors
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
tma.setBalance(a1, 1) // in FIL
tma.setBalance(a2, 1) // in FIL
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
for i := 0; i < 10; i++ {
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
_, err := mp.Push(m)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < 10; i++ {
m := makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
mustAdd(t, mp, m)
}
mp.Clear(true)
pending, _ := mp.Pending()
if len(pending) > 0 {
t.Fatalf("cleared the mpool, but got %d pending messages", len(pending))
}
}
func TestClearNonLocal(t *testing.T) {
tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest")
if err != nil {
t.Fatal(err)
}
// the actors
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
tma.setBalance(a1, 1) // in FIL
tma.setBalance(a2, 1) // in FIL
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
for i := 0; i < 10; i++ {
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
_, err := mp.Push(m)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < 10; i++ {
m := makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
mustAdd(t, mp, m)
}
mp.Clear(false)
pending, _ := mp.Pending()
if len(pending) != 10 {
t.Fatalf("expected 10 pending messages, but got %d instead", len(pending))
}
for _, m := range pending {
if m.Message.From != a1 {
t.Fatalf("expected message from %s but got one from %s instead", a1, m.Message.From)
}
}
}
func TestUpdates(t *testing.T) {
tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest")
if err != nil {
t.Fatal(err)
}
// the actors
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
ch, err := mp.Updates(ctx)
if err != nil {
t.Fatal(err)
}
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
tma.setBalance(a1, 1) // in FIL
tma.setBalance(a2, 1) // in FIL
for i := 0; i < 10; i++ {
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
_, err := mp.Push(m)
if err != nil {
t.Fatal(err)
}
_, ok := <-ch
if !ok {
t.Fatal("expected update, but got a closed channel instead")
}
}
err = mp.Close()
if err != nil {
t.Fatal(err)
}
_, ok := <-ch
if ok {
t.Fatal("expected closed channel, but got an update instead")
}
}
+4 -9
View File
@@ -16,7 +16,7 @@ type Provider interface {
SubscribeHeadChanges(func(rev, app []*types.TipSet) error) *types.TipSet
PutMessage(m types.ChainMsg) (cid.Cid, error)
PubSubPublish(string, []byte) error
GetActorAfter(address.Address, *types.TipSet) (*types.Actor, error)
StateGetActor(address.Address, *types.TipSet) (*types.Actor, error)
StateAccountKey(context.Context, address.Address, *types.TipSet) (address.Address, error)
MessagesForBlock(*types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error)
MessagesForTipset(*types.TipSet) ([]types.ChainMsg, error)
@@ -43,17 +43,12 @@ func (mpp *mpoolProvider) PutMessage(m types.ChainMsg) (cid.Cid, error) {
}
func (mpp *mpoolProvider) PubSubPublish(k string, v []byte) error {
return mpp.ps.Publish(k, v) //nolint
return mpp.ps.Publish(k, v)
}
func (mpp *mpoolProvider) GetActorAfter(addr address.Address, ts *types.TipSet) (*types.Actor, error) {
func (mpp *mpoolProvider) StateGetActor(addr address.Address, ts *types.TipSet) (*types.Actor, error) {
var act types.Actor
stcid, _, err := mpp.sm.TipSetState(context.TODO(), ts)
if err != nil {
return nil, xerrors.Errorf("computing tipset state for GetActor: %w", err)
}
return &act, mpp.sm.WithStateTree(stcid, mpp.sm.WithActor(addr, stmgr.GetActor(&act)))
return &act, mpp.sm.WithParentState(ts, mpp.sm.WithActor(addr, stmgr.GetActor(&act)))
}
func (mpp *mpoolProvider) StateAccountKey(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
+1 -1
View File
@@ -108,7 +108,7 @@ func (mp *MessagePool) republishPendingMessages() error {
// we can't fit the current chain but there is gas to spare
// trim it and push it down
chain.Trim(gasLimit, mp, baseFee, ts)
chain.Trim(gasLimit, mp, baseFee, ts, false)
for j := i; j < len(chains)-1; j++ {
if chains[j].Before(chains[j+1]) {
break
-66
View File
@@ -1,66 +0,0 @@
package messagepool
import (
"testing"
"time"
"github.com/filecoin-project/lotus/chain/messagepool/gasguess"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/ipfs/go-datastore"
)
func TestRepubMessages(t *testing.T) {
tma := newTestMpoolAPI()
ds := datastore.NewMapDatastore()
mp, err := New(tma, ds, "mptest")
if err != nil {
t.Fatal(err)
}
// the actors
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
tma.setBalance(a1, 1) // in FIL
for i := 0; i < 10; i++ {
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1))
_, err := mp.Push(m)
if err != nil {
t.Fatal(err)
}
}
if tma.published != 10 {
t.Fatalf("expected to have published 10 messages, but got %d instead", tma.published)
}
mp.repubTrigger <- struct{}{}
time.Sleep(100 * time.Millisecond)
if tma.published != 20 {
t.Fatalf("expected to have published 20 messages, but got %d instead", tma.published)
}
}
+84 -40
View File
@@ -14,6 +14,7 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
abig "github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/ipfs/go-cid"
)
var bigBlockGasLimit = big.NewInt(build.BlockGasLimit)
@@ -217,7 +218,7 @@ tailLoop:
for gasLimit >= minGas && last < len(chains) {
// trim if necessary
if chains[last].gasLimit > gasLimit {
chains[last].Trim(gasLimit, mp, baseFee, ts)
chains[last].Trim(gasLimit, mp, baseFee, ts, false)
}
// push down if it hasn't been invalidated
@@ -284,7 +285,7 @@ tailLoop:
}
// dependencies fit, just trim it
chain.Trim(gasLimit-depGasLimit, mp, baseFee, ts)
chain.Trim(gasLimit-depGasLimit, mp, baseFee, ts, false)
last += i
continue tailLoop
}
@@ -389,7 +390,7 @@ func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.S
tailLoop:
for gasLimit >= minGas && last < len(chains) {
// trim
chains[last].Trim(gasLimit, mp, baseFee, ts)
chains[last].Trim(gasLimit, mp, baseFee, ts, false)
// push down if it hasn't been invalidated
if chains[last].valid {
@@ -462,27 +463,15 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui
}
}
if len(chains) == 0 {
return nil, gasLimit
}
// 2. Sort the chains
sort.Slice(chains, func(i, j int) bool {
return chains[i].Before(chains[j])
})
if len(chains) != 0 && chains[0].gasPerf < 0 {
log.Warnw("all priority messages in mpool have negative gas performance", "bestGasPerf", chains[0].gasPerf)
return nil, gasLimit
}
// 3. Merge chains until the block limit, as long as they have non-negative gas performance
// 3. Merge chains until the block limit; we are willing to include negative performing chains
// as these are messages from our own miners
last := len(chains)
for i, chain := range chains {
if chain.gasPerf < 0 {
break
}
if chain.gasLimit <= gasLimit {
gasLimit -= chain.gasLimit
result = append(result, chain.msgs...)
@@ -496,8 +485,8 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui
tailLoop:
for gasLimit >= minGas && last < len(chains) {
// trim, discarding negative performing messages
chains[last].Trim(gasLimit, mp, baseFee, ts)
// trim, without discarding negative performing messages
chains[last].Trim(gasLimit, mp, baseFee, ts, true)
// push down if it hasn't been invalidated
if chains[last].valid {
@@ -515,12 +504,6 @@ tailLoop:
if !chain.valid {
continue
}
// if gasPerf < 0 we have no more profitable chains
if chain.gasPerf < 0 {
break tailLoop
}
// does it fit in the bock?
if chain.gasLimit <= gasLimit {
gasLimit -= chain.gasLimit
@@ -533,9 +516,9 @@ tailLoop:
continue tailLoop
}
// the merge loop ended after processing all the chains and we probably still have gas to spare;
// end the loop
break
// the merge loop ended after processing all the chains and we probably still have gas to spare
// -- mark the end.
last = len(chains)
}
return result, gasLimit
@@ -545,6 +528,7 @@ func (mp *MessagePool) getPendingMessages(curTs, ts *types.TipSet) (map[address.
start := time.Now()
result := make(map[address.Address]map[uint64]*types.SignedMessage)
haveCids := make(map[cid.Cid]struct{})
defer func() {
if dt := time.Since(start); dt > time.Millisecond {
log.Infow("get pending messages done", "took", dt)
@@ -570,6 +554,10 @@ func (mp *MessagePool) getPendingMessages(curTs, ts *types.TipSet) (map[address.
}
result[a] = msetCopy
// mark the messages as seen
for _, m := range mset.msgs {
haveCids[m.Cid()] = struct{}{}
}
}
}
@@ -578,11 +566,72 @@ func (mp *MessagePool) getPendingMessages(curTs, ts *types.TipSet) (map[address.
return result, nil
}
if err := mp.runHeadChange(curTs, ts, result); err != nil {
return nil, xerrors.Errorf("failed to process difference between mpool head and given head: %w", err)
}
// nope, we need to sync the tipsets
for {
if curTs.Height() == ts.Height() {
if curTs.Equals(ts) {
return result, nil
}
return result, nil
// different blocks in tipsets -- we mark them as seen so that they are not included in
// in the message set we return, but *neither me (vyzo) nor why understand why*
// this code is also probably completely untested in production, so I am adding a big fat
// warning to revisit this case and sanity check this decision.
log.Warnf("mpool tipset has same height as target tipset but it's not equal; beware of dragons!")
have, err := mp.MessagesForBlocks(ts.Blocks())
if err != nil {
return nil, xerrors.Errorf("error retrieving messages for tipset: %w", err)
}
for _, m := range have {
haveCids[m.Cid()] = struct{}{}
}
}
msgs, err := mp.MessagesForBlocks(ts.Blocks())
if err != nil {
return nil, xerrors.Errorf("error retrieving messages for tipset: %w", err)
}
for _, m := range msgs {
if _, have := haveCids[m.Cid()]; have {
continue
}
haveCids[m.Cid()] = struct{}{}
mset, ok := result[m.Message.From]
if !ok {
mset = make(map[uint64]*types.SignedMessage)
result[m.Message.From] = mset
}
other, dupNonce := mset[m.Message.Nonce]
if dupNonce {
// duplicate nonce, selfishly keep the message with the highest GasPrice
// if the gas prices are the same, keep the one with the highest GasLimit
switch m.Message.GasPremium.Int.Cmp(other.Message.GasPremium.Int) {
case 0:
if m.Message.GasLimit > other.Message.GasLimit {
mset[m.Message.Nonce] = m
}
case 1:
mset[m.Message.Nonce] = m
}
} else {
mset[m.Message.Nonce] = m
}
}
if curTs.Height() >= ts.Height() {
return result, nil
}
ts, err = mp.api.LoadTipSet(ts.Parents())
if err != nil {
return nil, xerrors.Errorf("error loading parent tipset: %w", err)
}
}
}
func (mp *MessagePool) getGasReward(msg *types.SignedMessage, baseFee types.BigInt, ts *types.TipSet) *big.Int {
@@ -622,12 +671,7 @@ func (mp *MessagePool) createMessageChains(actor address.Address, mset map[uint6
// cannot exceed the block limit; drop all messages that exceed the limit
// - the total gasReward cannot exceed the actor's balance; drop all messages that exceed
// the balance
a, err := mp.api.GetActorAfter(actor, ts)
if err != nil {
log.Errorf("failed to load actor state, not building chain for %s: %w", actor, err)
return nil
}
a, _ := mp.api.StateGetActor(actor, ts)
curNonce := a.Nonce
balance := a.Balance.Int
gasLimit := int64(0)
@@ -773,9 +817,9 @@ func (mc *msgChain) Before(other *msgChain) bool {
(mc.gasPerf == other.gasPerf && mc.gasReward.Cmp(other.gasReward) > 0)
}
func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt, ts *types.TipSet) {
func (mc *msgChain) Trim(gasLimit int64, mp *MessagePool, baseFee types.BigInt, ts *types.TipSet, priority bool) {
i := len(mc.msgs) - 1
for i >= 0 && (mc.gasLimit > gasLimit || mc.gasPerf < 0) {
for i >= 0 && (mc.gasLimit > gasLimit || (!priority && mc.gasPerf < 0)) {
gasReward := mp.getGasReward(mc.msgs[i], baseFee, ts)
mc.gasReward = new(big.Int).Sub(mc.gasReward, gasReward)
mc.gasLimit -= mc.msgs[i].Message.GasLimit
+20 -91
View File
@@ -23,11 +23,6 @@ import (
logging "github.com/ipfs/go-log"
)
func init() {
// bump this for the selection tests
MaxActorPendingMessages = 1000000
}
func makeTestMessage(w *wallet.Wallet, from, to address.Address, nonce uint64, gasLimit int64, gasPrice uint64) *types.SignedMessage {
msg := &types.Message{
From: from,
@@ -84,7 +79,7 @@ func TestMessageChains(t *testing.T) {
t.Fatal(err)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
@@ -322,7 +317,7 @@ func TestMessageChainSkipping(t *testing.T) {
t.Fatal(err)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
@@ -392,7 +387,7 @@ func TestBasicMessageSelection(t *testing.T) {
t.Fatal(err)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
tma.applyBlock(t, block)
@@ -445,12 +440,12 @@ func TestBasicMessageSelection(t *testing.T) {
}
// now we make a block with all the messages and advance the chain
block2 := tma.nextBlock()
block2 := mock.MkBlock(ts, 2, 2)
tma.setBlockMessages(block2, msgs...)
tma.applyBlock(t, block2)
// we should have no pending messages in the mpool
pend, _ := mp.Pending()
pend, ts2 := mp.Pending()
if len(pend) != 0 {
t.Fatalf("expected no pending messages, but got %d", len(pend))
}
@@ -463,13 +458,13 @@ func TestBasicMessageSelection(t *testing.T) {
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
msgs = append(msgs, m)
}
block3 := tma.nextBlock()
block3 := mock.MkBlock(ts2, 3, 3)
tma.setBlockMessages(block3, msgs...)
ts3 := mock.TipSet(block3)
// now create another set of messages and add them to the mpool
for i := 20; i < 30; i++ {
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(2*i+200))
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(2*i+1))
mustAdd(t, mp, m)
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(i+1))
mustAdd(t, mp, m)
@@ -485,12 +480,12 @@ func TestBasicMessageSelection(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if len(msgs) != 20 {
t.Fatalf("expected 20 messages, got %d", len(msgs))
if len(msgs) != 40 {
t.Fatalf("expected 40 messages, got %d", len(msgs))
}
nextNonce = 20
for i := 0; i < 10; i++ {
nextNonce = 10
for i := 0; i < 20; i++ {
if msgs[i].Message.From != a1 {
t.Fatalf("expected message from actor a1")
}
@@ -500,8 +495,8 @@ func TestBasicMessageSelection(t *testing.T) {
nextNonce++
}
nextNonce = 20
for i := 10; i < 20; i++ {
nextNonce = 10
for i := 20; i < 40; i++ {
if msgs[i].Message.From != a2 {
t.Fatalf("expected message from actor a2")
}
@@ -536,7 +531,7 @@ func TestMessageSelectionTrimming(t *testing.T) {
t.Fatal(err)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
tma.applyBlock(t, block)
@@ -599,7 +594,7 @@ func TestPriorityMessageSelection(t *testing.T) {
t.Fatal(err)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
tma.applyBlock(t, block)
@@ -654,73 +649,6 @@ func TestPriorityMessageSelection(t *testing.T) {
}
}
func TestPriorityMessageSelection2(t *testing.T) {
mp, tma := makeTestMpool()
// the actors
w1, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a1, err := w1.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
w2, err := wallet.NewWallet(wallet.NewMemKeyStore())
if err != nil {
t.Fatal(err)
}
a2, err := w2.GenerateKey(crypto.SigTypeSecp256k1)
if err != nil {
t.Fatal(err)
}
block := tma.nextBlock()
ts := mock.TipSet(block)
tma.applyBlock(t, block)
gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin.StorageMarketActorCodeID, M: 2}]
tma.setBalance(a1, 1) // in FIL
tma.setBalance(a2, 1) // in FIL
mp.cfg.PriorityAddrs = []address.Address{a1}
nMessages := int(2 * build.BlockGasLimit / gasLimit)
for i := 0; i < nMessages; i++ {
bias := (nMessages - i) / 3
m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(1+i%3+bias))
mustAdd(t, mp, m)
m = makeTestMessage(w2, a2, a1, uint64(i), gasLimit, uint64(1+i%3+bias))
mustAdd(t, mp, m)
}
msgs, err := mp.SelectMessages(ts, 1.0)
if err != nil {
t.Fatal(err)
}
expectedMsgs := int(build.BlockGasLimit / gasLimit)
if len(msgs) != expectedMsgs {
t.Fatalf("expected %d messages but got %d", expectedMsgs, len(msgs))
}
// all messages must be from a1
nextNonce := uint64(0)
for _, m := range msgs {
if m.Message.From != a1 {
t.Fatal("expected messages from a1 before messages from a2")
}
if m.Message.Nonce != nextNonce {
t.Fatalf("expected nonce %d but got %d", nextNonce, m.Message.Nonce)
}
nextNonce++
}
}
func TestOptimalMessageSelection1(t *testing.T) {
// this test uses just a single actor sending messages with a low tq
// the chain depenent merging algorithm should pick messages from the actor
@@ -748,7 +676,7 @@ func TestOptimalMessageSelection1(t *testing.T) {
t.Fatal(err)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
tma.applyBlock(t, block)
@@ -815,7 +743,7 @@ func TestOptimalMessageSelection2(t *testing.T) {
t.Fatal(err)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
tma.applyBlock(t, block)
@@ -893,7 +821,7 @@ func TestOptimalMessageSelection3(t *testing.T) {
wallets = append(wallets, w)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
tma.applyBlock(t, block)
@@ -951,6 +879,7 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu
// actors send with an randomly distributed premium dictated by the getPremium function.
// a number of miners select with varying ticket quality and we compare the
// capacity and rewards of greedy selection -vs- optimal selection
mp, tma := makeTestMpool()
nActors := 300
@@ -973,7 +902,7 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu
wallets = append(wallets, w)
}
block := tma.nextBlock()
block := mock.MkBlock(nil, 1, 1)
ts := mock.TipSet(block)
tma.applyBlock(t, block)
+1 -2
View File
@@ -44,7 +44,7 @@ func SendHeadNotifs(nickname string) func(mctx helpers.MetricsCtx, lc fx.Lifecyc
}
}()
go func() {
sub, err := ps.Subscribe(topic) //nolint
sub, err := ps.Subscribe(topic)
if err != nil {
return
}
@@ -116,7 +116,6 @@ func sendHeadNotifs(ctx context.Context, ps *pubsub.PubSub, topic string, chain
return err
}
//nolint
if err := ps.Publish(topic, b); err != nil {
return err
}
+2 -2
View File
@@ -246,7 +246,7 @@ func (st *StateTree) DeleteActor(addr address.Address) error {
}
func (st *StateTree) Flush(ctx context.Context) (cid.Cid, error) {
ctx, span := trace.StartSpan(ctx, "stateTree.Flush") //nolint:staticcheck
ctx, span := trace.StartSpan(ctx, "stateTree.Flush")
defer span.End()
if len(st.snaps.layers) != 1 {
return cid.Undef, xerrors.Errorf("tried to flush state tree with snapshots on the stack")
@@ -268,7 +268,7 @@ func (st *StateTree) Flush(ctx context.Context) (cid.Cid, error) {
}
func (st *StateTree) Snapshot(ctx context.Context) error {
ctx, span := trace.StartSpan(ctx, "stateTree.SnapShot") //nolint:staticcheck
ctx, span := trace.StartSpan(ctx, "stateTree.SnapShot")
defer span.End()
st.snaps.addLayer()
+1 -1
View File
@@ -157,7 +157,7 @@ func MinerSectorInfo(ctx context.Context, sm *StateManager, maddr address.Addres
return nil, err
}
if !ok {
return nil, nil
return nil, xerrors.New("sector not found")
}
return sectorInfo, nil
+1 -1
View File
@@ -42,7 +42,7 @@ func TestIndexSeeks(t *testing.T) {
if err := cs.PutTipSet(ctx, mock.TipSet(gen)); err != nil {
t.Fatal(err)
}
assert.NoError(t, cs.SetGenesis(gen))
cs.SetGenesis(gen)
// Put 113 blocks from genesis
for i := 0; i < 113; i++ {
+4 -9
View File
@@ -483,10 +483,6 @@ func (cs *ChainStore) NearestCommonAncestor(a, b *types.TipSet) (*types.TipSet,
}
func (cs *ChainStore) ReorgOps(a, b *types.TipSet) ([]*types.TipSet, []*types.TipSet, error) {
return ReorgOps(cs.LoadTipSet, a, b)
}
func ReorgOps(lts func(types.TipSetKey) (*types.TipSet, error), a, b *types.TipSet) ([]*types.TipSet, []*types.TipSet, error) {
left := a
right := b
@@ -494,7 +490,7 @@ func ReorgOps(lts func(types.TipSetKey) (*types.TipSet, error), a, b *types.TipS
for !left.Equals(right) {
if left.Height() > right.Height() {
leftChain = append(leftChain, left)
par, err := lts(left.Parents())
par, err := cs.LoadTipSet(left.Parents())
if err != nil {
return nil, nil, err
}
@@ -502,7 +498,7 @@ func ReorgOps(lts func(types.TipSetKey) (*types.TipSet, error), a, b *types.TipS
left = par
} else {
rightChain = append(rightChain, right)
par, err := lts(right.Parents())
par, err := cs.LoadTipSet(right.Parents())
if err != nil {
log.Infof("failed to fetch right.Parents: %s", err)
return nil, nil, err
@@ -513,7 +509,6 @@ func ReorgOps(lts func(types.TipSetKey) (*types.TipSet, error), a, b *types.TipS
}
return leftChain, rightChain, nil
}
// GetHeaviestTipSet returns the current heaviest tipset known (i.e. our head).
@@ -930,7 +925,7 @@ func (cs *ChainStore) LoadMessagesFromCids(cids []cid.Cid) ([]*types.Message, er
for i, c := range cids {
m, err := cs.GetMessage(c)
if err != nil {
return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err)
return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", err, c, i)
}
msgs = append(msgs, m)
@@ -944,7 +939,7 @@ func (cs *ChainStore) LoadSignedMessagesFromCids(cids []cid.Cid) ([]*types.Signe
for i, c := range cids {
m, err := cs.GetSignedMessage(c)
if err != nil {
return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err)
return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", err, c, i)
}
msgs = append(msgs, m)
+25 -42
View File
@@ -3,7 +3,6 @@ package sub
import (
"bytes"
"context"
"errors"
"fmt"
"sync"
"time"
@@ -41,9 +40,6 @@ import (
var log = logging.Logger("sub")
var ErrSoftFailure = errors.New("soft validation failure")
var ErrInsufficientPower = errors.New("incoming block's miner does not have minimum power")
func HandleIncomingBlocks(ctx context.Context, bsub *pubsub.Subscription, s *chain.Syncer, bserv bserv.BlockService, cmgr connmgr.ConnManager) {
for {
msg, err := bsub.Next(ctx)
@@ -262,15 +258,16 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
stats.Record(ctx, metrics.BlockReceived.M(1))
recordFailureFlagPeer := func(what string) {
recordFailure(ctx, metrics.BlockValidationFailure, what)
recordFailure := func(what string) {
ctx, _ = tag.New(ctx, tag.Insert(metrics.FailureType, what))
stats.Record(ctx, metrics.BlockValidationFailure.M(1))
bv.flagPeer(pid)
}
blk, what, err := bv.decodeAndCheckBlock(msg)
if err != nil {
log.Error("got invalid block over pubsub: ", err)
recordFailureFlagPeer(what)
recordFailure(what)
return pubsub.ValidationReject
}
@@ -278,7 +275,7 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
err = bv.validateMsgMeta(ctx, blk)
if err != nil {
log.Warnf("error validating message metadata: %s", err)
recordFailureFlagPeer("invalid_block_meta")
recordFailure("invalid_block_meta")
return pubsub.ValidationReject
}
@@ -291,26 +288,26 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub
// if we are synced and the miner is unknown, then the block is rejcected.
key, err := bv.checkPowerAndGetWorkerKey(ctx, blk.Header)
if err != nil {
if err != ErrSoftFailure && bv.isChainNearSynced() {
if bv.isChainNearSynced() {
log.Warnf("received block from unknown miner or miner that doesn't meet min power over pubsub; rejecting message")
recordFailureFlagPeer("unknown_miner")
recordFailure("unknown_miner")
return pubsub.ValidationReject
} else {
log.Warnf("cannot validate block message; unknown miner or miner that doesn't meet min power in unsynced chain")
return pubsub.ValidationIgnore
}
log.Warnf("cannot validate block message; unknown miner or miner that doesn't meet min power in unsynced chain")
return pubsub.ValidationIgnore
}
err = sigs.CheckBlockSignature(ctx, blk.Header, key)
if err != nil {
log.Errorf("block signature verification failed: %s", err)
recordFailureFlagPeer("signature_verification_failed")
recordFailure("signature_verification_failed")
return pubsub.ValidationReject
}
if blk.Header.ElectionProof.WinCount < 1 {
log.Errorf("block is not claiming to be winning")
recordFailureFlagPeer("not_winning")
recordFailure("not_winning")
return pubsub.ValidationReject
}
@@ -477,19 +474,19 @@ func (bv *BlockValidator) checkPowerAndGetWorkerKey(ctx context.Context, bh *typ
baseTs := bv.chain.GetHeaviestTipSet()
lbts, err := stmgr.GetLookbackTipSetForRound(ctx, bv.stmgr, baseTs, bh.Height)
if err != nil {
log.Warnf("failed to load lookback tipset for incoming block: %s", err)
return address.Undef, ErrSoftFailure
log.Warnf("failed to load lookback tipset for incoming block")
return address.Undef, err
}
hmp, err := stmgr.MinerHasMinPower(ctx, bv.stmgr, bh.Miner, lbts)
if err != nil {
log.Warnf("failed to determine if incoming block's miner has minimum power: %s", err)
return address.Undef, ErrSoftFailure
log.Warnf("failed to determine if incoming block's miner has minimum power")
return address.Undef, err
}
if !hmp {
log.Warnf("incoming block's miner does not have minimum power")
return address.Undef, ErrInsufficientPower
return address.Undef, xerrors.New("incoming block's miner does not have minimum power")
}
return key, nil
@@ -545,16 +542,14 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
log.Debugf("failed to add message from network to message pool (From: %s, To: %s, Nonce: %d, Value: %s): %s", m.Message.From, m.Message.To, m.Message.Nonce, types.FIL(m.Message.Value), err)
ctx, _ = tag.New(
ctx,
tag.Upsert(metrics.Local, "false"),
tag.Insert(metrics.FailureType, "add"),
)
recordFailure(ctx, metrics.MessageValidationFailure, "add")
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
switch {
case xerrors.Is(err, messagepool.ErrSoftValidationFailure):
case xerrors.Is(err, messagepool.ErrBroadcastAnyway):
fallthrough
case xerrors.Is(err, messagepool.ErrRBFTooLowPremium):
fallthrough
case xerrors.Is(err, messagepool.ErrTooManyPendingMessages):
fallthrough
case xerrors.Is(err, messagepool.ErrNonceTooLow):
return pubsub.ValidationIgnore
default:
@@ -566,41 +561,37 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs
}
func (mv *MessageValidator) validateLocalMessage(ctx context.Context, msg *pubsub.Message) pubsub.ValidationResult {
ctx, _ = tag.New(
ctx,
tag.Upsert(metrics.Local, "true"),
)
// do some lightweight validation
stats.Record(ctx, metrics.MessagePublished.M(1))
m, err := types.DecodeSignedMessage(msg.Message.GetData())
if err != nil {
log.Warnf("failed to decode local message: %s", err)
recordFailure(ctx, metrics.MessageValidationFailure, "decode")
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
return pubsub.ValidationIgnore
}
if m.Size() > 32*1024 {
log.Warnf("local message is too large! (%dB)", m.Size())
recordFailure(ctx, metrics.MessageValidationFailure, "oversize")
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
return pubsub.ValidationIgnore
}
if m.Message.To == address.Undef {
log.Warn("local message has invalid destination address")
recordFailure(ctx, metrics.MessageValidationFailure, "undef-addr")
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
return pubsub.ValidationIgnore
}
if !m.Message.Value.LessThan(types.TotalFilecoinInt) {
log.Warnf("local messages has too high value: %s", m.Message.Value)
recordFailure(ctx, metrics.MessageValidationFailure, "value-too-high")
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
return pubsub.ValidationIgnore
}
if err := mv.mpool.VerifyMsgSig(m); err != nil {
log.Warnf("signature verification failed for local message: %s", err)
recordFailure(ctx, metrics.MessageValidationFailure, "verify-sig")
stats.Record(ctx, metrics.MessageValidationFailure.M(1))
return pubsub.ValidationIgnore
}
@@ -623,11 +614,3 @@ func HandleIncomingMessages(ctx context.Context, mpool *messagepool.MessagePool,
// Do nothing... everything happens in validate
}
}
func recordFailure(ctx context.Context, metric *stats.Int64Measure, failureType string) {
ctx, _ = tag.New(
ctx,
tag.Upsert(metrics.FailureType, failureType),
)
stats.Record(ctx, metric.M(1))
}
+2 -41
View File
@@ -7,7 +7,6 @@ import (
"fmt"
"os"
"sort"
"strconv"
"strings"
"time"
@@ -53,19 +52,6 @@ import (
//the theoretical max height based on systime are quickly rejected
const MaxHeightDrift = 5
var defaultMessageFetchWindowSize = 200
func init() {
if s := os.Getenv("LOTUS_BSYNC_MSG_WINDOW"); s != "" {
val, err := strconv.Atoi(s)
if err != nil {
log.Errorf("failed to parse LOTUS_BSYNC_MSG_WINDOW: %s", err)
return
}
defaultMessageFetchWindowSize = val
}
}
var log = logging.Logger("chain")
var LocalIncoming = "incoming"
@@ -123,8 +109,6 @@ type Syncer struct {
receiptTracker *blockReceiptTracker
verifier ffiwrapper.Verifier
windowSize int
}
// NewSyncer creates a new Syncer object.
@@ -150,7 +134,6 @@ func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connm
receiptTracker: newBlockReceiptTracker(),
connmgr: connmgr,
verifier: verifier,
windowSize: defaultMessageFetchWindowSize,
incoming: pubsub.New(50),
}
@@ -658,7 +641,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er
validationStart := build.Clock.Now()
defer func() {
stats.Record(ctx, metrics.BlockValidationDurationMilliseconds.M(metrics.SinceInMilliseconds(validationStart)))
log.Infow("block validation", "took", time.Since(validationStart), "height", b.Header.Height, "age", time.Since(time.Unix(int64(b.Header.Timestamp), 0)))
log.Infow("block validation", "took", time.Since(validationStart), "height", b.Header.Height)
}()
ctx, span := trace.StartSpan(ctx, "validateBlock")
@@ -1416,8 +1399,7 @@ func (syncer *Syncer) iterFullTipsets(ctx context.Context, headers []*types.TipS
span.AddAttributes(trace.Int64Attribute("num_headers", int64(len(headers))))
windowSize := syncer.windowSize
mainLoop:
windowSize := 200
for i := len(headers) - 1; i >= 0; {
fts, err := syncer.store.TryFillTipSet(headers[i])
if err != nil {
@@ -1445,12 +1427,6 @@ mainLoop:
nreq := batchSize - len(bstout)
bstips, err := syncer.Bsync.GetChainMessages(ctx, next, uint64(nreq))
if err != nil {
// TODO check errors for temporary nature
if windowSize > 1 {
windowSize /= 2
log.Infof("error fetching messages: %s; reducing window size to %d and trying again", err, windowSize)
continue mainLoop
}
return xerrors.Errorf("message processing failed: %w", err)
}
@@ -1485,24 +1461,9 @@ mainLoop:
return xerrors.Errorf("message processing failed: %w", err)
}
}
if i >= windowSize {
newWindowSize := windowSize + 10
if newWindowSize > int(blocksync.MaxRequestLength) {
newWindowSize = int(blocksync.MaxRequestLength)
}
if newWindowSize > windowSize {
windowSize = newWindowSize
log.Infof("successfully fetched %d messages; increasing window size to %d", len(bstout), windowSize)
}
}
i -= batchSize
}
// remember our window size
syncer.windowSize = windowSize
return nil
}
+5 -5
View File
@@ -343,12 +343,12 @@ func (sm *SyncManager) scheduleProcessResult(res *syncResult) {
sm.syncQueue.buckets = append(sm.syncQueue.buckets, relbucket)
}
return
} else {
// TODO: this is the case where we try to sync a chain, and
// fail, and we have more blocks on top of that chain that
// have come in since. The question is, should we try to
// sync these? or just drop them?
}
// TODO: this is the case where we try to sync a chain, and
// fail, and we have more blocks on top of that chain that
// have come in since. The question is, should we try to
// sync these? or just drop them?
log.Error("failed to sync chain but have new unconnected blocks from chain")
}
if sm.nextSyncTarget == nil && !sm.syncQueue.Empty() {
+18 -8
View File
@@ -3,12 +3,11 @@ package chain_test
import (
"context"
"fmt"
"github.com/ipfs/go-cid"
"os"
"testing"
"time"
"github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore"
logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p-core/peer"
@@ -37,10 +36,7 @@ import (
func init() {
build.InsecurePoStValidation = true
err := os.Setenv("TRUST_PARAMS", "1")
if err != nil {
panic(err)
}
os.Setenv("TRUST_PARAMS", "1")
miner.SupportedProofTypes = map[abi.RegisteredSealProof]struct{}{
abi.RegisteredSealProof_StackedDrg2KiBV1: {},
}
@@ -216,6 +212,20 @@ func (tu *syncTestUtil) mineNewBlock(src int, miners []int) {
tu.g.CurTipset = mts
}
func fblkToBlkMsg(fb *types.FullBlock) *types.BlockMsg {
out := &types.BlockMsg{
Header: fb.Header,
}
for _, msg := range fb.BlsMessages {
out.BlsMessages = append(out.BlsMessages, msg.Cid())
}
for _, msg := range fb.SecpkMessages {
out.SecpkMessages = append(out.SecpkMessages, msg.Cid())
}
return out
}
func (tu *syncTestUtil) addSourceNode(gen int) {
if tu.genesis != nil {
tu.t.Fatal("source node already exists")
@@ -444,7 +454,7 @@ func (wpp badWpp) GenerateCandidates(context.Context, abi.PoStRandomness, uint64
func (wpp badWpp) ComputeProof(context.Context, []abi.SectorInfo, abi.PoStRandomness) ([]abi.PoStProof, error) {
return []abi.PoStProof{
{
abi.PoStProof{
PoStProof: abi.RegisteredPoStProof_StackedDrgWinning2KiBV1,
ProofBytes: []byte("evil"),
},
@@ -577,7 +587,7 @@ func TestDuplicateNonce(t *testing.T) {
msgs := make([][]*types.SignedMessage, 2)
// Each miner includes a message from the banker with the same nonce, but to different addresses
for k := range msgs {
for k, _ := range msgs {
msgs[k] = []*types.SignedMessage{makeMsg(tu.g.Miners[k])}
}
+31 -15
View File
@@ -6,10 +6,10 @@ import (
"fmt"
"io"
abi "github.com/filecoin-project/specs-actors/actors/abi"
crypto "github.com/filecoin-project/specs-actors/actors/crypto"
exitcode "github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
cid "github.com/ipfs/go-cid"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
"github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors"
)
@@ -637,10 +637,15 @@ func (t *Message) MarshalCBOR(w io.Writer) error {
scratch := make([]byte, 9)
// t.Version (uint64) (uint64)
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Version)); err != nil {
return err
// t.Version (int64) (int64)
if t.Version >= 0 {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajUnsignedInt, uint64(t.Version)); err != nil {
return err
}
} else {
if err := cbg.WriteMajorTypeHeaderBuf(scratch, w, cbg.MajNegativeInt, uint64(-t.Version-1)); err != nil {
return err
}
}
// t.To (address.Address) (struct)
@@ -724,19 +729,30 @@ func (t *Message) UnmarshalCBOR(r io.Reader) error {
return fmt.Errorf("cbor input had wrong number of fields")
}
// t.Version (uint64) (uint64)
// t.Version (int64) (int64)
{
maj, extra, err = cbg.CborReadHeaderBuf(br, scratch)
maj, extra, err := cbg.CborReadHeaderBuf(br, scratch)
var extraI int64
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
switch maj {
case cbg.MajUnsignedInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 positive overflow")
}
case cbg.MajNegativeInt:
extraI = int64(extra)
if extraI < 0 {
return fmt.Errorf("int64 negative oveflow")
}
extraI = -1 - extraI
default:
return fmt.Errorf("wrong type for int64 field: %d", maj)
}
t.Version = uint64(extra)
t.Version = int64(extraI)
}
// t.To (address.Address) (struct)
+1 -1
View File
@@ -25,7 +25,7 @@ type ChainMsg interface {
}
type Message struct {
Version uint64
Version int64
To address.Address
From address.Address
+2 -2
View File
@@ -62,8 +62,8 @@ func (sm *SignedMessage) Serialize() ([]byte, error) {
return buf.Bytes(), nil
}
func (sm *SignedMessage) ChainLength() int {
ser, err := sm.Serialize()
func (m *SignedMessage) ChainLength() int {
ser, err := m.Serialize()
if err != nil {
panic(err)
}
-4
View File
@@ -238,7 +238,3 @@ func (ts *TipSet) IsChildOf(parent *TipSet) bool {
// height for their processing logic at the moment to obviate it.
ts.height > parent.height
}
func (ts *TipSet) String() string {
return fmt.Sprintf("%v", ts.cids)
}
-1
View File
@@ -2,7 +2,6 @@ package validation
import (
"context"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/specs-actors/actors/runtime"
cbor "github.com/ipfs/go-ipld-cbor"
+2 -3
View File
@@ -2,9 +2,8 @@ package validation
import (
"fmt"
"math/rand"
"github.com/minio/blake2b-simd"
"math/rand"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-crypto"
@@ -70,7 +69,7 @@ func (k *KeyManager) Sign(addr address.Address, data []byte) (acrypto.Signature,
}
func (k *KeyManager) newSecp256k1Key() *wallet.Key {
randSrc := rand.New(rand.NewSource(k.secpSeed)) // nolint
randSrc := rand.New(rand.NewSource(k.secpSeed))
prv, err := crypto.GenerateKeyFromSeed(randSrc)
if err != nil {
panic(err)
+1 -1
View File
@@ -18,7 +18,7 @@ func LoadVector(t *testing.T, f string, out interface{}) {
if err != nil {
t.Fatal(err)
}
defer fi.Close() //nolint:errcheck
defer fi.Close()
if err := json.NewDecoder(fi).Decode(out); err != nil {
t.Fatal(err)
+5 -8
View File
@@ -34,6 +34,7 @@ type Runtime struct {
vm *VM
state *state.StateTree
msg *types.Message
vmsg vmr.Message
height abi.ChainEpoch
cst cbor.IpldStore
@@ -409,10 +410,8 @@ type shimStateHandle struct {
func (ssh *shimStateHandle) Create(obj vmr.CBORMarshaler) {
c := ssh.rt.Put(obj)
err := ssh.rt.stateCommit(EmptyObjectCid, c)
if err != nil {
panic(fmt.Errorf("failed to commit state after creating object: %w", err))
}
// TODO: handle error below
ssh.rt.stateCommit(EmptyObjectCid, c)
}
func (ssh *shimStateHandle) Readonly(obj vmr.CBORUnmarshaler) {
@@ -441,10 +440,8 @@ func (ssh *shimStateHandle) Transaction(obj vmr.CBORer, f func()) {
c := ssh.rt.Put(obj)
err = ssh.rt.stateCommit(baseState, c)
if err != nil {
panic(fmt.Errorf("failed to commit state after transaction: %w", err))
}
// TODO: handle error below
ssh.rt.stateCommit(baseState, c)
}
func (rt *Runtime) GetBalance(a address.Address) (types.BigInt, aerrors.ActorError) {
+1 -3
View File
@@ -97,6 +97,7 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
ctx: ctx,
vm: vm,
state: vm.cstate,
msg: msg,
origin: origin,
originNonce: originNonce,
height: vm.blockHeight,
@@ -253,9 +254,6 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
return nil, aerrors.Wrap(aerr, "not enough gas for method invocation")
}
// not charging any gas, just logging
//nolint:errcheck
defer rt.chargeGasSafe(newGasCharge("OnMethodInvocationDone", 0, 0))
if types.BigCmp(msg.Value, types.NewInt(0)) != 0 {
-1
View File
@@ -2,7 +2,6 @@ package cli
import (
"fmt"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
+2 -2
View File
@@ -319,7 +319,7 @@ var chainSetHeadCmd = &cli.Command{
ts, err = api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(cctx.Uint64("epoch")), types.EmptyTSK)
}
if ts == nil {
ts, err = parseTipSet(ctx, api, cctx.Args().Slice())
ts, err = parseTipSet(api, ctx, cctx.Args().Slice())
}
if err != nil {
return err
@@ -337,7 +337,7 @@ var chainSetHeadCmd = &cli.Command{
},
}
func parseTipSet(ctx context.Context, api api.FullNode, vals []string) (*types.TipSet, error) {
func parseTipSet(api api.FullNode, ctx context.Context, vals []string) (*types.TipSet, error) {
var headers []*types.BlockHeader
for _, c := range vals {
blkc, err := cid.Decode(c)
+110 -163
View File
@@ -1,7 +1,6 @@
package cli
import (
"context"
"encoding/json"
"fmt"
"io"
@@ -931,11 +930,11 @@ var clientQueryAskCmd = &cli.Command{
return xerrors.Errorf("failed to get peerID for miner: %w", err)
}
if *mi.PeerId == peer.ID("SETME") {
if peer.ID(*mi.PeerId) == peer.ID("SETME") {
return fmt.Errorf("the miner hasn't initialized yet")
}
pid = *mi.PeerId
pid = peer.ID(*mi.PeerId)
}
ask, err := api.ClientQueryAsk(ctx, pid, maddr)
@@ -979,10 +978,6 @@ var clientListDeals = &cli.Command{
Usage: "use color in display output",
Value: true,
},
&cli.BoolFlag{
Name: "watch",
Usage: "watch deal updates in real-time, rather than a one time list",
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
@@ -992,158 +987,114 @@ var clientListDeals = &cli.Command{
defer closer()
ctx := ReqContext(cctx)
verbose := cctx.Bool("verbose")
color := cctx.Bool("color")
watch := cctx.Bool("watch")
head, err := api.ChainHead(ctx)
if err != nil {
return err
}
localDeals, err := api.ClientListDeals(ctx)
if err != nil {
return err
}
if watch {
updates, err := api.ClientGetDealUpdates(ctx)
if err != nil {
return err
}
for {
tm.Clear()
tm.MoveCursor(1, 1)
err = outputStorageDeals(ctx, tm.Screen, api, localDeals, verbose, color)
if err != nil {
return err
}
tm.Flush()
select {
case <-ctx.Done():
return nil
case updated := <-updates:
var found bool
for i, existing := range localDeals {
if existing.ProposalCid.Equals(updated.ProposalCid) {
localDeals[i] = updated
found = true
break
}
}
if !found {
localDeals = append(localDeals, updated)
}
}
}
}
return outputStorageDeals(ctx, os.Stdout, api, localDeals, cctx.Bool("verbose"), cctx.Bool("color"))
},
}
func dealFromDealInfo(ctx context.Context, full api.FullNode, head *types.TipSet, v api.DealInfo) deal {
if v.DealID == 0 {
return deal{
LocalDeal: v,
OnChainDealState: market.DealState{
SectorStartEpoch: -1,
LastUpdatedEpoch: -1,
SlashEpoch: -1,
},
}
}
onChain, err := full.StateMarketStorageDeal(ctx, v.DealID, head.Key())
if err != nil {
return deal{LocalDeal: v}
}
return deal{
LocalDeal: v,
OnChainDealState: onChain.State,
}
}
func outputStorageDeals(ctx context.Context, out io.Writer, full api.FullNode, localDeals []api.DealInfo, verbose bool, color bool) error {
sort.Slice(localDeals, func(i, j int) bool {
return localDeals[i].CreationTime.Before(localDeals[j].CreationTime)
})
head, err := full.ChainHead(ctx)
if err != nil {
return err
}
var deals []deal
for _, localDeal := range localDeals {
deals = append(deals, dealFromDealInfo(ctx, full, head, localDeal))
}
if verbose {
w := tabwriter.NewWriter(out, 2, 4, 2, ' ', 0)
fmt.Fprintf(w, "Created\tDealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tMessage\n")
for _, d := range deals {
onChain := "N"
if d.OnChainDealState.SectorStartEpoch != -1 {
onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch)
}
slashed := "N"
if d.OnChainDealState.SlashEpoch != -1 {
slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch)
}
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\n", d.LocalDeal.CreationTime.Format(time.Stamp), d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, dealStateString(color, d.LocalDeal.State), onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), price, d.LocalDeal.Duration, d.LocalDeal.Message)
}
return w.Flush()
}
w := tablewriter.New(tablewriter.Col("DealCid"),
tablewriter.Col("DealId"),
tablewriter.Col("Provider"),
tablewriter.Col("State"),
tablewriter.Col("On Chain?"),
tablewriter.Col("Slashed?"),
tablewriter.Col("PieceCID"),
tablewriter.Col("Size"),
tablewriter.Col("Price"),
tablewriter.Col("Duration"),
tablewriter.NewLineCol("Message"))
for _, d := range deals {
propcid := ellipsis(d.LocalDeal.ProposalCid.String(), 8)
onChain := "N"
if d.OnChainDealState.SectorStartEpoch != -1 {
onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch)
}
slashed := "N"
if d.OnChainDealState.SlashEpoch != -1 {
slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch)
}
piece := ellipsis(d.LocalDeal.PieceCID.String(), 8)
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
w.Write(map[string]interface{}{
"DealCid": propcid,
"DealId": d.LocalDeal.DealID,
"Provider": d.LocalDeal.Provider,
"State": dealStateString(color, d.LocalDeal.State),
"On Chain?": onChain,
"Slashed?": slashed,
"PieceCID": piece,
"Size": types.SizeStr(types.NewInt(d.LocalDeal.Size)),
"Price": price,
"Duration": d.LocalDeal.Duration,
"Message": d.LocalDeal.Message,
sort.Slice(localDeals, func(i, j int) bool {
return localDeals[i].CreationTime.Before(localDeals[j].CreationTime)
})
}
return w.Flush(out)
var deals []deal
for _, v := range localDeals {
if v.DealID == 0 {
deals = append(deals, deal{
LocalDeal: v,
OnChainDealState: market.DealState{
SectorStartEpoch: -1,
LastUpdatedEpoch: -1,
SlashEpoch: -1,
},
})
} else {
onChain, err := api.StateMarketStorageDeal(ctx, v.DealID, head.Key())
if err != nil {
deals = append(deals, deal{LocalDeal: v})
} else {
deals = append(deals, deal{
LocalDeal: v,
OnChainDealState: onChain.State,
})
}
}
}
color := cctx.Bool("color")
if cctx.Bool("verbose") {
w := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
fmt.Fprintf(w, "Created\tDealCid\tDealId\tProvider\tState\tOn Chain?\tSlashed?\tPieceCID\tSize\tPrice\tDuration\tMessage\n")
for _, d := range deals {
onChain := "N"
if d.OnChainDealState.SectorStartEpoch != -1 {
onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch)
}
slashed := "N"
if d.OnChainDealState.SlashEpoch != -1 {
slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch)
}
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%d\t%s\n", d.LocalDeal.CreationTime.Format(time.Stamp), d.LocalDeal.ProposalCid, d.LocalDeal.DealID, d.LocalDeal.Provider, dealStateString(color, d.LocalDeal.State), onChain, slashed, d.LocalDeal.PieceCID, types.SizeStr(types.NewInt(d.LocalDeal.Size)), price, d.LocalDeal.Duration, d.LocalDeal.Message)
}
return w.Flush()
} else {
w := tablewriter.New(tablewriter.Col("DealCid"),
tablewriter.Col("DealId"),
tablewriter.Col("Provider"),
tablewriter.Col("State"),
tablewriter.Col("On Chain?"),
tablewriter.Col("Slashed?"),
tablewriter.Col("PieceCID"),
tablewriter.Col("Size"),
tablewriter.Col("Price"),
tablewriter.Col("Duration"),
tablewriter.NewLineCol("Message"))
for _, d := range deals {
propcid := d.LocalDeal.ProposalCid.String()
propcid = "..." + propcid[len(propcid)-8:]
onChain := "N"
if d.OnChainDealState.SectorStartEpoch != -1 {
onChain = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SectorStartEpoch)
}
slashed := "N"
if d.OnChainDealState.SlashEpoch != -1 {
slashed = fmt.Sprintf("Y (epoch %d)", d.OnChainDealState.SlashEpoch)
}
piece := d.LocalDeal.PieceCID.String()
piece = "..." + piece[len(piece)-8:]
price := types.FIL(types.BigMul(d.LocalDeal.PricePerEpoch, types.NewInt(d.LocalDeal.Duration)))
w.Write(map[string]interface{}{
"DealCid": propcid,
"DealId": d.LocalDeal.DealID,
"Provider": d.LocalDeal.Provider,
"State": dealStateString(color, d.LocalDeal.State),
"On Chain?": onChain,
"Slashed?": slashed,
"PieceCID": piece,
"Size": types.SizeStr(types.NewInt(d.LocalDeal.Size)),
"Price": price,
"Duration": d.LocalDeal.Duration,
"Message": d.LocalDeal.Message,
})
}
return w.Flush(os.Stdout)
}
},
}
func dealStateString(c bool, state storagemarket.StorageDealStatus) string {
@@ -1367,7 +1318,7 @@ func OutputDataTransferChannels(out io.Writer, channels []lapi.DataTransferChann
for _, channel := range sendingChannels {
w.Write(toChannelOutput(color, "Sending To", channel))
}
w.Flush(out) //nolint:errcheck
w.Flush(out)
fmt.Fprintf(out, "\nReceiving Channels\n\n")
w = tablewriter.New(tablewriter.Col("ID"),
@@ -1381,7 +1332,7 @@ func OutputDataTransferChannels(out io.Writer, channels []lapi.DataTransferChann
for _, channel := range receivingChannels {
w.Write(toChannelOutput(color, "Receiving From", channel))
}
w.Flush(out) //nolint:errcheck
w.Flush(out)
}
func channelStatusString(useColor bool, status datatransfer.Status) string {
@@ -1401,8 +1352,11 @@ func channelStatusString(useColor bool, status datatransfer.Status) string {
}
func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTransferChannel) map[string]interface{} {
rootCid := ellipsis(channel.BaseCID.String(), 8)
otherParty := ellipsis(channel.OtherPeer.String(), 8)
rootCid := channel.BaseCID.String()
rootCid = "..." + rootCid[len(rootCid)-8:]
otherParty := channel.OtherPeer.String()
otherParty = "..." + otherParty[len(otherParty)-8:]
initiated := "N"
if channel.IsInitiator {
@@ -1411,7 +1365,7 @@ func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTr
voucher := channel.Voucher
if len(voucher) > 40 {
voucher = ellipsis(voucher, 37)
voucher = "..." + voucher[len(voucher)-37:]
}
return map[string]interface{}{
@@ -1425,10 +1379,3 @@ func toChannelOutput(useColor bool, otherPartyColumn string, channel lapi.DataTr
"Message": channel.Message,
}
}
func ellipsis(s string, length int) string {
if length > 0 && len(s) > length {
return "..." + s[len(s)-length:]
}
return s
}
+4 -21
View File
@@ -12,7 +12,7 @@ import (
logging "github.com/ipfs/go-log/v2"
"github.com/mitchellh/go-homedir"
"github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
manet "github.com/multiformats/go-multiaddr-net"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
@@ -75,8 +75,6 @@ func flagForAPI(t repo.RepoType) string {
return "api"
case repo.StorageMiner:
return "miner-api"
case repo.Worker:
return "worker-api"
default:
panic(fmt.Sprintf("Unknown repo type: %v", t))
}
@@ -88,8 +86,6 @@ func flagForRepo(t repo.RepoType) string {
return "repo"
case repo.StorageMiner:
return "miner-repo"
case repo.Worker:
return "worker-repo"
default:
panic(fmt.Sprintf("Unknown repo type: %v", t))
}
@@ -101,8 +97,6 @@ func envForRepo(t repo.RepoType) string {
return "FULLNODE_API_INFO"
case repo.StorageMiner:
return "MINER_API_INFO"
case repo.Worker:
return "WORKER_API_INFO"
default:
panic(fmt.Sprintf("Unknown repo type: %v", t))
}
@@ -115,8 +109,6 @@ func envForRepoDeprecation(t repo.RepoType) string {
return "FULLNODE_API_INFO"
case repo.StorageMiner:
return "STORAGE_API_INFO"
case repo.Worker:
return "WORKER_API_INFO"
default:
panic(fmt.Sprintf("Unknown repo type: %v", t))
}
@@ -221,7 +213,7 @@ func GetAPI(ctx *cli.Context) (api.Common, jsonrpc.ClientCloser, error) {
return nil, nil, err
}
return client.NewCommonRPC(ctx.Context, addr, headers)
return client.NewCommonRPC(addr, headers)
}
func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error) {
@@ -230,7 +222,7 @@ func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error
return nil, nil, err
}
return client.NewFullNodeRPC(ctx.Context, addr, headers)
return client.NewFullNodeRPC(addr, headers)
}
func GetStorageMinerAPI(ctx *cli.Context, opts ...jsonrpc.Option) (api.StorageMiner, jsonrpc.ClientCloser, error) {
@@ -239,16 +231,7 @@ func GetStorageMinerAPI(ctx *cli.Context, opts ...jsonrpc.Option) (api.StorageMi
return nil, nil, err
}
return client.NewStorageMinerRPC(ctx.Context, addr, headers, opts...)
}
func GetWorkerAPI(ctx *cli.Context) (api.WorkerAPI, jsonrpc.ClientCloser, error) {
addr, headers, err := GetRawAPI(ctx, repo.Worker)
if err != nil {
return nil, nil, err
}
return client.NewWorkerRPC(ctx.Context, addr, headers)
return client.NewStorageMinerRPC(addr, headers, opts...)
}
func DaemonContext(cctx *cli.Context) context.Context {
+1 -1
View File
@@ -39,7 +39,7 @@ func RunApp(app *cli.App) {
}
var phe *PrintHelpErr
if xerrors.As(err, &phe) {
_ = cli.ShowCommandHelp(phe.Ctx, phe.Ctx.Command.Name)
cli.ShowCommandHelp(phe.Ctx, phe.Ctx.Command.Name)
}
os.Exit(1)
}
+3 -4
View File
@@ -20,7 +20,7 @@ var logList = &cli.Command{
Name: "list",
Usage: "List log systems",
Action: func(cctx *cli.Context) error {
api, closer, err := GetAPI(cctx)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
@@ -60,8 +60,7 @@ var logSetLevel = &cli.Command{
Environment Variables:
GOLOG_LOG_LEVEL - Default log level for all log systems
GOLOG_LOG_FMT - Change output log format (json, nocolor)
GOLOG_FILE - Write logs to file
GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr
GOLOG_FILE - Write logs to file in addition to stderr
`,
Flags: []cli.Flag{
&cli.StringSliceFlag{
@@ -71,7 +70,7 @@ var logSetLevel = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetAPI(cctx)
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
}
+2 -3
View File
@@ -106,8 +106,7 @@ var mpoolClear = &cli.Command{
really := cctx.Bool("really-do-it")
if !really {
//nolint:golint
return fmt.Errorf("--really-do-it must be specified for this action to have an effect; you have been warned")
return fmt.Errorf("--really-do-it must be specified for this action to have an effect; you have been warned.")
}
local := cctx.Bool("local")
@@ -347,7 +346,7 @@ var mpoolReplaceCmd = &cli.Command{
if err != nil {
return fmt.Errorf("parsing gas-premium: %w", err)
}
// TODO: estimate fee cap here
// TODO: estiamte fee cap here
msg.GasFeeCap, err = types.BigFromString(cctx.String("gas-feecap"))
if err != nil {
return fmt.Errorf("parsing gas-feecap: %w", err)
+1 -1
View File
@@ -180,7 +180,7 @@ var netFindPeer = &cli.Command{
return nil
}
pid, err := peer.Decode(cctx.Args().First())
pid, err := peer.IDB58Decode(cctx.Args().First())
if err != nil {
return err
}
+29 -50
View File
@@ -4,10 +4,6 @@ import (
"bytes"
"encoding/base64"
"fmt"
"io"
"sort"
"github.com/filecoin-project/lotus/paychmgr"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/build"
@@ -326,7 +322,7 @@ var paychVoucherListCmd = &cli.Command{
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "export",
Usage: "Print voucher as serialized string",
Usage: "Print export strings",
},
},
Action: func(cctx *cli.Context) error {
@@ -352,11 +348,16 @@ var paychVoucherListCmd = &cli.Command{
return err
}
for _, v := range sortVouchers(vouchers) {
export := cctx.Bool("export")
err := outputVoucher(cctx.App.Writer, v, export)
if err != nil {
return err
for _, v := range vouchers {
if cctx.Bool("export") {
enc, err := EncodedString(v)
if err != nil {
return err
}
fmt.Fprintf(cctx.App.Writer, "Lane %d, Nonce %d: %s; %s\n", v.Lane, v.Nonce, v.Amount.String(), enc)
} else {
fmt.Fprintf(cctx.App.Writer, "Lane %d, Nonce %d: %s\n", v.Lane, v.Nonce, v.Amount.String())
}
}
@@ -366,14 +367,8 @@ var paychVoucherListCmd = &cli.Command{
var paychVoucherBestSpendableCmd = &cli.Command{
Name: "best-spendable",
Usage: "Print vouchers with highest value that is currently spendable for each lane",
Usage: "Print voucher with highest value that is currently spendable",
ArgsUsage: "[channelAddress]",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "export",
Usage: "Print voucher as serialized string",
},
},
Action: func(cctx *cli.Context) error {
if cctx.Args().Len() != 1 {
return ShowHelp(cctx, fmt.Errorf("must pass payment channel address"))
@@ -392,53 +387,37 @@ var paychVoucherBestSpendableCmd = &cli.Command{
ctx := ReqContext(cctx)
vouchersByLane, err := paychmgr.BestSpendableByLane(ctx, api, ch)
vouchers, err := api.PaychVoucherList(ctx, ch)
if err != nil {
return err
}
var vouchers []*paych.SignedVoucher
for _, vchr := range vouchersByLane {
vouchers = append(vouchers, vchr)
}
for _, best := range sortVouchers(vouchers) {
export := cctx.Bool("export")
err := outputVoucher(cctx.App.Writer, best, export)
var best *paych.SignedVoucher
for _, v := range vouchers {
spendable, err := api.PaychVoucherCheckSpendable(ctx, ch, v, nil, nil)
if err != nil {
return err
}
if spendable {
if best == nil || v.Amount.GreaterThan(best.Amount) {
best = v
}
}
}
return nil
},
}
func sortVouchers(vouchers []*paych.SignedVoucher) []*paych.SignedVoucher {
sort.Slice(vouchers, func(i, j int) bool {
if vouchers[i].Lane == vouchers[j].Lane {
return vouchers[i].Nonce < vouchers[j].Nonce
if best == nil {
return fmt.Errorf("No spendable vouchers for that channel")
}
return vouchers[i].Lane < vouchers[j].Lane
})
return vouchers
}
func outputVoucher(w io.Writer, v *paych.SignedVoucher, export bool) error {
var enc string
if export {
var err error
enc, err = EncodedString(v)
enc, err := EncodedString(best)
if err != nil {
return err
}
}
fmt.Fprintf(w, "Lane %d, Nonce %d: %s", v.Lane, v.Nonce, v.Amount.String())
if export {
fmt.Fprintf(w, "; %s", enc)
}
fmt.Fprintln(w)
return nil
fmt.Fprintln(cctx.App.Writer, enc)
fmt.Fprintf(cctx.App.Writer, "Amount: %s\n", best.Amount)
return nil
},
}
var paychVoucherSubmitCmd = &cli.Command{
@@ -468,7 +447,7 @@ var paychVoucherSubmitCmd = &cli.Command{
ctx := ReqContext(cctx)
mcid, err := api.PaychVoucherSubmit(ctx, ch, sv, nil, nil)
mcid, err := api.PaychVoucherSubmit(ctx, ch, sv)
if err != nil {
return err
}
+33 -223
View File
@@ -4,7 +4,6 @@ import (
"bytes"
"context"
"flag"
"fmt"
"os"
"strconv"
"strings"
@@ -50,227 +49,6 @@ func TestPaymentChannels(t *testing.T) {
blocktime := 5 * time.Millisecond
ctx := context.Background()
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
paymentCreator := nodes[0]
paymentReceiver := nodes[0]
creatorAddr := addrs[0]
receiverAddr := addrs[1]
// Create mock CLI
mockCLI := newMockCLI(t)
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
// creator: paych get <creator> <receiver> <amount>
channelAmt := "100000"
cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt}
chstr := creatorCLI.runCmd(paychGetCmd, cmd)
chAddr, err := address.NewFromString(chstr)
require.NoError(t, err)
// creator: paych voucher create <channel> <amount>
voucherAmt := 100
vamt := strconv.Itoa(voucherAmt)
cmd = []string{chAddr.String(), vamt}
voucher := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
// receiver: paych voucher add <channel> <voucher>
cmd = []string{chAddr.String(), voucher}
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
// creator: paych settle <channel>
cmd = []string{chAddr.String()}
creatorCLI.runCmd(paychSettleCmd, cmd)
// Wait for the chain to reach the settle height
chState := getPaychState(ctx, t, paymentReceiver, chAddr)
waitForHeight(ctx, t, paymentReceiver, chState.SettlingAt)
// receiver: paych collect <channel>
cmd = []string{chAddr.String()}
receiverCLI.runCmd(paychCloseCmd, cmd)
}
type voucherSpec struct {
serialized string
amt int
lane int
}
// TestPaymentChannelVouchers does a basic test to exercise some payment
// channel voucher commands
func TestPaymentChannelVouchers(t *testing.T) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
blocktime := 5 * time.Millisecond
ctx := context.Background()
nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime)
paymentCreator := nodes[0]
paymentReceiver := nodes[1]
creatorAddr := addrs[0]
receiverAddr := addrs[1]
// Create mock CLI
mockCLI := newMockCLI(t)
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
// creator: paych get <creator> <receiver> <amount>
channelAmt := "100000"
cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt}
chstr := creatorCLI.runCmd(paychGetCmd, cmd)
chAddr, err := address.NewFromString(chstr)
require.NoError(t, err)
var vouchers []voucherSpec
// creator: paych voucher create <channel> <amount>
// Note: implied --lane=0
voucherAmt1 := 100
cmd = []string{chAddr.String(), strconv.Itoa(voucherAmt1)}
voucher1 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
vouchers = append(vouchers, voucherSpec{serialized: voucher1, lane: 0, amt: voucherAmt1})
// creator: paych voucher create <channel> <amount> --lane=5
lane5 := "--lane=5"
voucherAmt2 := 50
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt2)}
voucher2 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
vouchers = append(vouchers, voucherSpec{serialized: voucher2, lane: 5, amt: voucherAmt2})
// creator: paych voucher create <channel> <amount> --lane=5
voucherAmt3 := 70
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt3)}
voucher3 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
vouchers = append(vouchers, voucherSpec{serialized: voucher3, lane: 5, amt: voucherAmt3})
// creator: paych voucher create <channel> <amount> --lane=5
voucherAmt4 := 80
cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt4)}
voucher4 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
vouchers = append(vouchers, voucherSpec{serialized: voucher4, lane: 5, amt: voucherAmt4})
// creator: paych voucher list <channel> --export
cmd = []string{"--export", chAddr.String()}
list := creatorCLI.runCmd(paychVoucherListCmd, cmd)
// Check that voucher list output is correct on creator
checkVoucherOutput(t, list, vouchers)
// creator: paych voucher best-spendable <channel>
cmd = []string{"--export", chAddr.String()}
bestSpendable := creatorCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
// Check that best spendable output is correct on creator
bestVouchers := []voucherSpec{
{serialized: voucher1, lane: 0, amt: voucherAmt1},
{serialized: voucher4, lane: 5, amt: voucherAmt4},
}
checkVoucherOutput(t, bestSpendable, bestVouchers)
// receiver: paych voucher add <voucher>
cmd = []string{chAddr.String(), voucher1}
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
// receiver: paych voucher add <voucher>
cmd = []string{chAddr.String(), voucher2}
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
// receiver: paych voucher add <voucher>
cmd = []string{chAddr.String(), voucher3}
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
// receiver: paych voucher add <voucher>
cmd = []string{chAddr.String(), voucher4}
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
// receiver: paych voucher list <channel> --export
cmd = []string{"--export", chAddr.String()}
list = receiverCLI.runCmd(paychVoucherListCmd, cmd)
// Check that voucher list output is correct on receiver
checkVoucherOutput(t, list, vouchers)
// receiver: paych voucher best-spendable <channel>
cmd = []string{"--export", chAddr.String()}
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
// Check that best spendable output is correct on receiver
bestVouchers = []voucherSpec{
{serialized: voucher1, lane: 0, amt: voucherAmt1},
{serialized: voucher4, lane: 5, amt: voucherAmt4},
}
checkVoucherOutput(t, bestSpendable, bestVouchers)
// receiver: paych voucher submit <channel> <voucher>
cmd = []string{chAddr.String(), voucher1}
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
// receiver: paych voucher best-spendable <channel>
cmd = []string{"--export", chAddr.String()}
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
// Check that best spendable output no longer includes submitted voucher
bestVouchers = []voucherSpec{
{serialized: voucher4, lane: 5, amt: voucherAmt4},
}
checkVoucherOutput(t, bestSpendable, bestVouchers)
// There are three vouchers in lane 5: 50, 70, 80
// Submit the voucher for 50. Best spendable should still be 80.
// receiver: paych voucher submit <channel> <voucher>
cmd = []string{chAddr.String(), voucher2}
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
// receiver: paych voucher best-spendable <channel>
cmd = []string{"--export", chAddr.String()}
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
// Check that best spendable output still includes the voucher for 80
bestVouchers = []voucherSpec{
{serialized: voucher4, lane: 5, amt: voucherAmt4},
}
checkVoucherOutput(t, bestSpendable, bestVouchers)
// Submit the voucher for 80
// receiver: paych voucher submit <channel> <voucher>
cmd = []string{chAddr.String(), voucher4}
receiverCLI.runCmd(paychVoucherSubmitCmd, cmd)
// receiver: paych voucher best-spendable <channel>
cmd = []string{"--export", chAddr.String()}
bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd)
// Check that best spendable output no longer includes submitted voucher
bestVouchers = []voucherSpec{}
checkVoucherOutput(t, bestSpendable, bestVouchers)
}
func checkVoucherOutput(t *testing.T, list string, vouchers []voucherSpec) {
lines := strings.Split(list, "\n")
listVouchers := make(map[string]string)
for _, line := range lines {
parts := strings.Split(line, ";")
if len(parts) == 2 {
serialized := strings.TrimSpace(parts[1])
listVouchers[serialized] = strings.TrimSpace(parts[0])
}
}
for _, vchr := range vouchers {
res, ok := listVouchers[vchr.serialized]
require.True(t, ok)
require.Regexp(t, fmt.Sprintf("Lane %d", vchr.lane), res)
require.Regexp(t, fmt.Sprintf("%d", vchr.amt), res)
delete(listVouchers, vchr.serialized)
}
for _, vchr := range listVouchers {
require.Fail(t, "Extra voucher "+vchr)
}
}
func startTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) {
n, sn := builder.RPCMockSbBuilder(t, 2, test.OneMiner)
paymentCreator := n[0]
@@ -310,7 +88,39 @@ func startTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Dur
}
// Create mock CLI
return n, []address.Address{creatorAddr, receiverAddr}
mockCLI := newMockCLI(t)
creatorCLI := mockCLI.client(paymentCreator.ListenAddr)
receiverCLI := mockCLI.client(paymentReceiver.ListenAddr)
// creator: paych get <creator> <receiver> <amount>
channelAmt := "100000"
cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt}
chstr := creatorCLI.runCmd(paychGetCmd, cmd)
chAddr, err := address.NewFromString(chstr)
require.NoError(t, err)
// creator: paych voucher create <channel> <amount>
voucherAmt := 100
vamt := strconv.Itoa(voucherAmt)
cmd = []string{chAddr.String(), vamt}
voucher := creatorCLI.runCmd(paychVoucherCreateCmd, cmd)
// receiver: paych voucher add <channel> <voucher>
cmd = []string{chAddr.String(), voucher}
receiverCLI.runCmd(paychVoucherAddCmd, cmd)
// creator: paych settle <channel>
cmd = []string{chAddr.String()}
creatorCLI.runCmd(paychSettleCmd, cmd)
// Wait for the chain to reach the settle height
chState := getPaychState(ctx, t, paymentReceiver, chAddr)
waitForHeight(ctx, t, paymentReceiver, chState.SettlingAt)
// receiver: paych collect <channel>
cmd = []string{chAddr.String()}
receiverCLI.runCmd(paychCloseCmd, cmd)
}
type mockCLI struct {
+2 -2
View File
@@ -9,7 +9,7 @@ import (
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/node/repo"
manet "github.com/multiformats/go-multiaddr/net"
manet "github.com/multiformats/go-multiaddr-net"
)
var pprofCmd = &cli.Command{
@@ -44,7 +44,7 @@ var PprofGoroutines = &cli.Command{
addr = "http://" + addr + "/debug/pprof/goroutine?debug=2"
r, err := http.Get(addr) //nolint:gosec
r, err := http.Get(addr)
if err != nil {
return err
}
+4 -10
View File
@@ -353,9 +353,6 @@ var stateReplaySetCmd = &cli.Command{
}
ts, err = types.NewTipSet(headers)
if err != nil {
return err
}
} else {
var r *api.MsgLookup
r, err = fapi.StateWaitMsg(ctx, mcid, build.MessageConfidence)
@@ -368,9 +365,9 @@ var stateReplaySetCmd = &cli.Command{
return xerrors.Errorf("loading tipset: %w", err)
}
ts, err = fapi.ChainGetTipSet(ctx, childTs.Parents())
if err != nil {
return err
}
}
if err != nil {
return err
}
}
@@ -1502,7 +1499,7 @@ func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, er
}
p.Elem().Field(i).Set(reflect.ValueOf(val))
case reflect.TypeOf(peer.ID("")):
pid, err := peer.Decode(args[i])
pid, err := peer.IDB58Decode(args[i])
if err != nil {
return nil, fmt.Errorf("failed to parse peer ID: %s", err)
}
@@ -1587,9 +1584,6 @@ var stateMarketBalanceCmd = &cli.Command{
}
balance, err := api.StateMarketBalance(ctx, addr, ts.Key())
if err != nil {
return err
}
fmt.Printf("Escrow: %s\n", types.FIL(balance.Escrow))
fmt.Printf("Locked: %s\n", types.FIL(balance.Locked))
+3 -2
View File
@@ -385,9 +385,10 @@ var walletVerify = &cli.Command{
if api.WalletVerify(ctx, addr, msg, &sig) {
fmt.Println("valid")
return nil
} else {
fmt.Println("invalid")
return NewCliError("CLI Verify called with invalid signature")
}
fmt.Println("invalid")
return NewCliError("CLI Verify called with invalid signature")
},
}
+26 -8
View File
@@ -214,7 +214,7 @@ func countGasCosts(et *types.ExecutionTrace) (int64, int64) {
}
for _, sub := range et.Subcalls {
c, v := countGasCosts(&sub) //nolint
c, v := countGasCosts(&sub)
cgas += c
vgas += v
}
@@ -222,6 +222,24 @@ func countGasCosts(et *types.ExecutionTrace) (int64, int64) {
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
@@ -246,20 +264,20 @@ func (cov1 *covar) VarianceX() float64 {
return cov1.m2x / (cov1.n - 1)
}
func (cov1 *covar) StddevX() float64 {
return math.Sqrt(cov1.VarianceX())
func (v1 *covar) StddevX() float64 {
return math.Sqrt(v1.VarianceX())
}
func (cov1 *covar) VarianceY() float64 {
return cov1.m2y / (cov1.n - 1)
}
func (cov1 *covar) StddevY() float64 {
return math.Sqrt(cov1.VarianceY())
func (v1 *covar) StddevY() float64 {
return math.Sqrt(v1.VarianceY())
}
func (cov1 *covar) AddPoint(x, y float64) {
cov1.n++
cov1.n += 1
dx := x - cov1.meanX
cov1.meanX += dx / cov1.n
@@ -326,7 +344,7 @@ type meanVar struct {
func (v1 *meanVar) AddPoint(value float64) {
// based on https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
v1.n++
v1.n += 1
delta := value - v1.mean
v1.mean += delta / v1.n
delta2 := value - v1.mean
@@ -463,7 +481,7 @@ var importAnalyzeCmd = &cli.Command{
}
go func() {
http.ListenAndServe("localhost:6060", nil) //nolint:errcheck
http.ListenAndServe("localhost:6060", nil)
}()
fi, err := os.Open(cctx.Args().First())
-5
View File
@@ -26,11 +26,6 @@ func main() {
EnvVars: []string{"LOTUS_PATH"},
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
},
&cli.StringFlag{
Name: "api",
EnvVars: []string{"FULLNODE_API_INFO"},
Value: "",
},
&cli.StringFlag{
Name: "db",
EnvVars: []string{"LOTUS_DB"},
+48
View File
@@ -96,6 +96,12 @@ func (p *Processor) HandleMarketChanges(ctx context.Context, marketTips ActorTip
log.Fatalw("Failed to persist market actors", "error", err)
}
// we persist the dealID <--> minerID,sectorID here since the dealID needs to be stored above first
if err := p.storePreCommitDealInfo(p.sectorDealEvents); err != nil {
close(p.sectorDealEvents)
return err
}
if err := p.updateMarket(ctx, marketChanges); err != nil {
log.Fatalw("Failed to update market actors", "error", err)
}
@@ -266,6 +272,48 @@ func (p *Processor) storeMarketActorDealProposals(ctx context.Context, marketTip
}
func (p *Processor) storePreCommitDealInfo(dealEvents <-chan *SectorDealEvent) error {
tx, err := p.db.Begin()
if err != nil {
return err
}
if _, err := tx.Exec(`create temp table mds (like minerid_dealid_sectorid excluding constraints) on commit drop;`); err != nil {
return xerrors.Errorf("Failed to create temp table for minerid_dealid_sectorid: %w", err)
}
stmt, err := tx.Prepare(`copy mds (deal_id, miner_id, sector_id) from STDIN`)
if err != nil {
return xerrors.Errorf("Failed to prepare minerid_dealid_sectorid statement: %w", err)
}
for sde := range dealEvents {
for _, did := range sde.DealIDs {
if _, err := stmt.Exec(
uint64(did),
sde.MinerID.String(),
sde.SectorID,
); err != nil {
return err
}
}
}
if err := stmt.Close(); err != nil {
return xerrors.Errorf("Failed to close miner sector deals statement: %w", err)
}
if _, err := tx.Exec(`insert into minerid_dealid_sectorid select * from mds on conflict do nothing`); err != nil {
return xerrors.Errorf("Failed to insert into miner deal sector table: %w", err)
}
if err := tx.Commit(); err != nil {
return xerrors.Errorf("Failed to commit miner deal sector table: %w", err)
}
return nil
}
func (p *Processor) updateMarketActorDealProposals(ctx context.Context, marketTip []marketActorInfo) error {
start := time.Now()
defer func() {
@@ -3,6 +3,7 @@ package processor
import (
"context"
"sync"
"time"
"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"
@@ -119,6 +120,10 @@ func (p *Processor) persistMessagesAndReceipts(ctx context.Context, blocks map[c
}
func (p *Processor) storeReceipts(recs map[mrec]*types.MessageReceipt) error {
start := time.Now()
defer func() {
log.Debugw("Persisted Receipts", "duration", time.Since(start).String())
}()
tx, err := p.db.Begin()
if err != nil {
return err
@@ -159,6 +164,10 @@ create temp table recs (like receipts excluding constraints) on commit drop;
}
func (p *Processor) storeMsgInclusions(incls map[cid.Cid][]cid.Cid) error {
start := time.Now()
defer func() {
log.Debugw("Persisted Message Inclusions", "duration", time.Since(start).String())
}()
tx, err := p.db.Begin()
if err != nil {
return err
@@ -197,6 +206,10 @@ create temp table mi (like block_messages excluding constraints) on commit drop;
}
func (p *Processor) storeMessages(msgs map[cid.Cid]*types.Message) error {
start := time.Now()
defer func() {
log.Debugw("Persisted Messages", "duration", time.Since(start).String())
}()
tx, err := p.db.Begin()
if err != nil {
return err
+141 -202
View File
@@ -271,11 +271,7 @@ func (p *Processor) persistMiners(ctx context.Context, miners []minerActorInfo)
preCommitEvents := make(chan *MinerSectorsEvent, 8)
sectorEvents := make(chan *MinerSectorsEvent, 8)
partitionEvents := make(chan *MinerSectorsEvent, 8)
dealEvents := make(chan *SectorDealEvent, 8)
grp.Go(func() error {
return p.storePreCommitDealInfo(dealEvents)
})
p.sectorDealEvents = make(chan *SectorDealEvent, 8)
grp.Go(func() error {
return p.storeMinerSectorEvents(ctx, sectorEvents, preCommitEvents, partitionEvents)
@@ -284,9 +280,9 @@ func (p *Processor) persistMiners(ctx context.Context, miners []minerActorInfo)
grp.Go(func() error {
defer func() {
close(preCommitEvents)
close(dealEvents)
close(p.sectorDealEvents)
}()
return p.storeMinerPreCommitInfo(ctx, miners, preCommitEvents, dealEvents)
return p.storeMinerPreCommitInfo(ctx, miners, preCommitEvents, p.sectorDealEvents)
})
grp.Go(func() error {
@@ -318,107 +314,100 @@ func (p *Processor) storeMinerPreCommitInfo(ctx context.Context, miners []minerA
return xerrors.Errorf("Failed to prepare miner precommit info statement: %w", err)
}
grp, _ := errgroup.WithContext(ctx)
for _, m := range miners {
m := m
grp.Go(func() error {
minerSectors, err := adt.AsArray(p.ctxStore, m.state.Sectors)
if err != nil {
minerSectors, err := adt.AsArray(p.ctxStore, m.state.Sectors)
if err != nil {
return err
}
changes, err := p.getMinerPreCommitChanges(ctx, m)
if err != nil {
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
continue
} else {
return err
}
}
if changes == nil {
continue
}
changes, err := p.getMinerPreCommitChanges(ctx, m)
if err != nil {
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
return nil
}
return err
}
if changes == nil {
return nil
}
preCommitAdded := make([]uint64, len(changes.Added))
for i, added := range changes.Added {
if len(added.Info.DealIDs) > 0 {
sectorDeals <- &SectorDealEvent{
MinerID: m.common.addr,
SectorID: uint64(added.Info.SectorNumber),
DealIDs: added.Info.DealIDs,
}
}
if added.Info.ReplaceCapacity {
if _, err := stmt.Exec(
m.common.addr.String(),
added.Info.SectorNumber,
added.Info.SealedCID.String(),
m.common.stateroot.String(),
added.Info.SealRandEpoch,
added.Info.Expiration,
added.PreCommitDeposit.String(),
added.PreCommitEpoch,
added.DealWeight.String(),
added.VerifiedDealWeight.String(),
added.Info.ReplaceCapacity,
added.Info.ReplaceSectorDeadline,
added.Info.ReplaceSectorPartition,
added.Info.ReplaceSectorNumber,
); err != nil {
return err
}
} else {
if _, err := stmt.Exec(
m.common.addr.String(),
added.Info.SectorNumber,
added.Info.SealedCID.String(),
m.common.stateroot.String(),
added.Info.SealRandEpoch,
added.Info.Expiration,
added.PreCommitDeposit.String(),
added.PreCommitEpoch,
added.DealWeight.String(),
added.VerifiedDealWeight.String(),
added.Info.ReplaceCapacity,
nil, // replace deadline
nil, // replace partition
nil, // replace sector
); err != nil {
return err
}
}
preCommitAdded[i] = uint64(added.Info.SectorNumber)
}
if len(preCommitAdded) > 0 {
sectorEvents <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: preCommitAdded,
Event: PreCommitAdded,
preCommitAdded := make([]uint64, len(changes.Added))
for i, added := range changes.Added {
if len(added.Info.DealIDs) > 0 {
sectorDeals <- &SectorDealEvent{
MinerID: m.common.addr,
SectorID: uint64(added.Info.SectorNumber),
DealIDs: added.Info.DealIDs,
}
}
var preCommitExpired []uint64
for _, removed := range changes.Removed {
var sector miner.SectorOnChainInfo
if found, err := minerSectors.Get(uint64(removed.Info.SectorNumber), &sector); err != nil {
if added.Info.ReplaceCapacity {
if _, err := stmt.Exec(
m.common.addr.String(),
added.Info.SectorNumber,
added.Info.SealedCID.String(),
m.common.stateroot.String(),
added.Info.SealRandEpoch,
added.Info.Expiration,
added.PreCommitDeposit.String(),
added.PreCommitEpoch,
added.DealWeight.String(),
added.VerifiedDealWeight.String(),
added.Info.ReplaceCapacity,
added.Info.ReplaceSectorDeadline,
added.Info.ReplaceSectorPartition,
added.Info.ReplaceSectorNumber,
); err != nil {
return err
} else if !found {
preCommitExpired = append(preCommitExpired, uint64(removed.Info.SectorNumber))
}
}
if len(preCommitExpired) > 0 {
sectorEvents <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: preCommitExpired,
Event: PreCommitExpired,
} else {
if _, err := stmt.Exec(
m.common.addr.String(),
added.Info.SectorNumber,
added.Info.SealedCID.String(),
m.common.stateroot.String(),
added.Info.SealRandEpoch,
added.Info.Expiration,
added.PreCommitDeposit.String(),
added.PreCommitEpoch,
added.DealWeight.String(),
added.VerifiedDealWeight.String(),
added.Info.ReplaceCapacity,
nil, // replace deadline
nil, // replace partition
nil, // replace sector
); err != nil {
return err
}
}
return nil
})
}
if err := grp.Wait(); err != nil {
return err
preCommitAdded[i] = uint64(added.Info.SectorNumber)
}
if len(preCommitAdded) > 0 {
sectorEvents <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: preCommitAdded,
Event: PreCommitAdded,
}
}
var preCommitExpired []uint64
for _, removed := range changes.Removed {
var sector miner.SectorOnChainInfo
if found, err := minerSectors.Get(uint64(removed.Info.SectorNumber), &sector); err != nil {
return err
} else if !found {
preCommitExpired = append(preCommitExpired, uint64(removed.Info.SectorNumber))
}
}
if len(preCommitExpired) > 0 {
sectorEvents <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: preCommitExpired,
Event: PreCommitExpired,
}
}
}
if err := stmt.Close(); err != nil {
@@ -450,75 +439,67 @@ func (p *Processor) storeMinerSectorInfo(ctx context.Context, miners []minerActo
return xerrors.Errorf("Failed to prepare miner sector info statement: %w", err)
}
grp, _ := errgroup.WithContext(ctx)
for _, m := range miners {
m := m
grp.Go(func() error {
changes, err := p.getMinerSectorChanges(ctx, m)
if err != nil {
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
return nil
}
changes, err := p.getMinerSectorChanges(ctx, m)
if err != nil {
if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) {
continue
} else {
return err
}
if changes == nil {
return nil
}
if changes == nil {
continue
}
var sectorsAdded []uint64
var ccAdded []uint64
var extended []uint64
for _, added := range changes.Added {
// add the sector to the table
if _, err := stmt.Exec(
m.common.addr.String(),
added.SectorNumber,
added.SealedCID.String(),
m.common.stateroot.String(),
added.Activation.String(),
added.Expiration.String(),
added.DealWeight.String(),
added.VerifiedDealWeight.String(),
added.InitialPledge.String(),
added.ExpectedDayReward.String(),
added.ExpectedStoragePledge.String(),
); err != nil {
return err
}
var sectorsAdded []uint64
var ccAdded []uint64
var extended []uint64
for _, added := range changes.Added {
// add the sector to the table
if _, err := stmt.Exec(
m.common.addr.String(),
added.SectorNumber,
added.SealedCID.String(),
m.common.stateroot.String(),
added.Activation.String(),
added.Expiration.String(),
added.DealWeight.String(),
added.VerifiedDealWeight.String(),
added.InitialPledge.String(),
added.ExpectedDayReward.String(),
added.ExpectedStoragePledge.String(),
); err != nil {
log.Errorw("writing miner sector changes statement", "error", err.Error())
}
if len(added.DealIDs) == 0 {
ccAdded = append(ccAdded, uint64(added.SectorNumber))
} else {
sectorsAdded = append(sectorsAdded, uint64(added.SectorNumber))
}
if len(added.DealIDs) == 0 {
ccAdded = append(ccAdded, uint64(added.SectorNumber))
} else {
sectorsAdded = append(sectorsAdded, uint64(added.SectorNumber))
}
}
for _, mod := range changes.Extended {
extended = append(extended, uint64(mod.To.SectorNumber))
}
for _, mod := range changes.Extended {
extended = append(extended, uint64(mod.To.SectorNumber))
}
events <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: ccAdded,
Event: CommitCapacityAdded,
}
events <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: sectorsAdded,
Event: SectorAdded,
}
events <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: extended,
Event: SectorExtended,
}
return nil
})
}
if err := grp.Wait(); err != nil {
return err
events <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: ccAdded,
Event: CommitCapacityAdded,
}
events <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: sectorsAdded,
Event: SectorAdded,
}
events <- &MinerSectorsEvent{
MinerID: m.common.addr,
StateRoot: m.common.stateroot,
SectorIDs: extended,
Event: SectorExtended,
}
}
if err := stmt.Close(); err != nil {
@@ -930,48 +911,6 @@ func (p *Processor) storeMinersActorInfoState(ctx context.Context, miners []mine
return tx.Commit()
}
func (p *Processor) storePreCommitDealInfo(dealEvents <-chan *SectorDealEvent) error {
tx, err := p.db.Begin()
if err != nil {
return err
}
if _, err := tx.Exec(`create temp table mds (like minerid_dealid_sectorid excluding constraints) on commit drop;`); err != nil {
return xerrors.Errorf("Failed to create temp table for minerid_dealid_sectorid: %w", err)
}
stmt, err := tx.Prepare(`copy mds (deal_id, miner_id, sector_id) from STDIN`)
if err != nil {
return xerrors.Errorf("Failed to prepare minerid_dealid_sectorid statement: %w", err)
}
for sde := range dealEvents {
for _, did := range sde.DealIDs {
if _, err := stmt.Exec(
uint64(did),
sde.MinerID.String(),
sde.SectorID,
); err != nil {
return err
}
}
}
if err := stmt.Close(); err != nil {
return xerrors.Errorf("Failed to close miner sector deals statement: %w", err)
}
if _, err := tx.Exec(`insert into minerid_dealid_sectorid select * from mds on conflict do nothing`); err != nil {
return xerrors.Errorf("Failed to insert into miner deal sector table: %w", err)
}
if err := tx.Commit(); err != nil {
return xerrors.Errorf("Failed to commit miner deal sector table: %w", err)
}
return nil
}
func (p *Processor) storeMinersPower(miners []minerActorInfo) error {
start := time.Now()
defer func() {
+2
View File
@@ -47,6 +47,8 @@ func (p *Processor) subMpool(ctx context.Context) {
msgs[v.Message.Message.Cid()] = &v.Message.Message
}
log.Debugf("Processing %d mpool updates", len(msgs))
err := p.storeMessages(msgs)
if err != nil {
log.Error(err)
+26 -78
View File
@@ -7,7 +7,6 @@ import (
"golang.org/x/xerrors"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/power"
"github.com/filecoin-project/specs-actors/actors/util/smoothing"
@@ -16,19 +15,7 @@ import (
type powerActorInfo struct {
common actorInfo
totalRawBytes big.Int
totalRawBytesCommitted big.Int
totalQualityAdjustedBytes big.Int
totalQualityAdjustedBytesCommitted big.Int
totalPledgeCollateral big.Int
newRawBytes big.Int
newQualityAdjustedBytes big.Int
newPledgeCollateral big.Int
newQAPowerSmoothed *smoothing.FilterEstimate
minerCount int64
minerCountAboveMinimumPower int64
epochSmoothingEstimate *smoothing.FilterEstimate
}
func (p *Processor) setupPower() error {
@@ -38,27 +25,13 @@ func (p *Processor) setupPower() error {
}
if _, err := tx.Exec(`
create table if not exists chain_power
create table if not exists power_smoothing_estimates
(
state_root text not null
constraint power_smoothing_estimates_pk
primary key,
new_raw_bytes_power text not null,
new_qa_bytes_power text not null,
new_pledge_collateral text not null,
total_raw_bytes_power text not null,
total_raw_bytes_committed text not null,
total_qa_bytes_power text not null,
total_qa_bytes_committed text not null,
total_pledge_collateral text not null,
qa_smoothed_position_estimate text not null,
qa_smoothed_velocity_estimate text not null,
miner_count int not null,
minimum_consensus_miner_count int not null
state_root text not null
constraint power_smoothing_estimates_pk
primary key,
position_estimate text not null,
velocity_estimate text not null
);
`); err != nil {
return err
@@ -87,8 +60,8 @@ func (p *Processor) processPowerActors(ctx context.Context, powerTips ActorTips)
}()
var out []powerActorInfo
for tipset, powerStates := range powerTips {
for _, act := range powerStates {
for tipset, powers := range powerTips {
for _, act := range powers {
var pw powerActorInfo
pw.common = act
@@ -107,19 +80,7 @@ func (p *Processor) processPowerActors(ctx context.Context, powerTips ActorTips)
return nil, xerrors.Errorf("unmarshal state (@ %s): %w", pw.common.stateroot.String(), err)
}
pw.totalRawBytes = powerActorState.TotalRawBytePower
pw.totalRawBytesCommitted = powerActorState.TotalBytesCommitted
pw.totalQualityAdjustedBytes = powerActorState.TotalQualityAdjPower
pw.totalQualityAdjustedBytesCommitted = powerActorState.TotalQABytesCommitted
pw.totalPledgeCollateral = powerActorState.TotalPledgeCollateral
pw.newRawBytes = powerActorState.ThisEpochRawBytePower
pw.newQualityAdjustedBytes = powerActorState.ThisEpochQualityAdjPower
pw.newPledgeCollateral = powerActorState.ThisEpochPledgeCollateral
pw.newQAPowerSmoothed = powerActorState.ThisEpochQAPowerSmoothed
pw.minerCount = powerActorState.MinerCount
pw.minerCountAboveMinimumPower = powerActorState.MinerAboveMinPowerCount
pw.epochSmoothingEstimate = powerActorState.ThisEpochQAPowerSmoothed
out = append(out, pw)
}
}
@@ -127,59 +88,46 @@ func (p *Processor) processPowerActors(ctx context.Context, powerTips ActorTips)
return out, nil
}
func (p *Processor) persistPowerActors(ctx context.Context, powerStates []powerActorInfo) error {
func (p *Processor) persistPowerActors(ctx context.Context, powers []powerActorInfo) error {
// NB: use errgroup when there is more than a single store operation
return p.storePowerSmoothingEstimates(powerStates)
return p.storePowerSmoothingEstimates(powers)
}
func (p *Processor) storePowerSmoothingEstimates(powerStates []powerActorInfo) error {
func (p *Processor) storePowerSmoothingEstimates(powers []powerActorInfo) error {
tx, err := p.db.Begin()
if err != nil {
return xerrors.Errorf("begin chain_power tx: %w", err)
return xerrors.Errorf("begin power_smoothing_estimates tx: %w", err)
}
if _, err := tx.Exec(`create temp table cp (like chain_power) on commit drop`); err != nil {
return xerrors.Errorf("prep chain_power: %w", err)
if _, err := tx.Exec(`create temp table rse (like power_smoothing_estimates) on commit drop`); err != nil {
return xerrors.Errorf("prep power_smoothing_estimates: %w", err)
}
stmt, err := tx.Prepare(`copy cp (state_root, new_raw_bytes_power, new_qa_bytes_power, new_pledge_collateral, total_raw_bytes_power, total_raw_bytes_committed, total_qa_bytes_power, total_qa_bytes_committed, total_pledge_collateral, qa_smoothed_position_estimate, qa_smoothed_velocity_estimate, miner_count, minimum_consensus_miner_count) from stdin;`)
stmt, err := tx.Prepare(`copy rse (state_root, position_estimate, velocity_estimate) from stdin;`)
if err != nil {
return xerrors.Errorf("prepare tmp chain_power: %w", err)
return xerrors.Errorf("prepare tmp power_smoothing_estimates: %w", err)
}
for _, ps := range powerStates {
for _, powerState := range powers {
if _, err := stmt.Exec(
ps.common.stateroot.String(),
ps.newRawBytes.String(),
ps.newQualityAdjustedBytes.String(),
ps.newPledgeCollateral.String(),
ps.totalRawBytes.String(),
ps.totalRawBytesCommitted.String(),
ps.totalQualityAdjustedBytes.String(),
ps.totalQualityAdjustedBytesCommitted.String(),
ps.totalPledgeCollateral.String(),
ps.newQAPowerSmoothed.PositionEstimate.String(),
ps.newQAPowerSmoothed.VelocityEstimate.String(),
ps.minerCount,
ps.minerCountAboveMinimumPower,
powerState.common.stateroot.String(),
powerState.epochSmoothingEstimate.PositionEstimate.String(),
powerState.epochSmoothingEstimate.VelocityEstimate.String(),
); err != nil {
return xerrors.Errorf("failed to store smoothing estimate: %w", err)
}
}
if err := stmt.Close(); err != nil {
return xerrors.Errorf("close prepared chain_power: %w", err)
return xerrors.Errorf("close prepared power_smoothing_estimates: %w", err)
}
if _, err := tx.Exec(`insert into chain_power select * from cp on conflict do nothing`); err != nil {
return xerrors.Errorf("insert chain_power from tmp: %w", err)
if _, err := tx.Exec(`insert into power_smoothing_estimates select * from rse on conflict do nothing`); err != nil {
return xerrors.Errorf("insert power_smoothing_estimates from tmp: %w", err)
}
if err := tx.Commit(); err != nil {
return xerrors.Errorf("commit chain_power tx: %w", err)
return xerrors.Errorf("commit power_smoothing_estimates tx: %w", err)
}
return nil
+40 -39
View File
@@ -8,6 +8,7 @@ import (
"sync"
"time"
"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
@@ -35,6 +36,9 @@ type Processor struct {
// number of blocks processed at a time
batch int
// process communication channels
sectorDealEvents chan *SectorDealEvent
}
type ActorTips map[types.TipSetKey][]actorInfo
@@ -140,63 +144,60 @@ func (p *Processor) Start(ctx context.Context) {
"AccountChanges", len(actorChanges[builtin.AccountActorCodeID]),
"nullRounds", len(nullRounds))
grp := sync.WaitGroup{}
grp, ctx := errgroup.WithContext(ctx)
grp.Add(1)
go func() {
defer grp.Done()
grp.Go(func() error {
if err := p.HandleMarketChanges(ctx, actorChanges[builtin.StorageMarketActorCodeID]); err != nil {
log.Errorf("Failed to handle market changes: %w", err)
return
return xerrors.Errorf("Failed to handle market changes: %w", err)
}
}()
log.Info("Processed Market Changes")
return nil
})
grp.Add(1)
go func() {
defer grp.Done()
grp.Go(func() error {
if err := p.HandleMinerChanges(ctx, actorChanges[builtin.StorageMinerActorCodeID]); err != nil {
log.Errorf("Failed to handle miner changes: %w", err)
return
return xerrors.Errorf("Failed to handle miner changes: %w", err)
}
}()
log.Info("Processed Miner Changes")
return nil
})
grp.Add(1)
go func() {
defer grp.Done()
grp.Go(func() error {
if err := p.HandleRewardChanges(ctx, actorChanges[builtin.RewardActorCodeID], nullRounds); err != nil {
log.Errorf("Failed to handle reward changes: %w", err)
return
return xerrors.Errorf("Failed to handle reward changes: %w", err)
}
}()
log.Info("Processed Reward Changes")
return nil
})
grp.Add(1)
go func() {
defer grp.Done()
grp.Go(func() error {
if err := p.HandlePowerChanges(ctx, actorChanges[builtin.StoragePowerActorCodeID]); err != nil {
log.Errorf("Failed to handle power actor changes: %w", err)
return
return xerrors.Errorf("Failed to handle power actor changes: %w", err)
}
}()
log.Info("Processes Power Changes")
return nil
})
grp.Add(1)
go func() {
defer grp.Done()
grp.Go(func() error {
if err := p.HandleMessageChanges(ctx, toProcess); err != nil {
log.Errorf("Failed to handle message changes: %w", err)
return
return xerrors.Errorf("Failed to handle message changes: %w", err)
}
}()
log.Info("Processed Message Changes")
return nil
})
grp.Add(1)
go func() {
defer grp.Done()
grp.Go(func() error {
if err := p.HandleCommonActorsChanges(ctx, actorChanges); err != nil {
log.Errorf("Failed to handle common actor changes: %w", err)
return
return xerrors.Errorf("Failed to handle common actor changes: %w", err)
}
}()
log.Info("Processed CommonActor Changes")
return nil
})
grp.Wait()
if err := grp.Wait(); err != nil {
log.Errorw("Failed to handle actor changes...retrying", "error", err)
continue
}
if err := p.markBlocksProcessed(ctx, toProcess); err != nil {
log.Fatalw("Failed to mark blocks as processed", "error", err)
@@ -205,7 +206,7 @@ func (p *Processor) Start(ctx context.Context) {
if err := p.refreshViews(); err != nil {
log.Errorw("Failed to refresh views", "error", err)
}
log.Infow("Processed Batch Complete", "duration", time.Since(loopStart).String())
log.Infow("Processed Batch", "duration", time.Since(loopStart).String())
}
}
}()
+155 -55
View File
@@ -5,6 +5,7 @@ import (
"context"
"time"
"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"
"github.com/filecoin-project/specs-actors/actors/abi/big"
@@ -12,23 +13,20 @@ import (
"github.com/filecoin-project/specs-actors/actors/builtin/reward"
"github.com/filecoin-project/specs-actors/actors/util/smoothing"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/types"
)
type rewardActorInfo struct {
common actorInfo
cumSumBaselinePower big.Int
cumSumRealizedPower big.Int
// expected power in bytes during this epoch
baselinePower big.Int
effectiveNetworkTime int64
effectiveBaselinePower big.Int
// base reward in attofil for each block found during this epoch
baseBlockReward big.Int
newBaselinePower big.Int
newBaseReward big.Int
newSmoothingEstimate *smoothing.FilterEstimate
totalMinedReward big.Int
epochSmoothingEstimate *smoothing.FilterEstimate
}
func (p *Processor) setupRewards() error {
@@ -38,23 +36,34 @@ func (p *Processor) setupRewards() error {
}
if _, err := tx.Exec(`
/* captures chain-specific power state for any given stateroot */
create table if not exists chain_reward
/*
* captures base block reward per miner per state root and does not
* include penalties or gas reward
*/
create table if not exists base_block_rewards
(
state_root text not null
constraint chain_reward_pk
constraint block_rewards_pk
primary key,
cum_sum_baseline text not null,
cum_sum_realized text not null,
effective_network_time int not null,
effective_baseline_power text not null,
base_block_reward numeric not null
);
new_baseline_power text not null,
new_reward numeric not null,
new_reward_smoothed_position_estimate text not null,
new_reward_smoothed_velocity_estimate text not null,
/* captures chain-specific power state for any given stateroot */
create table if not exists chain_power
(
state_root text not null
constraint chain_power_pk
primary key,
baseline_power text not null
);
total_mined_reward text not null
create table if not exists reward_smoothing_estimates
(
state_root text not null
constraint reward_smoothing_estimates_pk
primary key,
position_estimate text not null,
velocity_estimate text not null
);
`); err != nil {
return err
@@ -104,14 +113,9 @@ func (p *Processor) processRewardActors(ctx context.Context, rewardTips ActorTip
return nil, xerrors.Errorf("unmarshal state (@ %s): %w", rw.common.stateroot.String(), err)
}
rw.cumSumBaselinePower = rewardActorState.CumsumBaseline
rw.cumSumRealizedPower = rewardActorState.CumsumRealized
rw.effectiveNetworkTime = int64(rewardActorState.EffectiveNetworkTime)
rw.effectiveBaselinePower = rewardActorState.EffectiveBaselinePower
rw.newBaselinePower = rewardActorState.ThisEpochBaselinePower
rw.newBaseReward = rewardActorState.ThisEpochReward
rw.newSmoothingEstimate = rewardActorState.ThisEpochRewardSmoothed
rw.totalMinedReward = rewardActorState.TotalMined
rw.baseBlockReward = rewardActorState.ThisEpochReward
rw.baselinePower = rewardActorState.ThisEpochBaselinePower
rw.epochSmoothingEstimate = rewardActorState.ThisEpochRewardSmoothed
out = append(out, rw)
}
}
@@ -141,14 +145,8 @@ func (p *Processor) processRewardActors(ctx context.Context, rewardTips ActorTip
return nil, err
}
rw.cumSumBaselinePower = rewardActorState.CumsumBaseline
rw.cumSumRealizedPower = rewardActorState.CumsumRealized
rw.effectiveNetworkTime = int64(rewardActorState.EffectiveNetworkTime)
rw.effectiveBaselinePower = rewardActorState.EffectiveBaselinePower
rw.newBaselinePower = rewardActorState.ThisEpochBaselinePower
rw.newBaseReward = rewardActorState.ThisEpochReward
rw.newSmoothingEstimate = rewardActorState.ThisEpochRewardSmoothed
rw.totalMinedReward = rewardActorState.TotalMined
rw.baseBlockReward = rewardActorState.ThisEpochReward
rw.baselinePower = rewardActorState.ThisEpochBaselinePower
out = append(out, rw)
}
@@ -161,47 +159,149 @@ func (p *Processor) persistRewardActors(ctx context.Context, rewards []rewardAct
log.Debugw("Persisted Reward Actors", "duration", time.Since(start).String())
}()
grp, ctx := errgroup.WithContext(ctx)
grp.Go(func() error {
if err := p.storeChainPower(rewards); err != nil {
return err
}
return nil
})
grp.Go(func() error {
if err := p.storeBaseBlockReward(rewards); err != nil {
return err
}
return nil
})
grp.Go(func() error {
if err := p.storeRewardSmoothingEstimates(rewards); err != nil {
return err
}
return nil
})
return grp.Wait()
}
func (p *Processor) storeChainPower(rewards []rewardActorInfo) error {
tx, err := p.db.Begin()
if err != nil {
return xerrors.Errorf("begin chain_reward tx: %w", err)
return xerrors.Errorf("begin chain_power tx: %w", err)
}
if _, err := tx.Exec(`create temp table cr (like chain_reward excluding constraints) on commit drop`); err != nil {
return xerrors.Errorf("prep chain_reward temp: %w", err)
if _, err := tx.Exec(`create temp table cp (like chain_power excluding constraints) on commit drop`); err != nil {
return xerrors.Errorf("prep chain_power temp: %w", err)
}
stmt, err := tx.Prepare(`copy cr ( state_root, cum_sum_baseline, cum_sum_realized, effective_network_time, effective_baseline_power, new_baseline_power, new_reward, new_reward_smoothed_position_estimate, new_reward_smoothed_velocity_estimate, total_mined_reward) from STDIN`)
stmt, err := tx.Prepare(`copy cp (state_root, baseline_power) from STDIN`)
if err != nil {
return xerrors.Errorf("prepare tmp chain_reward: %w", err)
return xerrors.Errorf("prepare tmp chain_power: %w", err)
}
for _, rewardState := range rewards {
if _, err := stmt.Exec(
rewardState.common.stateroot.String(),
rewardState.cumSumBaselinePower.String(),
rewardState.cumSumRealizedPower.String(),
rewardState.effectiveNetworkTime,
rewardState.effectiveBaselinePower.String(),
rewardState.newBaselinePower.String(),
rewardState.newBaseReward.String(),
rewardState.newSmoothingEstimate.PositionEstimate.String(),
rewardState.newSmoothingEstimate.VelocityEstimate.String(),
rewardState.totalMinedReward.String(),
rewardState.baselinePower.String(),
); err != nil {
log.Errorw("failed to store chain power", "state_root", rewardState.common.stateroot, "error", err)
}
}
if err := stmt.Close(); err != nil {
return xerrors.Errorf("close prepared chain_reward: %w", err)
return xerrors.Errorf("close prepared chain_power: %w", err)
}
if _, err := tx.Exec(`insert into chain_reward select * from cr on conflict do nothing`); err != nil {
return xerrors.Errorf("insert chain_reward from tmp: %w", err)
if _, err := tx.Exec(`insert into chain_power select * from cp on conflict do nothing`); err != nil {
return xerrors.Errorf("insert chain_power from tmp: %w", err)
}
if err := tx.Commit(); err != nil {
return xerrors.Errorf("commit chain_reward tx: %w", err)
return xerrors.Errorf("commit chain_power tx: %w", err)
}
return nil
}
func (p *Processor) storeBaseBlockReward(rewards []rewardActorInfo) error {
tx, err := p.db.Begin()
if err != nil {
return xerrors.Errorf("begin base_block_reward tx: %w", err)
}
if _, err := tx.Exec(`create temp table bbr (like base_block_rewards excluding constraints) on commit drop`); err != nil {
return xerrors.Errorf("prep base_block_reward temp: %w", err)
}
stmt, err := tx.Prepare(`copy bbr (state_root, base_block_reward) from STDIN`)
if err != nil {
return xerrors.Errorf("prepare tmp base_block_reward: %w", err)
}
for _, rewardState := range rewards {
baseBlockReward := big.Div(rewardState.baseBlockReward, big.NewIntUnsigned(build.BlocksPerEpoch))
if _, err := stmt.Exec(
rewardState.common.stateroot.String(),
baseBlockReward.String(),
); err != nil {
log.Errorw("failed to store base block reward", "state_root", rewardState.common.stateroot, "error", err)
}
}
if err := stmt.Close(); err != nil {
return xerrors.Errorf("close prepared base_block_reward: %w", err)
}
if _, err := tx.Exec(`insert into base_block_rewards select * from bbr on conflict do nothing`); err != nil {
return xerrors.Errorf("insert base_block_reward from tmp: %w", err)
}
if err := tx.Commit(); err != nil {
return xerrors.Errorf("commit base_block_reward tx: %w", err)
}
return nil
}
func (p *Processor) storeRewardSmoothingEstimates(rewards []rewardActorInfo) error {
tx, err := p.db.Begin()
if err != nil {
return xerrors.Errorf("begin reward_smoothing_estimates tx: %w", err)
}
if _, err := tx.Exec(`create temp table rse (like reward_smoothing_estimates) on commit drop`); err != nil {
return xerrors.Errorf("prep reward_smoothing_estimates: %w", err)
}
stmt, err := tx.Prepare(`copy rse (state_root, position_estimate, velocity_estimate) from stdin;`)
if err != nil {
return xerrors.Errorf("prepare tmp reward_smoothing_estimates: %w", err)
}
for _, rewardState := range rewards {
if rewardState.epochSmoothingEstimate == nil {
continue
}
if _, err := stmt.Exec(
rewardState.common.stateroot.String(),
rewardState.epochSmoothingEstimate.PositionEstimate.String(),
rewardState.epochSmoothingEstimate.VelocityEstimate.String(),
); err != nil {
return xerrors.Errorf("failed to store smoothing estimate: %w", err)
}
}
if err := stmt.Close(); err != nil {
return xerrors.Errorf("close prepared reward_smoothing_estimates: %w", err)
}
if _, err := tx.Exec(`insert into reward_smoothing_estimates select * from rse on conflict do nothing`); err != nil {
return xerrors.Errorf("insert reward_smoothing_estimates from tmp: %w", err)
}
if err := tx.Commit(); err != nil {
return xerrors.Errorf("commit reward_smoothing_estimates tx: %w", err)
}
return nil
+7 -27
View File
@@ -2,25 +2,20 @@ package main
import (
"database/sql"
"fmt"
"net/http"
_ "net/http/pprof"
"os"
"strings"
_ "github.com/lib/pq"
"github.com/filecoin-project/go-jsonrpc"
lcli "github.com/filecoin-project/lotus/cli"
logging "github.com/ipfs/go-log/v2"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/api"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/cmd/lotus-chainwatch/processor"
"github.com/filecoin-project/lotus/cmd/lotus-chainwatch/scheduler"
"github.com/filecoin-project/lotus/cmd/lotus-chainwatch/syncer"
"github.com/filecoin-project/lotus/cmd/lotus-chainwatch/util"
)
var runCmd = &cli.Command{
@@ -29,12 +24,12 @@ var runCmd = &cli.Command{
Flags: []cli.Flag{
&cli.IntFlag{
Name: "max-batch",
Value: 50,
Value: 1000,
},
},
Action: func(cctx *cli.Context) error {
go func() {
http.ListenAndServe(":6060", nil) //nolint:errcheck
http.ListenAndServe(":6060", nil)
}()
ll := cctx.String("log-level")
if err := logging.SetLogLevel("*", ll); err != nil {
@@ -44,24 +39,9 @@ var runCmd = &cli.Command{
return err
}
var api api.FullNode
var closer jsonrpc.ClientCloser
var err error
if tokenMaddr := cctx.String("api"); tokenMaddr != "" {
toks := strings.Split(tokenMaddr, ":")
if len(toks) != 2 {
return fmt.Errorf("invalid api tokens, expected <token>:<maddr>, got: %s", tokenMaddr)
}
api, closer, err = util.GetFullNodeAPIUsingCredentials(cctx.Context, toks[1], toks[0])
if err != nil {
return err
}
} else {
api, closer, err = lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
api, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
@@ -90,7 +70,7 @@ var runCmd = &cli.Command{
}
db.SetMaxOpenConns(1350)
sync := syncer.NewSyncer(db, api, 1400)
sync := syncer.NewSyncer(db, api)
sync.Start(ctx)
proc := processor.NewProcessor(ctx, db, api, maxBatch)
@@ -3,6 +3,7 @@ package scheduler
import (
"context"
"database/sql"
"time"
"golang.org/x/xerrors"
)
@@ -23,9 +24,9 @@ func setupTopMinerByBaseRewardSchema(ctx context.Context, db *sql.DB) error {
with total_rewards_by_miner as (
select
b.miner,
sum(cr.new_reward * b.win_count) as total_reward
sum(bbr.base_block_reward * b.win_count) as total_reward
from blocks b
inner join chain_reward cr on b.parentstateroot = cr.state_root
inner join base_block_rewards bbr on b.parentstateroot = bbr.state_root
group by 1
) select
rank() over (order by total_reward desc),
@@ -42,17 +43,17 @@ func setupTopMinerByBaseRewardSchema(ctx context.Context, db *sql.DB) error {
b."timestamp"as current_timestamp,
max(b.height) as current_height
from blocks b
join chain_reward cr on b.parentstateroot = cr.state_root
where cr.new_reward is not null
join base_block_rewards bbr on b.parentstateroot = bbr.state_root
where bbr.base_block_reward is not null
group by 1
order by 1 desc
limit 1;
`); err != nil {
return xerrors.Errorf("create top_miners_by_base_reward views: %w", err)
return xerrors.Errorf("create top_miner_by_base_reward views", err)
}
if err := tx.Commit(); err != nil {
return xerrors.Errorf("committing top_miners_by_base_reward views; %w", err)
return xerrors.Errorf("commiting top_miner_by_base_reward views", err)
}
return nil
}
@@ -64,6 +65,11 @@ func refreshTopMinerByBaseReward(ctx context.Context, db *sql.DB) error {
default:
}
t := time.Now()
defer func() {
log.Debugw("refresh top_miners_by_base_reward", "duration", time.Since(t).String())
}()
_, err := db.Exec("refresh materialized view top_miners_by_base_reward;")
if err != nil {
return xerrors.Errorf("refresh top_miners_by_base_reward: %w", err)
+5 -5
View File
@@ -25,7 +25,7 @@ func PrepareScheduler(db *sql.DB) *Scheduler {
func (s *Scheduler) setupSchema(ctx context.Context) error {
if err := setupTopMinerByBaseRewardSchema(ctx, s.db); err != nil {
return xerrors.Errorf("setup top miners by reward schema: %w", err)
return xerrors.Errorf("setup top miners by reward schema", err)
}
return nil
}
@@ -35,14 +35,14 @@ func (s *Scheduler) Start(ctx context.Context) {
log.Debug("Starting Scheduler")
if err := s.setupSchema(ctx); err != nil {
log.Fatalw("applying scheduling schema", "error", err)
log.Fatalw("applying scheduling schema", err)
}
go func() {
// run once on start after schema has initialized
time.Sleep(1 * time.Minute)
time.Sleep(5 * time.Second)
if err := refreshTopMinerByBaseReward(ctx, s.db); err != nil {
log.Errorw("failed to refresh top miner", "error", err)
log.Errorf(err.Error())
}
refreshTopMinerCh := time.NewTicker(30 * time.Second)
defer refreshTopMinerCh.Stop()
@@ -50,7 +50,7 @@ func (s *Scheduler) Start(ctx context.Context) {
select {
case <-refreshTopMinerCh.C:
if err := refreshTopMinerByBaseReward(ctx, s.db); err != nil {
log.Errorw("failed to refresh top miner", "error", err)
log.Errorf(err.Error())
}
case <-ctx.Done():
return
+33 -54
View File
@@ -23,17 +23,14 @@ var log = logging.Logger("syncer")
type Syncer struct {
db *sql.DB
lookbackLimit uint64
headerLk sync.Mutex
node api.FullNode
}
func NewSyncer(db *sql.DB, node api.FullNode, lookbackLimit uint64) *Syncer {
func NewSyncer(db *sql.DB, node api.FullNode) *Syncer {
return &Syncer{
db: db,
node: node,
lookbackLimit: lookbackLimit,
db: db,
node: node,
}
}
@@ -151,34 +148,40 @@ create index if not exists state_heights_parentstateroot_index
}
func (s *Syncer) Start(ctx context.Context) {
if err := logging.SetLogLevel("syncer", "info"); err != nil {
log.Fatal(err)
}
log.Debug("Starting Syncer")
if err := s.setupSchemas(); err != nil {
log.Fatal(err)
}
// doing the initial sync here lets us avoid the HCCurrent case in the switch
head, err := s.node.ChainHead(ctx)
if err != nil {
log.Fatalw("Failed to get chain head form lotus", "error", err)
}
unsynced, err := s.unsyncedBlocks(ctx, head, time.Unix(0, 0))
if err != nil {
log.Fatalw("failed to gather unsynced blocks", "error", err)
}
if err := s.storeHeaders(unsynced, true, time.Now()); err != nil {
log.Fatalw("failed to store unsynced blocks", "error", err)
}
// continue to keep the block headers table up to date.
notifs, err := s.node.ChainNotify(ctx)
if err != nil {
log.Fatal(err)
}
// we need to ensure that on a restart we don't reprocess the whole flarping chain
blkCID, height, err := s.mostRecentlySyncedBlockHeight()
if err != nil {
log.Fatalw("failed to find most recently synced block", "error", err)
}
log.Infow("Found starting point for syncing", "blockCID", blkCID.String(), "height", height)
sinceEpoch := uint64(height)
lastSynced := time.Now()
go func() {
for notif := range notifs {
for _, change := range notif {
switch change.Type {
case store.HCApply:
unsynced, err := s.unsyncedBlocks(ctx, change.Val, sinceEpoch)
unsynced, err := s.unsyncedBlocks(ctx, change.Val, lastSynced)
if err != nil {
log.Errorw("failed to gather unsynced blocks", "error", err)
}
@@ -191,13 +194,13 @@ func (s *Syncer) Start(ctx context.Context) {
continue
}
if err := s.storeHeaders(unsynced, true, time.Now()); err != nil {
if err := s.storeHeaders(unsynced, true, lastSynced); err != nil {
// so this is pretty bad, need some kind of retry..
// for now just log an error and the blocks will be attempted again on next notifi
log.Errorw("failed to store unsynced blocks", "error", err)
}
sinceEpoch = uint64(change.Val.Height())
lastSynced = time.Now()
case store.HCRevert:
log.Debug("revert todo")
}
@@ -206,8 +209,12 @@ func (s *Syncer) Start(ctx context.Context) {
}()
}
func (s *Syncer) unsyncedBlocks(ctx context.Context, head *types.TipSet, since uint64) (map[cid.Cid]*types.BlockHeader, error) {
hasList, err := s.syncedBlocks(since, s.lookbackLimit)
func (s *Syncer) unsyncedBlocks(ctx context.Context, head *types.TipSet, since time.Time) (map[cid.Cid]*types.BlockHeader, error) {
// get a list of blocks we have already synced in the past 3 mins. This ensures we aren't returning the entire
// table every time.
lookback := since.Add(-(time.Minute * 3))
log.Debugw("Gathering unsynced blocks", "since", lookback.String())
hasList, err := s.syncedBlocks(lookback)
if err != nil {
return nil, err
}
@@ -250,8 +257,9 @@ func (s *Syncer) unsyncedBlocks(ctx context.Context, head *types.TipSet, since u
return toSync, nil
}
func (s *Syncer) syncedBlocks(since, limit uint64) (map[cid.Cid]struct{}, error) {
rws, err := s.db.Query(`select bs.cid FROM blocks_synced bs left join blocks b on b.cid = bs.cid where b.height <= $1 and bs.processed_at is not null limit $2`, since, limit)
func (s *Syncer) syncedBlocks(timestamp time.Time) (map[cid.Cid]struct{}, error) {
// timestamp is used to return a configurable amount of rows based on when they were last added.
rws, err := s.db.Query(`select cid FROM blocks_synced where synced_at > $1`, timestamp.Unix())
if err != nil {
return nil, xerrors.Errorf("Failed to query blocks_synced: %w", err)
}
@@ -273,43 +281,14 @@ func (s *Syncer) syncedBlocks(since, limit uint64) (map[cid.Cid]struct{}, error)
return out, nil
}
func (s *Syncer) mostRecentlySyncedBlockHeight() (cid.Cid, int64, error) {
rw := s.db.QueryRow(`
select blocks_synced.cid, b.height
from blocks_synced
left join blocks b on blocks_synced.cid = b.cid
where processed_at is not null
order by height desc
limit 1
`)
var c string
var h int64
if err := rw.Scan(&c, &h); err != nil {
if err == sql.ErrNoRows {
return cid.Undef, 0, nil
}
return cid.Undef, -1, err
}
ci, err := cid.Parse(c)
if err != nil {
return cid.Undef, -1, err
}
return ci, h, nil
}
func (s *Syncer) storeCirculatingSupply(ctx context.Context, tipset *types.TipSet) error {
supply, err := s.node.StateCirculatingSupply(ctx, tipset.Key())
if err != nil {
return err
}
ceInsert := `insert into chain_economics (parent_state_root, circulating_fil, vested_fil, mined_fil, burnt_fil, locked_fil) ` +
`values ('%s', '%s', '%s', '%s', '%s', '%s') on conflict on constraint chain_economics_pk do ` +
`update set (circulating_fil, vested_fil, mined_fil, burnt_fil, locked_fil) = ('%[2]s', '%[3]s', '%[4]s', '%[5]s', '%[6]s') ` +
`where chain_economics.parent_state_root = '%[1]s';`
ceInsert := `insert into chain_economics (parent_state_root, circulating_fil, vested_fil, mined_fil, burnt_fil, locked_fil)` +
`values ('%s', '%s', '%s', '%s', '%s', '%s');`
if _, err := s.db.Exec(fmt.Sprintf(ceInsert,
tipset.ParentState().String(),
-34
View File
@@ -1,34 +0,0 @@
package util
import (
"context"
"net/http"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/client"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
func GetFullNodeAPIUsingCredentials(ctx context.Context, listenAddr, token string) (api.FullNode, jsonrpc.ClientCloser, error) {
parsedAddr, err := ma.NewMultiaddr(listenAddr)
if err != nil {
return nil, nil, err
}
_, addr, err := manet.DialArgs(parsedAddr)
if err != nil {
return nil, nil, err
}
return client.NewFullNodeRPC(ctx, apiURI(addr), apiHeaders(token))
}
func apiURI(addr string) string {
return "ws://" + addr + "/rpc/v0"
}
func apiHeaders(token string) http.Header {
headers := http.Header{}
headers.Add("Authorization", "Bearer "+token)
return headers
}
+265 -3
View File
@@ -1,27 +1,77 @@
package main
import (
"bytes"
"context"
"fmt"
"html/template"
"io"
"io/ioutil"
"net"
"net/http"
"os"
"sort"
"strconv"
"time"
rice "github.com/GeertJohan/go.rice"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/specs-actors/actors/builtin/power"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/specs-actors/actors/builtin/miner"
)
var log = logging.Logger("main")
var supportedSectors struct {
SectorSizes []struct {
Name string
Value uint64
Default bool
}
}
func init() {
for supportedSector, _ := range miner.SupportedProofTypes {
sectorSize, err := supportedSector.SectorSize()
if err != nil {
panic(err)
}
supportedSectors.SectorSizes = append(supportedSectors.SectorSizes, struct {
Name string
Value uint64
Default bool
}{
Name: sectorSize.ShortString(),
Value: uint64(sectorSize),
Default: false,
})
}
sort.Slice(supportedSectors.SectorSizes[:], func(i, j int) bool {
return supportedSectors.SectorSizes[i].Value < supportedSectors.SectorSizes[j].Value
})
supportedSectors.SectorSizes[0].Default = true
}
func main() {
logging.SetLogLevel("*", "INFO")
@@ -94,6 +144,11 @@ var runCmd = &cli.Command{
return xerrors.Errorf("parsing source address (provide correct --from flag!): %w", err)
}
defaultMinerPeer, err := peer.Decode("12D3KooWJpBNhwgvoZ15EB1JwRTRpxgM9D2fwq6eEktrJJG74aP6")
if err != nil {
return err
}
h := &handler{
ctx: ctx,
api: nodeApi,
@@ -107,10 +162,23 @@ var runCmd = &cli.Command{
WalletRate: 15 * time.Minute,
WalletBurst: 2,
}),
minerLimiter: NewLimiter(LimiterConfig{
TotalRate: 500 * time.Millisecond,
TotalBurst: build.BlockMessageLimit,
IPRate: 10 * time.Minute,
IPBurst: 2,
WalletRate: 1 * time.Hour,
WalletBurst: 2,
}),
defaultMinerPeer: defaultMinerPeer,
}
http.Handle("/", http.FileServer(rice.MustFindBox("site").HTTPBox()))
http.HandleFunc("/miner.html", h.minerhtml)
http.HandleFunc("/send", h.send)
http.HandleFunc("/mkminer", h.mkminer)
http.HandleFunc("/msgwait", h.msgwait)
http.HandleFunc("/msgwaitaddr", h.msgwaitaddr)
fmt.Printf("Open http://%s\n", cctx.String("front"))
@@ -130,13 +198,48 @@ type handler struct {
from address.Address
sendPerRequest types.FIL
limiter *Limiter
limiter *Limiter
minerLimiter *Limiter
defaultMinerPeer peer.ID
}
func (h *handler) minerhtml(w http.ResponseWriter, r *http.Request) {
f, err := rice.MustFindBox("site").Open("_miner.html")
if err != nil {
w.WriteHeader(500)
_, _ = w.Write([]byte(err.Error()))
return
}
tmpl, err := ioutil.ReadAll(f)
if err != nil {
w.WriteHeader(500)
_, _ = w.Write([]byte(err.Error()))
return
}
var executedTmpl bytes.Buffer
t, err := template.New("miner.html").Parse(string(tmpl))
if err := t.Execute(&executedTmpl, supportedSectors); err != nil {
w.WriteHeader(500)
_, _ = w.Write([]byte(err.Error()))
return
}
if _, err := io.Copy(w, &executedTmpl); err != nil {
log.Errorf("failed to write template to string %s", err)
}
return
}
func (h *handler) send(w http.ResponseWriter, r *http.Request) {
to, err := address.NewFromString(r.FormValue("address"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
w.WriteHeader(400)
_, _ = w.Write([]byte(err.Error()))
return
}
@@ -179,9 +282,168 @@ func (h *handler) send(w http.ResponseWriter, r *http.Request) {
To: to,
}, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
w.WriteHeader(400)
_, _ = w.Write([]byte(err.Error()))
return
}
_, _ = w.Write([]byte(smsg.Cid().String()))
}
func (h *handler) mkminer(w http.ResponseWriter, r *http.Request) {
owner, err := address.NewFromString(r.FormValue("address"))
if err != nil {
w.WriteHeader(400)
_, _ = w.Write([]byte(err.Error()))
return
}
if owner.Protocol() != address.BLS {
w.WriteHeader(400)
_, _ = w.Write([]byte("Miner address must use BLS. A BLS address starts with the prefix 't3'."))
_, _ = w.Write([]byte("Please create a BLS address by running \"lotus wallet new bls\" while connected to a Lotus node."))
return
}
ssize, err := strconv.ParseInt(r.FormValue("sectorSize"), 10, 64)
if err != nil {
return
}
log.Infof("%s: create actor start", owner)
// Limit based on wallet address
limiter := h.minerLimiter.GetWalletLimiter(owner.String())
if !limiter.Allow() {
http.Error(w, http.StatusText(http.StatusTooManyRequests)+": wallet limit", http.StatusTooManyRequests)
return
}
// Limit based on IP
reqIP := r.Header.Get("X-Real-IP")
if reqIP == "" {
h, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
log.Errorf("could not get ip from: %s, err: %s", r.RemoteAddr, err)
}
reqIP = h
}
limiter = h.minerLimiter.GetIPLimiter(reqIP)
if !limiter.Allow() {
http.Error(w, http.StatusText(http.StatusTooManyRequests)+": IP limit", http.StatusTooManyRequests)
return
}
// General limiter owner allow throttling all messages that can make it into the mpool
if !h.minerLimiter.Allow() {
http.Error(w, http.StatusText(http.StatusTooManyRequests)+": global limit", http.StatusTooManyRequests)
return
}
smsg, err := h.api.MpoolPushMessage(h.ctx, &types.Message{
Value: types.BigInt(h.sendPerRequest),
From: h.from,
To: owner,
}, nil)
if err != nil {
w.WriteHeader(400)
w.Write([]byte("pushfunds: " + err.Error()))
return
}
log.Infof("%s: push funds %s", owner, smsg.Cid())
spt, err := ffiwrapper.SealProofTypeFromSectorSize(abi.SectorSize(ssize))
if err != nil {
w.WriteHeader(400)
w.Write([]byte("sealprooftype: " + err.Error()))
return
}
params, err := actors.SerializeParams(&power.CreateMinerParams{
Owner: owner,
Worker: owner,
SealProofType: spt,
Peer: abi.PeerID(h.defaultMinerPeer),
})
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
createStorageMinerMsg := &types.Message{
To: builtin.StoragePowerActorAddr,
From: h.from,
Value: big.Zero(),
Method: builtin.MethodsPower.CreateMiner,
Params: params,
}
signed, err := h.api.MpoolPushMessage(r.Context(), createStorageMinerMsg, nil)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
log.Infof("%s: create miner msg: %s", owner, signed.Cid())
http.Redirect(w, r, fmt.Sprintf("/wait.html?f=%s&m=%s&o=%s", signed.Cid(), smsg.Cid(), owner), 303)
}
func (h *handler) msgwait(w http.ResponseWriter, r *http.Request) {
c, err := cid.Parse(r.FormValue("cid"))
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
mw, err := h.api.StateWaitMsg(r.Context(), c, build.MessageConfidence)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
if mw.Receipt.ExitCode != 0 {
w.WriteHeader(400)
w.Write([]byte(xerrors.Errorf("create miner failed: exit code %d", mw.Receipt.ExitCode).Error()))
return
}
w.WriteHeader(200)
}
func (h *handler) msgwaitaddr(w http.ResponseWriter, r *http.Request) {
c, err := cid.Parse(r.FormValue("cid"))
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
mw, err := h.api.StateWaitMsg(r.Context(), c, build.MessageConfidence)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
if mw.Receipt.ExitCode != 0 {
w.WriteHeader(400)
w.Write([]byte(xerrors.Errorf("create miner failed: exit code %d", mw.Receipt.ExitCode).Error()))
return
}
w.WriteHeader(200)
var ma power.CreateMinerReturn
if err := ma.UnmarshalCBOR(bytes.NewReader(mw.Receipt.Return)); err != nil {
log.Errorf("%w", err)
w.WriteHeader(400)
w.Write([]byte(err.Error()))
return
}
fmt.Fprintf(w, "{\"addr\": \"%s\"}", ma.IDAddress)
}
+51
View File
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<title>Creating Miner - Lotus Fountain</title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<div class="Index">
<div class="Index-nodes">
<div class="Index-node">
[CREATING MINER]
</div>
<div class="Index-node" id="formnd">
<form id="f" action='/mkminer' method='POST'>
<span>Enter owner/worker address:</span>
<input type='text' name='address' style="width: 300px" placeholder="t3...">
<select name="sectorSize">
{{range .SectorSizes}}
<option {{if .Default}}selected{{end}} value="{{ .Value }}">{{ .Name }}</option>
{{end}}
</select>
<button type='submit'>Create Miner</button>
</form>
</div>
<div id="plswait" style="display: none" class="Index-node">
<b>Waiting for transaction on chain..</b>
</div>
<div class="Index-node">
<span>When creating miner, DO NOT REFRESH THE PAGE, wait for it to load. This can take more than 5min.</span>
</div>
<div class="Index-node">
<span>If you don't have an owner/worker address, you can create it by following <a target="_blank" href="https://lotu.sh/en+mining#get-started-22083">these instructions</a>.</span>
</div>
</div>
<div class="Index-footer">
<div>
<a href="index.html">[Back]</a>
<span style="float: right">Not dispensing real Filecoin tokens</span>
</div>
</div>
</div>
<script>
let f = document.getElementById('f')
f.onsubmit = ev => {
document.getElementById('plswait').style.display = 'block'
document.getElementById('formnd').style.display = 'none'
}
</script>
</body>
</html>
+3
View File
@@ -13,6 +13,9 @@
<div class="Index-node">
<a href="funds.html">[Send Funds]</a>
</div>
<div class="Index-node">
<a href="miner.html">[Create Miner]</a>
</div>
</div>
<div class="Index-footer">
<div>
+69
View File
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<title>Creating Miner (wait) - Lotus Fountain</title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<div class="Index">
<div class="Index-nodes">
<div class="Index-node">
[CREATING MINER]
</div>
<div class="Index-node">
Gas Funds:&nbsp;&nbsp;&nbsp;<span id="fcid"></span> - <span id="fstate">WAIT</span>
</div>
<div class="Index-node">
Miner Actor:&nbsp;<span id="mcid"></span> - <span id="mstate">WAIT</span>
</div>
<div class="Index-node" style="display: none" id="fwait">
New miners address is: <b id="actaddr">t</b>
</div>
<div class="Index-node" style="display: none" id="mwait">
<div style="padding-bottom: 1em">To initialize the miner run the following command:</div>
<div style="overflow-x: visible; white-space: nowrap; background: #353500">
<code>lotus-miner init --actor=<span id="actaddr2">t</span> --owner=<span id="owner">t3</span></code>
</div>
</div>
</div>
<div class="Index-footer">
<div>
<a href="index.html">[Back]</a>
<span style="float: right">Not dispensing real Filecoin tokens</span>
</div>
</div>
</div>
<script>
const params = new URLSearchParams(window.location.search);
const fcid = document.getElementById('fcid')
const mcid = document.getElementById('mcid')
fcid.innerText = params.get('f')
mcid.innerText = params.get('m')
async function waitFunds() {
await fetch('/msgwait?cid=' + params.get('f'))
document.getElementById('fstate').innerText = "OK"
document.getElementById('fwait').style.display = 'block'
}
async function waitMiner() {
const resp = await fetch('/msgwaitaddr?cid=' + params.get('f'))
document.getElementById('mstate').innerText = "OK"
const addr = (await resp.json()).addr
document.getElementById('actaddr').innerText = addr
document.getElementById('actaddr2').innerText = addr
document.getElementById('owner').innerText = params.get('o')
document.getElementById('mwait').style.display = 'block'
}
waitFunds()
waitMiner()
</script>
</body>
</html>
+1 -6
View File
@@ -54,12 +54,7 @@ func main() {
if err != nil {
return err
}
defer func() {
err2 := fi.Close()
if err == nil {
err = err2
}
}()
defer fi.Close()
b, err := json.Marshal(ki)
if err != nil {
+11 -11
View File
@@ -142,11 +142,11 @@ var runCmd = &cli.Command{
},
Action: func(cctx *cli.Context) error {
go func() {
http.ListenAndServe(":6060", nil) //nolint:errcheck
http.ListenAndServe(":6060", nil)
}()
ctx := context.Background()
api, closer, err := stats.GetFullNodeAPI(cctx.Context, cctx.String("lotus-path"))
api, closer, err := stats.GetFullNodeAPI(cctx.String("lotus-path"))
if err != nil {
log.Fatal(err)
}
@@ -445,7 +445,7 @@ func (r *refunder) Refund(ctx context.Context, tipset *types.TipSet, refunds *Mi
// Calculate the minimum balance as the total refund we need to issue plus 5% to cover fees
minBalance := types.BigAdd(refundSum, types.BigDiv(refundSum, types.NewInt(500)))
if balance.LessThan(minBalance) {
log.Errorw("not sufficient funds to cover refunds", "balance", balance, "refund_sum", refundSum, "minimum_required", minBalance)
log.Errorw("not sufficent funds to cover refunds", "balance", balance, "refund_sum", refundSum, "minimum_required", minBalance)
return xerrors.Errorf("wallet does not have enough balance to cover refund")
}
@@ -467,24 +467,24 @@ func (r *refunder) Refund(ctx context.Context, tipset *types.TipSet, refunds *Mi
return nil
}
type Repo struct {
type repo struct {
last abi.ChainEpoch
path string
}
func NewRepo(path string) (*Repo, error) {
func NewRepo(path string) (*repo, error) {
path, err := homedir.Expand(path)
if err != nil {
return nil, err
}
return &Repo{
return &repo{
last: 0,
path: path,
}, nil
}
func (r *Repo) exists() (bool, error) {
func (r *repo) exists() (bool, error) {
_, err := os.Stat(r.path)
notexist := os.IsNotExist(err)
if notexist {
@@ -494,7 +494,7 @@ func (r *Repo) exists() (bool, error) {
}
func (r *Repo) init() error {
func (r *repo) init() error {
exist, err := r.exists()
if err != nil {
return err
@@ -511,7 +511,7 @@ func (r *Repo) init() error {
return nil
}
func (r *Repo) Open() (err error) {
func (r *repo) Open() (err error) {
if err = r.init(); err != nil {
return
}
@@ -542,11 +542,11 @@ func (r *Repo) Open() (err error) {
return
}
func (r *Repo) Height() abi.ChainEpoch {
func (r *repo) Height() abi.ChainEpoch {
return r.last
}
func (r *Repo) SetHeight(last abi.ChainEpoch) (err error) {
func (r *repo) SetHeight(last abi.ChainEpoch) (err error) {
r.last = last
var f *os.File
f, err = os.OpenFile(filepath.Join(r.path, "height"), os.O_RDWR, 0644)
-71
View File
@@ -1,71 +0,0 @@
package main
import (
"fmt"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
)
var infoCmd = &cli.Command{
Name: "info",
Usage: "Print worker info",
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetWorkerAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
ver, err := api.Version(ctx)
if err != nil {
return xerrors.Errorf("getting version: %w", err)
}
fmt.Println("Worker version: ", ver)
fmt.Print("CLI version: ")
cli.VersionPrinter(cctx)
fmt.Println()
info, err := api.Info(ctx)
if err != nil {
return xerrors.Errorf("getting info: %w", err)
}
fmt.Printf("Hostname: %s\n", info.Hostname)
fmt.Printf("CPUs: %d; GPUs: %v\n", info.Resources.CPUs, info.Resources.GPUs)
fmt.Printf("RAM: %s; Swap: %s\n", types.SizeStr(types.NewInt(info.Resources.MemPhysical)), types.SizeStr(types.NewInt(info.Resources.MemSwap)))
fmt.Printf("Reserved memory: %s\n", types.SizeStr(types.NewInt(info.Resources.MemReserved)))
fmt.Println()
paths, err := api.Paths(ctx)
if err != nil {
return xerrors.Errorf("getting path info: %w", err)
}
for _, path := range paths {
fmt.Printf("%s:\n", path.ID)
fmt.Printf("\tWeight: %d; Use: ", path.Weight)
if path.CanSeal || path.CanStore {
fmt.Printf("Weight: %d; Use: ", path.Weight)
if path.CanSeal {
fmt.Print("Seal ")
}
if path.CanStore {
fmt.Print("Store")
}
fmt.Println("")
} else {
fmt.Print("Use: ReadOnly")
}
fmt.Printf("\tLocal: %s\n", path.LocalPath)
}
return nil
},
}
+6 -38
View File
@@ -16,7 +16,6 @@ import (
"github.com/google/uuid"
"github.com/gorilla/mux"
logging "github.com/ipfs/go-log/v2"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
@@ -47,10 +46,10 @@ const FlagWorkerRepoDeprecation = "workerrepo"
func main() {
lotuslog.SetupLogLevels()
log.Info("Starting lotus worker")
local := []*cli.Command{
runCmd,
infoCmd,
storageCmd,
}
app := &cli.App{
@@ -154,8 +153,6 @@ var runCmd = &cli.Command{
return nil
},
Action: func(cctx *cli.Context) error {
log.Info("Starting lotus worker")
if !cctx.Bool("enable-gpu-proving") {
if err := os.Setenv("BELLMAN_NO_GPU", "true"); err != nil {
return xerrors.Errorf("could not set no-gpu env: %+v", err)
@@ -188,7 +185,7 @@ var runCmd = &cli.Command{
return err
}
if v.APIVersion != build.APIVersion {
return xerrors.Errorf("lotus-miner API version doesn't match: local: %s", api.Version{APIVersion: build.APIVersion})
return xerrors.Errorf("lotus-miner API version doesn't match: local: ", api.Version{APIVersion: build.APIVersion})
}
log.Infof("Remote version %s", v)
@@ -345,8 +342,6 @@ var runCmd = &cli.Command{
SealProof: spt,
TaskTypes: taskTypes,
}, remote, localStore, nodeApi),
localStore: localStore,
ls: lr,
}
mux := mux.NewRouter()
@@ -388,32 +383,6 @@ var runCmd = &cli.Command{
return err
}
{
a, err := net.ResolveTCPAddr("tcp", address)
if err != nil {
return xerrors.Errorf("parsing address: %w", err)
}
ma, err := manet.FromNetAddr(a)
if err != nil {
return xerrors.Errorf("creating api multiaddress: %w", err)
}
if err := lr.SetAPIEndpoint(ma); err != nil {
return xerrors.Errorf("setting api endpoint: %w", err)
}
ainfo, err := lcli.GetAPIInfo(cctx, repo.StorageMiner)
if err != nil {
return xerrors.Errorf("could not get miner API info: %w", err)
}
// TODO: ideally this would be a token with some permissions dropped
if err := lr.SetAPIToken(ainfo.Token); err != nil {
return xerrors.Errorf("setting api token: %w", err)
}
}
log.Info("Waiting for tasks")
go func() {
@@ -451,11 +420,10 @@ func watchMinerConn(ctx context.Context, cctx *cli.Context, nodeApi api.StorageM
log.Errorf("getting executable for auto-restart: %+v", err)
}
_ = log.Sync()
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)
//nolint:gosec
if err := syscall.Exec(exe, []string{exe,
fmt.Sprintf("--worker-repo=%s", cctx.String("worker-repo")),
fmt.Sprintf("--miner-repo=%s", cctx.String("miner-repo")),
@@ -482,7 +450,7 @@ func extractRoutableIP(timeout time.Duration) (string, error) {
env, ok := os.LookupEnv(minerMultiAddrKey)
if !ok {
// TODO remove after deprecation period
_, ok = os.LookupEnv(deprecatedMinerMultiAddrKey)
env, ok = os.LookupEnv(deprecatedMinerMultiAddrKey)
if ok {
log.Warnf("Using a deprecated env(%s) value, please use env(%s) instead.", deprecatedMinerMultiAddrKey, minerMultiAddrKey)
}
@@ -493,7 +461,7 @@ func extractRoutableIP(timeout time.Duration) (string, error) {
if err != nil {
return "", err
}
defer conn.Close() //nolint:errcheck
defer conn.Close()
localAddr := conn.LocalAddr().(*net.TCPAddr)
+2 -27
View File
@@ -3,44 +3,19 @@ package main
import (
"context"
"github.com/mitchellh/go-homedir"
"golang.org/x/xerrors"
"github.com/filecoin-project/specs-storage/storage"
"github.com/filecoin-project/lotus/build"
sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage"
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
"github.com/filecoin-project/lotus/build"
)
type worker struct {
*sectorstorage.LocalWorker
localStore *stores.Local
ls stores.LocalStorage
}
func (w *worker) Version(context.Context) (build.Version, error) {
return build.APIVersion, nil
}
func (w *worker) StorageAddLocal(ctx context.Context, path string) error {
path, err := homedir.Expand(path)
if err != nil {
return xerrors.Errorf("expanding local path: %w", err)
}
if err := w.localStore.OpenPath(ctx, path); err != nil {
return xerrors.Errorf("opening local path: %w", err)
}
if err := w.ls.SetStorage(func(sc *stores.StorageConfig) {
sc.StoragePaths = append(sc.StoragePaths, stores.LocalPath{Path: path})
}); err != nil {
return xerrors.Errorf("get storage config: %w", err)
}
return nil
}
var _ storage.Sealer = &worker{}
-105
View File
@@ -1,105 +0,0 @@
package main
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"github.com/google/uuid"
"github.com/mitchellh/go-homedir"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
)
const metaFile = "sectorstore.json"
var storageCmd = &cli.Command{
Name: "storage",
Usage: "manage sector storage",
Subcommands: []*cli.Command{
storageAttachCmd,
},
}
var storageAttachCmd = &cli.Command{
Name: "attach",
Usage: "attach local storage path",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "init",
Usage: "initialize the path first",
},
&cli.Uint64Flag{
Name: "weight",
Usage: "(for init) path weight",
Value: 10,
},
&cli.BoolFlag{
Name: "seal",
Usage: "(for init) use path for sealing",
},
&cli.BoolFlag{
Name: "store",
Usage: "(for init) use path for long-term storage",
},
},
Action: func(cctx *cli.Context) error {
nodeApi, closer, err := lcli.GetWorkerAPI(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
if !cctx.Args().Present() {
return xerrors.Errorf("must specify storage path to attach")
}
p, err := homedir.Expand(cctx.Args().First())
if err != nil {
return xerrors.Errorf("expanding path: %w", err)
}
if cctx.Bool("init") {
if err := os.MkdirAll(p, 0755); err != nil {
if !os.IsExist(err) {
return err
}
}
_, err := os.Stat(filepath.Join(p, metaFile))
if !os.IsNotExist(err) {
if err == nil {
return xerrors.Errorf("path is already initialized")
}
return err
}
cfg := &stores.LocalStorageMeta{
ID: stores.ID(uuid.New().String()),
Weight: cctx.Uint64("weight"),
CanSeal: cctx.Bool("seal"),
CanStore: cctx.Bool("store"),
}
if !(cfg.CanStore || cfg.CanSeal) {
return xerrors.Errorf("must specify at least one of --store of --seal")
}
b, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return xerrors.Errorf("marshaling storage config: %w", err)
}
if err := ioutil.WriteFile(filepath.Join(p, metaFile), b, 0644); err != nil {
return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(p, metaFile), err)
}
}
return nodeApi.StorageAddLocal(ctx, p)
},
}
-3
View File
@@ -114,9 +114,6 @@ var preSealCmd = &cli.Command{
return err
}
kb, err := hex.DecodeString(string(kh))
if err != nil {
return err
}
if err := json.Unmarshal(kb, k); err != nil {
return err
}
-160
View File
@@ -1,160 +0,0 @@
package main
import (
"context"
"fmt"
"os"
"sort"
"github.com/fatih/color"
"github.com/ipfs/go-datastore"
cbor "github.com/ipfs/go-ipld-cbor"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/blockstore"
"github.com/filecoin-project/specs-actors/actors/abi/big"
"github.com/filecoin-project/specs-actors/actors/builtin"
saacc "github.com/filecoin-project/specs-actors/actors/builtin/account"
saminer "github.com/filecoin-project/specs-actors/actors/builtin/miner"
samsig "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
)
type addrInfo struct {
Key address.Address
Balance types.FIL
}
type msigInfo struct {
Signers []address.Address
Balance types.FIL
Threshold uint64
}
type minerInfo struct {
}
var genesisVerifyCmd = &cli.Command{
Name: "verify-genesis",
Description: "verify some basic attributes of a genesis car file",
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("must pass genesis car file")
}
bs := blockstore.NewBlockstore(datastore.NewMapDatastore())
cs := store.NewChainStore(bs, datastore.NewMapDatastore(), nil)
cf := cctx.Args().Get(0)
f, err := os.Open(cf)
if err != nil {
return xerrors.Errorf("opening the car file: %w", err)
}
ts, err := cs.Import(f)
if err != nil {
return err
}
sm := stmgr.NewStateManager(cs)
total, err := stmgr.CheckTotalFIL(context.TODO(), sm, ts)
if err != nil {
return err
}
fmt.Println("Genesis: ", ts.Key())
expFIL := big.Mul(big.NewInt(int64(build.FilBase)), big.NewInt(int64(build.FilecoinPrecision)))
fmt.Printf("Total FIL: %s", types.FIL(total))
if !expFIL.Equals(total) {
color.Red(" INCORRECT!")
}
fmt.Println()
cst := cbor.NewCborStore(bs)
stree, err := state.LoadStateTree(cst, ts.ParentState())
if err != nil {
return err
}
var accAddrs, msigAddrs []address.Address
kaccounts := make(map[address.Address]addrInfo)
kmultisigs := make(map[address.Address]msigInfo)
kminers := make(map[address.Address]minerInfo)
ctx := context.TODO()
if err := stree.ForEach(func(addr address.Address, act *types.Actor) error {
switch act.Code {
case builtin.StorageMinerActorCodeID:
var st saminer.State
if err := cst.Get(ctx, act.Head, &st); err != nil {
return err
}
kminers[addr] = minerInfo{}
case builtin.MultisigActorCodeID:
var st samsig.State
if err := cst.Get(ctx, act.Head, &st); err != nil {
return xerrors.Errorf("multisig actor: %w", err)
}
kmultisigs[addr] = msigInfo{
Balance: types.FIL(act.Balance),
Signers: st.Signers,
Threshold: st.NumApprovalsThreshold,
}
msigAddrs = append(msigAddrs, addr)
case builtin.AccountActorCodeID:
var st saacc.State
if err := cst.Get(ctx, act.Head, &st); err != nil {
log.Warn(xerrors.Errorf("account actor %s: %w", addr, err))
}
kaccounts[addr] = addrInfo{
Key: st.Address,
Balance: types.FIL(act.Balance.Copy()),
}
accAddrs = append(accAddrs, addr)
}
return nil
}); err != nil {
return err
}
sort.Slice(accAddrs, func(i, j int) bool {
return accAddrs[i].String() < accAddrs[j].String()
})
sort.Slice(msigAddrs, func(i, j int) bool {
return msigAddrs[i].String() < msigAddrs[j].String()
})
fmt.Println("Account Actors:")
for _, acc := range accAddrs {
a := kaccounts[acc]
fmt.Printf("%s\t%s\t%s\n", acc, a.Key, a.Balance)
}
fmt.Println("Multisig Actors:")
for _, acc := range msigAddrs {
m := kmultisigs[acc]
fmt.Printf("%s\t%s\t%d\t[", acc, m.Balance, m.Threshold)
for i, s := range m.Signers {
fmt.Print(s)
if i != len(m.Signers)-1 {
fmt.Print(",")
}
}
fmt.Printf("]\n")
}
return nil
},
}
+5 -5
View File
@@ -69,7 +69,7 @@ var keyinfoImportCmd = &cli.Command{
if err != nil {
return err
}
defer inputFile.Close() //nolint:errcheck
defer inputFile.Close()
input = bufio.NewReader(inputFile)
}
@@ -98,7 +98,7 @@ var keyinfoImportCmd = &cli.Command{
return err
}
defer lkrepo.Close() //nolint:errcheck
defer lkrepo.Close()
keystore, err := lkrepo.KeyStore()
if err != nil {
@@ -150,7 +150,7 @@ var keyinfoInfoCmd = &cli.Command{
The 'format' flag takes a golang text/template template as its value.
The following fields can be retrieved through this command
The following fields can be retrived through this command
Type
Address
PublicKey
@@ -159,7 +159,7 @@ var keyinfoInfoCmd = &cli.Command{
Examples
Retrieve the address of a lotus wallet
Retreive the address of a lotus wallet
lotus-shed keyinfo info --format '{{ .Address }}' wallet.keyinfo
`,
Flags: []cli.Flag{
@@ -181,7 +181,7 @@ var keyinfoInfoCmd = &cli.Command{
if err != nil {
return err
}
defer inputFile.Close() //nolint:errcheck
defer inputFile.Close()
input = bufio.NewReader(inputFile)
}
Loaded 100 of 209 files, more files were not shown because too many files have changed in this diff. Show more