2020-09-11 00:28:25 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2020-11-01 13:03:21 +00:00
|
|
|
"io"
|
2020-09-11 00:28:25 +00:00
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
2020-11-01 13:03:21 +00:00
|
|
|
|
2020-09-11 00:28:25 +00:00
|
|
|
"github.com/filecoin-project/lotus/chain/store"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
|
|
lcli "github.com/filecoin-project/lotus/cli"
|
|
|
|
"github.com/filecoin-project/lotus/node/repo"
|
|
|
|
)
|
|
|
|
|
|
|
|
var exportChainCmd = &cli.Command{
|
|
|
|
Name: "export",
|
|
|
|
Description: "Export chain from repo (requires node to be offline)",
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "repo",
|
|
|
|
Value: "~/.lotus",
|
|
|
|
},
|
|
|
|
&cli.StringFlag{
|
|
|
|
Name: "tipset",
|
|
|
|
Usage: "tipset to export from",
|
|
|
|
},
|
|
|
|
&cli.Int64Flag{
|
|
|
|
Name: "recent-stateroots",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "full-state",
|
|
|
|
},
|
|
|
|
&cli.BoolFlag{
|
|
|
|
Name: "skip-old-msgs",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Action: func(cctx *cli.Context) error {
|
|
|
|
if !cctx.Args().Present() {
|
2020-09-11 08:55:10 +00:00
|
|
|
return lcli.ShowHelp(cctx, fmt.Errorf("must specify file name to write export to"))
|
2020-09-11 00:28:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx := context.TODO()
|
|
|
|
|
|
|
|
r, err := repo.NewFS(cctx.String("repo"))
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("opening fs repo: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
exists, err := r.Exists()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
return xerrors.Errorf("lotus repo doesn't exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
lr, err := r.Lock(repo.FullNode)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer lr.Close() //nolint:errcheck
|
|
|
|
|
|
|
|
fi, err := os.Create(cctx.Args().First())
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("opening the output file: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-09-11 08:55:10 +00:00
|
|
|
defer fi.Close() //nolint:errcheck
|
2020-09-11 00:28:25 +00:00
|
|
|
|
2020-12-30 10:04:00 +00:00
|
|
|
bs, err := lr.Blockstore(ctx, repo.BlockstoreChain)
|
2020-09-11 00:28:25 +00:00
|
|
|
if err != nil {
|
2020-11-01 13:03:21 +00:00
|
|
|
return fmt.Errorf("failed to open blockstore: %w", err)
|
2020-09-11 00:28:25 +00:00
|
|
|
}
|
|
|
|
|
2020-11-01 13:03:21 +00:00
|
|
|
defer func() {
|
|
|
|
if c, ok := bs.(io.Closer); ok {
|
|
|
|
if err := c.Close(); err != nil {
|
|
|
|
log.Warnf("failed to close blockstore: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2021-01-26 10:25:34 +00:00
|
|
|
mds, err := lr.Datastore(context.Background(), "/metadata")
|
2020-09-11 00:28:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-16 22:22:08 +00:00
|
|
|
cs := store.NewChainStore(bs, bs, mds, nil, nil)
|
|
|
|
defer cs.Close() //nolint:errcheck
|
|
|
|
|
2020-09-11 00:28:25 +00:00
|
|
|
if err := cs.Load(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
nroots := abi.ChainEpoch(cctx.Int64("recent-stateroots"))
|
|
|
|
fullstate := cctx.Bool("full-state")
|
|
|
|
skipoldmsgs := cctx.Bool("skip-old-msgs")
|
|
|
|
|
|
|
|
var ts *types.TipSet
|
|
|
|
if tss := cctx.String("tipset"); tss != "" {
|
|
|
|
cids, err := lcli.ParseTipSetString(tss)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("failed to parse tipset (%q): %w", tss, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
tsk := types.NewTipSetKey(cids...)
|
|
|
|
|
|
|
|
selts, err := cs.LoadTipSet(tsk)
|
|
|
|
if err != nil {
|
|
|
|
return xerrors.Errorf("loading tipset: %w", err)
|
|
|
|
}
|
|
|
|
ts = selts
|
|
|
|
} else {
|
|
|
|
ts = cs.GetHeaviestTipSet()
|
|
|
|
}
|
|
|
|
|
|
|
|
if fullstate {
|
|
|
|
nroots = ts.Height() + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := cs.Export(ctx, ts, nroots, skipoldmsgs, fi); err != nil {
|
|
|
|
return xerrors.Errorf("export failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|