feat: CLI: add claim-extend cli (#11711)
* add claim-extend cli * fix arg usage * add missing question * fix client addr, datacap prompt * replace waitGrp with errGrp * use promptUI * replace fmt.ErrorF with xerror * apply var name suggestion * GST rc3, update types * add itest * make gen * add changelog
This commit is contained in:
parent
2e75f3b796
commit
02a8848b54
@ -3,6 +3,7 @@
|
||||
# UNRELEASED
|
||||
|
||||
## New features
|
||||
- feat: CLI: add claim-extend cli (#11711) ([filecoin-project/lotus#11711](https://github.com/filecoin-project/lotus/pull/11711))
|
||||
|
||||
## Improvements
|
||||
|
||||
|
435
cli/filplus.go
435
cli/filplus.go
@ -4,23 +4,30 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
actorstypes "github.com/filecoin-project/go-state-types/actors"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
"github.com/filecoin-project/go-state-types/builtin"
|
||||
verifregtypes13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg"
|
||||
verifregtypes8 "github.com/filecoin-project/go-state-types/builtin/v8/verifreg"
|
||||
datacap2 "github.com/filecoin-project/go-state-types/builtin/v9/datacap"
|
||||
verifregtypes9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg"
|
||||
"github.com/filecoin-project/go-state-types/network"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/blockstore"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
@ -47,6 +54,7 @@ var filplusCmd = &cli.Command{
|
||||
filplusListClaimsCmd,
|
||||
filplusRemoveExpiredAllocationsCmd,
|
||||
filplusRemoveExpiredClaimsCmd,
|
||||
filplusExtendClaimCmd,
|
||||
},
|
||||
}
|
||||
|
||||
@ -923,3 +931,430 @@ var filplusSignRemoveDataCapProposal = &cli.Command{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var filplusExtendClaimCmd = &cli.Command{
|
||||
Name: "extend-claim",
|
||||
Usage: "extend claim expiration (TermMax)",
|
||||
Flags: []cli.Flag{
|
||||
&cli.Int64Flag{
|
||||
Name: "term-max",
|
||||
Usage: "The maximum period for which a provider can earn quality-adjusted power for the piece (epochs). Default is 5 years.",
|
||||
Aliases: []string{"tmax"},
|
||||
Value: verifregtypes13.MaximumVerifiedAllocationTerm,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "client",
|
||||
Usage: "the client address that will used to send the message",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "all",
|
||||
Usage: "automatically extend TermMax of all claims for specified miner[s] to --term-max (default: 5 years from claim start epoch)",
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "miner",
|
||||
Usage: "storage provider address[es]",
|
||||
Aliases: []string{"m", "provider", "p"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "assume-yes",
|
||||
Usage: "automatic yes to prompts; assume 'yes' as answer to all prompts and run non-interactively",
|
||||
Aliases: []string{"y", "yes"},
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "confidence",
|
||||
Usage: "number of block confirmations to wait for",
|
||||
Value: int(build.MessageConfidence),
|
||||
},
|
||||
},
|
||||
ArgsUsage: "<claim1> <claim2> ... or <miner1=claim1> <miner2=claims2> ...",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
|
||||
miners := cctx.StringSlice("miner")
|
||||
all := cctx.Bool("all")
|
||||
client := cctx.String("client")
|
||||
tmax := cctx.Int64("term-max")
|
||||
|
||||
// No miner IDs and no arguments
|
||||
if len(miners) == 0 && cctx.Args().Len() == 0 {
|
||||
return xerrors.Errorf("must specify at least one miner ID or argument[s]")
|
||||
}
|
||||
|
||||
// Single Miner with no claimID and no --all flag
|
||||
if len(miners) == 1 && cctx.Args().Len() == 0 && !all {
|
||||
return xerrors.Errorf("must specify either --all flag or claim IDs to extend in argument")
|
||||
}
|
||||
|
||||
// Multiple Miner with claimIDs
|
||||
if len(miners) > 1 && cctx.Args().Len() > 0 {
|
||||
return xerrors.Errorf("either specify multiple miner IDs or multiple arguments")
|
||||
}
|
||||
|
||||
// Multiple Miner with no claimID and no --all flag
|
||||
if len(miners) > 1 && cctx.Args().Len() == 0 && !all {
|
||||
return xerrors.Errorf("must specify --all flag with multiple miner IDs")
|
||||
}
|
||||
|
||||
// Tmax can't be more than policy max
|
||||
if tmax > verifregtypes13.MaximumVerifiedAllocationTerm {
|
||||
return xerrors.Errorf("specified term-max %d is larger than %d maximum allowed by verified regirty actor policy", tmax, verifregtypes13.MaximumVerifiedAllocationTerm)
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPIV1(cctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get full node api: %s", err)
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
clientAddr, err := address.NewFromString(client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
claimMap := make(map[verifregtypes13.ClaimId]ProvInfo)
|
||||
|
||||
// If no miners and arguments are present
|
||||
if len(miners) == 0 && cctx.Args().Len() > 0 {
|
||||
for _, arg := range cctx.Args().Slice() {
|
||||
detail := strings.Split(arg, "=")
|
||||
if len(detail) > 2 {
|
||||
return xerrors.Errorf("incorrect argument format: %s", detail)
|
||||
}
|
||||
|
||||
n, err := strconv.ParseInt(detail[1], 10, 64)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to parse the claim ID for %s for argument %s: %s", detail[0], detail, err)
|
||||
}
|
||||
|
||||
maddr, err := address.NewFromString(detail[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify that minerID exists
|
||||
_, err = api.StateMinerInfo(ctx, maddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mid, err := address.IDFromAddress(maddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pi := ProvInfo{
|
||||
Addr: maddr,
|
||||
ID: abi.ActorID(mid),
|
||||
}
|
||||
|
||||
claimMap[verifregtypes13.ClaimId(n)] = pi
|
||||
}
|
||||
}
|
||||
|
||||
// If 1 miner ID and multiple arguments
|
||||
if len(miners) == 1 && cctx.Args().Len() > 0 && !all {
|
||||
for _, arg := range cctx.Args().Slice() {
|
||||
detail := strings.Split(arg, "=")
|
||||
if len(detail) > 1 {
|
||||
return xerrors.Errorf("incorrect argument format %s. Must provide only claim IDs with single miner ID", detail)
|
||||
}
|
||||
|
||||
n, err := strconv.ParseInt(detail[0], 10, 64)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to parse the claim ID for %s for argument %s: %s", detail[0], detail, err)
|
||||
}
|
||||
|
||||
claimMap[verifregtypes13.ClaimId(n)] = ProvInfo{}
|
||||
}
|
||||
}
|
||||
|
||||
msgs, err := CreateExtendClaimMsg(ctx, api, claimMap, miners, clientAddr, abi.ChainEpoch(tmax), all, cctx.Bool("assume-yes"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If not msgs are found then no claims can be extended
|
||||
if msgs == nil {
|
||||
fmt.Println("No eligible claims to extend")
|
||||
return nil
|
||||
}
|
||||
|
||||
// MpoolBatchPushMessage method will take care of gas estimation and funds check
|
||||
smsgs, err := api.MpoolBatchPushMessage(ctx, msgs, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// wait for msgs to get mined into a block
|
||||
eg := errgroup.Group{}
|
||||
eg.SetLimit(10)
|
||||
for _, msg := range smsgs {
|
||||
msg := msg
|
||||
eg.Go(func() error {
|
||||
wait, err := api.StateWaitMsg(ctx, msg.Cid(), uint64(cctx.Int("confidence")), 2000, true)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("timeout waiting for message to land on chain %s", wait.Message)
|
||||
|
||||
}
|
||||
|
||||
if wait.Receipt.ExitCode.IsError() {
|
||||
return xerrors.Errorf("failed to execute message %s: %s", wait.Message, wait.Receipt.ExitCode)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return eg.Wait()
|
||||
},
|
||||
}
|
||||
|
||||
type ProvInfo struct {
|
||||
Addr address.Address
|
||||
ID abi.ActorID
|
||||
}
|
||||
|
||||
// CreateExtendClaimMsg creates extend message[s] based on the following conditions
|
||||
// 1. Extend all claims for a miner ID
|
||||
// 2. Extend all claims for multiple miner IDs
|
||||
// 3. Extend specified claims for a miner ID
|
||||
// 4. Extend specific claims for specific miner ID
|
||||
// 5. Extend all claims for a miner ID with different client address (2 messages)
|
||||
// 6. Extend all claims for multiple miner IDs with different client address (2 messages)
|
||||
// 7. Extend specified claims for a miner ID with different client address (2 messages)
|
||||
// 8. Extend specific claims for specific miner ID with different client address (2 messages)
|
||||
func CreateExtendClaimMsg(ctx context.Context, api api.FullNode, pcm map[verifregtypes13.ClaimId]ProvInfo, miners []string, wallet address.Address, tmax abi.ChainEpoch, all, assumeYes bool) ([]*types.Message, error) {
|
||||
|
||||
ac, err := api.StateLookupID(ctx, wallet, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w, err := address.IDFromAddress(ac)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("converting wallet address to ID: %w", err)
|
||||
}
|
||||
|
||||
wid := abi.ActorID(w)
|
||||
|
||||
head, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var terms []verifregtypes13.ClaimTerm
|
||||
var newClaims []verifregtypes13.ClaimExtensionRequest
|
||||
rDataCap := big.NewInt(0)
|
||||
|
||||
// If --all is set
|
||||
if all {
|
||||
for _, id := range miners {
|
||||
maddr, err := address.NewFromString(id)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parsing miner %s: %s", id, err)
|
||||
}
|
||||
mid, err := address.IDFromAddress(maddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("converting miner address to miner ID: %s", err)
|
||||
}
|
||||
claims, err := api.StateGetClaims(ctx, maddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting claims for miner %s: %s", maddr, err)
|
||||
}
|
||||
for claimID, claim := range claims {
|
||||
claimID := claimID
|
||||
claim := claim
|
||||
if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() {
|
||||
// If client is not same - needs to burn datacap
|
||||
if claim.Client != wid {
|
||||
newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{
|
||||
Claim: verifregtypes13.ClaimId(claimID),
|
||||
Provider: abi.ActorID(mid),
|
||||
TermMax: tmax,
|
||||
})
|
||||
rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int)
|
||||
continue
|
||||
}
|
||||
terms = append(terms, verifregtypes13.ClaimTerm{
|
||||
ClaimId: verifregtypes13.ClaimId(claimID),
|
||||
TermMax: tmax,
|
||||
Provider: abi.ActorID(mid),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Single miner and specific claims
|
||||
if len(miners) == 1 && len(pcm) > 0 {
|
||||
maddr, err := address.NewFromString(miners[0])
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parsing miner %s: %s", miners[0], err)
|
||||
}
|
||||
mid, err := address.IDFromAddress(maddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("converting miner address to miner ID: %s", err)
|
||||
}
|
||||
claims, err := api.StateGetClaims(ctx, maddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("getting claims for miner %s: %s", maddr, err)
|
||||
}
|
||||
|
||||
for claimID := range pcm {
|
||||
claimID := claimID
|
||||
claim, ok := claims[verifregtypes9.ClaimId(claimID)]
|
||||
if !ok {
|
||||
return nil, xerrors.Errorf("claim %d not found for provider %s", claimID, miners[0])
|
||||
}
|
||||
if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() {
|
||||
// If client is not same - needs to burn datacap
|
||||
if claim.Client != wid {
|
||||
newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{
|
||||
Claim: claimID,
|
||||
Provider: abi.ActorID(mid),
|
||||
TermMax: tmax,
|
||||
})
|
||||
rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int)
|
||||
continue
|
||||
}
|
||||
terms = append(terms, verifregtypes13.ClaimTerm{
|
||||
ClaimId: claimID,
|
||||
TermMax: tmax,
|
||||
Provider: abi.ActorID(mid),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(miners) == 0 && len(pcm) > 0 {
|
||||
for claimID, prov := range pcm {
|
||||
prov := prov
|
||||
claimID := claimID
|
||||
claim, err := api.StateGetClaim(ctx, prov.Addr, verifregtypes9.ClaimId(claimID), types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("could not load the claim %d: %s", claimID, err)
|
||||
}
|
||||
if claim == nil {
|
||||
return nil, xerrors.Errorf("claim %d not found in the actor state", claimID)
|
||||
}
|
||||
if claim.TermMax < tmax && claim.TermStart+claim.TermMax > head.Height() {
|
||||
// If client is not same - needs to burn datacap
|
||||
if claim.Client != wid {
|
||||
newClaims = append(newClaims, verifregtypes13.ClaimExtensionRequest{
|
||||
Claim: claimID,
|
||||
Provider: prov.ID,
|
||||
TermMax: tmax,
|
||||
})
|
||||
rDataCap.Add(big.NewInt(int64(claim.Size)).Int, rDataCap.Int)
|
||||
continue
|
||||
}
|
||||
terms = append(terms, verifregtypes13.ClaimTerm{
|
||||
ClaimId: claimID,
|
||||
TermMax: tmax,
|
||||
Provider: prov.ID,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var msgs []*types.Message
|
||||
|
||||
if len(terms) > 0 {
|
||||
params, err := actors.SerializeParams(&verifregtypes13.ExtendClaimTermsParams{
|
||||
Terms: terms,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to searialise the parameters: %s", err)
|
||||
}
|
||||
|
||||
oclaimMsg := &types.Message{
|
||||
To: verifreg.Address,
|
||||
From: wallet,
|
||||
Method: verifreg.Methods.ExtendClaimTerms,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
msgs = append(msgs, oclaimMsg)
|
||||
}
|
||||
|
||||
if len(newClaims) > 0 {
|
||||
// Get datacap balance
|
||||
aDataCap, err := api.StateVerifiedClientStatus(ctx, wallet, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if aDataCap == nil {
|
||||
return nil, xerrors.Errorf("wallet %s does not have any datacap", wallet)
|
||||
}
|
||||
|
||||
// Check that we have enough data cap to make the allocation
|
||||
if rDataCap.GreaterThan(big.NewInt(aDataCap.Int64())) {
|
||||
return nil, xerrors.Errorf("requested datacap %s is greater then the available datacap %s", rDataCap, aDataCap)
|
||||
}
|
||||
|
||||
ncparams, err := actors.SerializeParams(&verifregtypes13.AllocationRequests{
|
||||
Extensions: newClaims,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to searialise the parameters: %s", err)
|
||||
}
|
||||
|
||||
transferParams, err := actors.SerializeParams(&datacap2.TransferParams{
|
||||
To: builtin.VerifiedRegistryActorAddr,
|
||||
Amount: big.Mul(rDataCap, builtin.TokenPrecision),
|
||||
OperatorData: ncparams,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to serialize transfer parameters: %s", err)
|
||||
}
|
||||
|
||||
nclaimMsg := &types.Message{
|
||||
To: builtin.DatacapActorAddr,
|
||||
From: wallet,
|
||||
Method: datacap.Methods.TransferExported,
|
||||
Params: transferParams,
|
||||
Value: big.Zero(),
|
||||
}
|
||||
|
||||
if !assumeYes {
|
||||
out := fmt.Sprintf("Some of the specified allocation have a different client address and will require %d Datacap to extend. Proceed? Yes [Y/y] / No [N/n], Ctrl+C (^C) to exit", rDataCap.Int)
|
||||
validate := func(input string) error {
|
||||
if strings.EqualFold(input, "y") || strings.EqualFold(input, "yes") {
|
||||
return nil
|
||||
}
|
||||
if strings.EqualFold(input, "n") || strings.EqualFold(input, "no") {
|
||||
return nil
|
||||
}
|
||||
return errors.New("incorrect input")
|
||||
}
|
||||
|
||||
templates := &promptui.PromptTemplates{
|
||||
Prompt: "{{ . }} ",
|
||||
Valid: "{{ . | green }} ",
|
||||
Invalid: "{{ . | red }} ",
|
||||
Success: "{{ . | cyan | bold }} ",
|
||||
}
|
||||
|
||||
prompt := promptui.Prompt{
|
||||
Label: out,
|
||||
Templates: templates,
|
||||
Validate: validate,
|
||||
}
|
||||
|
||||
input, err := prompt.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.Contains(strings.ToLower(input), "n") {
|
||||
fmt.Println("Dropping the extension for claims that require Datacap")
|
||||
return msgs, nil
|
||||
}
|
||||
}
|
||||
|
||||
msgs = append(msgs, nclaimMsg)
|
||||
}
|
||||
|
||||
return msgs, nil
|
||||
}
|
||||
|
@ -1192,6 +1192,7 @@ COMMANDS:
|
||||
list-claims List claims available in verified registry actor or made by provider if specified
|
||||
remove-expired-allocations remove expired allocations (if no allocations are specified all eligible allocations are removed)
|
||||
remove-expired-claims remove expired claims (if no claims are specified all eligible claims are removed)
|
||||
extend-claim extend claim expiration (TermMax)
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
OPTIONS:
|
||||
@ -1325,6 +1326,24 @@ OPTIONS:
|
||||
--help, -h show help
|
||||
```
|
||||
|
||||
### lotus filplus extend-claim
|
||||
```
|
||||
NAME:
|
||||
lotus filplus extend-claim - extend claim expiration (TermMax)
|
||||
|
||||
USAGE:
|
||||
lotus filplus extend-claim [command options] <claim1> <claim2> ... or <miner1=claim1> <miner2=claims2> ...
|
||||
|
||||
OPTIONS:
|
||||
--term-max value, --tmax value The maximum period for which a provider can earn quality-adjusted power for the piece (epochs). Default is 5 years. (default: 5256000)
|
||||
--client value the client address that will used to send the message
|
||||
--all automatically extend TermMax of all claims for specified miner[s] to --term-max (default: 5 years from claim start epoch) (default: false)
|
||||
--miner value, -m value, --provider value, -p value [ --miner value, -m value, --provider value, -p value ] storage provider address[es]
|
||||
--assume-yes, -y, --yes automatic yes to prompts; assume 'yes' as answer to all prompts and run non-interactively (default: false)
|
||||
--confidence value number of block confirmations to wait for (default: 5)
|
||||
--help, -h show help
|
||||
```
|
||||
|
||||
## lotus paych
|
||||
```
|
||||
NAME:
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/wallet/key"
|
||||
"github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/itests/kit"
|
||||
"github.com/filecoin-project/lotus/lib/must"
|
||||
"github.com/filecoin-project/lotus/storage/pipeline/piece"
|
||||
@ -108,7 +109,8 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
||||
|
||||
/* --- Setup verified registry and client and allocate datacap to client */
|
||||
|
||||
verifierAddr, verifiedClientAddr := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, verifiedClientKey)
|
||||
verifierAddr, verifiedClientAddrses := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, []*key.Key{verifiedClientKey})
|
||||
verifiedClientAddr := verifiedClientAddrses[0]
|
||||
|
||||
/* --- Prepare piece for onboarding --- */
|
||||
|
||||
@ -121,7 +123,7 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) {
|
||||
|
||||
/* --- Allocate datacap for the piece by the verified client --- */
|
||||
|
||||
clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr)
|
||||
clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr, true, 0)
|
||||
|
||||
head, err := client.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
@ -396,40 +398,57 @@ func ddoVerifiedSetupAllocations(
|
||||
minerId uint64,
|
||||
dc abi.PieceInfo,
|
||||
verifiedClientAddr address.Address,
|
||||
setupBorkAlloc bool,
|
||||
tmax abi.ChainEpoch,
|
||||
) (clientID abi.ActorID, allocationID verifregtypes13.AllocationId) {
|
||||
if tmax == 0 {
|
||||
tmax = verifregtypes13.MaximumVerifiedAllocationTerm
|
||||
}
|
||||
|
||||
head, err := node.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
var requests []verifregtypes13.AllocationRequest
|
||||
|
||||
// design this one to expire so we can observe allocation-removed
|
||||
if setupBorkAlloc {
|
||||
head, err := node.ChainHead(ctx)
|
||||
require.NoError(t, err)
|
||||
expiringAllocationHeight := head.Height() + 100
|
||||
allocationRequestBork := verifregtypes13.AllocationRequest{
|
||||
Provider: abi.ActorID(minerId),
|
||||
Data: cid.MustParse("baga6ea4seaaqa"),
|
||||
Size: dc.Size,
|
||||
TermMin: verifregtypes13.MinimumVerifiedAllocationTerm,
|
||||
TermMax: verifregtypes13.MaximumVerifiedAllocationTerm,
|
||||
TermMax: tmax,
|
||||
Expiration: expiringAllocationHeight,
|
||||
}
|
||||
requests = append(requests, allocationRequestBork)
|
||||
}
|
||||
|
||||
allocationRequest := verifregtypes13.AllocationRequest{
|
||||
Provider: abi.ActorID(minerId),
|
||||
Data: dc.PieceCID,
|
||||
Size: dc.Size,
|
||||
TermMin: verifregtypes13.MinimumVerifiedAllocationTerm,
|
||||
TermMax: verifregtypes13.MaximumVerifiedAllocationTerm,
|
||||
TermMax: tmax,
|
||||
Expiration: verifregtypes13.MaximumVerifiedAllocationExpiration,
|
||||
}
|
||||
requests = append(requests, allocationRequest)
|
||||
|
||||
allocationRequests := verifregtypes13.AllocationRequests{
|
||||
Allocations: []verifregtypes13.AllocationRequest{allocationRequestBork, allocationRequest},
|
||||
Allocations: requests,
|
||||
}
|
||||
|
||||
receiverParams, aerr := actors.SerializeParams(&allocationRequests)
|
||||
require.NoError(t, aerr)
|
||||
|
||||
var amt abi.TokenAmount
|
||||
amt = big.Mul(big.NewInt(int64(dc.Size)), builtin.TokenPrecision)
|
||||
if setupBorkAlloc {
|
||||
amt = big.Mul(big.NewInt(int64(dc.Size*2)), builtin.TokenPrecision)
|
||||
}
|
||||
|
||||
transferParams, aerr := actors.SerializeParams(&datacap2.TransferParams{
|
||||
To: builtin.VerifiedRegistryActorAddr,
|
||||
Amount: big.Mul(big.NewInt(int64(dc.Size*2)), builtin.TokenPrecision),
|
||||
Amount: amt,
|
||||
OperatorData: receiverParams,
|
||||
})
|
||||
require.NoError(t, aerr)
|
||||
@ -452,7 +471,11 @@ func ddoVerifiedSetupAllocations(
|
||||
// check that we have an allocation
|
||||
allocations, err := node.StateGetAllocations(ctx, verifiedClientAddr, types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
if setupBorkAlloc {
|
||||
require.Len(t, allocations, 2) // allocation waiting to be claimed
|
||||
} else {
|
||||
require.Len(t, allocations, 1) // allocation waiting to be claimed
|
||||
}
|
||||
|
||||
for key, value := range allocations {
|
||||
if value.Data == dc.PieceCID {
|
||||
@ -603,18 +626,21 @@ func ddoVerifiedBuildActorEventsFromMessages(ctx context.Context, t *testing.T,
|
||||
return actorEvents
|
||||
}
|
||||
|
||||
func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *kit.TestFullNode, rootKey *key.Key, verifierKey *key.Key, verifiedClientKey *key.Key) (address.Address, address.Address) {
|
||||
func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *kit.TestFullNode, rootKey *key.Key, verifierKey *key.Key, verifiedClientKeys []*key.Key) (verifierAddr address.Address, ret []address.Address) {
|
||||
// import the root key.
|
||||
rootAddr, err := client.WalletImport(ctx, &rootKey.KeyInfo)
|
||||
require.NoError(t, err)
|
||||
|
||||
// import the verifiers' keys.
|
||||
verifierAddr, err := client.WalletImport(ctx, &verifierKey.KeyInfo)
|
||||
verifierAddr, err = client.WalletImport(ctx, &verifierKey.KeyInfo)
|
||||
require.NoError(t, err)
|
||||
|
||||
// import the verified client's key.
|
||||
verifiedClientAddr, err := client.WalletImport(ctx, &verifiedClientKey.KeyInfo)
|
||||
for _, k := range verifiedClientKeys {
|
||||
verifiedClientAddr, err := client.WalletImport(ctx, &k.KeyInfo)
|
||||
require.NoError(t, err)
|
||||
ret = append(ret, verifiedClientAddr)
|
||||
}
|
||||
|
||||
allowance := big.NewInt(100000000000)
|
||||
params, aerr := actors.SerializeParams(&verifregtypes13.AddVerifierParams{Address: verifierAddr, Allowance: allowance})
|
||||
@ -639,10 +665,11 @@ func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *k
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, allowance, *verifierAllowance)
|
||||
|
||||
// assign datacap to a client
|
||||
// assign datacap to clients
|
||||
for _, ad := range ret {
|
||||
initialDatacap := big.NewInt(10000)
|
||||
|
||||
params, aerr = actors.SerializeParams(&verifregtypes13.AddVerifiedClientParams{Address: verifiedClientAddr, Allowance: initialDatacap})
|
||||
params, aerr = actors.SerializeParams(&verifregtypes13.AddVerifiedClientParams{Address: ad, Allowance: initialDatacap})
|
||||
require.NoError(t, aerr)
|
||||
|
||||
msg = &types.Message{
|
||||
@ -659,8 +686,9 @@ func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *k
|
||||
res, err = client.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 0, res.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
return verifierAddr, verifiedClientAddr
|
||||
return
|
||||
}
|
||||
|
||||
func filterEvents(events []*types.ActorEvent, key string) []*types.ActorEvent {
|
||||
@ -711,3 +739,122 @@ func epochPtr(ei int64) *abi.ChainEpoch {
|
||||
ep := abi.ChainEpoch(ei)
|
||||
return &ep
|
||||
}
|
||||
|
||||
func TestVerifiedDDOExtendClaim(t *testing.T) {
|
||||
kit.QuietMiningLogs()
|
||||
|
||||
var (
|
||||
blocktime = 2 * time.Millisecond
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
rootKey, err := key.GenerateKey(types.KTSecp256k1)
|
||||
require.NoError(t, err)
|
||||
|
||||
verifierKey, err := key.GenerateKey(types.KTSecp256k1)
|
||||
require.NoError(t, err)
|
||||
|
||||
verifiedClientKey1, err := key.GenerateKey(types.KTBLS)
|
||||
require.NoError(t, err)
|
||||
|
||||
verifiedClientKey2, err := key.GenerateKey(types.KTBLS)
|
||||
require.NoError(t, err)
|
||||
|
||||
unverifiedClient, err := key.GenerateKey(types.KTBLS)
|
||||
require.NoError(t, err)
|
||||
|
||||
bal, err := types.ParseFIL("100fil")
|
||||
require.NoError(t, err)
|
||||
|
||||
client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(),
|
||||
kit.RootVerifier(rootKey, abi.NewTokenAmount(bal.Int64())),
|
||||
kit.Account(verifierKey, abi.NewTokenAmount(bal.Int64())),
|
||||
kit.Account(verifiedClientKey1, abi.NewTokenAmount(bal.Int64())),
|
||||
kit.Account(verifiedClientKey2, abi.NewTokenAmount(bal.Int64())),
|
||||
kit.Account(unverifiedClient, abi.NewTokenAmount(bal.Int64())),
|
||||
)
|
||||
|
||||
/* --- Start mining --- */
|
||||
|
||||
ens.InterconnectAll().BeginMiningMustPost(blocktime)
|
||||
|
||||
minerId, err := address.IDFromAddress(miner.ActorAddr)
|
||||
require.NoError(t, err)
|
||||
|
||||
/* --- Setup verified registry and clients and allocate datacap to client */
|
||||
|
||||
_, verifiedClientAddrses := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, []*key.Key{verifiedClientKey1, verifiedClientKey2})
|
||||
verifiedClientAddr1 := verifiedClientAddrses[0]
|
||||
verifiedClientAddr2 := verifiedClientAddrses[1]
|
||||
|
||||
/* --- Prepare piece for onboarding --- */
|
||||
|
||||
pieceSize := abi.PaddedPieceSize(2048).Unpadded()
|
||||
pieceData := make([]byte, pieceSize)
|
||||
_, _ = rand.Read(pieceData)
|
||||
|
||||
dc, err := miner.ComputeDataCid(ctx, pieceSize, bytes.NewReader(pieceData))
|
||||
require.NoError(t, err)
|
||||
|
||||
/* --- Allocate datacap for the piece by the verified client --- */
|
||||
clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr1, false, builtin.EpochsInYear*3)
|
||||
|
||||
/* --- Onboard the piece --- */
|
||||
|
||||
_, _ = ddoVerifiedOnboardPiece(ctx, t, miner, clientId, allocationId, dc, pieceData)
|
||||
|
||||
oldclaim, err := client.StateGetClaim(ctx, miner.ActorAddr, verifreg.ClaimId(allocationId), types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, oldclaim)
|
||||
|
||||
prov := cli.ProvInfo{
|
||||
Addr: miner.ActorAddr,
|
||||
ID: abi.ActorID(minerId),
|
||||
}
|
||||
|
||||
pcm := make(map[verifregtypes13.ClaimId]cli.ProvInfo)
|
||||
pcm[verifregtypes13.ClaimId(allocationId)] = prov
|
||||
|
||||
// Extend claim with same client
|
||||
msgs, err := cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, verifiedClientAddr1, (builtin.EpochsInYear*3)+3000, false, true)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, msgs)
|
||||
require.Len(t, msgs, 1)
|
||||
|
||||
// MpoolBatchPushMessage method will take care of gas estimation and funds check
|
||||
smsg, err := client.MpoolPushMessage(ctx, msgs[0], nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
wait, err := client.StateWaitMsg(ctx, smsg.Cid(), 1, 2000, true)
|
||||
require.NoError(t, err)
|
||||
require.True(t, wait.Receipt.ExitCode.IsSuccess())
|
||||
|
||||
newclaim, err := client.StateGetClaim(ctx, miner.ActorAddr, verifreg.ClaimId(allocationId), types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, newclaim)
|
||||
require.EqualValues(t, newclaim.TermMax-oldclaim.TermMax, 3000)
|
||||
|
||||
// Extend claim with non-verified client | should fail
|
||||
_, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, unverifiedClient.Address, verifregtypes13.MaximumVerifiedAllocationTerm, false, true)
|
||||
require.ErrorContains(t, err, "does not have any datacap")
|
||||
|
||||
// Extend all claim with verified client
|
||||
msgs, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, nil, []string{miner.ActorAddr.String()}, verifiedClientAddr2, verifregtypes13.MaximumVerifiedAllocationTerm, true, true)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, msgs, 1)
|
||||
smsg, err = client.MpoolPushMessage(ctx, msgs[0], nil)
|
||||
require.NoError(t, err)
|
||||
wait, err = client.StateWaitMsg(ctx, smsg.Cid(), 1, 2000, true)
|
||||
require.NoError(t, err)
|
||||
require.True(t, wait.Receipt.ExitCode.IsSuccess())
|
||||
|
||||
// Extend all claims with lower TermMax
|
||||
msgs, err = cli.CreateExtendClaimMsg(ctx, client.FullNode, pcm, []string{}, verifiedClientAddr2, builtin.EpochsInYear*4, false, true)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, msgs)
|
||||
|
||||
newclaim, err = client.StateGetClaim(ctx, miner.ActorAddr, verifreg.ClaimId(allocationId), types.EmptyTSK)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, newclaim)
|
||||
require.EqualValues(t, newclaim.TermMax, verifregtypes13.MaximumVerifiedAllocationTerm)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user