diff --git a/cmd/lotus-miner/sectors.go b/cmd/lotus-miner/sectors.go index f796b3a6218..b1951cd9bcd 100644 --- a/cmd/lotus-miner/sectors.go +++ b/cmd/lotus-miner/sectors.go @@ -59,6 +59,7 @@ var sectorsCmd = &cli.Command{ sectorsCapacityCollateralCmd, sectorsBatching, sectorsRefreshPieceMatchingCmd, + sectorsCompactPartitionsCmd, }, } @@ -2089,3 +2090,106 @@ func yesno(b bool) string { } return color.RedString("NO") } + +// TODO simulate this call if --really-do-it is not used +var sectorsCompactPartitionsCmd = &cli.Command{ + Name: "compact-partitions", + Usage: "removes dead sectors from partitions and reduces the number of partitions used if possible", + Flags: []cli.Flag{ + &cli.Uint64Flag{ + Name: "deadline", + Usage: "the deadline to compact the partitions in", + Required: true, + }, + &cli.Int64SliceFlag{ + Name: "partitions", + Usage: "list of partitions to compact sectors in", + Required: true, + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + &cli.StringFlag{ + Name: "actor", + Usage: "Specify the address of the miner to run this command", + }, + }, + Action: func(cctx *cli.Context) error { + if !cctx.Bool("really-do-it") { + fmt.Println("Pass --really-do-it to actually execute this action") + return nil + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := getActorAddress(ctx, cctx) + if err != nil { + return err + } + + minfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + deadline := cctx.Uint64("deadline") + if deadline > miner.WPoStPeriodDeadlines { + return fmt.Errorf("deadline %d out of range", deadline) + } + + parts := cctx.Int64Slice("partitions") + if len(parts) <= 0 { + return fmt.Errorf("must include at least one partition to compact") + } + fmt.Printf("compacting %d paritions\n", len(parts)) + + partitions := bitfield.New() + for _, partition := range parts { + partitions.Set(uint64(partition)) + } + + params := miner.CompactPartitionsParams{ + Deadline: deadline, + Partitions: partitions, + } + + sp, err := actors.SerializeParams(¶ms) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: minfo.Worker, + To: maddr, + Method: builtin.MethodsMiner.CompactPartitions, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Printf("Requested compact partitions in message %s\n", smsg.Cid()) + + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), 0) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Println(cctx.App.Writer, "compact partitions failed!") + return err + } + + return nil + }, +} diff --git a/cmd/lotus-shed/miner.go b/cmd/lotus-shed/miner.go index 479e081e957..fec00dbadfa 100644 --- a/cmd/lotus-shed/miner.go +++ b/cmd/lotus-shed/miner.go @@ -9,15 +9,20 @@ import ( "path/filepath" "strings" - miner2 "github.com/filecoin-project/specs-actors/actors/builtin/miner" + "github.com/ipfs/go-cid" - power6 "github.com/filecoin-project/specs-actors/v6/actors/builtin/power" - - "github.com/docker/go-units" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + miner8 "github.com/filecoin-project/go-state-types/builtin/v8/miner" + "github.com/filecoin-project/go-state-types/crypto" + power7 "github.com/filecoin-project/specs-actors/v7/actors/builtin/power" + "github.com/filecoin-project/specs-actors/v7/actors/runtime/proof" + + "github.com/docker/go-units" - "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" @@ -37,6 +42,8 @@ var minerCmd = &cli.Command{ minerUnpackInfoCmd, minerCreateCmd, minerFaultsCmd, + sendInvalidWindowPoStCmd, + generateAndSendConsensusFaultCmd, }, } @@ -75,7 +82,7 @@ var minerFaultsCmd = &cli.Command{ return err } - faults, err := faultBf.All(miner2.SectorsMax) + faults, err := faultBf.All(abi.MaxSectorNumber) if err != nil { return err } @@ -216,7 +223,7 @@ var minerCreateCmd = &cli.Command{ return xerrors.Errorf("getting post proof type: %w", err) } - params, err := actors.SerializeParams(&power6.CreateMinerParams{ + params, err := actors.SerializeParams(&power7.CreateMinerParams{ Owner: owner, Worker: worker, WindowPoStProofType: spt, @@ -252,7 +259,7 @@ var minerCreateCmd = &cli.Command{ return xerrors.Errorf("create miner failed: exit code %d", mw.Receipt.ExitCode) } - var retval power6.CreateMinerReturn + var retval power7.CreateMinerReturn if err := retval.UnmarshalCBOR(bytes.NewReader(mw.Receipt.Return)); err != nil { return err } @@ -354,3 +361,226 @@ var minerUnpackInfoCmd = &cli.Command{ } }, } + +var sendInvalidWindowPoStCmd = &cli.Command{ + Name: "send-invalid-windowed-post", + Usage: "Sends an invalid windowed post for a specific deadline", + Description: `Note: This is meant for testing purposes and should NOT be used on mainnet or you will be slashed`, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + &cli.Int64SliceFlag{ + Name: "partitions", + Usage: "list of partitions to submit invalid post for", + Required: true, + }, + &cli.StringFlag{ + Name: "actor", + Usage: "Specify the address of the miner to run this command", + }, + }, + Action: func(cctx *cli.Context) error { + if !cctx.Bool("really-do-it") { + return xerrors.Errorf("Pass --really-do-it to actually execute this action") + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return xerrors.Errorf("getting api: %w", err) + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := address.NewFromString(cctx.String("actor")) + if err != nil { + return xerrors.Errorf("getting actor address: %w", err) + } + + minfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting mienr info: %w", err) + } + + deadline, err := api.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting deadline: %w", err) + } + + partitionIndices := cctx.Int64Slice("partitions") + if len(partitionIndices) <= 0 { + return fmt.Errorf("must include at least one partition to compact") + } + + chainHead, err := api.ChainHead(ctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + + checkRand, err := api.StateGetRandomnessFromTickets(ctx, crypto.DomainSeparationTag_PoStChainCommit, deadline.Challenge, nil, chainHead.Key()) + if err != nil { + return xerrors.Errorf("getting randomness: %w", err) + } + + proofSize, err := minfo.WindowPoStProofType.ProofSize() + if err != nil { + return xerrors.Errorf("getting proof size: %w", err) + } + + var partitions []miner8.PoStPartition + + emptyProof := []proof.PoStProof{{ + PoStProof: minfo.WindowPoStProofType, + ProofBytes: make([]byte, proofSize)}} + + for _, partition := range partitionIndices { + newPartition := miner8.PoStPartition{ + Index: uint64(partition), + Skipped: bitfield.New(), + } + partitions = append(partitions, newPartition) + } + + params := miner8.SubmitWindowedPoStParams{ + Deadline: deadline.Index, + Partitions: partitions, + Proofs: emptyProof, + ChainCommitEpoch: deadline.Challenge, + ChainCommitRand: checkRand, + } + + sp, err := actors.SerializeParams(¶ms) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + fmt.Printf("submitting bad PoST for %d paritions\n", len(partitionIndices)) + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: minfo.Worker, + To: maddr, + Method: builtin.MethodsMiner.SubmitWindowedPoSt, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Printf("Invalid PoST in message %s\n", smsg.Cid()) + + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), 0) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Println(cctx.App.Writer, "Invalid PoST message failed!") + return err + } + + return nil + }, +} + +var generateAndSendConsensusFaultCmd = &cli.Command{ + Name: "generate-and-send-consensus-fault", + Usage: "Provided a block CID mined by the miner, will create another block at the same height, and send both block headers to generate a consensus fault.", + Description: `Note: This is meant for testing purposes and should NOT be used on mainnet or you will be slashed`, + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 1 { + return xerrors.Errorf("expected 1 arg (blockCID)") + } + + blockCid, err := cid.Parse(cctx.Args().First()) + if err != nil { + return xerrors.Errorf("getting first arg: %w", err) + } + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return xerrors.Errorf("getting chain head: %w", err) + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + blockHeader, err := api.ChainGetBlock(ctx, blockCid) + if err != nil { + return xerrors.Errorf("getting block header: %w", err) + } + + maddr := blockHeader.Miner + + minfo, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + // We are changing one field in the block header, then resigning the new block. + // This gives two different blocks signed by the same miner at the same height which will result in a consensus fault. + blockHeaderCopy := *blockHeader + blockHeaderCopy.ForkSignaling = blockHeader.ForkSignaling + 1 + + signingBytes, err := blockHeaderCopy.SigningBytes() + if err != nil { + return xerrors.Errorf("getting bytes to sign second block: %w", err) + } + + sig, err := api.WalletSign(ctx, minfo.Worker, signingBytes) + if err != nil { + return xerrors.Errorf("signing second block: %w", err) + } + blockHeaderCopy.BlockSig = sig + + buf1 := new(bytes.Buffer) + err = blockHeader.MarshalCBOR(buf1) + if err != nil { + return xerrors.Errorf("marshalling block header 1: %w", err) + } + buf2 := new(bytes.Buffer) + err = blockHeaderCopy.MarshalCBOR(buf2) + if err != nil { + return xerrors.Errorf("marshalling block header 2: %w", err) + } + + params := miner8.ReportConsensusFaultParams{ + BlockHeader1: buf1.Bytes(), + BlockHeader2: buf2.Bytes(), + } + + sp, err := actors.SerializeParams(¶ms) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: minfo.Worker, + To: maddr, + Method: builtin.MethodsMiner.ReportConsensusFault, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Printf("Consensus fault reported in message %s\n", smsg.Cid()) + + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), 0) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Println(cctx.App.Writer, "Report consensus fault failed!") + return err + } + + return nil + }, +} diff --git a/cmd/lotus-shed/verifreg.go b/cmd/lotus-shed/verifreg.go index c77ac2cbe0e..ebd6df4071e 100644 --- a/cmd/lotus-shed/verifreg.go +++ b/cmd/lotus-shed/verifreg.go @@ -195,7 +195,7 @@ var verifRegVerifyClientCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { - fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") + fmt.Println("DEPRECATED: This behavior is being moved to `lotus filplus`") froms := cctx.String("from") if froms == "" { return fmt.Errorf("must specify from address with --from") @@ -264,7 +264,7 @@ var verifRegListVerifiersCmd = &cli.Command{ Usage: "list all verifiers", Hidden: true, Action: func(cctx *cli.Context) error { - fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") + fmt.Println("DEPRECATED: This behavior is being moved to `lotus filplus`") api, closer, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -296,7 +296,7 @@ var verifRegListClientsCmd = &cli.Command{ Usage: "list all verified clients", Hidden: true, Action: func(cctx *cli.Context) error { - fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") + fmt.Println("DEPRECATED: This behavior is being moved to `lotus filplus`") api, closer, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -328,7 +328,7 @@ var verifRegCheckClientCmd = &cli.Command{ Usage: "check verified client remaining bytes", Hidden: true, Action: func(cctx *cli.Context) error { - fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") + fmt.Println("DEPRECATED: This behavior is being moved to `lotus filplus`") if !cctx.Args().Present() { return fmt.Errorf("must specify client address to check") } @@ -364,7 +364,7 @@ var verifRegCheckVerifierCmd = &cli.Command{ Usage: "check verifiers remaining bytes", Hidden: true, Action: func(cctx *cli.Context) error { - fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") + fmt.Println("DEPRECATED: This behavior is being moved to `lotus filplus`") if !cctx.Args().Present() { return fmt.Errorf("must specify verifier address to check") } diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index f2135c08799..552bf902c27 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -1685,6 +1685,7 @@ COMMANDS: get-cc-collateral Get the collateral required to pledge a committed capacity sector batching manage batch sector operations match-pending-pieces force a refreshed match of pending pieces to open sectors without manually waiting for more deals + compact-partitions removes dead sectors from partitions and reduces the number of partitions used if possible help, h Shows a list of commands or help for one command OPTIONS: @@ -2026,6 +2027,23 @@ OPTIONS: ``` +### lotus-miner sectors compact-partitions +``` +NAME: + lotus-miner sectors compact-partitions - removes dead sectors from partitions and reduces the number of partitions used if possible + +USAGE: + lotus-miner sectors compact-partitions [command options] [arguments...] + +OPTIONS: + --deadline value the deadline to compact the partitions in (default: 0) + --partitions value list of partitions to compact sectors in + --really-do-it Actually send transaction performing the action (default: false) + --actor value Specify the address of the miner to run this command + --help, -h show help (default: false) + +``` + ## lotus-miner proving ``` NAME: