lotus/cmd/lotus-miner/init.go

791 lines
22 KiB
Go
Raw Normal View History

2019-07-19 09:24:11 +00:00
package main
import (
2020-03-06 08:06:59 +00:00
"bytes"
"context"
2019-10-03 00:02:06 +00:00
"crypto/rand"
"encoding/binary"
"encoding/json"
"fmt"
2020-02-27 21:45:31 +00:00
"io/ioutil"
2021-05-18 11:35:25 +00:00
"net/http"
2020-02-27 21:45:31 +00:00
"os"
"path/filepath"
2020-02-27 21:45:31 +00:00
"strconv"
"github.com/docker/go-units"
"github.com/google/uuid"
2019-11-16 06:47:04 +00:00
"github.com/ipfs/go-datastore"
2020-09-14 07:44:55 +00:00
"github.com/ipfs/go-datastore/namespace"
2022-08-25 18:20:41 +00:00
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
2019-11-16 06:47:04 +00:00
"github.com/mitchellh/go-homedir"
"github.com/urfave/cli/v2"
2020-06-05 22:59:01 +00:00
"golang.org/x/xerrors"
2019-07-19 09:24:11 +00:00
2020-03-17 20:19:52 +00:00
"github.com/filecoin-project/go-address"
cborutil "github.com/filecoin-project/go-cbor-util"
"github.com/filecoin-project/go-paramfetch"
2020-09-07 03:49:10 +00:00
"github.com/filecoin-project/go-state-types/abi"
2020-09-14 07:44:55 +00:00
"github.com/filecoin-project/go-state-types/big"
2022-06-14 15:00:51 +00:00
"github.com/filecoin-project/go-state-types/builtin"
2022-09-26 16:47:48 +00:00
markettypes "github.com/filecoin-project/go-state-types/builtin/v9/market"
2020-09-14 07:44:55 +00:00
"github.com/filecoin-project/go-statestore"
2020-10-08 01:09:33 +00:00
miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner"
power2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/power"
2022-06-14 15:00:51 +00:00
power6 "github.com/filecoin-project/specs-actors/v6/actors/builtin/power"
2020-03-17 20:19:52 +00:00
lapi "github.com/filecoin-project/lotus/api"
2021-04-05 11:23:46 +00:00
"github.com/filecoin-project/lotus/api/v0api"
2021-04-05 17:56:53 +00:00
"github.com/filecoin-project/lotus/api/v1api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
2020-10-08 01:09:33 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
2020-08-06 01:14:13 +00:00
"github.com/filecoin-project/lotus/chain/gen/slashfilter"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/genesis"
"github.com/filecoin-project/lotus/journal"
2021-08-17 12:55:35 +00:00
"github.com/filecoin-project/lotus/journal/fsjournal"
storageminer "github.com/filecoin-project/lotus/miner"
"github.com/filecoin-project/lotus/node/modules"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/repo"
2019-11-25 04:45:13 +00:00
"github.com/filecoin-project/lotus/storage"
"github.com/filecoin-project/lotus/storage/paths"
pipeline "github.com/filecoin-project/lotus/storage/pipeline"
"github.com/filecoin-project/lotus/storage/sealer"
"github.com/filecoin-project/lotus/storage/sealer/ffiwrapper"
"github.com/filecoin-project/lotus/storage/sealer/storiface"
2019-07-19 09:24:11 +00:00
)
var initCmd = &cli.Command{
Name: "init",
Usage: "Initialize a lotus miner repo",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "actor",
Usage: "specify the address of an already created miner actor",
},
&cli.BoolFlag{
2019-09-26 20:57:20 +00:00
Name: "genesis-miner",
Usage: "enable genesis mining (DON'T USE ON BOOTSTRAPPED NETWORK)",
Hidden: true,
},
&cli.BoolFlag{
Name: "create-worker-key",
Usage: "create separate worker key",
},
&cli.StringFlag{
2019-09-26 20:57:20 +00:00
Name: "worker",
Aliases: []string{"w"},
2019-09-26 20:57:20 +00:00
Usage: "worker key to use (overrides --create-worker-key)",
},
&cli.StringFlag{
2019-09-26 20:57:20 +00:00
Name: "owner",
Aliases: []string{"o"},
2019-09-26 20:57:20 +00:00
Usage: "owner key to use",
},
&cli.StringFlag{
Name: "sector-size",
Usage: "specify sector size to use",
},
2020-03-03 22:19:22 +00:00
&cli.StringSliceFlag{
2019-11-25 04:45:13 +00:00
Name: "pre-sealed-sectors",
Usage: "specify set of presealed sectors for starting as a genesis miner",
},
&cli.StringFlag{
Name: "pre-sealed-metadata",
Usage: "specify the metadata file for the presealed sectors",
},
2019-12-04 16:53:32 +00:00
&cli.BoolFlag{
Name: "nosync",
Usage: "don't check full-node sync status",
},
&cli.BoolFlag{
Name: "symlink-imported-sectors",
Usage: "attempt to symlink to presealed sectors instead of copying them into place",
},
&cli.BoolFlag{
Name: "no-local-storage",
Usage: "don't use storageminer repo for sector storage",
},
&cli.StringFlag{
Name: "gas-premium",
Usage: "set gas premium for initialization messages in AttoFIL",
Value: "0",
},
2020-08-20 00:34:58 +00:00
&cli.StringFlag{
Name: "from",
Usage: "select which address to send actor creation message from",
},
},
2020-10-01 14:22:54 +00:00
Subcommands: []*cli.Command{
restoreCmd,
serviceCmd,
2020-10-01 14:22:54 +00:00
},
2019-07-19 09:24:11 +00:00
Action: func(cctx *cli.Context) error {
log.Info("Initializing lotus miner")
2019-07-19 09:24:11 +00:00
ssize, err := abi.RegisteredSealProof_StackedDrg32GiBV1.SectorSize()
if err != nil {
return xerrors.Errorf("failed to calculate default sector size: %w", err)
}
if cctx.IsSet("sector-size") {
sectorSizeInt, err := units.RAMInBytes(cctx.String("sector-size"))
if err != nil {
return err
}
ssize = abi.SectorSize(sectorSizeInt)
}
2019-12-04 19:44:15 +00:00
gasPrice, err := types.BigFromString(cctx.String("gas-premium"))
if err != nil {
return xerrors.Errorf("failed to parse gas-price flag: %s", err)
}
symlink := cctx.Bool("symlink-imported-sectors")
if symlink {
log.Info("will attempt to symlink to imported sectors")
}
ctx := lcli.ReqContext(cctx)
2019-10-02 17:20:30 +00:00
log.Info("Checking proof parameters")
2021-03-10 15:16:44 +00:00
if err := paramfetch.GetParams(ctx, build.ParametersJSON(), build.SrsJSON(), uint64(ssize)); err != nil {
2019-10-02 17:20:30 +00:00
return xerrors.Errorf("fetching proof parameters: %w", err)
}
2019-12-04 00:25:18 +00:00
log.Info("Trying to connect to full node RPC")
2021-04-15 22:19:26 +00:00
if err := checkV1ApiSupport(ctx, cctx); err != nil {
return err
}
2021-04-05 17:56:53 +00:00
api, closer, err := lcli.GetFullNodeAPIV1(cctx) // TODO: consider storing full node address in config
2019-12-04 00:25:18 +00:00
if err != nil {
return err
}
defer closer()
log.Info("Checking full node sync status")
2019-12-04 16:53:32 +00:00
if !cctx.Bool("genesis-miner") && !cctx.Bool("nosync") {
2021-04-05 17:56:53 +00:00
if err := lcli.SyncWait(ctx, &v0api.WrapperV1Full{FullNode: api}, false); err != nil {
2019-12-04 00:25:18 +00:00
return xerrors.Errorf("sync wait: %w", err)
}
}
2019-10-02 20:29:40 +00:00
log.Info("Checking if repo exists")
2020-07-08 10:38:59 +00:00
repoPath := cctx.String(FlagMinerRepo)
r, err := repo.NewFS(repoPath)
2019-07-19 09:24:11 +00:00
if err != nil {
return err
}
2019-07-23 21:54:54 +00:00
ok, err := r.Exists()
if err != nil {
return err
}
if ok {
2020-07-08 10:38:59 +00:00
return xerrors.Errorf("repo at '%s' is already initialized", cctx.String(FlagMinerRepo))
2019-07-19 09:24:11 +00:00
}
log.Info("Checking full node version")
v, err := api.Version(ctx)
if err != nil {
return err
}
2021-04-15 22:19:26 +00:00
if !v.APIVersion.EqMajorMinor(lapi.FullAPIVersion1) {
return xerrors.Errorf("Remote API version didn't match (expected %s, remote %s)", lapi.FullAPIVersion1, v.APIVersion)
}
log.Info("Initializing repo")
2019-11-12 17:59:38 +00:00
if err := r.Init(repo.StorageMiner); err != nil {
2019-07-19 09:24:11 +00:00
return err
}
{
lr, err := r.Lock(repo.StorageMiner)
if err != nil {
return err
}
var localPaths []paths.LocalPath
2020-02-27 21:45:31 +00:00
if pssb := cctx.StringSlice("pre-sealed-sectors"); len(pssb) != 0 {
log.Infof("Setting up storage config with presealed sectors: %v", pssb)
for _, psp := range pssb {
psp, err := homedir.Expand(psp)
if err != nil {
return err
}
localPaths = append(localPaths, paths.LocalPath{
Path: psp,
})
2020-03-03 22:19:22 +00:00
}
}
2019-11-30 23:17:50 +00:00
if !cctx.Bool("no-local-storage") {
b, err := json.MarshalIndent(&paths.LocalStorageMeta{
2022-01-18 10:57:04 +00:00
ID: storiface.ID(uuid.New().String()),
2020-03-05 19:21:06 +00:00
Weight: 10,
CanSeal: true,
CanStore: true,
}, "", " ")
if err != nil {
return xerrors.Errorf("marshaling storage config: %w", err)
}
2020-03-05 19:21:06 +00:00
if err := ioutil.WriteFile(filepath.Join(lr.Path(), "sectorstore.json"), b, 0644); err != nil {
2020-03-09 06:13:22 +00:00
return xerrors.Errorf("persisting storage metadata (%s): %w", filepath.Join(lr.Path(), "sectorstore.json"), err)
2020-03-05 19:21:06 +00:00
}
localPaths = append(localPaths, paths.LocalPath{
Path: lr.Path(),
})
}
if err := lr.SetStorage(func(sc *paths.StorageConfig) {
2020-03-09 19:22:30 +00:00
sc.StoragePaths = append(sc.StoragePaths, localPaths...)
}); err != nil {
2020-03-03 22:19:22 +00:00
return xerrors.Errorf("set storage config: %w", err)
}
2019-11-30 23:17:50 +00:00
if err := lr.Close(); err != nil {
return err
}
2019-11-25 04:45:13 +00:00
}
if err := storageMinerInit(ctx, cctx, api, r, ssize, gasPrice); err != nil {
2020-07-08 10:38:59 +00:00
log.Errorf("Failed to initialize lotus-miner: %+v", err)
2019-11-16 06:47:04 +00:00
path, err := homedir.Expand(repoPath)
if err != nil {
return err
}
2019-11-16 06:47:04 +00:00
log.Infof("Cleaning up %s after attempt...", path)
if err := os.RemoveAll(path); err != nil {
log.Errorf("Failed to clean up failed storage repo: %s", err)
}
return xerrors.Errorf("Storage-miner init failed")
2019-07-25 12:50:34 +00:00
}
// TODO: Point to setting storage price, maybe do it interactively or something
log.Info("Miner successfully created, you can now start it with 'lotus-miner run'")
2019-07-25 12:50:34 +00:00
return nil
},
}
2019-07-25 12:50:34 +00:00
2021-04-05 17:56:53 +00:00
func migratePreSealMeta(ctx context.Context, api v1api.FullNode, metadata string, maddr address.Address, mds dtypes.MetadataDS) error {
metadata, err := homedir.Expand(metadata)
2019-11-30 09:25:31 +00:00
if err != nil {
return xerrors.Errorf("expanding preseal dir: %w", err)
}
b, err := ioutil.ReadFile(metadata)
if err != nil {
return xerrors.Errorf("reading preseal metadata: %w", err)
}
psm := map[string]genesis.Miner{}
if err := json.Unmarshal(b, &psm); err != nil {
return xerrors.Errorf("unmarshaling preseal metadata: %w", err)
}
meta, ok := psm[maddr.String()]
if !ok {
return xerrors.Errorf("preseal file didn't contain metadata for miner %s", maddr)
}
maxSectorID := abi.SectorNumber(0)
for _, sector := range meta.Sectors {
sectorKey := datastore.NewKey(pipeline.SectorStorePrefix).ChildString(fmt.Sprint(sector.SectorID))
dealID, err := findMarketDealID(ctx, api, sector.Deal)
if err != nil {
return xerrors.Errorf("finding storage deal for pre-sealed sector %d: %w", sector.SectorID, err)
}
2020-02-27 21:45:31 +00:00
commD := sector.CommD
commR := sector.CommR
info := &pipeline.SectorInfo{
State: pipeline.Proving,
2020-04-06 22:31:33 +00:00
SectorNumber: sector.SectorID,
2022-08-26 00:37:36 +00:00
Pieces: []lapi.SectorPiece{
{
Piece: abi.PieceInfo{
Size: abi.PaddedPieceSize(meta.SectorSize),
PieceCID: commD,
},
DealInfo: &lapi.PieceDealInfo{
DealID: dealID,
DealProposal: &sector.Deal,
DealSchedule: lapi.DealSchedule{
StartEpoch: sector.Deal.StartEpoch,
EndEpoch: sector.Deal.EndEpoch,
},
},
},
},
2020-02-27 21:45:31 +00:00
CommD: &commD,
CommR: &commR,
Proof: nil,
TicketValue: abi.SealRandomness{},
TicketEpoch: 0,
PreCommitMessage: nil,
SeedValue: abi.InteractiveSealRandomness{},
SeedEpoch: 0,
CommitMessage: nil,
}
b, err := cborutil.Dump(info)
if err != nil {
return err
}
2021-12-17 09:42:09 +00:00
if err := mds.Put(ctx, sectorKey, b); err != nil {
return err
}
if sector.SectorID > maxSectorID {
maxSectorID = sector.SectorID
}
2020-02-21 18:20:22 +00:00
/* // TODO: Import deals into market
2020-02-10 18:21:10 +00:00
pnd, err := cborutil.AsIpld(sector.Deal)
if err != nil {
return err
}
2020-02-10 18:21:10 +00:00
dealKey := datastore.NewKey(deals.ProviderDsPrefix).ChildString(pnd.Cid().String())
deal := &deals.MinerDeal{
MinerDeal: storagemarket.MinerDeal{
2020-02-21 18:20:22 +00:00
ClientDealProposal: sector.Deal,
2020-02-10 18:21:10 +00:00
ProposalCid: pnd.Cid(),
State: storagemarket.StorageDealActive,
Ref: &storagemarket.DataRef{Root: proposalCid}, // TODO: This is super wrong, but there
// are no params for CommP CIDs, we can't recover unixfs cid easily,
// and this isn't even used after the deal enters Complete state
DealID: dealID,
},
}
b, err = cborutil.Dump(deal)
if err != nil {
return err
}
if err := mds.Put(dealKey, b); err != nil {
return err
2020-02-21 18:20:22 +00:00
}*/
}
buf := make([]byte, binary.MaxVarintLen64)
size := binary.PutUvarint(buf, uint64(maxSectorID))
return mds.Put(ctx, datastore.NewKey(pipeline.StorageCounterDSPrefix), buf[:size])
}
2022-09-26 16:47:48 +00:00
func findMarketDealID(ctx context.Context, api v1api.FullNode, deal markettypes.DealProposal) (abi.DealID, error) {
// TODO: find a better way
// (this is only used by genesis miners)
deals, err := api.StateMarketDeals(ctx, types.EmptyTSK)
if err != nil {
return 0, xerrors.Errorf("getting market deals: %w", err)
}
for k, v := range deals {
2020-02-10 18:21:10 +00:00
if v.Proposal.PieceCID.Equals(deal.PieceCID) {
id, err := strconv.ParseUint(k, 10, 64)
return abi.DealID(id), err
}
}
return 0, xerrors.New("deal not found")
}
2021-04-05 17:56:53 +00:00
func storageMinerInit(ctx context.Context, cctx *cli.Context, api v1api.FullNode, r repo.Repo, ssize abi.SectorSize, gasPrice types.BigInt) error {
2019-11-16 06:47:04 +00:00
lr, err := r.Lock(repo.StorageMiner)
if err != nil {
return err
}
defer lr.Close() //nolint:errcheck
2019-11-16 06:47:04 +00:00
log.Info("Initializing libp2p identity")
p2pSk, err := makeHostKey(lr)
if err != nil {
2019-11-15 03:19:16 +00:00
return xerrors.Errorf("make host key: %w", err)
}
peerid, err := peer.IDFromPrivateKey(p2pSk)
if err != nil {
2019-11-15 03:19:16 +00:00
return xerrors.Errorf("peer ID from private key: %w", err)
}
2019-07-25 12:50:34 +00:00
2021-12-17 09:42:09 +00:00
mds, err := lr.Datastore(ctx, "/metadata")
if err != nil {
return err
}
var addr address.Address
if act := cctx.String("actor"); act != "" {
a, err := address.NewFromString(act)
2019-07-25 12:50:34 +00:00
if err != nil {
2019-11-15 03:19:16 +00:00
return xerrors.Errorf("failed parsing actor flag value (%q): %w", act, err)
2019-07-25 12:50:34 +00:00
}
2019-11-25 04:45:13 +00:00
if cctx.Bool("genesis-miner") {
2021-12-17 09:42:09 +00:00
if err := mds.Put(ctx, datastore.NewKey("miner-address"), a.Bytes()); err != nil {
2019-11-25 04:45:13 +00:00
return err
}
2020-03-17 20:19:52 +00:00
mid, err := address.IDFromAddress(a)
if err != nil {
return xerrors.Errorf("getting id address: %w", err)
}
sa, err := modules.StorageAuth(ctx, api)
if err != nil {
return err
}
2020-09-14 07:44:55 +00:00
wsts := statestore.New(namespace.Wrap(mds, modules.WorkerCallsPrefix))
smsts := statestore.New(namespace.Wrap(mds, modules.ManagerWorkPrefix))
2020-09-14 07:44:55 +00:00
si := paths.NewIndex(nil)
lstor, err := paths.NewLocal(ctx, lr, si, nil)
if err != nil {
return err
}
stor := paths.NewRemote(lstor, si, http.Header(sa), 10, &paths.DefaultPartialFileHandler{})
smgr, err := sealer.New(ctx, lstor, stor, lr, si, sealer.Config{
ParallelFetchLimit: 10,
AllowAddPiece: true,
AllowPreCommit1: true,
AllowPreCommit2: true,
AllowCommit: true,
AllowUnseal: true,
AllowReplicaUpdate: true,
AllowProveReplicaUpdate2: true,
AllowRegenSectorKey: true,
2021-07-06 15:46:21 +00:00
}, wsts, smsts)
if err != nil {
return err
}
2020-04-17 14:47:19 +00:00
epp, err := storage.NewWinningPoStProver(api, smgr, ffiwrapper.ProofVerifier, dtypes.MinerID(mid))
if err != nil {
return err
}
2019-11-25 04:45:13 +00:00
2021-08-17 12:55:35 +00:00
j, err := fsjournal.OpenFSJournal(lr, journal.EnvDisabledEvents())
if err != nil {
2020-08-11 12:48:32 +00:00
return fmt.Errorf("failed to open filesystem journal: %w", err)
}
m := storageminer.NewMiner(api, epp, a, slashfilter.New(mds), j)
2019-11-25 04:45:13 +00:00
{
if err := m.Start(ctx); err != nil {
2019-11-25 04:45:13 +00:00
return xerrors.Errorf("failed to start up genesis miner: %w", err)
}
cerr := configureStorageMiner(ctx, api, a, peerid, gasPrice)
2019-11-25 04:45:13 +00:00
if err := m.Stop(ctx); err != nil {
log.Error("failed to shut down miner: ", err)
}
if cerr != nil {
return xerrors.Errorf("failed to configure miner: %w", cerr)
2019-11-25 04:45:13 +00:00
}
}
if pssb := cctx.String("pre-sealed-metadata"); pssb != "" {
pssb, err := homedir.Expand(pssb)
if err != nil {
return err
}
log.Infof("Importing pre-sealed sector metadata for %s", a)
if err := migratePreSealMeta(ctx, api, pssb, a, mds); err != nil {
return xerrors.Errorf("migrating presealed sector metadata: %w", err)
}
}
return nil
}
if pssb := cctx.String("pre-sealed-metadata"); pssb != "" {
pssb, err := homedir.Expand(pssb)
if err != nil {
return err
}
log.Infof("Importing pre-sealed sector metadata for %s", a)
if err := migratePreSealMeta(ctx, api, pssb, a, mds); err != nil {
return xerrors.Errorf("migrating presealed sector metadata: %w", err)
}
2019-12-02 12:51:16 +00:00
}
if err := configureStorageMiner(ctx, api, a, peerid, gasPrice); err != nil {
return xerrors.Errorf("failed to configure miner: %w", err)
}
addr = a
} else {
a, err := createStorageMiner(ctx, api, ssize, peerid, gasPrice, cctx)
if err != nil {
2020-03-06 08:06:59 +00:00
return xerrors.Errorf("creating miner failed: %w", err)
2019-07-25 12:50:34 +00:00
}
addr = a
}
2019-07-25 12:50:34 +00:00
log.Infof("Created new miner: %s", addr)
2021-12-17 09:42:09 +00:00
if err := mds.Put(ctx, datastore.NewKey("miner-address"), addr.Bytes()); err != nil {
return err
}
return nil
}
2019-07-25 12:50:34 +00:00
2019-10-03 00:02:06 +00:00
func makeHostKey(lr repo.LockedRepo) (crypto.PrivKey, error) {
pk, _, err := crypto.GenerateEd25519Key(rand.Reader)
if err != nil {
return nil, err
}
ks, err := lr.KeyStore()
if err != nil {
return nil, err
}
2021-09-21 11:23:01 +00:00
kbytes, err := crypto.MarshalPrivateKey(pk)
2019-10-03 00:02:06 +00:00
if err != nil {
return nil, err
}
if err := ks.Put("libp2p-host", types.KeyInfo{
Type: "libp2p-host",
PrivateKey: kbytes,
}); err != nil {
return nil, err
}
return pk, nil
}
2021-04-05 17:56:53 +00:00
func configureStorageMiner(ctx context.Context, api v1api.FullNode, addr address.Address, peerid peer.ID, gasPrice types.BigInt) error {
mi, err := api.StateMinerInfo(ctx, addr, types.EmptyTSK)
if err != nil {
return xerrors.Errorf("getWorkerAddr returned bad address: %w", err)
}
2020-10-08 01:09:33 +00:00
enc, err := actors.SerializeParams(&miner2.ChangePeerIDParams{NewID: abi.PeerID(peerid)})
if err != nil {
return err
}
msg := &types.Message{
To: addr,
From: mi.Worker,
2022-04-20 21:34:28 +00:00
Method: builtin.MethodsMiner.ChangePeerID,
Params: enc,
Value: types.NewInt(0),
GasPremium: gasPrice,
}
smsg, err := api.MpoolPushMessage(ctx, msg, nil)
if err != nil {
return err
}
2019-08-16 22:10:34 +00:00
log.Info("Waiting for message: ", smsg.Cid())
2021-04-05 17:56:53 +00:00
ret, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence, lapi.LookbackNoLimit, true)
if err != nil {
return err
}
if ret.Receipt.ExitCode != 0 {
return xerrors.Errorf("update peer id message failed with exit code %d", ret.Receipt.ExitCode)
}
return nil
}
2019-07-25 12:50:34 +00:00
func createStorageMiner(ctx context.Context, api v1api.FullNode, ssize abi.SectorSize, peerid peer.ID, gasPrice types.BigInt, cctx *cli.Context) (address.Address, error) {
2020-03-06 22:23:21 +00:00
var err error
var owner address.Address
if cctx.String("owner") != "" {
owner, err = address.NewFromString(cctx.String("owner"))
} else {
owner, err = api.WalletDefaultAddress(ctx)
}
if err != nil {
return address.Undef, err
}
2019-07-25 12:50:34 +00:00
worker := owner
if cctx.String("worker") != "" {
worker, err = address.NewFromString(cctx.String("worker"))
} else if cctx.Bool("create-worker-key") { // TODO: Do we need to force this if owner is Secpk?
worker, err = api.WalletNew(ctx, types.KTBLS)
}
if err != nil {
2020-10-19 14:08:33 +00:00
return address.Address{}, err
}
sender := owner
if fromstr := cctx.String("from"); fromstr != "" {
faddr, err := address.NewFromString(fromstr)
if err != nil {
return address.Undef, fmt.Errorf("could not parse from address: %w", err)
}
sender = faddr
}
// make sure the sender account exists on chain
_, err = api.StateLookupID(ctx, owner, types.EmptyTSK)
if err != nil {
return address.Undef, xerrors.Errorf("sender must exist on chain: %w", err)
}
2020-10-19 14:08:33 +00:00
// make sure the worker account exists on chain
_, err = api.StateLookupID(ctx, worker, types.EmptyTSK)
if err != nil {
signed, err := api.MpoolPushMessage(ctx, &types.Message{
From: sender,
2020-10-19 14:08:33 +00:00
To: worker,
Value: types.NewInt(0),
}, nil)
if err != nil {
return address.Undef, xerrors.Errorf("push worker init: %w", err)
}
log.Infof("Initializing worker account %s, message: %s", worker, signed.Cid())
log.Infof("Waiting for confirmation")
2021-04-05 17:56:53 +00:00
mw, err := api.StateWaitMsg(ctx, signed.Cid(), build.MessageConfidence, lapi.LookbackNoLimit, true)
2020-10-19 14:08:33 +00:00
if err != nil {
return address.Undef, xerrors.Errorf("waiting for worker init: %w", err)
}
if mw.Receipt.ExitCode != 0 {
return address.Undef, xerrors.Errorf("initializing worker account failed: exit code %d", mw.Receipt.ExitCode)
}
}
2019-07-25 12:50:34 +00:00
// make sure the owner account exists on chain
_, err = api.StateLookupID(ctx, owner, types.EmptyTSK)
2020-04-29 18:06:05 +00:00
if err != nil {
signed, err := api.MpoolPushMessage(ctx, &types.Message{
From: sender,
To: owner,
Value: types.NewInt(0),
}, nil)
if err != nil {
return address.Undef, xerrors.Errorf("push owner init: %w", err)
}
log.Infof("Initializing owner account %s, message: %s", worker, signed.Cid())
log.Infof("Waiting for confirmation")
mw, err := api.StateWaitMsg(ctx, signed.Cid(), build.MessageConfidence, lapi.LookbackNoLimit, true)
if err != nil {
return address.Undef, xerrors.Errorf("waiting for owner init: %w", err)
}
if mw.Receipt.ExitCode != 0 {
return address.Undef, xerrors.Errorf("initializing owner account failed: exit code %d", mw.Receipt.ExitCode)
}
2020-11-05 11:51:46 +00:00
}
// Note: the correct thing to do would be to call SealProofTypeFromSectorSize if actors version is v3 or later, but this still works
spt, err := miner.WindowPoStProofTypeFromSectorSize(ssize)
2020-11-05 11:51:46 +00:00
if err != nil {
return address.Undef, xerrors.Errorf("getting post proof type: %w", err)
2020-04-29 18:06:05 +00:00
}
params, err := actors.SerializeParams(&power6.CreateMinerParams{
Owner: owner,
Worker: worker,
WindowPoStProofType: spt,
Peer: abi.PeerID(peerid),
})
if err != nil {
return address.Undef, err
}
2019-07-25 12:50:34 +00:00
2019-09-17 08:15:26 +00:00
createStorageMinerMsg := &types.Message{
2020-10-08 01:09:33 +00:00
To: power.Address,
2020-08-20 00:34:58 +00:00
From: sender,
Value: big.Zero(),
2019-07-25 12:50:34 +00:00
Method: power.Methods.CreateMiner,
Params: params,
GasLimit: 0,
GasPremium: gasPrice,
}
2019-07-25 12:50:34 +00:00
signed, err := api.MpoolPushMessage(ctx, createStorageMinerMsg, nil)
if err != nil {
2020-10-19 14:08:33 +00:00
return address.Undef, xerrors.Errorf("pushing createMiner message: %w", err)
}
2020-10-19 14:08:33 +00:00
log.Infof("Pushed CreateMiner message: %s", signed.Cid())
log.Infof("Waiting for confirmation")
2021-04-05 17:56:53 +00:00
mw, err := api.StateWaitMsg(ctx, signed.Cid(), build.MessageConfidence, lapi.LookbackNoLimit, true)
if err != nil {
2020-10-19 14:08:33 +00:00
return address.Undef, xerrors.Errorf("waiting for createMiner message: %w", err)
}
if mw.Receipt.ExitCode != 0 {
return address.Undef, xerrors.Errorf("create miner failed: exit code %d", mw.Receipt.ExitCode)
}
2020-10-08 01:09:33 +00:00
var retval power2.CreateMinerReturn
2020-03-06 08:06:59 +00:00
if err := retval.UnmarshalCBOR(bytes.NewReader(mw.Receipt.Return)); err != nil {
return address.Undef, err
}
log.Infof("New miners address is: %s (%s)", retval.IDAddress, retval.RobustAddress)
2020-03-06 22:23:21 +00:00
return retval.IDAddress, nil
2019-07-19 09:24:11 +00:00
}
2021-04-15 22:19:26 +00:00
2021-06-15 12:47:11 +00:00
// checkV1ApiSupport uses v0 api version to signal support for v1 API
// trying to query the v1 api on older lotus versions would get a 404, which can happen for any number of other reasons
2021-04-15 22:19:26 +00:00
func checkV1ApiSupport(ctx context.Context, cctx *cli.Context) error {
// check v0 api version to make sure it supports v1 api
api0, closer, err := lcli.GetFullNodeAPI(cctx)
if err != nil {
return err
}
v, err := api0.Version(ctx)
closer()
if err != nil {
return err
}
if !v.APIVersion.EqMajorMinor(lapi.FullAPIVersion0) {
return xerrors.Errorf("Remote API version didn't match (expected %s, remote %s)", lapi.FullAPIVersion0, v.APIVersion)
}
return nil
}