Merge remote-tracking branch 'origin/master' into next
This commit is contained in:
+125
@@ -0,0 +1,125 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/lib/backupds"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
|
||||
type BackupAPI interface {
|
||||
CreateBackup(ctx context.Context, fpath string) error
|
||||
}
|
||||
|
||||
type BackupApiFn func(ctx *cli.Context) (BackupAPI, jsonrpc.ClientCloser, error)
|
||||
|
||||
func BackupCmd(repoFlag string, rt repo.RepoType, getApi BackupApiFn) *cli.Command {
|
||||
var offlineBackup = func(cctx *cli.Context) error {
|
||||
logging.SetLogLevel("badger", "ERROR") // nolint:errcheck
|
||||
|
||||
repoPath := cctx.String(repoFlag)
|
||||
r, err := repo.NewFS(repoPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ok, err := r.Exists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return xerrors.Errorf("repo at '%s' is not initialized", cctx.String(repoFlag))
|
||||
}
|
||||
|
||||
lr, err := r.LockRO(rt)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("locking repo: %w", err)
|
||||
}
|
||||
defer lr.Close() // nolint:errcheck
|
||||
|
||||
mds, err := lr.Datastore("/metadata")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting metadata datastore: %w", err)
|
||||
}
|
||||
|
||||
bds := backupds.Wrap(mds)
|
||||
|
||||
fpath, err := homedir.Expand(cctx.Args().First())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("expanding file path: %w", err)
|
||||
}
|
||||
|
||||
out, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("opening backup file %s: %w", fpath, err)
|
||||
}
|
||||
|
||||
if err := bds.Backup(out); err != nil {
|
||||
if cerr := out.Close(); cerr != nil {
|
||||
log.Errorw("error closing backup file while handling backup error", "closeErr", cerr, "backupErr", err)
|
||||
}
|
||||
return xerrors.Errorf("backup error: %w", err)
|
||||
}
|
||||
|
||||
if err := out.Close(); err != nil {
|
||||
return xerrors.Errorf("closing backup file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var onlineBackup = func(cctx *cli.Context) error {
|
||||
api, closer, err := getApi(cctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting api: %w (if the node isn't running you can use the --offline flag)", err)
|
||||
}
|
||||
defer closer()
|
||||
|
||||
err = api.CreateBackup(ReqContext(cctx), cctx.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Success")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return &cli.Command{
|
||||
Name: "backup",
|
||||
Usage: "Create node metadata backup",
|
||||
Description: `The backup command writes a copy of node metadata under the specified path
|
||||
|
||||
Online backups:
|
||||
For security reasons, the daemon must be have LOTUS_BACKUP_BASE_PATH env var set
|
||||
to a path where backup files are supposed to be saved, and the path specified in
|
||||
this command must be within this base path`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "offline",
|
||||
Usage: "create backup without the node running",
|
||||
},
|
||||
},
|
||||
ArgsUsage: "[backup file path]",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.Args().Len() != 1 {
|
||||
return xerrors.Errorf("expected 1 argument")
|
||||
}
|
||||
|
||||
if cctx.Bool("offline") {
|
||||
return offlineBackup(cctx)
|
||||
}
|
||||
|
||||
return onlineBackup(cctx)
|
||||
},
|
||||
}
|
||||
}
|
||||
+153
-6
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -27,9 +28,10 @@ import (
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
lapi "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/stmgr"
|
||||
types "github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@@ -50,6 +52,7 @@ var chainCmd = &cli.Command{
|
||||
chainExportCmd,
|
||||
slashConsensusFault,
|
||||
chainGasPriceCmd,
|
||||
chainInspectUsage,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -159,7 +162,7 @@ var chainGetBlock = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func apiMsgCids(in []api.Message) []cid.Cid {
|
||||
func apiMsgCids(in []lapi.Message) []cid.Cid {
|
||||
out := make([]cid.Cid, len(in))
|
||||
for k, v := range in {
|
||||
out[k] = v.Cid
|
||||
@@ -375,6 +378,143 @@ var chainSetHeadCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var chainInspectUsage = &cli.Command{
|
||||
Name: "inspect-usage",
|
||||
Usage: "Inspect block space usage of a given tipset",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "tipset",
|
||||
Usage: "specify tipset to view block space usage of",
|
||||
Value: "@head",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "length",
|
||||
Usage: "length of chain to inspect block space usage for",
|
||||
Value: 1,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "num-results",
|
||||
Usage: "number of results to print per category",
|
||||
Value: 10,
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
ts, err := LoadTipSet(ctx, cctx, api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cur := ts
|
||||
var msgs []lapi.Message
|
||||
for i := 0; i < cctx.Int("length"); i++ {
|
||||
pmsgs, err := api.ChainGetParentMessages(ctx, cur.Blocks()[0].Cid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgs = append(msgs, pmsgs...)
|
||||
|
||||
next, err := api.ChainGetTipSet(ctx, cur.Parents())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cur = next
|
||||
}
|
||||
|
||||
codeCache := make(map[address.Address]cid.Cid)
|
||||
|
||||
lookupActorCode := func(a address.Address) (cid.Cid, error) {
|
||||
c, ok := codeCache[a]
|
||||
if ok {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
act, err := api.StateGetActor(ctx, a, ts.Key())
|
||||
if err != nil {
|
||||
return cid.Undef, err
|
||||
}
|
||||
|
||||
codeCache[a] = act.Code
|
||||
return act.Code, nil
|
||||
}
|
||||
|
||||
bySender := make(map[string]int64)
|
||||
byDest := make(map[string]int64)
|
||||
byMethod := make(map[string]int64)
|
||||
|
||||
var sum int64
|
||||
for _, m := range msgs {
|
||||
bySender[m.Message.From.String()] += m.Message.GasLimit
|
||||
byDest[m.Message.To.String()] += m.Message.GasLimit
|
||||
sum += m.Message.GasLimit
|
||||
|
||||
code, err := lookupActorCode(m.Message.To)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mm := stmgr.MethodsMap[code][m.Message.Method]
|
||||
|
||||
byMethod[mm.Name] += m.Message.GasLimit
|
||||
|
||||
}
|
||||
|
||||
type keyGasPair struct {
|
||||
Key string
|
||||
Gas int64
|
||||
}
|
||||
|
||||
mapToSortedKvs := func(m map[string]int64) []keyGasPair {
|
||||
var vals []keyGasPair
|
||||
for k, v := range m {
|
||||
vals = append(vals, keyGasPair{
|
||||
Key: k,
|
||||
Gas: v,
|
||||
})
|
||||
}
|
||||
sort.Slice(vals, func(i, j int) bool {
|
||||
return vals[i].Gas > vals[j].Gas
|
||||
})
|
||||
return vals
|
||||
}
|
||||
|
||||
senderVals := mapToSortedKvs(bySender)
|
||||
destVals := mapToSortedKvs(byDest)
|
||||
methodVals := mapToSortedKvs(byMethod)
|
||||
|
||||
numRes := cctx.Int("num-results")
|
||||
|
||||
fmt.Printf("Total Gas Limit: %d\n", sum)
|
||||
fmt.Printf("By Sender:\n")
|
||||
for i := 0; i < numRes && i < len(senderVals); i++ {
|
||||
sv := senderVals[i]
|
||||
fmt.Printf("%s\t%0.2f%%\t(%d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas)
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Printf("By Receiver:\n")
|
||||
for i := 0; i < numRes && i < len(destVals); i++ {
|
||||
sv := destVals[i]
|
||||
fmt.Printf("%s\t%0.2f%%\t(%d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas)
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Printf("By Method:\n")
|
||||
for i := 0; i < numRes && i < len(methodVals); i++ {
|
||||
sv := methodVals[i]
|
||||
fmt.Printf("%s\t%0.2f%%\t(%d)\n", sv.Key, (100*float64(sv.Gas))/float64(sum), sv.Gas)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var chainListCmd = &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "View a segment of the chain",
|
||||
@@ -648,7 +788,7 @@ var chainGetCmd = &cli.Command{
|
||||
|
||||
type apiIpldStore struct {
|
||||
ctx context.Context
|
||||
api api.FullNode
|
||||
api lapi.FullNode
|
||||
}
|
||||
|
||||
func (ht *apiIpldStore) Context() context.Context {
|
||||
@@ -676,7 +816,7 @@ func (ht *apiIpldStore) Put(ctx context.Context, v interface{}) (cid.Cid, error)
|
||||
panic("No mutations allowed")
|
||||
}
|
||||
|
||||
func handleAmt(ctx context.Context, api api.FullNode, r cid.Cid) error {
|
||||
func handleAmt(ctx context.Context, api lapi.FullNode, r cid.Cid) error {
|
||||
s := &apiIpldStore{ctx, api}
|
||||
mp, err := adt.AsArray(s, r)
|
||||
if err != nil {
|
||||
@@ -689,7 +829,7 @@ func handleAmt(ctx context.Context, api api.FullNode, r cid.Cid) error {
|
||||
})
|
||||
}
|
||||
|
||||
func handleHamtEpoch(ctx context.Context, api api.FullNode, r cid.Cid) error {
|
||||
func handleHamtEpoch(ctx context.Context, api lapi.FullNode, r cid.Cid) error {
|
||||
s := &apiIpldStore{ctx, api}
|
||||
mp, err := adt.AsMap(s, r)
|
||||
if err != nil {
|
||||
@@ -707,7 +847,7 @@ func handleHamtEpoch(ctx context.Context, api api.FullNode, r cid.Cid) error {
|
||||
})
|
||||
}
|
||||
|
||||
func handleHamtAddress(ctx context.Context, api api.FullNode, r cid.Cid) error {
|
||||
func handleHamtAddress(ctx context.Context, api lapi.FullNode, r cid.Cid) error {
|
||||
s := &apiIpldStore{ctx, api}
|
||||
mp, err := adt.AsMap(s, r)
|
||||
if err != nil {
|
||||
@@ -930,13 +1070,20 @@ var chainExportCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
var last bool
|
||||
for b := range stream {
|
||||
last = len(b) == 0
|
||||
|
||||
_, err := fi.Write(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !last {
|
||||
return xerrors.Errorf("incomplete export (remote connection lost?)")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
+15
@@ -216,6 +216,13 @@ func GetAPI(ctx *cli.Context) (api.Common, jsonrpc.ClientCloser, error) {
|
||||
log.Errorf("repoType type does not match the type of repo.RepoType")
|
||||
}
|
||||
|
||||
if tn, ok := ctx.App.Metadata["testnode-storage"]; ok {
|
||||
return tn.(api.StorageMiner), func() {}, nil
|
||||
}
|
||||
if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
|
||||
return tn.(api.FullNode), func() {}, nil
|
||||
}
|
||||
|
||||
addr, headers, err := GetRawAPI(ctx, t)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -225,6 +232,10 @@ func GetAPI(ctx *cli.Context) (api.Common, jsonrpc.ClientCloser, error) {
|
||||
}
|
||||
|
||||
func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error) {
|
||||
if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
|
||||
return tn.(api.FullNode), func() {}, nil
|
||||
}
|
||||
|
||||
addr, headers, err := GetRawAPI(ctx, repo.FullNode)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -234,6 +245,10 @@ func GetFullNodeAPI(ctx *cli.Context) (api.FullNode, jsonrpc.ClientCloser, error
|
||||
}
|
||||
|
||||
func GetStorageMinerAPI(ctx *cli.Context, opts ...jsonrpc.Option) (api.StorageMiner, jsonrpc.ClientCloser, error) {
|
||||
if tn, ok := ctx.App.Metadata["testnode-storage"]; ok {
|
||||
return tn.(api.StorageMiner), func() {}, nil
|
||||
}
|
||||
|
||||
addr, headers, err := GetRawAPI(ctx, repo.StorageMiner)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
+47
-22
@@ -164,14 +164,6 @@ var mpoolSub = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
type statBucket struct {
|
||||
msgs map[uint64]*types.SignedMessage
|
||||
}
|
||||
type mpStat struct {
|
||||
addr string
|
||||
past, cur, future uint64
|
||||
}
|
||||
|
||||
var mpoolStat = &cli.Command{
|
||||
Name: "stat",
|
||||
Usage: "print mempool stats",
|
||||
@@ -180,6 +172,11 @@ var mpoolStat = &cli.Command{
|
||||
Name: "local",
|
||||
Usage: "print stats for addresses in local wallet only",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "basefee-lookback",
|
||||
Usage: "number of blocks to look back for minimum basefee",
|
||||
Value: 60,
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
@@ -194,6 +191,20 @@ var mpoolStat = &cli.Command{
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting chain head: %w", err)
|
||||
}
|
||||
currBF := ts.Blocks()[0].ParentBaseFee
|
||||
minBF := currBF
|
||||
{
|
||||
currTs := ts
|
||||
for i := 0; i < cctx.Int("basefee-lookback"); i++ {
|
||||
currTs, err = api.ChainGetTipSet(ctx, currTs.Parents())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("walking chain: %w", err)
|
||||
}
|
||||
if newBF := currTs.Blocks()[0].ParentBaseFee; newBF.LessThan(minBF) {
|
||||
minBF = newBF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var filter map[address.Address]struct{}
|
||||
if cctx.Bool("local") {
|
||||
@@ -214,8 +225,16 @@ var mpoolStat = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
buckets := map[address.Address]*statBucket{}
|
||||
type statBucket struct {
|
||||
msgs map[uint64]*types.SignedMessage
|
||||
}
|
||||
type mpStat struct {
|
||||
addr string
|
||||
past, cur, future uint64
|
||||
belowCurr, belowPast uint64
|
||||
}
|
||||
|
||||
buckets := map[address.Address]*statBucket{}
|
||||
for _, v := range msgs {
|
||||
if filter != nil {
|
||||
if _, has := filter[v.Message.From]; !has {
|
||||
@@ -252,23 +271,27 @@ var mpoolStat = &cli.Command{
|
||||
cur++
|
||||
}
|
||||
|
||||
past := uint64(0)
|
||||
future := uint64(0)
|
||||
var s mpStat
|
||||
s.addr = a.String()
|
||||
|
||||
for _, m := range bkt.msgs {
|
||||
if m.Message.Nonce < act.Nonce {
|
||||
past++
|
||||
s.past++
|
||||
} else if m.Message.Nonce > cur {
|
||||
s.future++
|
||||
} else {
|
||||
s.cur++
|
||||
}
|
||||
if m.Message.Nonce > cur {
|
||||
future++
|
||||
|
||||
if m.Message.GasFeeCap.LessThan(currBF) {
|
||||
s.belowCurr++
|
||||
}
|
||||
if m.Message.GasFeeCap.LessThan(minBF) {
|
||||
s.belowPast++
|
||||
}
|
||||
}
|
||||
|
||||
out = append(out, mpStat{
|
||||
addr: a.String(),
|
||||
past: past,
|
||||
cur: cur - act.Nonce,
|
||||
future: future,
|
||||
})
|
||||
out = append(out, s)
|
||||
}
|
||||
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
@@ -281,12 +304,14 @@ var mpoolStat = &cli.Command{
|
||||
total.past += stat.past
|
||||
total.cur += stat.cur
|
||||
total.future += stat.future
|
||||
total.belowCurr += stat.belowCurr
|
||||
total.belowPast += stat.belowPast
|
||||
|
||||
fmt.Printf("%s: past: %d, cur: %d, future: %d\n", stat.addr, stat.past, stat.cur, stat.future)
|
||||
fmt.Printf("%s: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d \n", stat.addr, stat.past, stat.cur, stat.future, stat.belowCurr, cctx.Int("basefee-lookback"), stat.belowPast)
|
||||
}
|
||||
|
||||
fmt.Println("-----")
|
||||
fmt.Printf("total: past: %d, cur: %d, future: %d\n", total.past, total.cur, total.future)
|
||||
fmt.Printf("total: Nonce past: %d, cur: %d, future: %d; FeeCap cur: %d, min-%d: %d \n", total.past, total.cur, total.future, total.belowCurr, cctx.Int("basefee-lookback"), total.belowPast)
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user