diff --git a/chain/actors/builtin/verifreg/actor.go.template b/chain/actors/builtin/verifreg/actor.go.template index 9ea8e155a..adc156948 100644 --- a/chain/actors/builtin/verifreg/actor.go.template +++ b/chain/actors/builtin/verifreg/actor.go.template @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/types" + verifreg7 "github.com/filecoin-project/specs-actors/v7/actors/builtin/verifreg" ) func init() { @@ -62,6 +63,11 @@ func GetActorCodeID(av actors.Version) (cid.Cid, error) { return cid.Undef, xerrors.Errorf("unknown actor version %d", av) } +type RemoveDataCapProposal = verifreg{{.latestVersion}}.RemoveDataCapProposal +type RemoveDataCapRequest = verifreg{{.latestVersion}}.RemoveDataCapRequest +type RemoveDataCapParams = verifreg{{.latestVersion}}.RemoveDataCapParams +type RmDcProposalID = verifreg{{.latestVersion}}.RmDcProposalID +const SignatureDomainSeparation_RemoveDataCap = verifreg{{.latestVersion}}.SignatureDomainSeparation_RemoveDataCap type State interface { cbor.Marshaler @@ -69,6 +75,7 @@ type State interface { RootKey() (address.Address, error) VerifiedClientDataCap(address.Address) (bool, abi.StoragePower, error) VerifierDataCap(address.Address) (bool, abi.StoragePower, error) + RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) ForEachVerifier(func(addr address.Address, dcap abi.StoragePower) error) error ForEachClient(func(addr address.Address, dcap abi.StoragePower) error) error GetState() interface{} diff --git a/chain/actors/builtin/verifreg/state.go.template b/chain/actors/builtin/verifreg/state.go.template index b59cfb628..4dfc11469 100644 --- a/chain/actors/builtin/verifreg/state.go.template +++ b/chain/actors/builtin/verifreg/state.go.template @@ -61,6 +61,10 @@ func (s *state{{.v}}) VerifierDataCap(addr address.Address) (bool, abi.StoragePo return getDataCap(s.store, actors.Version{{.v}}, s.verifiers, addr) } +func (s *state{{.v}}) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version{{.v}}, s.removeDataCapProposalIDs, verifier, client) +} + func (s *state{{.v}}) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { return forEachCap(s.store, actors.Version{{.v}}, s.verifiers, cb) } @@ -77,6 +81,11 @@ func (s *state{{.v}}) verifiers() (adt.Map, error) { return adt{{.v}}.AsMap(s.store, s.Verifiers{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) } +func (s *state{{.v}}) removeDataCapProposalIDs() (adt.Map, error) { + {{if le .v 6}}return nil, nil + {{else}}return adt{{.v}}.AsMap(s.store, s.RemoveDataCapProposalIDs, builtin{{.v}}.DefaultHamtBitwidth){{end}} +} + func (s *state{{.v}}) GetState() interface{} { return &s.State } \ No newline at end of file diff --git a/chain/actors/builtin/verifreg/util.go b/chain/actors/builtin/verifreg/util.go index 16e50c50a..197a79215 100644 --- a/chain/actors/builtin/verifreg/util.go +++ b/chain/actors/builtin/verifreg/util.go @@ -6,6 +6,7 @@ import ( "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/specs-actors/v7/actors/builtin/verifreg" "golang.org/x/xerrors" ) @@ -50,3 +51,28 @@ func forEachCap(store adt.Store, ver actors.Version, root rootFunc, cb func(addr return cb(a, dcap) }) } + +func getRemoveDataCapProposalID(store adt.Store, ver actors.Version, root rootFunc, verifier address.Address, client address.Address) (bool, uint64, error) { + if verifier.Protocol() != address.ID { + return false, 0, xerrors.Errorf("can only look up ID addresses") + } + if client.Protocol() != address.ID { + return false, 0, xerrors.Errorf("can only look up ID addresses") + } + vh, err := root() + if err != nil { + return false, 0, xerrors.Errorf("loading verifreg: %w", err) + } + if vh == nil { + return false, 0, xerrors.Errorf("remove data cap proposal hamt not found. you are probably using an incompatible version of actors") + } + + var id verifreg.RmDcProposalID + if found, err := vh.Get(abi.NewAddrPairKey(verifier, client), &id); err != nil { + return false, 0, xerrors.Errorf("looking up addr pair: %w", err) + } else if !found { + return false, 0, nil + } + + return true, id.ProposalID, nil +} diff --git a/chain/actors/builtin/verifreg/v0.go b/chain/actors/builtin/verifreg/v0.go index e70b0e3c9..dcd34c72a 100644 --- a/chain/actors/builtin/verifreg/v0.go +++ b/chain/actors/builtin/verifreg/v0.go @@ -53,6 +53,10 @@ func (s *state0) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, return getDataCap(s.store, actors.Version0, s.verifiers, addr) } +func (s *state0) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version0, s.removeDataCapProposalIDs, verifier, client) +} + func (s *state0) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { return forEachCap(s.store, actors.Version0, s.verifiers, cb) } @@ -69,6 +73,11 @@ func (s *state0) verifiers() (adt.Map, error) { return adt0.AsMap(s.store, s.Verifiers) } +func (s *state0) removeDataCapProposalIDs() (adt.Map, error) { + return nil, nil + +} + func (s *state0) GetState() interface{} { return &s.State } diff --git a/chain/actors/builtin/verifreg/v2.go b/chain/actors/builtin/verifreg/v2.go index 0bcbe0212..dfe25f054 100644 --- a/chain/actors/builtin/verifreg/v2.go +++ b/chain/actors/builtin/verifreg/v2.go @@ -53,6 +53,10 @@ func (s *state2) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, return getDataCap(s.store, actors.Version2, s.verifiers, addr) } +func (s *state2) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version2, s.removeDataCapProposalIDs, verifier, client) +} + func (s *state2) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { return forEachCap(s.store, actors.Version2, s.verifiers, cb) } @@ -69,6 +73,11 @@ func (s *state2) verifiers() (adt.Map, error) { return adt2.AsMap(s.store, s.Verifiers) } +func (s *state2) removeDataCapProposalIDs() (adt.Map, error) { + return nil, nil + +} + func (s *state2) GetState() interface{} { return &s.State } diff --git a/chain/actors/builtin/verifreg/v3.go b/chain/actors/builtin/verifreg/v3.go index 32003ca3a..c71c69f92 100644 --- a/chain/actors/builtin/verifreg/v3.go +++ b/chain/actors/builtin/verifreg/v3.go @@ -54,6 +54,10 @@ func (s *state3) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, return getDataCap(s.store, actors.Version3, s.verifiers, addr) } +func (s *state3) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version3, s.removeDataCapProposalIDs, verifier, client) +} + func (s *state3) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { return forEachCap(s.store, actors.Version3, s.verifiers, cb) } @@ -70,6 +74,11 @@ func (s *state3) verifiers() (adt.Map, error) { return adt3.AsMap(s.store, s.Verifiers, builtin3.DefaultHamtBitwidth) } +func (s *state3) removeDataCapProposalIDs() (adt.Map, error) { + return nil, nil + +} + func (s *state3) GetState() interface{} { return &s.State } diff --git a/chain/actors/builtin/verifreg/v4.go b/chain/actors/builtin/verifreg/v4.go index b752e747b..d3adc5169 100644 --- a/chain/actors/builtin/verifreg/v4.go +++ b/chain/actors/builtin/verifreg/v4.go @@ -54,6 +54,10 @@ func (s *state4) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, return getDataCap(s.store, actors.Version4, s.verifiers, addr) } +func (s *state4) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version4, s.removeDataCapProposalIDs, verifier, client) +} + func (s *state4) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { return forEachCap(s.store, actors.Version4, s.verifiers, cb) } @@ -70,6 +74,11 @@ func (s *state4) verifiers() (adt.Map, error) { return adt4.AsMap(s.store, s.Verifiers, builtin4.DefaultHamtBitwidth) } +func (s *state4) removeDataCapProposalIDs() (adt.Map, error) { + return nil, nil + +} + func (s *state4) GetState() interface{} { return &s.State } diff --git a/chain/actors/builtin/verifreg/v5.go b/chain/actors/builtin/verifreg/v5.go index 6fefd7115..2af501af3 100644 --- a/chain/actors/builtin/verifreg/v5.go +++ b/chain/actors/builtin/verifreg/v5.go @@ -54,6 +54,10 @@ func (s *state5) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, return getDataCap(s.store, actors.Version5, s.verifiers, addr) } +func (s *state5) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version5, s.removeDataCapProposalIDs, verifier, client) +} + func (s *state5) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { return forEachCap(s.store, actors.Version5, s.verifiers, cb) } @@ -70,6 +74,11 @@ func (s *state5) verifiers() (adt.Map, error) { return adt5.AsMap(s.store, s.Verifiers, builtin5.DefaultHamtBitwidth) } +func (s *state5) removeDataCapProposalIDs() (adt.Map, error) { + return nil, nil + +} + func (s *state5) GetState() interface{} { return &s.State } diff --git a/chain/actors/builtin/verifreg/v6.go b/chain/actors/builtin/verifreg/v6.go index b2c5078e7..454c9478f 100644 --- a/chain/actors/builtin/verifreg/v6.go +++ b/chain/actors/builtin/verifreg/v6.go @@ -54,6 +54,10 @@ func (s *state6) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, return getDataCap(s.store, actors.Version6, s.verifiers, addr) } +func (s *state6) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version6, s.removeDataCapProposalIDs, verifier, client) +} + func (s *state6) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { return forEachCap(s.store, actors.Version6, s.verifiers, cb) } @@ -70,6 +74,11 @@ func (s *state6) verifiers() (adt.Map, error) { return adt6.AsMap(s.store, s.Verifiers, builtin6.DefaultHamtBitwidth) } +func (s *state6) removeDataCapProposalIDs() (adt.Map, error) { + return nil, nil + +} + func (s *state6) GetState() interface{} { return &s.State } diff --git a/chain/actors/builtin/verifreg/v7.go b/chain/actors/builtin/verifreg/v7.go index 9b2ca928a..3bcfa10bd 100644 --- a/chain/actors/builtin/verifreg/v7.go +++ b/chain/actors/builtin/verifreg/v7.go @@ -54,6 +54,10 @@ func (s *state7) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, return getDataCap(s.store, actors.Version7, s.verifiers, addr) } +func (s *state7) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version7, s.removeDataCapProposalIDs, verifier, client) +} + func (s *state7) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { return forEachCap(s.store, actors.Version7, s.verifiers, cb) } @@ -70,6 +74,10 @@ func (s *state7) verifiers() (adt.Map, error) { return adt7.AsMap(s.store, s.Verifiers, builtin7.DefaultHamtBitwidth) } +func (s *state7) removeDataCapProposalIDs() (adt.Map, error) { + return adt7.AsMap(s.store, s.RemoveDataCapProposalIDs, builtin7.DefaultHamtBitwidth) +} + func (s *state7) GetState() interface{} { return &s.State } diff --git a/chain/actors/builtin/verifreg/verifreg.go b/chain/actors/builtin/verifreg/verifreg.go index f6281334d..cb26e324b 100644 --- a/chain/actors/builtin/verifreg/verifreg.go +++ b/chain/actors/builtin/verifreg/verifreg.go @@ -27,6 +27,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" + verifreg7 "github.com/filecoin-project/specs-actors/v7/actors/builtin/verifreg" ) func init() { @@ -151,12 +152,20 @@ func GetActorCodeID(av actors.Version) (cid.Cid, error) { return cid.Undef, xerrors.Errorf("unknown actor version %d", av) } +type RemoveDataCapProposal = verifreg7.RemoveDataCapProposal +type RemoveDataCapRequest = verifreg7.RemoveDataCapRequest +type RemoveDataCapParams = verifreg7.RemoveDataCapParams +type RmDcProposalID = verifreg7.RmDcProposalID + +const SignatureDomainSeparation_RemoveDataCap = verifreg7.SignatureDomainSeparation_RemoveDataCap + type State interface { cbor.Marshaler RootKey() (address.Address, error) VerifiedClientDataCap(address.Address) (bool, abi.StoragePower, error) VerifierDataCap(address.Address) (bool, abi.StoragePower, error) + RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) ForEachVerifier(func(addr address.Address, dcap abi.StoragePower) error) error ForEachClient(func(addr address.Address, dcap abi.StoragePower) error) error GetState() interface{} diff --git a/cli/filplus.go b/cli/filplus.go index 02aac0b7b..b3a98d487 100644 --- a/cli/filplus.go +++ b/cli/filplus.go @@ -1,7 +1,9 @@ package cli import ( + "bytes" "context" + "encoding/hex" "fmt" verifreg4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg" @@ -34,6 +36,7 @@ var filplusCmd = &cli.Command{ filplusListClientsCmd, filplusCheckClientCmd, filplusCheckNotaryCmd, + filplusSignRemoveDataCapProposal, }, } @@ -274,3 +277,112 @@ func checkNotary(ctx context.Context, api v0api.FullNode, vaddr address.Address) return st.VerifierDataCap(vid) } + +var filplusSignRemoveDataCapProposal = &cli.Command{ + Name: "sign-remove-data-cap-proposal", + Usage: "allows a notary to sign a Remove Data Cap Proposal", + Flags: []cli.Flag{ + &cli.Int64Flag{ + Name: "id", + Usage: "specify the RemoveDataCapProposal ID (will look up on chain if unspecified)", + Required: false, + }, + }, + Action: func(cctx *cli.Context) error { + if cctx.Args().Len() != 3 { + return fmt.Errorf("must specify three arguments: notary address, client address, and allowance to remove") + } + + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return xerrors.Errorf("failed to get full node api: %w", err) + } + defer closer() + ctx := ReqContext(cctx) + + act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("failed to get verifreg actor: %w", err) + } + + apibs := blockstore.NewAPIBlockstore(api) + store := adt.WrapStore(ctx, cbor.NewCborStore(apibs)) + + st, err := verifreg.Load(store, act) + if err != nil { + return xerrors.Errorf("failed to load verified registry state: %w", err) + } + + verifier, err := address.NewFromString(cctx.Args().Get(0)) + if err != nil { + return err + } + verifierIdAddr, err := api.StateLookupID(ctx, verifier, types.EmptyTSK) + if err != nil { + return err + } + + client, err := address.NewFromString(cctx.Args().Get(1)) + if err != nil { + return err + } + clientIdAddr, err := api.StateLookupID(ctx, client, types.EmptyTSK) + if err != nil { + return err + } + + allowanceToRemove, err := types.BigFromString(cctx.Args().Get(2)) + if err != nil { + return err + } + + _, dataCap, err := st.VerifiedClientDataCap(clientIdAddr) + if err != nil { + return xerrors.Errorf("failed to find verified client data cap: %w", err) + } + if dataCap.LessThanEqual(big.Zero()) { + return xerrors.Errorf("client data cap %s is less than amount requested to be removed %s", dataCap.String(), allowanceToRemove.String()) + } + + found, _, err := checkNotary(ctx, api, verifier) + if err != nil { + return xerrors.Errorf("failed to check notary status: %w", err) + } + + if !found { + return xerrors.New("verifier address must be a notary") + } + + id := cctx.Uint64("id") + if id == 0 { + _, id, err = st.RemoveDataCapProposalID(verifierIdAddr, clientIdAddr) + if err != nil { + return xerrors.Errorf("failed find remove data cap proposal id: %w", err) + } + } + + params := verifreg.RemoveDataCapProposal{ + RemovalProposalID: verifreg.RmDcProposalID{ProposalID: id}, + DataCapAmount: allowanceToRemove, + VerifiedClient: clientIdAddr, + } + + paramBuf := new(bytes.Buffer) + paramBuf.WriteString(verifreg.SignatureDomainSeparation_RemoveDataCap) + err = params.MarshalCBOR(paramBuf) + if err != nil { + return xerrors.Errorf("failed to marshall paramBuf: %w", err) + } + + sig, err := api.WalletSign(ctx, verifier, paramBuf.Bytes()) + if err != nil { + return xerrors.Errorf("failed to sign message: %w", err) + } + + sigBytes := append([]byte{byte(sig.Type)}, sig.Data...) + + fmt.Println(hex.EncodeToString(sigBytes)) + + return nil + }, +} diff --git a/cmd/lotus-shed/verifreg.go b/cmd/lotus-shed/verifreg.go index 03be5f916..fb2598fda 100644 --- a/cmd/lotus-shed/verifreg.go +++ b/cmd/lotus-shed/verifreg.go @@ -1,8 +1,13 @@ package main import ( + "encoding/hex" "fmt" + "github.com/filecoin-project/lotus/chain/actors/builtin/multisig" + + "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/go-state-types/big" "github.com/urfave/cli/v2" @@ -35,6 +40,7 @@ var verifRegCmd = &cli.Command{ verifRegListClientsCmd, verifRegCheckClientCmd, verifRegCheckVerifierCmd, + verifRegRemoveVerifiedClientDataCapCmd, }, } @@ -409,3 +415,154 @@ var verifRegCheckVerifierCmd = &cli.Command{ return nil }, } + +var verifRegRemoveVerifiedClientDataCapCmd = &cli.Command{ + Name: "remove-verified-client-data-cap", + Usage: "Remove data cap from verified client", + ArgsUsage: " ", + Action: func(cctx *cli.Context) error { + if cctx.Args().Len() != 7 { + return fmt.Errorf("must specify seven arguments: sender, client, allowance to remove, verifier 1 address, verifier 1 signature, verifier 2 address, verifier 2 signature") + } + + srv, err := lcli.GetFullNodeServices(cctx) + if err != nil { + return err + } + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() + ctx := lcli.ReqContext(cctx) + + sender, err := address.NewFromString(cctx.Args().Get(0)) + if err != nil { + return err + } + + client, err := address.NewFromString(cctx.Args().Get(1)) + if err != nil { + return err + } + + allowanceToRemove, err := types.BigFromString(cctx.Args().Get(2)) + if err != nil { + return err + } + + verifier1Addr, err := address.NewFromString(cctx.Args().Get(3)) + if err != nil { + return err + } + + verifier1Sig, err := hex.DecodeString(cctx.Args().Get(4)) + if err != nil { + return err + } + + verifier2Addr, err := address.NewFromString(cctx.Args().Get(5)) + if err != nil { + return err + } + + verifier2Sig, err := hex.DecodeString(cctx.Args().Get(6)) + if err != nil { + return err + } + + var sig1 crypto.Signature + if err := sig1.UnmarshalBinary(verifier1Sig); err != nil { + return xerrors.Errorf("couldn't unmarshal sig: %w", err) + } + + var sig2 crypto.Signature + if err := sig2.UnmarshalBinary(verifier2Sig); err != nil { + return xerrors.Errorf("couldn't unmarshal sig: %w", err) + } + + params, err := actors.SerializeParams(&verifreg.RemoveDataCapParams{ + VerifiedClientToRemove: client, + DataCapAmountToRemove: allowanceToRemove, + VerifierRequest1: verifreg.RemoveDataCapRequest{ + Verifier: verifier1Addr, + VerifierSignature: sig1, + }, + VerifierRequest2: verifreg.RemoveDataCapRequest{ + Verifier: verifier2Addr, + VerifierSignature: sig2, + }, + }) + if err != nil { + return err + } + + vrk, err := api.StateVerifiedRegistryRootKey(ctx, types.EmptyTSK) + if err != nil { + return err + } + + vrkState, err := api.StateGetActor(ctx, vrk, types.EmptyTSK) + if err != nil { + return err + } + + apibs := blockstore.NewAPIBlockstore(api) + store := adt.WrapStore(ctx, cbor.NewCborStore(apibs)) + + st, err := multisig.Load(store, vrkState) + if err != nil { + return err + } + + signers, err := st.Signers() + if err != nil { + return err + } + + senderIsSigner := false + senderIdAddr, err := address.IDFromAddress(sender) + if err != nil { + return err + } + + for _, signer := range signers { + signerIdAddr, err := address.IDFromAddress(signer) + if err != nil { + return err + } + + if signerIdAddr == senderIdAddr { + senderIsSigner = true + } + } + + if !senderIsSigner { + return fmt.Errorf("sender must be a vrk signer") + } + + proto, err := api.MsigPropose(ctx, vrk, verifreg.Address, big.Zero(), sender, uint64(verifreg.Methods.RemoveVerifiedClientDataCap), params) + if err != nil { + return err + } + + sm, _, err := srv.PublishMessage(ctx, proto, false) + if err != nil { + return err + } + + msgCid := sm.Cid() + + fmt.Printf("message sent, now waiting on cid: %s\n", msgCid) + + mwait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + if err != nil { + return err + } + + if mwait.Receipt.ExitCode != 0 { + return fmt.Errorf("failed to removed verified data cap: %d", mwait.Receipt.ExitCode) + } + + //TODO: Internal msg might still have failed + return nil + }, +} diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index e7ebf8af6..d136fe290 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -1234,12 +1234,13 @@ USAGE: lotus filplus command [command options] [arguments...] COMMANDS: - grant-datacap give allowance to the specified verified client address - list-notaries list all notaries - list-clients list all verified clients - check-client-datacap check verified client remaining bytes - check-notary-datacap check a notary's remaining bytes - help, h Shows a list of commands or help for one command + grant-datacap give allowance to the specified verified client address + list-notaries list all notaries + list-clients list all verified clients + check-client-datacap check verified client remaining bytes + check-notary-datacap check a notary's remaining bytes + sign-remove-data-cap-proposal allows a notary to sign a Remove Data Cap Proposal + help, h Shows a list of commands or help for one command OPTIONS: --help, -h show help (default: false) @@ -1313,6 +1314,20 @@ OPTIONS: ``` +### lotus filplus sign-remove-data-cap-proposal +``` +NAME: + lotus filplus sign-remove-data-cap-proposal - allows a notary to sign a Remove Data Cap Proposal + +USAGE: + lotus filplus sign-remove-data-cap-proposal [command options] [arguments...] + +OPTIONS: + --id value specify the RemoveDataCapProposal ID (will look up on chain if unspecified) (default: 0) + --help, -h show help (default: false) + +``` + ## lotus paych ``` NAME: