From 74a9dfc99f4480ce86e73e9747d7bc681e897580 Mon Sep 17 00:00:00 2001 From: mitchellsoo Date: Sat, 18 Jul 2020 14:21:02 +0800 Subject: [PATCH 01/97] Add script to generate lotus cli document. Generate lotus command lines documents as text and markdown in folder "lotus/documentation/en". --- scripts/generate-lotus-cli.py | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 scripts/generate-lotus-cli.py diff --git a/scripts/generate-lotus-cli.py b/scripts/generate-lotus-cli.py new file mode 100644 index 000000000..8fb27026b --- /dev/null +++ b/scripts/generate-lotus-cli.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# Generate lotus command lines documents as text and markdown in folder "lotus/documentation/en". +# Python 2.7 + +import os + + +def generate_lotus_cli(): + output_folder = '../documentation/en' + txt_file = open('%s/lotus-cli.txt' % output_folder, 'w') # set the name of txt output + md_file = open('%s/lotus-cli.md' % output_folder, 'w') # set the name of md output + + def get_cmd_recursively(cur_cmd): + txt_file.writelines('\n\n%s\n' % cur_cmd[2:]) + md_file.writelines('#' * cur_cmd.count(' ') + '# ' + cur_cmd[2:] + '\n') + + cmd_flag = False + + cmd_help_output = os.popen('cd ..' + ' && ' + cur_cmd + ' -h') + cmd_help_output_lines = cmd_help_output.readlines() + + txt_file.writelines(cmd_help_output_lines) + md_file.writelines('```\n') + md_file.writelines(cmd_help_output_lines) + md_file.writelines('```\n') + + for line in cmd_help_output_lines: + try: + line = line.strip() + if line == 'COMMANDS:': + cmd_flag = True + if cmd_flag is True and line == '': + cmd_flag = False + if cmd_flag is True and line[-1] != ':' and 'help, h' not in line: + gap_pos = 0 + sub_cmd = line + if ' ' in line: + gap_pos = sub_cmd.index(' ') + if gap_pos: + sub_cmd = cur_cmd + ' ' + sub_cmd[:gap_pos] + get_cmd_recursively(sub_cmd) + except Exception as e: + print('Fail to deal with "%s" with error:\n%s' % (line, e)) + + get_cmd_recursively('./lotus') + txt_file.close() + md_file.close() + + +if __name__ == "__main__": + generate_lotus_cli() From 526cd739f671cbe0a39d928090bee6a5d9cffa45 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 20 Nov 2020 00:31:04 -0500 Subject: [PATCH 02/97] Return total power when GetPowerRaw doesn't find miner claim --- chain/stmgr/utils.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index fb0b91378..5979bf705 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -103,8 +103,7 @@ func GetPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr addres var found bool mpow, found, err = pas.MinerPower(maddr) if err != nil || !found { - // TODO: return an error when not found? - return power.Claim{}, power.Claim{}, false, err + return power.Claim{}, tpow, false, err } minpow, err = pas.MinerNominalPowerMeetsConsensusMinimum(maddr) From 4395f27143577c93aa98066d3d9f5a68a3ef4bb9 Mon Sep 17 00:00:00 2001 From: chadwick2143 Date: Mon, 29 Mar 2021 12:27:08 +0800 Subject: [PATCH 03/97] Transplant some useful commands to lotus-shed actor Transplant some useful commands from lotus-miner actor to lotus-shed actor, so that you can excute them without miner api. --- cmd/lotus-shed/actor.go | 625 ++++++++++++++++++++++++++++++++++++++++ cmd/lotus-shed/main.go | 1 + 2 files changed, 626 insertions(+) create mode 100644 cmd/lotus-shed/actor.go diff --git a/cmd/lotus-shed/actor.go b/cmd/lotus-shed/actor.go new file mode 100644 index 000000000..47ec78024 --- /dev/null +++ b/cmd/lotus-shed/actor.go @@ -0,0 +1,625 @@ +package main + +import ( + "fmt" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" +) + +var actorCmd = &cli.Command{ + Name: "actor", + Usage: "manipulate the miner actor", + Subcommands: []*cli.Command{ + actorWithdrawCmd, + actorSetOwnerCmd, + actorControl, + actorProposeChangeWorker, + actorConfirmChangeWorker, + }, +} + +var actorWithdrawCmd = &cli.Command{ + Name: "withdraw", + Usage: "withdraw available balance", + ArgsUsage: "[amount (FIL)]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "actor", + Usage: "specify the address of miner actor", + }, + }, + Action: func(cctx *cli.Context) error { + var maddr address.Address + if act := cctx.String("actor"); act != "" { + var err error + maddr, err = address.NewFromString(act) + if err != nil { + return fmt.Errorf("parsing address %s: %w", act, err) + } + } + + nodeAPI, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + if maddr.Empty() { + minerAPI, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + + maddr, err = minerAPI.ActorAddress(ctx) + if err != nil { + return err + } + } + + mi, err := nodeAPI.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + available, err := nodeAPI.StateMinerAvailableBalance(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + amount := available + if cctx.Args().Present() { + f, err := types.ParseFIL(cctx.Args().First()) + if err != nil { + return xerrors.Errorf("parsing 'amount' argument: %w", err) + } + + amount = abi.TokenAmount(f) + + if amount.GreaterThan(available) { + return xerrors.Errorf("can't withdraw more funds than available; requested: %s; available: %s", amount, available) + } + } + + params, err := actors.SerializeParams(&miner2.WithdrawBalanceParams{ + AmountRequested: amount, // Default to attempting to withdraw all the extra funds in the miner actor + }) + if err != nil { + return err + } + + smsg, err := nodeAPI.MpoolPushMessage(ctx, &types.Message{ + To: maddr, + From: mi.Owner, + Value: types.NewInt(0), + Method: miner.Methods.WithdrawBalance, + Params: params, + }, nil) + if err != nil { + return err + } + + fmt.Printf("Requested rewards withdrawal in message %s\n", smsg.Cid()) + + return nil + }, +} + +var actorSetOwnerCmd = &cli.Command{ + Name: "set-owner", + Usage: "Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner)", + ArgsUsage: "[newOwnerAddress senderAddress]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "actor", + Usage: "specify the address of miner actor", + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + 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 + } + + if cctx.NArg() != 2 { + return fmt.Errorf("must pass new owner address and sender address") + } + + var maddr address.Address + if act := cctx.String("actor"); act != "" { + var err error + maddr, err = address.NewFromString(act) + if err != nil { + return fmt.Errorf("parsing address %s: %w", act, err) + } + } + + nodeAPI, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + na, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + newAddrId, err := nodeAPI.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return err + } + + fa, err := address.NewFromString(cctx.Args().Get(1)) + if err != nil { + return err + } + + fromAddrId, err := nodeAPI.StateLookupID(ctx, fa, types.EmptyTSK) + if err != nil { + return err + } + + if maddr.Empty() { + minerAPI, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + + maddr, err = minerAPI.ActorAddress(ctx) + if err != nil { + return err + } + } + + mi, err := nodeAPI.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + if fromAddrId != mi.Owner && fromAddrId != newAddrId { + return xerrors.New("from address must either be the old owner or the new owner") + } + + sp, err := actors.SerializeParams(&newAddrId) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := nodeAPI.MpoolPushMessage(ctx, &types.Message{ + From: fromAddrId, + To: maddr, + Method: miner.Methods.ChangeOwnerAddress, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := nodeAPI.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Println("owner change failed!") + return err + } + + fmt.Println("message succeeded!") + + return nil + }, +} + +var actorControl = &cli.Command{ + Name: "control", + Usage: "Manage control addresses", + Subcommands: []*cli.Command{ + actorControlSet, + }, +} + +var actorControlSet = &cli.Command{ + Name: "set", + Usage: "Set control address(-es)", + ArgsUsage: "[...address]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "actor", + Usage: "specify the address of miner actor", + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + 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 + } + + var maddr address.Address + if act := cctx.String("actor"); act != "" { + var err error + maddr, err = address.NewFromString(act) + if err != nil { + return fmt.Errorf("parsing address %s: %w", act, err) + } + } + + nodeAPI, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + if maddr.Empty() { + minerAPI, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + + maddr, err = minerAPI.ActorAddress(ctx) + if err != nil { + return err + } + } + + mi, err := nodeAPI.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + del := map[address.Address]struct{}{} + existing := map[address.Address]struct{}{} + for _, controlAddress := range mi.ControlAddresses { + ka, err := nodeAPI.StateAccountKey(ctx, controlAddress, types.EmptyTSK) + if err != nil { + return err + } + + del[ka] = struct{}{} + existing[ka] = struct{}{} + } + + var toSet []address.Address + + for i, as := range cctx.Args().Slice() { + a, err := address.NewFromString(as) + if err != nil { + return xerrors.Errorf("parsing address %d: %w", i, err) + } + + ka, err := nodeAPI.StateAccountKey(ctx, a, types.EmptyTSK) + if err != nil { + return err + } + + // make sure the address exists on chain + _, err = nodeAPI.StateLookupID(ctx, ka, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("looking up %s: %w", ka, err) + } + + delete(del, ka) + toSet = append(toSet, ka) + } + + for a := range del { + fmt.Println("Remove", a) + } + for _, a := range toSet { + if _, exists := existing[a]; !exists { + fmt.Println("Add", a) + } + } + + cwp := &miner2.ChangeWorkerAddressParams{ + NewWorker: mi.Worker, + NewControlAddrs: toSet, + } + + sp, err := actors.SerializeParams(cwp) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := nodeAPI.MpoolPushMessage(ctx, &types.Message{ + From: mi.Owner, + To: maddr, + Method: miner.Methods.ChangeWorkerAddress, + + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("Message CID:", smsg.Cid()) + + return nil + }, +} + +var actorProposeChangeWorker = &cli.Command{ + Name: "propose-change-worker", + Usage: "Propose a worker address change", + ArgsUsage: "[address]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "actor", + Usage: "specify the address of miner actor", + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must pass address of new worker address") + } + + if !cctx.Bool("really-do-it") { + fmt.Fprintln(cctx.App.Writer, "Pass --really-do-it to actually execute this action") + return nil + } + + var maddr address.Address + if act := cctx.String("actor"); act != "" { + var err error + maddr, err = address.NewFromString(act) + if err != nil { + return fmt.Errorf("parsing address %s: %w", act, err) + } + } + + nodeAPI, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + na, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + newAddr, err := nodeAPI.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return err + } + + if maddr.Empty() { + minerAPI, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + + maddr, err = minerAPI.ActorAddress(ctx) + if err != nil { + return err + } + } + + mi, err := nodeAPI.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + if mi.NewWorker.Empty() { + if mi.Worker == newAddr { + return fmt.Errorf("worker address already set to %s", na) + } + } else { + if mi.NewWorker == newAddr { + return fmt.Errorf("change to worker address %s already pending", na) + } + } + + cwp := &miner2.ChangeWorkerAddressParams{ + NewWorker: newAddr, + NewControlAddrs: mi.ControlAddresses, + } + + sp, err := actors.SerializeParams(cwp) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := nodeAPI.MpoolPushMessage(ctx, &types.Message{ + From: mi.Owner, + To: maddr, + Method: miner.Methods.ChangeWorkerAddress, + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Fprintln(cctx.App.Writer, "Propose Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := nodeAPI.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Fprintln(cctx.App.Writer, "Propose worker change failed!") + return err + } + + mi, err = nodeAPI.StateMinerInfo(ctx, maddr, wait.TipSet) + if err != nil { + return err + } + if mi.NewWorker != newAddr { + return fmt.Errorf("Proposed worker address change not reflected on chain: expected '%s', found '%s'", na, mi.NewWorker) + } + + fmt.Fprintf(cctx.App.Writer, "Worker key change to %s successfully proposed.\n", na) + fmt.Fprintf(cctx.App.Writer, "Call 'confirm-change-worker' at or after height %d to complete.\n", mi.WorkerChangeEpoch) + + return nil + }, +} + +var actorConfirmChangeWorker = &cli.Command{ + Name: "confirm-change-worker", + Usage: "Confirm a worker address change", + ArgsUsage: "[address]", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "actor", + Usage: "specify the address of miner actor", + }, + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must pass address of new worker address") + } + + if !cctx.Bool("really-do-it") { + fmt.Fprintln(cctx.App.Writer, "Pass --really-do-it to actually execute this action") + return nil + } + + var maddr address.Address + if act := cctx.String("actor"); act != "" { + var err error + maddr, err = address.NewFromString(act) + if err != nil { + return fmt.Errorf("parsing address %s: %w", act, err) + } + } + + nodeAPI, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + na, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + newAddr, err := nodeAPI.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return err + } + + if maddr.Empty() { + minerAPI, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + + maddr, err = minerAPI.ActorAddress(ctx) + if err != nil { + return err + } + } + + mi, err := nodeAPI.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + if mi.NewWorker.Empty() { + return xerrors.Errorf("no worker key change proposed") + } else if mi.NewWorker != newAddr { + return xerrors.Errorf("worker key %s does not match current worker key proposal %s", newAddr, mi.NewWorker) + } + + if head, err := nodeAPI.ChainHead(ctx); err != nil { + return xerrors.Errorf("failed to get the chain head: %w", err) + } else if head.Height() < mi.WorkerChangeEpoch { + return xerrors.Errorf("worker key change cannot be confirmed until %d, current height is %d", mi.WorkerChangeEpoch, head.Height()) + } + + smsg, err := nodeAPI.MpoolPushMessage(ctx, &types.Message{ + From: mi.Owner, + To: maddr, + Method: miner.Methods.ConfirmUpdateWorkerKey, + Value: big.Zero(), + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Fprintln(cctx.App.Writer, "Confirm Message CID:", smsg.Cid()) + + // wait for it to get mined into a block + wait, err := nodeAPI.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Fprintln(cctx.App.Writer, "Worker change failed!") + return err + } + + mi, err = nodeAPI.StateMinerInfo(ctx, maddr, wait.TipSet) + if err != nil { + return err + } + if mi.Worker != newAddr { + return fmt.Errorf("Confirmed worker address change not reflected on chain: expected '%s', found '%s'", newAddr, mi.Worker) + } + + return nil + }, +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index ebe4f014a..da1fcbd92 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -54,6 +54,7 @@ func main() { cidCmd, blockmsgidCmd, signaturesCmd, + actorCmd, } app := &cli.App{ From 7fddbb528dff3aa540ca37b27951e2667ec44e36 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Mon, 5 Apr 2021 13:11:10 +0200 Subject: [PATCH 04/97] Introduce stateless offline dealflow, bypassing the FSM/deallists This is aproposal for an additional flag --manual-stateless-deal and a corresponding API endpoint ClientStatelessDeal. This allows firing off an offline-style deal against a miner without keeping further track of it locally. Not keeping any local state introduces the limitation of requiring free storage deals, as there is nothing to tie the payment channel setup to. Rationale/need for this type of flow is the case of incredibly large sets of data nd deals, where the client and providers have prearranged payment ahead of time, and the client has a separate-from-lotus database of deal inventory. This way the client can use their lotus node merely as a network gateway, without running into any limitations currently present in both lotus as a whole and go-fil-markets in particular. Specific context for this work is filecoin-discover, where the requirement is to onboard ~ 12,000,000 individual deals against a pool of miners with whom the client has prearranged a relationship. --- api/api_full.go | 2 + api/apistruct/struct.go | 5 ++ cli/client.go | 19 ++++- node/impl/client/client.go | 140 ++++++++++++++++++++++++++++++++----- 4 files changed, 145 insertions(+), 21 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index ca3a02c74..3ed28f429 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -289,6 +289,8 @@ type FullNode interface { ClientRemoveImport(ctx context.Context, importID multistore.StoreID) error // ClientStartDeal proposes a deal with a miner. ClientStartDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error) + // ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking. + ClientStatelessDeal(ctx context.Context, params *StartDealParams) (*cid.Cid, error) // ClientGetDealInfo returns the latest information about a given deal. ClientGetDealInfo(context.Context, cid.Cid) (*DealInfo, error) // ClientListDeals returns information about the deals made by the local client. diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 34b18cd41..fb08c24ee 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -163,6 +163,7 @@ type FullNodeStruct struct { ClientFindData func(ctx context.Context, root cid.Cid, piece *cid.Cid) ([]api.QueryOffer, error) `perm:"read"` ClientMinerQueryOffer func(ctx context.Context, miner address.Address, root cid.Cid, piece *cid.Cid) (api.QueryOffer, error) `perm:"read"` ClientStartDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"admin"` + ClientStatelessDeal func(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) `perm:"write"` ClientGetDealInfo func(context.Context, cid.Cid) (*api.DealInfo, error) `perm:"read"` ClientGetDealStatus func(context.Context, uint64) (string, error) `perm:"read"` ClientListDeals func(ctx context.Context) ([]api.DealInfo, error) `perm:"write"` @@ -604,6 +605,10 @@ func (c *FullNodeStruct) ClientStartDeal(ctx context.Context, params *api.StartD return c.Internal.ClientStartDeal(ctx, params) } +func (c *FullNodeStruct) ClientStatelessDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) { + return c.Internal.ClientStatelessDeal(ctx, params) +} + func (c *FullNodeStruct) ClientGetDealInfo(ctx context.Context, deal cid.Cid) (*api.DealInfo, error) { return c.Internal.ClientGetDealInfo(ctx, deal) } diff --git a/cli/client.go b/cli/client.go index 98f4b0229..189d34882 100644 --- a/cli/client.go +++ b/cli/client.go @@ -311,6 +311,10 @@ var clientDealCmd = &cli.Command{ Name: "manual-piece-size", Usage: "if manually specifying piece cid, used to specify size (dataCid must be to a car file)", }, + &cli.BoolFlag{ + Name: "manual-stateless-deal", + Usage: "instructs the node to send an offline deal without registering it with the deallist/fsm", + }, &cli.StringFlag{ Name: "from", Usage: "specify address to fund the deal with", @@ -447,7 +451,7 @@ var clientDealCmd = &cli.Command{ isVerified = verifiedDealParam } - proposal, err := api.ClientStartDeal(ctx, &lapi.StartDealParams{ + sdParams := &lapi.StartDealParams{ Data: ref, Wallet: a, Miner: miner, @@ -457,7 +461,18 @@ var clientDealCmd = &cli.Command{ FastRetrieval: cctx.Bool("fast-retrieval"), VerifiedDeal: isVerified, ProviderCollateral: provCol, - }) + } + + var proposal *cid.Cid + if cctx.Bool("manual-stateless-deal") { + if ref.TransferType != storagemarket.TTManual { + return xerrors.New("when manual-stateless-deal is enabled, you must also provide a 'price' of 0 and specify 'manual-piece-cid' and 'manual-piece-size'") + } + proposal, err = api.ClientStatelessDeal(ctx, sdParams) + } else { + proposal, err = api.ClientStartDeal(ctx, sdParams) + } + if err != nil { return err } diff --git a/node/impl/client/client.go b/node/impl/client/client.go index ac526ac60..0576fcbf4 100644 --- a/node/impl/client/client.go +++ b/node/impl/client/client.go @@ -31,10 +31,12 @@ import ( "github.com/ipld/go-ipld-prime/traversal/selector/builder" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/peer" + "github.com/multiformats/go-multibase" mh "github.com/multiformats/go-multihash" "go.uber.org/fx" "github.com/filecoin-project/go-address" + cborutil "github.com/filecoin-project/go-cbor-util" "github.com/filecoin-project/go-commp-utils/ffiwrapper" "github.com/filecoin-project/go-commp-utils/writer" datatransfer "github.com/filecoin-project/go-data-transfer" @@ -43,8 +45,10 @@ import ( rm "github.com/filecoin-project/go-fil-markets/retrievalmarket" "github.com/filecoin-project/go-fil-markets/shared" "github.com/filecoin-project/go-fil-markets/storagemarket" + "github.com/filecoin-project/go-fil-markets/storagemarket/network" "github.com/filecoin-project/go-multistore" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/specs-actors/v3/actors/builtin/market" marketevents "github.com/filecoin-project/lotus/markets/loggers" @@ -97,8 +101,23 @@ func (a *API) imgr() *importmgr.Mgr { } func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) { + return a.dealStarter(ctx, params, false) +} + +func (a *API) ClientStatelessDeal(ctx context.Context, params *api.StartDealParams) (*cid.Cid, error) { + return a.dealStarter(ctx, params, true) +} + +func (a *API) dealStarter(ctx context.Context, params *api.StartDealParams, isStateless bool) (*cid.Cid, error) { var storeID *multistore.StoreID - if params.Data.TransferType == storagemarket.TTGraphsync { + if isStateless { + if params.Data.TransferType != storagemarket.TTManual { + return nil, xerrors.Errorf("invalid transfer type %s for stateless storage deal", params.Data.TransferType) + } + if !params.EpochPrice.IsZero() { + return nil, xerrors.New("stateless storage deals can only be initiated with storage price of 0") + } + } else if params.Data.TransferType == storagemarket.TTGraphsync { importIDs := a.imgr().List() for _, importID := range importIDs { info, err := a.imgr().Info(importID) @@ -146,8 +165,6 @@ func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams) return nil, xerrors.New("data doesn't fit in a sector") } - providerInfo := utils.NewStorageProviderInfo(params.Miner, mi.Worker, mi.SectorSize, *mi.PeerId, mi.Multiaddrs) - dealStart := params.DealStartEpoch if dealStart <= 0 { // unset, or explicitly 'epoch undefined' ts, err := a.ChainHead(ctx) @@ -169,25 +186,110 @@ func (a *API) ClientStartDeal(ctx context.Context, params *api.StartDealParams) return nil, xerrors.Errorf("failed to get seal proof type: %w", err) } - result, err := a.SMDealClient.ProposeStorageDeal(ctx, storagemarket.ProposeStorageDealParams{ - Addr: params.Wallet, - Info: &providerInfo, - Data: params.Data, - StartEpoch: dealStart, - EndEpoch: calcDealExpiration(params.MinBlocksDuration, md, dealStart), - Price: params.EpochPrice, - Collateral: params.ProviderCollateral, - Rt: st, - FastRetrieval: params.FastRetrieval, - VerifiedDeal: params.VerifiedDeal, - StoreID: storeID, - }) + // regular flow + if !isStateless { + providerInfo := utils.NewStorageProviderInfo(params.Miner, mi.Worker, mi.SectorSize, *mi.PeerId, mi.Multiaddrs) - if err != nil { - return nil, xerrors.Errorf("failed to start deal: %w", err) + result, err := a.SMDealClient.ProposeStorageDeal(ctx, storagemarket.ProposeStorageDealParams{ + Addr: params.Wallet, + Info: &providerInfo, + Data: params.Data, + StartEpoch: dealStart, + EndEpoch: calcDealExpiration(params.MinBlocksDuration, md, dealStart), + Price: params.EpochPrice, + Collateral: params.ProviderCollateral, + Rt: st, + FastRetrieval: params.FastRetrieval, + VerifiedDeal: params.VerifiedDeal, + StoreID: storeID, + }) + + if err != nil { + return nil, xerrors.Errorf("failed to start deal: %w", err) + } + + return &result.ProposalCid, nil } - return &result.ProposalCid, nil + // + // stateless flow from here to the end + // + + dealProposal := &market.DealProposal{ + PieceCID: *params.Data.PieceCid, + PieceSize: params.Data.PieceSize.Padded(), + Client: walletKey, + Provider: params.Miner, + Label: params.Data.Root.Encode(multibase.MustNewEncoder('u')), + StartEpoch: dealStart, + EndEpoch: calcDealExpiration(params.MinBlocksDuration, md, dealStart), + StoragePricePerEpoch: big.Zero(), + ProviderCollateral: params.ProviderCollateral, + ClientCollateral: big.Zero(), + VerifiedDeal: params.VerifiedDeal, + } + + if dealProposal.ProviderCollateral.IsZero() { + networkCollateral, err := a.StateDealProviderCollateralBounds(ctx, params.Data.PieceSize.Padded(), params.VerifiedDeal, types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("failed to determine minimum provider collateral: %w", err) + } + dealProposal.ProviderCollateral = networkCollateral.Min + } + + dealProposalSerialized, err := cborutil.Dump(dealProposal) + if err != nil { + return nil, xerrors.Errorf("failed to serialize deal proposal: %w", err) + } + + dealProposalSig, err := a.WalletSign(ctx, walletKey, dealProposalSerialized) + if err != nil { + return nil, xerrors.Errorf("failed to sign proposal : %w", err) + } + + dealProposalSigned := &market.ClientDealProposal{ + Proposal: *dealProposal, + ClientSignature: *dealProposalSig, + } + dStream, err := network.NewFromLibp2pHost(a.Host, + network.RetryParameters(0, 0, 0, 0), + ).NewDealStream(ctx, *mi.PeerId) + if err != nil { + return nil, xerrors.Errorf("opening dealstream to %s/%s failed: %w", params.Miner, *mi.PeerId, err) + } + + if err = dStream.WriteDealProposal(network.Proposal{ + FastRetrieval: true, + DealProposal: dealProposalSigned, + Piece: &storagemarket.DataRef{ + TransferType: storagemarket.TTManual, + Root: params.Data.Root, + PieceCid: params.Data.PieceCid, + PieceSize: params.Data.PieceSize, + }, + }); err != nil { + return nil, xerrors.Errorf("sending deal proposal failed: %w", err) + } + + resp, _, err := dStream.ReadDealResponse() + if err != nil { + return nil, xerrors.Errorf("reading proposal response failed: %w", err) + } + + dealProposalIpld, err := cborutil.AsIpld(dealProposalSigned) + if err != nil { + return nil, xerrors.Errorf("serializing proposal node failed: %w", err) + } + + if !dealProposalIpld.Cid().Equals(resp.Response.Proposal) { + return nil, xerrors.Errorf("provider returned proposal cid %s but we expected %s", resp.Response.Proposal, dealProposalIpld.Cid()) + } + + if resp.Response.State != storagemarket.StorageDealWaitingForData { + return nil, xerrors.Errorf("provider returned unexpected state %d for proposal %s, with message: %s", resp.Response.State, resp.Response.Proposal, resp.Response.Message) + } + + return &resp.Response.Proposal, nil } func (a *API) ClientListDeals(ctx context.Context) ([]api.DealInfo, error) { From b8dff22a401a549d84ef026a8e02117c8bad8c20 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Mon, 5 Apr 2021 19:15:32 +0200 Subject: [PATCH 05/97] `make gen` --- build/openrpc/full.json.gz | Bin 22798 -> 22899 bytes build/openrpc/miner.json.gz | Bin 7828 -> 7829 bytes build/openrpc/worker.json.gz | Bin 2574 -> 2577 bytes documentation/en/api-methods.md | 34 ++++++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 297a6411e87e5ee299eef28d9911538e51e12e9a..88ab4a3d32b69b798a84e20e3e315eed496467c6 100644 GIT binary patch literal 22899 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 M;~*6mhJo_`0778xjQ{`u literal 22798 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 SkN+hY_~` diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 07ec95fd47ef22b9fbe2614ac6dbb01c244cde80..85e7821919e28976b0aec0204a4334c50e409f8c 100644 GIT binary patch literal 7829 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 literal 7828 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index cfe344989ae17abed055d93710ffbc919a380d23..64e17f73d76221ed8ce76c8177b93face6942748 100644 GIT binary patch delta 1116 zcmV-i1f%*mYBpP=knMJWhrs(FnoV`<8D|Jo@C-Nx0!5mB?K zxSuF~Z-=x0i91lkd0etv&U0*B#24ry7K!KX{HHBAPgx%@t4M#Pqz+3UE@e}kU`i?x zmTbqP%cyZVzC3;hwV7(1y2hzHP^a$QX#tp1MqK(6qE}K8)lqGdLHn@w58ouJ}aVg-DtG`%du0o@R zu`&wyA*vahm<$i=CLuLPWx7aBTS)>l>5WxbS%rSSRrr%l1SNkJdkWUkMnuhIMbrst z@_kYh;(}~Nh@h)(0+3~MJ8??$7%B+NGnIB5oev4SgvkFgBDh?zTUg;k%~JkTqrVw1 z?<2Q~8}gp_^c&FWtW-8&i!uJ5YsE>ys^Um+8>GAhDUFO(QjZjIzMcdDby{D)ZuYJ+ zjpb{JA=D;Y5axey59ziMpP>7YH6#tag{Htj=(iD7bm!=P1}v&~#eCBrl8OjbZO2)j z`o-hS%wh6LiKUcMFu0SQPMd-4uvDTTH-7=7)-KJMt|&mEy!A~`1tJbi*29(L3j1=4 z*gxkA&Y1%b;Zl3<=CK;FOp@8(y4)U5!f)F%w$Wv80<0nZ46vSBHqU6s4mVKtJH5BN z*+cn!wt_YRV-qkQXu#MllamE@e~5+@EE`+`afjUVsLBe_(`u5Stmge0*(B~on73pg zWqf8gR2^a~dIy;r{piGR7)Y8eGXCqeE{4>RHN466P}cKBx}V0Y+za^H2d^?Kz6W@K zw@?wE^;okRsx_0c^AI8IX&5d0MUxU*Os3Ge15D8gO-k}=)$jihFeGN~VD$#0r44UM zs}(0429LO{4JMh)yW^k42GYBi&g^vFBfV2K?#Wy5{2%<`Z$FrG&;K#@CNQ7$`V-m< z=BGgb{q9}gINaN8ucE13bzbd#Ca0ka)phJnfRL+S$n+mI+R>NRoQC8yGx&{pMY_ iHV*E&Koyh02|5&Q2QL=ai~j`x0RR60!d2b@djJ6UNggZ! delta 1116 zcmV-i1f%m*u#+ zEM$fh6>e5e{fZMtP16Bk2O3PPG{UgOGGnsVL0b_znbarJOS)GR9Q zCyL+O;p~6n4%BcSm+Y4F92*z$1-ghu;<-EjX$#I%)(6ZgQYn9_!xD%~*%T+3l1hXn z+wtf!YFv&lkKaLUrW&WNaq14#se9Kc0CUQSOMgQ2N-CmyinwZ0Xjy(i*t8Z%ulK%0 z>jtIwLX-9ZrPrjaUIM4W)P9*vB&c$88)jQ-PE5^oPy=ucz#S^U_45JloK1K|Skol= z`HJ;50$p=; zaq*zvwRFMdJ@MlY7a&30X}Qq_!_il1{`wI9r48<)qK2u4jQX0jsc zgf#g+sR?mGwjxB(RW||1vbmi&rFje$1m>AayN%9=gk3`9e;E;6F4!%s@S$cY|Eba6 zjFxD8TXf|N$aDyc_`IA2eKfI6+OUpISK zna1+9#1LweEeL;exQBGxh)>Xc$QqJ{-a=DgAoSaaD!OxYKLZxkyJEiS4@pIYs~CFek0;@`?HSwXvNt0E)_w+9Pc55gv}1=GDEpn> z+uiJ;d_G%2n}D$i7!NdHJSmfu1$KXihI9*-4K9JWL+*K0WrgTzHAzrb^Ztx%689p^ zTQZO`KC>ID4zU%zgG`NnbmBJ*B+V8X|MglIL+Z#H-eh_x>-i$xPvceY1$^y;SD6*x z13bW6sEE&ctl13Jnn~Gth!FNPjF$bPNeL|`Q|R0Qrs#wwC3&^#_kRc&5;I`;dV|r@ zhPR~EiW3flN8Hv1lT7B_@lRp{>D^0bb~^8o-l-b*{z`1I43W!Ok0P z*GB^Cr(0IUTgkYr1wS>BD#cVXchUgXUMSf<0M^0hOqx7G1N9Bmzd)#yNe90(N(gTph4?)>?m2p<76+9fPVUPdZi(H)Ga@}nH$(^hOLo_5Jr?d;?Z%LFG`BuPEn4Gf;Xe)Fzd i8wdAWpxzXd!wEVQO?aYj@xK570RR6dg{F=HdjJ52nIfeC diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index 2830be0bd..f6ee521f8 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -57,6 +57,7 @@ * [ClientRetrieveTryRestartInsufficientFunds](#ClientRetrieveTryRestartInsufficientFunds) * [ClientRetrieveWithEvents](#ClientRetrieveWithEvents) * [ClientStartDeal](#ClientStartDeal) + * [ClientStatelessDeal](#ClientStatelessDeal) * [Create](#Create) * [CreateBackup](#CreateBackup) * [Gas](#Gas) @@ -1501,6 +1502,39 @@ Inputs: Response: `null` +### ClientStatelessDeal +ClientStatelessDeal fire-and-forget-proposes an offline deal to a miner without subsequent tracking. + + +Perms: write + +Inputs: +```json +[ + { + "Data": { + "TransferType": "string value", + "Root": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "PieceCid": null, + "PieceSize": 1024, + "RawBlockSize": 42 + }, + "Wallet": "f01234", + "Miner": "f01234", + "EpochPrice": "0", + "MinBlocksDuration": 42, + "ProviderCollateral": "0", + "DealStartEpoch": 10101, + "FastRetrieval": true, + "VerifiedDeal": true + } +] +``` + +Response: `null` + ## Create From e6045bba85fef72352c98c778df4e01f61f9c5e7 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 17 Mar 2021 02:35:44 -0400 Subject: [PATCH 06/97] Introduce a release issue template --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 103 ++++++++ scripts/mkreleaselog | 233 +++++++++++++++++++ 2 files changed, 336 insertions(+) create mode 100644 documentation/misc/RELEASE_ISSUE_TEMPLATE.md create mode 100644 scripts/mkreleaselog diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md new file mode 100644 index 000000000..76b61bab3 --- /dev/null +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -0,0 +1,103 @@ +> Release Issue Template + +# Lotus X.Y.Z Release + +We're happy to announce Lotus X.Y.Z... + +## 🗺 What's left for release + + + +## 🚢 Estimated shipping date + + + +## 🔦 Highlights + +< top highlights for this release notes > + +## Changelog + +< changelog generated by bin/mkreleaselog > + +## ✅ Release Checklist + +First steps: + + - [ ] Fork a new branch (`release/vX.Y.Z`) from `master` and make any further release related changes to this branch. If any "non-trivial" changes get added to the release, uncheck all the checkboxes and return to this stage. + - [ ] Prep the changelog + - [ ] Bump the version in `version.go` in the `master` branch to `vX.(Y+1).0-dev`. + - [ ] Follow the RC release process to cut the first RC. + +Prepping an RC: + +- [ ] version string in `build/version.go` has been updated (in the `release/vX.Y.Z` branch). +- [ ] tag commit with `vX.Y.Z-rcN` +- [ ] cut a pre-release on [github](https://github.com/filecoin-project/lotus/releases) + +Testing an RC: + +- [ ] **Stage 0 - Automated Testing** + - Automated Testing + - [ ] CI: Ensure that all tests are passing. + - [ ] Testground tests + +- [ ] **Stage 1 - Internal Testing** + - Upgrade our testnet infra + - [ ] 1 bootstrap node + - [ ] 1 miner + - [ ] Scratch nodes + - [ ] Wait 24 hours + - [ ] Remaining testnet infra + - Upgrade our mainnet infra + - [ ] Subset of development full archival nodes + - [ ]Subset of bootstrappers (1 per region) + - Report on new block validation time + - TODO: What other stats would we care about? + - If anything has worsened significantly, investigate + fix + - Confirm the following work (some combination of Testground / Calibnet / Mainnet / MinerX) + - [ ] Seal a sector + - [ ] make a deal + - [ ] Submit a PoSt + - [ ] (ideally) let a sector go faulty, and see it be recovered + +- [ ] **Stage 2 - Community Dev Testing** + - [ ] Inform MinerX / early testers + - [ ] Ask close ecosystem partners to test their projects with the upgrade + - [ ] Powergate + - TODO: List of partners + +- [ ] **Stage 3 - Community Prod Testing** + - [ ] Documentation + - [ ] Ensure that [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) is up to date + - [ ] TODO: Other docs checks? + - [ ] Invite the wider community through (link to the release issue): + - [ ] TODO: How should we announce this? + +- [ ] **Stage 4 - Release** + - [ ] Final preparation + - [ ] Verify that version string in [`version.go`](https://github.com/ipfs/go-ipfs/tree/master/version.go) has been updated. + - [ ] Ensure that [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) is up to date + - [ ] Ensure that [README.md](https://github.com/filecoin-project/lotus/blob/master/README.md) is up to date + - [ ] Merge `release-vX.Y.Z` into the `releases` branch. + - [ ] Tag this merge commit (on the `releases` branch) with `vX.Y.Z`. + - [ ] Cut the release on Github. + - [ ] Final announcements + - [ ] Update network.filecoin.io + - [ ] TODO: What / where else? + +- [ ] **Post-Release** + - [ ] Merge the `releases` branch back into `master`, ignoring the changes to `version.go` (keep the `-dev` version from master). + - [ ] Create an issue using this release issue template for the _next_ release. + +## ❤️ Contributors + +< list generated by bin/mkreleaselog > + +Would you like to contribute to Lotus and don't know how? Well, there are a few places you can get started: + +- TODO + +## ⁉️ Do you have questions? + +TODO \ No newline at end of file diff --git a/scripts/mkreleaselog b/scripts/mkreleaselog new file mode 100644 index 000000000..0588adaee --- /dev/null +++ b/scripts/mkreleaselog @@ -0,0 +1,233 @@ +#!/bin/zsh +#set -x +set -euo pipefail +export GO111MODULE=on +export GOPATH="$(go env GOPATH)" + +alias jq="jq --unbuffered" + +AUTHORS=( + # orgs + ipfs + ipld + libp2p + multiformats + filecoin-project + ipfs-shipyard + + # Authors of personal repos used by go-ipfs that should be mentioned in the + # release notes. + whyrusleeping + Kubuxu + jbenet + Stebalien + marten-seemann + hsanjuan + lucas-clemente + warpfork +) + +[[ -n "${REPO_FILTER+x}" ]] || REPO_FILTER="github.com/(${$(printf "|%s" "${AUTHORS[@]}"):1})" + +[[ -n "${IGNORED_FILES+x}" ]] || IGNORED_FILES='^\(\.gx\|package\.json\|\.travis\.yml\|go.mod\|go\.sum|\.github|\.circleci\)$' + +NL=$'\n' + +ROOT_DIR="$(git rev-parse --show-toplevel)" + +msg() { + echo "$*" >&2 +} + +statlog() { + local rpath="$GOPATH/src/$1" + local start="${2:-}" + local end="${3:-HEAD}" + local mailmap_file="$rpath/.mailmap" + if ! [[ -e "$mailmap_file" ]]; then + mailmap_file="$ROOT_DIR/.mailmap" + fi + + git -C "$rpath" -c mailmap.file="$mailmap_file" log --use-mailmap --shortstat --no-merges --pretty="tformat:%H%n%aN%n%aE" "$start..$end" | while + read hash + read name + read email + read _ # empty line + read changes + do + changed=0 + insertions=0 + deletions=0 + while read count event; do + if [[ "$event" =~ ^file ]]; then + changed=$count + elif [[ "$event" =~ ^insertion ]]; then + insertions=$count + elif [[ "$event" =~ ^deletion ]]; then + deletions=$count + else + echo "unknown event $event" >&2 + exit 1 + fi + done<<<"${changes//,/$NL}" + + jq -n \ + --arg "hash" "$hash" \ + --arg "name" "$name" \ + --arg "email" "$email" \ + --argjson "changed" "$changed" \ + --argjson "insertions" "$insertions" \ + --argjson "deletions" "$deletions" \ + '{Commit: $hash, Author: $name, Email: $email, Files: $changed, Insertions: $insertions, Deletions: $deletions}' + done +} + +# Returns a stream of deps changed between $1 and $2. +dep_changes() { + { + <"$1" + <"$2" + } | jq -s 'JOIN(INDEX(.[0][]; .Path); .[1][]; .Path; {Path: .[0].Path, Old: (.[1] | del(.Path)), New: (.[0] | del(.Path))}) | select(.New.Version != .Old.Version)' +} + +# resolve_commits resolves a git ref for each version. +resolve_commits() { + jq '. + {Ref: (.Version|capture("^((?.*)\\+incompatible|v.*-(0\\.)?[0-9]{14}-(?[a-f0-9]{12})|(?v.*))$") | .ref1 // .ref2 // .ref3)}' +} + +pr_link() { + local repo="$1" + local prnum="$2" + local ghname="${repo##github.com/}" + printf -- "[%s#%s](https://%s/pull/%s)" "$ghname" "$prnum" "$repo" "$prnum" +} + +# Generate a release log for a range of commits in a single repo. +release_log() { + setopt local_options BASH_REMATCH + + local repo="$1" + local start="$2" + local end="${3:-HEAD}" + local dir="$GOPATH/src/$repo" + + local commit pr + git -C "$dir" log \ + --format='tformat:%H %s' \ + --first-parent \ + "$start..$end" | + while read commit subject; do + # Skip gx-only PRs. + git -C "$dir" diff-tree --no-commit-id --name-only "$commit^" "$commit" | + grep -v "${IGNORED_FILES}" >/dev/null || continue + + if [[ "$subject" =~ '^Merge pull request #([0-9]+) from' ]]; then + local prnum="${BASH_REMATCH[2]}" + local desc="$(git -C "$dir" show --summary --format='tformat:%b' "$commit" | head -1)" + printf -- "- %s (%s)\n" "$desc" "$(pr_link "$repo" "$prnum")" + elif [[ "$subject" =~ '\(#([0-9]+)\)$' ]]; then + local prnum="${BASH_REMATCH[2]}" + printf -- "- %s (%s)\n" "$subject" "$(pr_link "$repo" "$prnum")" + else + printf -- "- %s\n" "$subject" + fi + done +} + +indent() { + sed -e 's/^/ /' +} + +mod_deps() { + go list -json -m all | jq 'select(.Version != null)' +} + +ensure() { + local repo="$1" + local commit="$2" + local rpath="$GOPATH/src/$repo" + if [[ ! -d "$rpath" ]]; then + msg "Cloning $repo..." + git clone "http://$repo" "$rpath" >&2 + fi + + if ! git -C "$rpath" rev-parse --verify "$commit" >/dev/null; then + msg "Fetching $repo..." + git -C "$rpath" fetch --all >&2 + fi + + git -C "$rpath" rev-parse --verify "$commit" >/dev/null || return 1 +} + +statsummary() { + jq -s 'group_by(.Author)[] | {Author: .[0].Author, Commits: (. | length), Insertions: (map(.Insertions) | add), Deletions: (map(.Deletions) | add), Files: (map(.Files) | add)}' | + jq '. + {Lines: (.Deletions + .Insertions)}' +} + +recursive_release_log() { + local start="${1:-$(git tag -l | sort -V | grep -v -- '-rc' | grep 'v'| tail -n1)}" + local end="${2:-$(git rev-parse HEAD)}" + local repo_root="$(git rev-parse --show-toplevel)" + local package="$(cd "$repo_root" && go list)" + + if ! [[ "${GOPATH}/${package}" != "${repo_root}" ]]; then + echo "This script requires the target package and all dependencies to live in a GOPATH." + return 1 + fi + + ( + local result=0 + local workspace="$(mktemp -d)" + trap "$(printf 'rm -rf "%q"' "$workspace")" INT TERM EXIT + cd "$workspace" + + echo "Computing old deps..." >&2 + git -C "$repo_root" show "$start:go.mod" >go.mod + mod_deps | resolve_commits | jq -s > old_deps.json + + echo "Computing new deps..." >&2 + git -C "$repo_root" show "$end:go.mod" >go.mod + mod_deps | resolve_commits | jq -s > new_deps.json + + rm -f go.mod go.sum + + printf -- "Generating Changelog for %s %s..%s\n" "$package" "$start" "$end" >&2 + + printf -- "- %s:\n" "$package" + release_log "$package" "$start" "$end" | indent + + + statlog "$package" "$start" "$end" > statlog.json + + dep_changes old_deps.json new_deps.json | + jq --arg filter "$REPO_FILTER" 'select(.Path | match($filter))' | + # Compute changelogs + jq -r '"\(.Path) \(.New.Version) \(.New.Ref) \(.Old.Version) \(.Old.Ref // "")"' | + while read repo new new_ref old old_ref; do + if ! ensure "$repo" "$new_ref"; then + result=1 + local changelog="failed to fetch repo" + else + statlog "$repo" "$old_ref" "$new_ref" >> statlog.json + local changelog="$(release_log "$repo" "$old_ref" "$new_ref")" + fi + if [[ -n "$changelog" ]]; then + printf -- "- %s (%s -> %s):\n" "$repo" "$old" "$new" + echo "$changelog" | indent + fi + done + + echo + echo "Contributors" + echo + + echo "| Contributor | Commits | Lines ± | Files Changed |" + echo "|-------------|---------|---------|---------------|" + statsummary Date: Thu, 8 Apr 2021 00:28:24 -0400 Subject: [PATCH 07/97] Update documentation/misc/RELEASE_ISSUE_TEMPLATE.md Co-authored-by: Jennifer <42981373+jennijuju@users.noreply.github.com> --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 76b61bab3..af1e4ffc5 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -72,7 +72,8 @@ Testing an RC: - [ ] Ensure that [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) is up to date - [ ] TODO: Other docs checks? - [ ] Invite the wider community through (link to the release issue): - - [ ] TODO: How should we announce this? + - [ ] Create a lotus disucssion, example [here](https://github.com/filecoin-project/lotus/discussions/5595) + - [ ] Link the disucssion in #fil-lotus on Filecoin slack - [ ] **Stage 4 - Release** - [ ] Final preparation @@ -100,4 +101,4 @@ Would you like to contribute to Lotus and don't know how? Well, there are a few ## ⁉️ Do you have questions? -TODO \ No newline at end of file +TODO From 536d267cbba925532ad009402656ceed0116bd3f Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 8 Apr 2021 01:34:53 -0400 Subject: [PATCH 08/97] Update documentation/misc/RELEASE_ISSUE_TEMPLATE.md Co-authored-by: Jennifer <42981373+jennijuju@users.noreply.github.com> --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index af1e4ffc5..f33123e1c 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -85,7 +85,8 @@ Testing an RC: - [ ] Cut the release on Github. - [ ] Final announcements - [ ] Update network.filecoin.io - - [ ] TODO: What / where else? + - [ ] Add a comment when the final release is tagged, example [here](https://github.com/filecoin-project/lotus/discussions/5905#discussioncomment-571752) + - [ ] repost in #fil-lotus in filecoin slack - [ ] **Post-Release** - [ ] Merge the `releases` branch back into `master`, ignoring the changes to `version.go` (keep the `-dev` version from master). From b6949aa4ba88ba1148ff320ea028d43d8e77d226 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 8 Apr 2021 01:36:24 -0400 Subject: [PATCH 09/97] Incorporate feedback into release issue template --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 47 +++++++++++--------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index f33123e1c..d9e02efba 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -16,24 +16,19 @@ We're happy to announce Lotus X.Y.Z... < top highlights for this release notes > -## Changelog - -< changelog generated by bin/mkreleaselog > - ## ✅ Release Checklist First steps: - [ ] Fork a new branch (`release/vX.Y.Z`) from `master` and make any further release related changes to this branch. If any "non-trivial" changes get added to the release, uncheck all the checkboxes and return to this stage. - - [ ] Prep the changelog + - [ ] Prep the changelog using `bin/mkreleaselog`, and add it to `CHANGELOG.md` - [ ] Bump the version in `version.go` in the `master` branch to `vX.(Y+1).0-dev`. - - [ ] Follow the RC release process to cut the first RC. Prepping an RC: - [ ] version string in `build/version.go` has been updated (in the `release/vX.Y.Z` branch). - [ ] tag commit with `vX.Y.Z-rcN` -- [ ] cut a pre-release on [github](https://github.com/filecoin-project/lotus/releases) +- [ ] cut a pre-release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=true) Testing an RC: @@ -47,30 +42,42 @@ Testing an RC: - [ ] 1 bootstrap node - [ ] 1 miner - [ ] Scratch nodes - - [ ] Wait 24 hours + - [ ] Wait 24 hours, confirm nodes stay in sync - [ ] Remaining testnet infra - Upgrade our mainnet infra - [ ] Subset of development full archival nodes - - [ ]Subset of bootstrappers (1 per region) - - Report on new block validation time - - TODO: What other stats would we care about? - - If anything has worsened significantly, investigate + fix - - Confirm the following work (some combination of Testground / Calibnet / Mainnet / MinerX) + - [ ] Subset of bootstrappers (1 per region) + - [ ] Confirm nodes stay in sync + - Metrics report + - Block validation time + - Memory / CPU usage + - Number of goroutines + - IPLD block read latency + - Bandwidth usage + - [ ] If anything has worsened significantly, investigate + fix + - Confirm the following work (some combination of Testground / Calibnet / Mainnet / beta users) - [ ] Seal a sector - [ ] make a deal - [ ] Submit a PoSt - - [ ] (ideally) let a sector go faulty, and see it be recovered + - [ ] (optional) let a sector go faulty, and see it be recovered - [ ] **Stage 2 - Community Dev Testing** - - [ ] Inform MinerX / early testers - - [ ] Ask close ecosystem partners to test their projects with the upgrade + - [ ] Inform beta miners (@lotus-early-testers-miner in Filecoin Slack #fil-lotus) + - [ ] Ask close ecosystem partners to test their projects (@lotus-early-testers-eco-dev in Filecoin slack #fil-lotus) - [ ] Powergate - - TODO: List of partners + - [ ] Glif + - [ ] Zondax + - [ ] Stats dashboard + - [ ] Community dashboards + - [ ] Infura + - [ ] Sentinel + - [ ] Protofire + - [ ] Fleek - [ ] **Stage 3 - Community Prod Testing** - [ ] Documentation - [ ] Ensure that [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) is up to date - - [ ] TODO: Other docs checks? + - [ ] Check if any [config](https://docs.filecoin.io/get-started/lotus/configuration-and-advanced-usage/#configuration) updates are needed - [ ] Invite the wider community through (link to the release issue): - [ ] Create a lotus disucssion, example [here](https://github.com/filecoin-project/lotus/discussions/5595) - [ ] Link the disucssion in #fil-lotus on Filecoin slack @@ -82,7 +89,7 @@ Testing an RC: - [ ] Ensure that [README.md](https://github.com/filecoin-project/lotus/blob/master/README.md) is up to date - [ ] Merge `release-vX.Y.Z` into the `releases` branch. - [ ] Tag this merge commit (on the `releases` branch) with `vX.Y.Z`. - - [ ] Cut the release on Github. + - [ ] Cut the release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=true&target=releases). - [ ] Final announcements - [ ] Update network.filecoin.io - [ ] Add a comment when the final release is tagged, example [here](https://github.com/filecoin-project/lotus/discussions/5905#discussioncomment-571752) @@ -102,4 +109,4 @@ Would you like to contribute to Lotus and don't know how? Well, there are a few ## ⁉️ Do you have questions? -TODO +Leave a comment [here]() if you have any questions. From ff48a70ed9daffc638377b54db3dc37480066e90 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sat, 10 Apr 2021 02:33:34 -0400 Subject: [PATCH 10/97] Speed up StateListMessages in some cases --- node/impl/full/state.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 7fcd9dc13..29b7d13f2 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -822,8 +822,31 @@ func (a *StateAPI) StateListMessages(ctx context.Context, match *api.MessageMatc if match.To == address.Undef && match.From == address.Undef { return nil, xerrors.Errorf("must specify at least To or From in message filter") + } else if match.To != address.Undef { + _, err := a.StateLookupID(ctx, match.To, tsk) + + // if the recipient doesn't exist at the start point, we're not gonna find any matches + if xerrors.Is(err, types.ErrActorNotFound) { + return nil, nil + } + + if err != nil { + return nil, xerrors.Errorf("looking up match.To: %w", err) + } + } else if match.From != address.Undef { + _, err := a.StateLookupID(ctx, match.From, tsk) + + // if the sender doesn't exist at the start point, we're not gonna find any matches + if xerrors.Is(err, types.ErrActorNotFound) { + return nil, nil + } + + if err != nil { + return nil, xerrors.Errorf("looking up match.From: %w", err) + } } + // TODO: This should probably match on both ID and robust address, no? matchFunc := func(msg *types.Message) bool { if match.From != address.Undef && match.From != msg.From { return false From 0bc94b554c550f9482070da942fe2492a6bad505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 13 Apr 2021 12:02:10 +0200 Subject: [PATCH 11/97] Refer to scripts/mkreleaselog --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 4 ++-- scripts/mkreleaselog | 0 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 scripts/mkreleaselog diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index d9e02efba..7692058cb 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -21,7 +21,7 @@ We're happy to announce Lotus X.Y.Z... First steps: - [ ] Fork a new branch (`release/vX.Y.Z`) from `master` and make any further release related changes to this branch. If any "non-trivial" changes get added to the release, uncheck all the checkboxes and return to this stage. - - [ ] Prep the changelog using `bin/mkreleaselog`, and add it to `CHANGELOG.md` + - [ ] Prep the changelog using `scripts/mkreleaselog`, and add it to `CHANGELOG.md` - [ ] Bump the version in `version.go` in the `master` branch to `vX.(Y+1).0-dev`. Prepping an RC: @@ -101,7 +101,7 @@ Testing an RC: ## ❤️ Contributors -< list generated by bin/mkreleaselog > +< list generated by scripts/mkreleaselog > Would you like to contribute to Lotus and don't know how? Well, there are a few places you can get started: diff --git a/scripts/mkreleaselog b/scripts/mkreleaselog old mode 100644 new mode 100755 From 34aa267100584632db98c90ed26822e608a0e78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 13 Apr 2021 12:20:30 +0200 Subject: [PATCH 12/97] Make mkreleaselog work a bit more --- scripts/mkreleaselog | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/mkreleaselog b/scripts/mkreleaselog index 0588adaee..3c30a3195 100755 --- a/scripts/mkreleaselog +++ b/scripts/mkreleaselog @@ -1,5 +1,5 @@ #!/bin/zsh -#set -x +set -x set -euo pipefail export GO111MODULE=on export GOPATH="$(go env GOPATH)" @@ -139,6 +139,7 @@ indent() { } mod_deps() { + go mod download go list -json -m all | jq 'select(.Version != null)' } @@ -178,9 +179,13 @@ recursive_release_log() { ( local result=0 local workspace="$(mktemp -d)" - trap "$(printf 'rm -rf "%q"' "$workspace")" INT TERM EXIT + #trap "$(printf 'rm -rf "%q"' "$workspace")" INT TERM EXIT cd "$workspace" + mkdir extern + ln -s "$repo_root"/extern/filecoin-ffi extern/filecoin-ffi + ln -s "$repo_root"/extern/test-vectors extern/test-vectors + echo "Computing old deps..." >&2 git -C "$repo_root" show "$start:go.mod" >go.mod mod_deps | resolve_commits | jq -s > old_deps.json From 8972152eb12e64d7a9bd2e2a5626004acb23b1d4 Mon Sep 17 00:00:00 2001 From: chadwick2143 Date: Wed, 14 Apr 2021 16:34:57 +0800 Subject: [PATCH 13/97] Set MaxFee for lotus-shed actor withdraw to 0.1FIL --- cmd/lotus-shed/actor.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-shed/actor.go b/cmd/lotus-shed/actor.go index 47ec78024..614cf877f 100644 --- a/cmd/lotus-shed/actor.go +++ b/cmd/lotus-shed/actor.go @@ -9,6 +9,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/api" miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" @@ -109,7 +110,7 @@ var actorWithdrawCmd = &cli.Command{ Value: types.NewInt(0), Method: miner.Methods.WithdrawBalance, Params: params, - }, nil) + }, &api.MessageSendSpec{MaxFee: abi.TokenAmount(types.MustParseFIL("0.1"))}) if err != nil { return err } From cba911ab5ece902fd58b9132c12e0d37507e0800 Mon Sep 17 00:00:00 2001 From: chadwick2143 Date: Wed, 14 Apr 2021 16:37:59 +0800 Subject: [PATCH 14/97] Add actorControlList command --- cmd/lotus-shed/actor.go | 111 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/cmd/lotus-shed/actor.go b/cmd/lotus-shed/actor.go index 614cf877f..9d242e2df 100644 --- a/cmd/lotus-shed/actor.go +++ b/cmd/lotus-shed/actor.go @@ -2,7 +2,9 @@ package main import ( "fmt" + "os" + "github.com/fatih/color" "github.com/urfave/cli/v2" "golang.org/x/xerrors" @@ -18,6 +20,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/lib/tablewriter" ) var actorCmd = &cli.Command{ @@ -245,10 +248,118 @@ var actorControl = &cli.Command{ Name: "control", Usage: "Manage control addresses", Subcommands: []*cli.Command{ + actorControlList, actorControlSet, }, } +var actorControlList = &cli.Command{ + Name: "list", + Usage: "Get currently set control addresses", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "actor", + Usage: "specify the address of miner actor", + }, + &cli.BoolFlag{ + Name: "verbose", + }, + &cli.BoolFlag{ + Name: "color", + Value: true, + }, + }, + Action: func(cctx *cli.Context) error { + color.NoColor = !cctx.Bool("color") + + var maddr address.Address + if act := cctx.String("actor"); act != "" { + var err error + maddr, err = address.NewFromString(act) + if err != nil { + return fmt.Errorf("parsing address %s: %w", act, err) + } + } + + nodeAPI, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + if maddr.Empty() { + minerAPI, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + + maddr, err = minerAPI.ActorAddress(ctx) + if err != nil { + return err + } + } + + mi, err := nodeAPI.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + tw := tablewriter.New( + tablewriter.Col("name"), + tablewriter.Col("ID"), + tablewriter.Col("key"), + tablewriter.Col("balance"), + ) + + printKey := func(name string, a address.Address) { + b, err := nodeAPI.WalletBalance(ctx, a) + if err != nil { + fmt.Printf("%s\t%s: error getting balance: %s\n", name, a, err) + return + } + + k, err := nodeAPI.StateAccountKey(ctx, a, types.EmptyTSK) + if err != nil { + fmt.Printf("%s\t%s: error getting account key: %s\n", name, a, err) + return + } + + kstr := k.String() + if !cctx.Bool("verbose") { + kstr = kstr[:9] + "..." + } + + bstr := types.FIL(b).String() + switch { + case b.LessThan(types.FromFil(10)): + bstr = color.RedString(bstr) + case b.LessThan(types.FromFil(50)): + bstr = color.YellowString(bstr) + default: + bstr = color.GreenString(bstr) + } + + tw.Write(map[string]interface{}{ + "name": name, + "ID": a, + "key": kstr, + "balance": bstr, + }) + } + + printKey("owner", mi.Owner) + printKey("worker", mi.Worker) + for i, ca := range mi.ControlAddresses { + printKey(fmt.Sprintf("control-%d", i), ca) + } + + return tw.Flush(os.Stdout) + }, +} + var actorControlSet = &cli.Command{ Name: "set", Usage: "Set control address(-es)", From c915170b58dc8c2ec7f1a80c7659b3177e045b56 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 6 Apr 2021 12:45:25 +0800 Subject: [PATCH 15/97] remove duplicate ask and calculate ping before lock --- cli/client.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/cli/client.go b/cli/client.go index d3074e91d..022480807 100644 --- a/cli/client.go +++ b/cli/client.go @@ -1296,7 +1296,8 @@ var clientListAsksCmd = &cli.Command{ Usage: "List asks for top miners", Flags: []cli.Flag{ &cli.BoolFlag{ - Name: "by-ping", + Name: "by-ping", + Usage: "sort by ping", }, &cli.StringFlag{ Name: "output-format", @@ -1445,23 +1446,18 @@ loop: return } + rt := time.Now() ask, err := api.ClientQueryAsk(ctx, *mi.PeerId, miner) if err != nil { return } - - rt := time.Now() - - _, err = api.ClientQueryAsk(ctx, *mi.PeerId, miner) - if err != nil { - return - } + pingDuration := time.Now().Sub(rt) atomic.AddInt64(&got, 1) lk.Lock() asks = append(asks, QueriedAsk{ Ask: ask, - Ping: time.Now().Sub(rt), + Ping: pingDuration, }) lk.Unlock() }(miner) From b9cd3645359f30a8d724e510b489862f02b9e491 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 20 Apr 2021 10:26:02 +0800 Subject: [PATCH 16/97] update ping lock --- cli/client.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cli/client.go b/cli/client.go index 022480807..d7307e067 100644 --- a/cli/client.go +++ b/cli/client.go @@ -1446,11 +1446,16 @@ loop: return } - rt := time.Now() ask, err := api.ClientQueryAsk(ctx, *mi.PeerId, miner) if err != nil { return } + + rt := time.Now() + _, err = api.ClientQueryAsk(ctx, *mi.PeerId, miner) + if err != nil { + return + } pingDuration := time.Now().Sub(rt) atomic.AddInt64(&got, 1) From 26d14a6262434d04504da38e857d861d1129d0db Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 21 Apr 2021 12:55:14 -0700 Subject: [PATCH 17/97] fix: partially support v2+ go modules This patch supports branch-based v2 modules, but not directory-based (i.e., not vN modules with `vN/` directories). --- scripts/mkreleaselog | 46 +++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/scripts/mkreleaselog b/scripts/mkreleaselog index 3c30a3195..ffc3d935e 100755 --- a/scripts/mkreleaselog +++ b/scripts/mkreleaselog @@ -40,7 +40,8 @@ msg() { } statlog() { - local rpath="$GOPATH/src/$1" + local module="$1" + local rpath="$GOPATH/src/$(strip_version "$module")" local start="${2:-}" local end="${3:-HEAD}" local mailmap_file="$rpath/.mailmap" @@ -106,9 +107,10 @@ pr_link() { release_log() { setopt local_options BASH_REMATCH - local repo="$1" + local module="$1" local start="$2" local end="${3:-HEAD}" + local repo="$(strip_version "$1")" local dir="$GOPATH/src/$repo" local commit pr @@ -139,12 +141,11 @@ indent() { } mod_deps() { - go mod download - go list -json -m all | jq 'select(.Version != null)' + go list -mod=mod -json -m all | jq 'select(.Version != null)' } ensure() { - local repo="$1" + local repo="$(strip_version "$1")" local commit="$2" local rpath="$GOPATH/src/$repo" if [[ ! -d "$rpath" ]]; then @@ -165,21 +166,30 @@ statsummary() { jq '. + {Lines: (.Deletions + .Insertions)}' } +strip_version() { + local repo="$1" + if [[ "$repo" =~ '.*/v[0-9]+$' ]]; then + repo="$(dirname "$repo")" + fi + echo "$repo" +} + recursive_release_log() { local start="${1:-$(git tag -l | sort -V | grep -v -- '-rc' | grep 'v'| tail -n1)}" local end="${2:-$(git rev-parse HEAD)}" local repo_root="$(git rev-parse --show-toplevel)" - local package="$(cd "$repo_root" && go list)" + local module="$(go list -m)" + local dir="$(go list -m -f '{{.Dir}}')" - if ! [[ "${GOPATH}/${package}" != "${repo_root}" ]]; then - echo "This script requires the target package and all dependencies to live in a GOPATH." + if [[ "${GOPATH}/${module}" -ef "${dir}" ]]; then + echo "This script requires the target module and all dependencies to live in a GOPATH." return 1 fi ( local result=0 local workspace="$(mktemp -d)" - #trap "$(printf 'rm -rf "%q"' "$workspace")" INT TERM EXIT + trap "$(printf 'rm -rf "%q"' "$workspace")" INT TERM EXIT cd "$workspace" mkdir extern @@ -196,28 +206,28 @@ recursive_release_log() { rm -f go.mod go.sum - printf -- "Generating Changelog for %s %s..%s\n" "$package" "$start" "$end" >&2 + printf -- "Generating Changelog for %s %s..%s\n" "$module" "$start" "$end" >&2 - printf -- "- %s:\n" "$package" - release_log "$package" "$start" "$end" | indent + printf -- "- %s:\n" "$module" + release_log "$module" "$start" "$end" | indent - statlog "$package" "$start" "$end" > statlog.json + statlog "$module" "$start" "$end" > statlog.json dep_changes old_deps.json new_deps.json | jq --arg filter "$REPO_FILTER" 'select(.Path | match($filter))' | # Compute changelogs jq -r '"\(.Path) \(.New.Version) \(.New.Ref) \(.Old.Version) \(.Old.Ref // "")"' | - while read repo new new_ref old old_ref; do - if ! ensure "$repo" "$new_ref"; then + while read module new new_ref old old_ref; do + if ! ensure "$module" "$new_ref"; then result=1 local changelog="failed to fetch repo" else - statlog "$repo" "$old_ref" "$new_ref" >> statlog.json - local changelog="$(release_log "$repo" "$old_ref" "$new_ref")" + statlog "$module" "$old_ref" "$new_ref" >> statlog.json + local changelog="$(release_log "$module" "$old_ref" "$new_ref")" fi if [[ -n "$changelog" ]]; then - printf -- "- %s (%s -> %s):\n" "$repo" "$old" "$new" + printf -- "- %s (%s -> %s):\n" "$module" "$old" "$new" echo "$changelog" | indent fi done From a9e4a0d22b03304ea53c5bd71731c246a96443e0 Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Tue, 27 Apr 2021 16:28:57 +0200 Subject: [PATCH 18/97] upgrade docker images --- testplans/Makefile | 18 +++++++++--------- .../docker-images/Dockerfile.oni-buildbase | 4 ++-- testplans/docker-images/Dockerfile.oni-runtime | 4 ++-- .../docker-images/Dockerfile.oni-runtime-debug | 6 +++--- testplans/lotus-soup/manifest.toml | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/testplans/Makefile b/testplans/Makefile index 410553b90..0bf685005 100644 --- a/testplans/Makefile +++ b/testplans/Makefile @@ -6,18 +6,18 @@ download-proofs: go run github.com/filecoin-project/go-paramfetch/paramfetch 2048 ./docker-images/proof-parameters.json build-images: - docker build -t "iptestground/oni-buildbase:v13-lotus" -f "docker-images/Dockerfile.oni-buildbase" "docker-images" - docker build -t "iptestground/oni-runtime:v7" -f "docker-images/Dockerfile.oni-runtime" "docker-images" - docker build -t "iptestground/oni-runtime:v8-debug" -f "docker-images/Dockerfile.oni-runtime-debug" "docker-images" + docker build -t "iptestground/oni-buildbase:v14-lotus" -f "docker-images/Dockerfile.oni-buildbase" "docker-images" + docker build -t "iptestground/oni-runtime:v9" -f "docker-images/Dockerfile.oni-runtime" "docker-images" + docker build -t "iptestground/oni-runtime:v9-debug" -f "docker-images/Dockerfile.oni-runtime-debug" "docker-images" push-images: - docker push iptestground/oni-buildbase:v13-lotus - docker push iptestground/oni-runtime:v7 - docker push iptestground/oni-runtime:v8-debug + docker push iptestground/oni-buildbase:v14-lotus + docker push iptestground/oni-runtime:v9 + docker push iptestground/oni-runtime:v9-debug pull-images: - docker pull iptestground/oni-buildbase:v13-lotus - docker pull iptestground/oni-runtime:v7 - docker pull iptestground/oni-runtime:v8-debug + docker pull iptestground/oni-buildbase:v14-lotus + docker pull iptestground/oni-runtime:v9 + docker pull iptestground/oni-runtime:v9-debug .PHONY: download-proofs build-images push-images pull-images diff --git a/testplans/docker-images/Dockerfile.oni-buildbase b/testplans/docker-images/Dockerfile.oni-buildbase index 012a27fc7..306d40f9a 100644 --- a/testplans/docker-images/Dockerfile.oni-buildbase +++ b/testplans/docker-images/Dockerfile.oni-buildbase @@ -1,10 +1,10 @@ -ARG GO_VERSION=1.15.6 +ARG GO_VERSION=1.16.3 FROM golang:${GO_VERSION}-buster RUN apt-get update && apt-get install -y ca-certificates llvm clang mesa-opencl-icd ocl-icd-opencl-dev jq gcc git pkg-config bzr libhwloc-dev -ARG FILECOIN_FFI_COMMIT=62f89f108a6a8fe9ad6ed52fb7ffbf8594d7ae5c +ARG FILECOIN_FFI_COMMIT=d82899449741ce190e950a3582ebe33806f018a9 ARG FFI_DIR=/extern/filecoin-ffi RUN mkdir -p ${FFI_DIR} \ diff --git a/testplans/docker-images/Dockerfile.oni-runtime b/testplans/docker-images/Dockerfile.oni-runtime index 2ccb7337c..27144069a 100644 --- a/testplans/docker-images/Dockerfile.oni-runtime +++ b/testplans/docker-images/Dockerfile.oni-runtime @@ -1,4 +1,4 @@ -ARG GO_VERSION=1.15.6 +ARG GO_VERSION=1.16.3 FROM golang:${GO_VERSION}-buster as downloader @@ -8,7 +8,7 @@ FROM golang:${GO_VERSION}-buster as downloader ## 3. Trigger the download. ## Output will be in /var/tmp/filecoin-proof-parameters. -RUN go get github.com/filecoin-project/go-paramfetch/paramfetch +RUN go get github.com/filecoin-project/go-paramfetch/paramfetch@master COPY /proof-parameters.json / RUN paramfetch 8388608 /proof-parameters.json diff --git a/testplans/docker-images/Dockerfile.oni-runtime-debug b/testplans/docker-images/Dockerfile.oni-runtime-debug index a349a70da..126ae8de7 100644 --- a/testplans/docker-images/Dockerfile.oni-runtime-debug +++ b/testplans/docker-images/Dockerfile.oni-runtime-debug @@ -1,4 +1,4 @@ -ARG GO_VERSION=1.15.6 +ARG GO_VERSION=1.16.3 FROM golang:${GO_VERSION}-buster as downloader @@ -8,11 +8,11 @@ FROM golang:${GO_VERSION}-buster as downloader ## 3. Trigger the download. ## Output will be in /var/tmp/filecoin-proof-parameters. -RUN go get github.com/filecoin-project/go-paramfetch/paramfetch +RUN go get github.com/filecoin-project/go-paramfetch/paramfetch@master COPY /proof-parameters.json / RUN paramfetch 8388608 /proof-parameters.json -ARG LOTUS_COMMIT=b4ad2e5e93dc710d985eb9cf3ee04142efb47bf0 +ARG LOTUS_COMMIT=7e25a811c3d80ea3e007a54aa1da089985110c2c ## for debug purposes RUN apt update && apt install -y mesa-opencl-icd ocl-icd-opencl-dev gcc git bzr jq pkg-config libhwloc-dev curl && git clone https://github.com/filecoin-project/lotus.git && cd lotus/ && git checkout ${LOTUS_COMMIT} && make clean && make all && make install diff --git a/testplans/lotus-soup/manifest.toml b/testplans/lotus-soup/manifest.toml index 8cc2f4caf..f33acffb7 100644 --- a/testplans/lotus-soup/manifest.toml +++ b/testplans/lotus-soup/manifest.toml @@ -9,8 +9,8 @@ enabled = true [builders."docker:go"] enabled = true -build_base_image = "iptestground/oni-buildbase:v13-lotus" -runtime_image = "iptestground/oni-runtime:v8-debug" +build_base_image = "iptestground/oni-buildbase:v14-lotus" +runtime_image = "iptestground/oni-runtime:v9-debug" [runners."local:exec"] enabled = true From 6daaf6ac2e10cffbdcd8820298c555a8145f4c6a Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Wed, 7 Apr 2021 15:14:14 -0700 Subject: [PATCH 19/97] attempt to do better padding on pieces being written into sectors --- extern/sector-storage/ffiwrapper/sealer_cgo.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/extern/sector-storage/ffiwrapper/sealer_cgo.go b/extern/sector-storage/ffiwrapper/sealer_cgo.go index dca8b44b5..51f70e132 100644 --- a/extern/sector-storage/ffiwrapper/sealer_cgo.go +++ b/extern/sector-storage/ffiwrapper/sealer_cgo.go @@ -195,12 +195,16 @@ func (sb *Sealer) AddPiece(ctx context.Context, sector storage.SectorRef, existi return piecePromises[0]() } + var payloadRoundedBytes abi.PaddedPieceSize pieceCids := make([]abi.PieceInfo, len(piecePromises)) for i, promise := range piecePromises { - pieceCids[i], err = promise() + pinfo, err := promise() if err != nil { return abi.PieceInfo{}, err } + + pieceCids[i] = pinfo + payloadRoundedBytes += pinfo.Size } pieceCID, err := ffi.GenerateUnsealedCID(sector.ProofType, pieceCids) @@ -208,6 +212,15 @@ func (sb *Sealer) AddPiece(ctx context.Context, sector storage.SectorRef, existi return abi.PieceInfo{}, xerrors.Errorf("generate unsealed CID: %w", err) } + if payloadRoundedBytes < pieceSize.Padded() { + paddedCid, err := commpffi.ZeroPadPieceCommitment(pieceCID, payloadRoundedBytes.Unpadded(), pieceSize) + if err != nil { + return abi.PieceInfo{}, xerrors.Errorf("failed to pad data: %w", err) + } + + pieceCID = paddedCid + } + // validate that the pieceCID was properly formed if _, err := commcid.CIDToPieceCommitmentV1(pieceCID); err != nil { return abi.PieceInfo{}, err From 48feb52cbf0f42c5e86020f4090e90f2a61d0e16 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Tue, 27 Apr 2021 14:15:25 -0700 Subject: [PATCH 20/97] add a test for adding padded pieces --- .../sector-storage/ffiwrapper/sealer_test.go | 43 +++++++++++++++++++ go.mod | 2 +- go.sum | 2 + 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index 2efcfc6a0..39e37f0cc 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -709,3 +709,46 @@ func BenchmarkAddPiece512M(b *testing.B) { fmt.Println(c) } } + +func TestAddPiece512MPadded(t *testing.T) { + sz := abi.PaddedPieceSize(512 << 20).Unpadded() + + cdir, err := ioutil.TempDir("", "sbtest-c-") + if err != nil { + t.Fatal(err) + } + miner := abi.ActorID(123) + + sp := &basicfs.Provider{ + Root: cdir, + } + sb, err := New(sp) + if err != nil { + t.Fatalf("%+v", err) + } + cleanup := func() { + if t.Failed() { + fmt.Printf("not removing %s\n", cdir) + return + } + if err := os.RemoveAll(cdir); err != nil { + t.Error(err) + } + } + t.Cleanup(cleanup) + + r := rand.New(rand.NewSource(0x7e5)) + + c, err := sb.AddPiece(context.TODO(), storage.SectorRef{ + ID: abi.SectorID{ + Miner: miner, + Number: 0, + }, + ProofType: abi.RegisteredSealProof_StackedDrg512MiBV1_1, + }, nil, sz, io.LimitReader(r, int64(sz/4))) + if err != nil { + t.Fatalf("add piece failed: %s", err) + } + + require.Equal(t, "baga6ea4seaqonenxyku4o7hr5xkzbqsceipf6xgli3on54beqbk6k246sbooobq", c.PieceCID.String()) +} diff --git a/go.mod b/go.mod index 86e6da40d..3c9cfdfc0 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20201006184820-924ee87a1349 // indirect github.com/filecoin-project/go-bitfield v0.2.4 github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 - github.com/filecoin-project/go-commp-utils v0.1.0 + github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427180530-4606b1a6cbdd github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 github.com/filecoin-project/go-data-transfer v1.4.3 github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a diff --git a/go.sum b/go.sum index 8bcfafdfc..7ce7ede20 100644 --- a/go.sum +++ b/go.sum @@ -262,6 +262,8 @@ github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.m github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= github.com/filecoin-project/go-commp-utils v0.1.0 h1:PaDxoXYh1TXnnz5kA/xSObpAQwcJSUs4Szb72nuaNdk= github.com/filecoin-project/go-commp-utils v0.1.0/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= +github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427180530-4606b1a6cbdd h1:cpwbE1z6a+0fp62P0Qv7Z7ZqIy8Jiay0SWR0xcuZ0qs= +github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427180530-4606b1a6cbdd/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ= github.com/filecoin-project/go-data-transfer v1.0.1/go.mod h1:UxvfUAY9v3ub0a21BSK9u3pB2aq30Y0KMsG+w9/ysyo= From 0304bebf2065308762f6b2e5e6609d19363b7ca0 Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Tue, 27 Apr 2021 16:29:12 +0200 Subject: [PATCH 21/97] remove deals concurrency --- testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml | 2 +- testplans/lotus-soup/deals_e2e.go | 4 +++- testplans/lotus-soup/init.go | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml b/testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml index 18ce024bb..dc6519656 100644 --- a/testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml +++ b/testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml @@ -45,7 +45,7 @@ [[groups]] id = "miners" [groups.resources] - memory = "4096Mi" + memory = "8192Mi" cpu = "1000m" [groups.instances] count = 1 diff --git a/testplans/lotus-soup/deals_e2e.go b/testplans/lotus-soup/deals_e2e.go index 234754ae9..42d969762 100644 --- a/testplans/lotus-soup/deals_e2e.go +++ b/testplans/lotus-soup/deals_e2e.go @@ -75,7 +75,9 @@ func dealsE2E(t *testkit.TestEnvironment) error { // give some time to the miner, otherwise, we get errors like: // deal errored deal failed: (State=26) error calling node: publishing deal: GasEstimateMessageGas // error: estimating gas used: message execution failed: exit 19, reason: failed to lock balance: failed to lock client funds: not enough balance to lock for addr t0102: escrow balance 0 < locked 0 + required 640297000 (RetCode=19) - time.Sleep(50 * time.Second) + time.Sleep(40 * time.Second) + + time.Sleep(time.Duration(t.GlobalSeq) * 5 * time.Second) // generate 1600 bytes of random data data := make([]byte, 5000000) diff --git a/testplans/lotus-soup/init.go b/testplans/lotus-soup/init.go index 5690e803a..cad15a4fb 100644 --- a/testplans/lotus-soup/init.go +++ b/testplans/lotus-soup/init.go @@ -22,6 +22,12 @@ func init() { _ = log.SetLogLevel("stats", "WARN") _ = log.SetLogLevel("dht/RtRefreshManager", "ERROR") // noisy _ = log.SetLogLevel("bitswap", "ERROR") // noisy + _ = log.SetLogLevel("badgerbs", "ERROR") // noisy + _ = log.SetLogLevel("sub", "ERROR") // noisy + _ = log.SetLogLevel("pubsub", "ERROR") // noisy + _ = log.SetLogLevel("chain", "ERROR") // noisy + _ = log.SetLogLevel("chainstore", "ERROR") // noisy + _ = log.SetLogLevel("basichost", "ERROR") // noisy _ = os.Setenv("BELLMAN_NO_GPU", "1") From bc8d6a1d877105f6df58c9df47f2590f6061b889 Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Wed, 28 Apr 2021 14:36:20 +0200 Subject: [PATCH 22/97] go mod tidy for lotus-soup testplans --- testplans/lotus-soup/go.mod | 4 ++-- testplans/lotus-soup/go.sum | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/testplans/lotus-soup/go.mod b/testplans/lotus-soup/go.mod index f4b8687dc..872c17735 100644 --- a/testplans/lotus-soup/go.mod +++ b/testplans/lotus-soup/go.mod @@ -8,11 +8,11 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/drand/drand v1.2.1 github.com/filecoin-project/go-address v0.0.5 - github.com/filecoin-project/go-fil-markets v1.2.4 + github.com/filecoin-project/go-fil-markets v1.2.5 github.com/filecoin-project/go-jsonrpc v0.1.4-0.20210217175800-45ea43ac2bec github.com/filecoin-project/go-state-types v0.1.0 github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b - github.com/filecoin-project/lotus v1.5.2 + github.com/filecoin-project/lotus v1.8.1-0.20210428122447-4688da51781a github.com/filecoin-project/specs-actors v0.9.13 github.com/google/uuid v1.1.2 github.com/gorilla/mux v1.7.4 diff --git a/testplans/lotus-soup/go.sum b/testplans/lotus-soup/go.sum index 458cac486..929e2a612 100644 --- a/testplans/lotus-soup/go.sum +++ b/testplans/lotus-soup/go.sum @@ -273,16 +273,16 @@ github.com/filecoin-project/go-commp-utils v0.1.0/go.mod h1:6s95K91mCyHY51RPWECZ github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ= github.com/filecoin-project/go-data-transfer v1.0.1/go.mod h1:UxvfUAY9v3ub0a21BSK9u3pB2aq30Y0KMsG+w9/ysyo= -github.com/filecoin-project/go-data-transfer v1.4.1 h1:4GoMGEdMeDLqbKR74Q5ceZTN35nv+66JZERqQ+SjxWU= -github.com/filecoin-project/go-data-transfer v1.4.1/go.mod h1:n8kbDQXWrY1c4UgfMa9KERxNCWbOTDwdNhf2MpN9dpo= +github.com/filecoin-project/go-data-transfer v1.4.3 h1:ECEw69NOfmEZ7XN1NSBvj3KTbbH2mIczQs+Z2w4bD7c= +github.com/filecoin-project/go-data-transfer v1.4.3/go.mod h1:n8kbDQXWrY1c4UgfMa9KERxNCWbOTDwdNhf2MpN9dpo= github.com/filecoin-project/go-ds-versioning v0.1.0 h1:y/X6UksYTsK8TLCI7rttCKEvl8btmWxyFMEeeWGUxIQ= github.com/filecoin-project/go-ds-versioning v0.1.0/go.mod h1:mp16rb4i2QPmxBnmanUx8i/XANp+PFCCJWiAb+VW4/s= github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ= github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a h1:hyJ+pUm/4U4RdEZBlg6k8Ma4rDiuvqyGpoICXAxwsTg= github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ= github.com/filecoin-project/go-fil-markets v1.0.5-0.20201113164554-c5eba40d5335/go.mod h1:AJySOJC00JRWEZzRG2KsfUnqEf5ITXxeX09BE9N4f9c= -github.com/filecoin-project/go-fil-markets v1.2.4 h1:AcNMy/XGvSdv4GjuVoeqe67Q7OvppkSx1zWEGqVHixg= -github.com/filecoin-project/go-fil-markets v1.2.4/go.mod h1:8WEpiMkwdvtHb5dXmRIWX4vz4XjkVlhxRdHJdouV1b0= +github.com/filecoin-project/go-fil-markets v1.2.5 h1:bQgtXbwxKyPxSEQoUI5EaTHJ0qfzyd5NosspuADCm6Y= +github.com/filecoin-project/go-fil-markets v1.2.5/go.mod h1:7JIqNBmFvOyBzk/EiPYnweVdQnWhshixb5B9b1653Ag= github.com/filecoin-project/go-hamt-ipld v0.1.5 h1:uoXrKbCQZ49OHpsTCkrThPNelC4W3LPEk0OrS/ytIBM= github.com/filecoin-project/go-hamt-ipld v0.1.5/go.mod h1:6Is+ONR5Cd5R6XZoCse1CWaXZc0Hdb/JeX+EQCQzX24= github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0 h1:b3UDemBYN2HNfk3KOXNuxgTTxlWi3xVvbQP0IT38fvM= @@ -310,16 +310,20 @@ github.com/filecoin-project/go-statestore v0.1.1 h1:ufMFq00VqnT2CAuDpcGnwLnCX1I/ github.com/filecoin-project/go-statestore v0.1.1/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI= github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b h1:fkRZSPrYpk42PV3/lIXiL0LHetxde7vyYYvSsttQtfg= github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b/go.mod h1:Q0GQOBtKf1oE10eSXSlhN45kDBdGvEcVOqMiffqX+N8= +github.com/filecoin-project/lotus v1.8.1-0.20210428122447-4688da51781a h1:8M4BZHB+R3psW72CGarBdyc5OvbFBRRlebzw8u5EWsg= +github.com/filecoin-project/lotus v1.8.1-0.20210428122447-4688da51781a/go.mod h1:4YC/8rizrrp2wKOYvHQEjCxZbziXi68BhrzvI+FCye0= github.com/filecoin-project/specs-actors v0.9.4/go.mod h1:BStZQzx5x7TmCkLv0Bpa07U6cPKol6fd3w9KjMPZ6Z4= github.com/filecoin-project/specs-actors v0.9.12/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao= github.com/filecoin-project/specs-actors v0.9.13 h1:rUEOQouefi9fuVY/2HOroROJlZbOzWYXXeIh41KF2M4= github.com/filecoin-project/specs-actors v0.9.13/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao= github.com/filecoin-project/specs-actors/v2 v2.0.1/go.mod h1:v2NZVYinNIKA9acEMBm5wWXxqv5+frFEbekBFemYghY= github.com/filecoin-project/specs-actors/v2 v2.3.2/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y= -github.com/filecoin-project/specs-actors/v2 v2.3.4 h1:NZK2oMCcA71wNsUzDBmLQyRMzcCnX9tDGvwZ53G67j8= -github.com/filecoin-project/specs-actors/v2 v2.3.4/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y= -github.com/filecoin-project/specs-actors/v3 v3.0.3 h1:bq9B1Jnq+Z0A+Yj3KnYhN3kcTpUyP6Umo3MZgai0BRE= -github.com/filecoin-project/specs-actors/v3 v3.0.3/go.mod h1:oMcmEed6B7H/wHabM3RQphTIhq0ibAKsbpYs+bQ/uxQ= +github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb h1:orr/sMzrDZUPAveRE+paBdu1kScIUO5zm+HYeh+VlhA= +github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb/go.mod h1:LljnY2Mn2homxZsmokJZCpRuhOPxfXhvcek5gWkmqAc= +github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7K9cTZdM1rTiSonHrg= +github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= +github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= +github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E= From 5769a453803e8579c37c4c911449e1efe4530224 Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Wed, 28 Apr 2021 16:58:03 +0200 Subject: [PATCH 23/97] use 1 miner for payment channel tests --- testplans/lotus-soup/_compositions/paych-stress-k8s.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testplans/lotus-soup/_compositions/paych-stress-k8s.toml b/testplans/lotus-soup/_compositions/paych-stress-k8s.toml index cf98960b7..b5d7f9bd4 100644 --- a/testplans/lotus-soup/_compositions/paych-stress-k8s.toml +++ b/testplans/lotus-soup/_compositions/paych-stress-k8s.toml @@ -5,7 +5,7 @@ [global] plan = "lotus-soup" case = "paych-stress" - total_instances = 5 # 2 clients + 2 miners + 1 bootstrapper + total_instances = 4 # 2 clients + 1 miners + 1 bootstrapper builder = "docker:go" runner = "cluster:k8s" @@ -23,7 +23,7 @@ [global.run.test_params] clients = "2" - miners = "2" + miners = "1" genesis_timestamp_offset = "0" balance = "100" ## be careful, this is in FIL. sectors = "10" @@ -44,7 +44,7 @@ [[groups]] id = "miners" - instances = { count = 2 } + instances = { count = 1 } [groups.run.test_params] role = "miner" [groups.resources] From 69da6a2a295a7e403c24d89d731fb259cd19d4ba Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 26 Apr 2021 03:50:29 -0700 Subject: [PATCH 24/97] feat: allow checkpointing to forks Previously, `lotus sync checkpoint` would only checkpoint on the current chain. Now, it can switch to a new fork. --- chain/checkpoint.go | 27 ++++++++++++++++++++------- node/impl/full/sync.go | 2 +- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/chain/checkpoint.go b/chain/checkpoint.go index 8f99d73e4..4718b294d 100644 --- a/chain/checkpoint.go +++ b/chain/checkpoint.go @@ -1,6 +1,7 @@ package chain import ( + "context" "encoding/json" "github.com/filecoin-project/lotus/chain/types" @@ -36,17 +37,20 @@ func loadCheckpoint(ds dtypes.MetadataDS) (types.TipSetKey, error) { return tsk, err } -func (syncer *Syncer) SetCheckpoint(tsk types.TipSetKey) error { +func (syncer *Syncer) SyncCheckpoint(ctx context.Context, tsk types.TipSetKey) error { if tsk == types.EmptyTSK { return xerrors.Errorf("called with empty tsk") } - syncer.checkptLk.Lock() - defer syncer.checkptLk.Unlock() - ts, err := syncer.ChainStore().LoadTipSet(tsk) if err != nil { - return xerrors.Errorf("cannot find tipset: %w", err) + tss, err := syncer.Exchange.GetBlocks(ctx, tsk, 1) + if err != nil { + return xerrors.Errorf("failed to fetch tipset: %w", err) + } else if len(tss) != 1 { + return xerrors.Errorf("expected 1 tipset, got %d", len(tss)) + } + ts = tss[0] } hts := syncer.ChainStore().GetHeaviestTipSet() @@ -54,11 +58,18 @@ func (syncer *Syncer) SetCheckpoint(tsk types.TipSetKey) error { if err != nil { return xerrors.Errorf("cannot determine whether checkpoint tipset is in main-chain: %w", err) } - if !hts.Equals(ts) && !anc { - return xerrors.Errorf("cannot mark tipset as checkpoint, since it isn't in the main-chain: %w", err) + if err := syncer.collectChain(ctx, ts, hts); err != nil { + return xerrors.Errorf("failed to collect chain for checkpoint: %w", err) + } + if err := syncer.ChainStore().SetHead(ts); err != nil { + return xerrors.Errorf("failed to set the chain head: %w", err) + } } + syncer.checkptLk.Lock() + defer syncer.checkptLk.Unlock() + tskBytes, err := json.Marshal(tsk) if err != nil { return err @@ -69,6 +80,8 @@ func (syncer *Syncer) SetCheckpoint(tsk types.TipSetKey) error { return err } + // TODO: This is racy. as there may be a concurrent sync in progress. + // The only real solution is to checkpoint inside the chainstore, not here. syncer.checkpt = tsk return nil diff --git a/node/impl/full/sync.go b/node/impl/full/sync.go index 1a088fb77..2c697483b 100644 --- a/node/impl/full/sync.go +++ b/node/impl/full/sync.go @@ -104,7 +104,7 @@ func (a *SyncAPI) SyncIncomingBlocks(ctx context.Context) (<-chan *types.BlockHe func (a *SyncAPI) SyncCheckpoint(ctx context.Context, tsk types.TipSetKey) error { log.Warnf("Marking tipset %s as checkpoint", tsk) - return a.Syncer.SetCheckpoint(tsk) + return a.Syncer.SyncCheckpoint(ctx, tsk) } func (a *SyncAPI) SyncMarkBad(ctx context.Context, bcid cid.Cid) error { From 0ad51f8a14b0c238a0564267064da6323d8271f8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Apr 2021 13:48:13 -0700 Subject: [PATCH 25/97] test(sync): fix tipset check and re-enable checkpoint tests We need to wait until the node _has_ the tipset, not until it doesn't. This probably only worked before due to race conditions. fixes #4716 (probably) --- chain/sync_test.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/chain/sync_test.go b/chain/sync_test.go index 21bc208ed..fb2528c59 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -346,12 +346,15 @@ func (tu *syncTestUtil) checkpointTs(node int, tsk types.TipSetKey) { require.NoError(tu.t, tu.nds[node].SyncCheckpoint(context.TODO(), tsk)) } +func (tu *syncTestUtil) nodeHasTs(node int, tsk types.TipSetKey) bool { + _, err := tu.nds[node].ChainGetTipSet(context.TODO(), tsk) + return err == nil +} + func (tu *syncTestUtil) waitUntilNodeHasTs(node int, tsk types.TipSetKey) { - for { - _, err := tu.nds[node].ChainGetTipSet(context.TODO(), tsk) - if err != nil { - break - } + for !tu.nodeHasTs(node, tsk) { + // Time to allow for syncing and validation + time.Sleep(10 * time.Millisecond) } // Time to allow for syncing and validation @@ -751,8 +754,6 @@ func TestSyncInputs(t *testing.T) { } func TestSyncCheckpointHead(t *testing.T) { - t.Skip("flaky") - H := 10 tu := prepSyncTest(t, H) @@ -790,13 +791,11 @@ func TestSyncCheckpointHead(t *testing.T) { tu.connect(p1, p2) tu.waitUntilNodeHasTs(p1, b.TipSet().Key()) p1Head := tu.getHead(p1) - require.Equal(tu.t, p1Head, a.TipSet()) + require.True(tu.t, p1Head.Equals(a.TipSet())) tu.assertBad(p1, b.TipSet()) } func TestSyncCheckpointEarlierThanHead(t *testing.T) { - t.Skip("flaky") - H := 10 tu := prepSyncTest(t, H) @@ -834,6 +833,6 @@ func TestSyncCheckpointEarlierThanHead(t *testing.T) { tu.connect(p1, p2) tu.waitUntilNodeHasTs(p1, b.TipSet().Key()) p1Head := tu.getHead(p1) - require.Equal(tu.t, p1Head, a.TipSet()) + require.True(tu.t, p1Head.Equals(a.TipSet())) tu.assertBad(p1, b.TipSet()) } From 8f309b214b3c67237fa885276a69f3d1d632e4b6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Apr 2021 12:49:21 -0700 Subject: [PATCH 26/97] chain: move checkpoint logic into chainstore That way, checkpoints can be enforced by the chainstore, removing a potential race where an in-progress sync of a fork could bypass a sync checkpoint. --- chain/checkpoint.go | 83 +++++++---------------- chain/store/checkpoint_test.go | 89 ++++++++++++++++++++++++ chain/store/store.go | 119 ++++++++++++++++++++++++++++++++- chain/sync.go | 33 ++++----- chain/sync_test.go | 10 +++ 5 files changed, 251 insertions(+), 83 deletions(-) create mode 100644 chain/store/checkpoint_test.go diff --git a/chain/checkpoint.go b/chain/checkpoint.go index 4718b294d..a3660a45c 100644 --- a/chain/checkpoint.go +++ b/chain/checkpoint.go @@ -2,41 +2,12 @@ package chain import ( "context" - "encoding/json" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/node/modules/dtypes" - "github.com/ipfs/go-datastore" "golang.org/x/xerrors" ) -var CheckpointKey = datastore.NewKey("/chain/checks") - -func loadCheckpoint(ds dtypes.MetadataDS) (types.TipSetKey, error) { - haveChks, err := ds.Has(CheckpointKey) - if err != nil { - return types.EmptyTSK, err - } - - if !haveChks { - return types.EmptyTSK, nil - } - - tskBytes, err := ds.Get(CheckpointKey) - if err != nil { - return types.EmptyTSK, err - } - - var tsk types.TipSetKey - err = json.Unmarshal(tskBytes, &tsk) - if err != nil { - return types.EmptyTSK, err - } - - return tsk, err -} - func (syncer *Syncer) SyncCheckpoint(ctx context.Context, tsk types.TipSetKey) error { if tsk == types.EmptyTSK { return xerrors.Errorf("called with empty tsk") @@ -53,42 +24,34 @@ func (syncer *Syncer) SyncCheckpoint(ctx context.Context, tsk types.TipSetKey) e ts = tss[0] } - hts := syncer.ChainStore().GetHeaviestTipSet() - anc, err := syncer.ChainStore().IsAncestorOf(ts, hts) - if err != nil { - return xerrors.Errorf("cannot determine whether checkpoint tipset is in main-chain: %w", err) - } - if !hts.Equals(ts) && !anc { - if err := syncer.collectChain(ctx, ts, hts); err != nil { - return xerrors.Errorf("failed to collect chain for checkpoint: %w", err) - } - if err := syncer.ChainStore().SetHead(ts); err != nil { - return xerrors.Errorf("failed to set the chain head: %w", err) - } + if err := syncer.switchChain(ctx, ts); err != nil { + return xerrors.Errorf("failed to switch chain when syncing checkpoint: %w", err) } - syncer.checkptLk.Lock() - defer syncer.checkptLk.Unlock() - - tskBytes, err := json.Marshal(tsk) - if err != nil { - return err + if err := syncer.ChainStore().SetCheckpoint(ts); err != nil { + return xerrors.Errorf("failed to set the chain checkpoint: %w", err) } - err = syncer.ds.Put(CheckpointKey, tskBytes) - if err != nil { - return err - } - - // TODO: This is racy. as there may be a concurrent sync in progress. - // The only real solution is to checkpoint inside the chainstore, not here. - syncer.checkpt = tsk - return nil } -func (syncer *Syncer) GetCheckpoint() types.TipSetKey { - syncer.checkptLk.Lock() - defer syncer.checkptLk.Unlock() - return syncer.checkpt +func (syncer *Syncer) switchChain(ctx context.Context, ts *types.TipSet) error { + hts := syncer.ChainStore().GetHeaviestTipSet() + if hts.Equals(ts) { + return nil + } + + if anc, err := syncer.store.IsAncestorOf(ts, hts); err == nil && anc { + return nil + } + + // Otherwise, sync the chain and set the head. + if err := syncer.collectChain(ctx, ts, hts, true); err != nil { + return xerrors.Errorf("failed to collect chain for checkpoint: %w", err) + } + + if err := syncer.ChainStore().SetHead(ts); err != nil { + return xerrors.Errorf("failed to set the chain head: %w", err) + } + return nil } diff --git a/chain/store/checkpoint_test.go b/chain/store/checkpoint_test.go new file mode 100644 index 000000000..320b76797 --- /dev/null +++ b/chain/store/checkpoint_test.go @@ -0,0 +1,89 @@ +package store_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/lotus/chain/gen" +) + +func TestChainCheckpoint(t *testing.T) { + cg, err := gen.NewGenerator() + if err != nil { + t.Fatal(err) + } + + // Let the first miner mine some blocks. + last := cg.CurTipset.TipSet() + for i := 0; i < 4; i++ { + ts, err := cg.NextTipSetFromMiners(last, cg.Miners[:1]) + require.NoError(t, err) + + last = ts.TipSet.TipSet() + } + + cs := cg.ChainStore() + + checkpoint := last + checkpointParents, err := cs.GetTipSetFromKey(checkpoint.Parents()) + require.NoError(t, err) + + // Set the head to the block before the checkpoint. + err = cs.SetHead(checkpointParents) + require.NoError(t, err) + + // Verify it worked. + head := cs.GetHeaviestTipSet() + require.True(t, head.Equals(checkpointParents)) + + // Try to set the checkpoint in the future, it should fail. + err = cs.SetCheckpoint(checkpoint) + require.Error(t, err) + + // Then move the head back. + err = cs.SetHead(checkpoint) + require.NoError(t, err) + + // Verify it worked. + head = cs.GetHeaviestTipSet() + require.True(t, head.Equals(checkpoint)) + + // And checkpoint it. + err = cs.SetCheckpoint(checkpoint) + require.NoError(t, err) + + // Let the second miner miner mine a fork + last = checkpointParents + for i := 0; i < 4; i++ { + ts, err := cg.NextTipSetFromMiners(last, cg.Miners[1:]) + require.NoError(t, err) + + last = ts.TipSet.TipSet() + } + + // See if the chain will take the fork, it shouldn't. + err = cs.MaybeTakeHeavierTipSet(context.Background(), last) + require.NoError(t, err) + head = cs.GetHeaviestTipSet() + require.True(t, head.Equals(checkpoint)) + + // Remove the checkpoint. + err = cs.RemoveCheckpoint() + require.NoError(t, err) + + // Now switch to the other fork. + err = cs.MaybeTakeHeavierTipSet(context.Background(), last) + require.NoError(t, err) + head = cs.GetHeaviestTipSet() + require.True(t, head.Equals(last)) + + // Setting a checkpoint on the other fork should fail. + err = cs.SetCheckpoint(checkpoint) + require.Error(t, err) + + // Setting a checkpoint on this fork should succeed. + err = cs.SetCheckpoint(checkpointParents) + require.NoError(t, err) +} diff --git a/chain/store/store.go b/chain/store/store.go index 7ebe31ec4..1e78ce73d 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -54,8 +54,11 @@ import ( var log = logging.Logger("chainstore") -var chainHeadKey = dstore.NewKey("head") -var blockValidationCacheKeyPrefix = dstore.NewKey("blockValidation") +var ( + chainHeadKey = dstore.NewKey("head") + checkpointKey = dstore.NewKey("/chain/checks") + blockValidationCacheKeyPrefix = dstore.NewKey("blockValidation") +) var DefaultTipSetCacheSize = 8192 var DefaultMsgMetaCacheSize = 2048 @@ -115,6 +118,7 @@ type ChainStore struct { heaviestLk sync.RWMutex heaviest *types.TipSet + checkpoint *types.TipSet bestTips *pubsub.PubSub pubLk sync.Mutex @@ -215,6 +219,15 @@ func (cs *ChainStore) Close() error { } func (cs *ChainStore) Load() error { + if err := cs.loadHead(); err != nil { + return err + } + if err := cs.loadCheckpoint(); err != nil { + return err + } + return nil +} +func (cs *ChainStore) loadHead() error { head, err := cs.metadataDs.Get(chainHeadKey) if err == dstore.ErrNotFound { log.Warn("no previous chain state found") @@ -239,6 +252,31 @@ func (cs *ChainStore) Load() error { return nil } +func (cs *ChainStore) loadCheckpoint() error { + tskBytes, err := cs.metadataDs.Get(checkpointKey) + if err == dstore.ErrNotFound { + return nil + } + if err != nil { + return xerrors.Errorf("failed to load checkpoint from datastore: %w", err) + } + + var tsk types.TipSetKey + err = json.Unmarshal(tskBytes, &tsk) + if err != nil { + return err + } + + ts, err := cs.LoadTipSet(tsk) + if err != nil { + return xerrors.Errorf("loading tipset: %w", err) + } + + cs.checkpoint = ts + + return nil +} + func (cs *ChainStore) writeHead(ts *types.TipSet) error { data, err := json.Marshal(ts.Cids()) if err != nil { @@ -439,6 +477,11 @@ func (cs *ChainStore) exceedsForkLength(synced, external *types.TipSet) (bool, e return false, nil } + // Now check to see if we've walked back to the checkpoint. + if synced.Equals(cs.checkpoint) { + return true, nil + } + // If we didn't, go back *one* tipset on the `synced` side (incrementing // the `forkLength`). if synced.Height() == 0 { @@ -467,6 +510,9 @@ func (cs *ChainStore) ForceHeadSilent(_ context.Context, ts *types.TipSet) error cs.heaviestLk.Lock() defer cs.heaviestLk.Unlock() + if err := cs.removeCheckpoint(); err != nil { + return err + } cs.heaviest = ts err := cs.writeHead(ts) @@ -642,13 +688,80 @@ func FlushValidationCache(ds datastore.Batching) error { } // SetHead sets the chainstores current 'best' head node. -// This should only be called if something is broken and needs fixing +// This should only be called if something is broken and needs fixing. +// +// This function will bypass and remove any checkpoints. func (cs *ChainStore) SetHead(ts *types.TipSet) error { cs.heaviestLk.Lock() defer cs.heaviestLk.Unlock() + if err := cs.removeCheckpoint(); err != nil { + return err + } return cs.takeHeaviestTipSet(context.TODO(), ts) } +// RemoveCheckpoint removes the current checkpoint. +func (cs *ChainStore) RemoveCheckpoint() error { + cs.heaviestLk.Lock() + defer cs.heaviestLk.Unlock() + return cs.removeCheckpoint() +} + +func (cs *ChainStore) removeCheckpoint() error { + if err := cs.metadataDs.Delete(checkpointKey); err != nil { + return err + } + cs.checkpoint = nil + return nil +} + +// SetCheckpoint will set a checkpoint past which the chainstore will not allow forks. +// +// NOTE: Checkpoints cannot be set beyond ForkLengthThreshold epochs in the past. +func (cs *ChainStore) SetCheckpoint(ts *types.TipSet) error { + tskBytes, err := json.Marshal(ts.Key()) + if err != nil { + return err + } + + cs.heaviestLk.Lock() + defer cs.heaviestLk.Unlock() + + if ts.Height() > cs.heaviest.Height() { + return xerrors.Errorf("cannot set a checkpoint in the future") + } + + // Otherwise, this operation could get _very_ expensive. + if cs.heaviest.Height()-ts.Height() > build.ForkLengthThreshold { + return xerrors.Errorf("cannot set a checkpoint before the fork threshold") + } + + if !ts.Equals(cs.heaviest) { + anc, err := cs.IsAncestorOf(ts, cs.heaviest) + if err != nil { + return xerrors.Errorf("cannot determine whether checkpoint tipset is in main-chain: %w", err) + } + + if !anc { + return xerrors.Errorf("cannot mark tipset as checkpoint, since it isn't in the main-chain: %w", err) + } + } + err = cs.metadataDs.Put(checkpointKey, tskBytes) + if err != nil { + return err + } + + cs.checkpoint = ts + return nil +} + +func (cs *ChainStore) GetCheckpoint() *types.TipSet { + cs.heaviestLk.RLock() + chkpt := cs.checkpoint + cs.heaviestLk.RUnlock() + return chkpt +} + // Contains returns whether our BlockStore has all blocks in the supplied TipSet. func (cs *ChainStore) Contains(ts *types.TipSet) (bool, error) { for _, c := range ts.Cids() { diff --git a/chain/sync.go b/chain/sync.go index 66c9c18bd..6f594024d 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -131,10 +131,6 @@ type Syncer struct { tickerCtxCancel context.CancelFunc - checkptLk sync.Mutex - - checkpt types.TipSetKey - ds dtypes.MetadataDS } @@ -152,14 +148,8 @@ func NewSyncer(ds dtypes.MetadataDS, sm *stmgr.StateManager, exchange exchange.C return nil, err } - cp, err := loadCheckpoint(ds) - if err != nil { - return nil, xerrors.Errorf("error loading mpool config: %w", err) - } - s := &Syncer{ ds: ds, - checkpt: cp, beacon: beacon, bad: NewBadBlockCache(), Genesis: gent, @@ -561,7 +551,7 @@ func (syncer *Syncer) Sync(ctx context.Context, maybeHead *types.TipSet) error { return nil } - if err := syncer.collectChain(ctx, maybeHead, hts); err != nil { + if err := syncer.collectChain(ctx, maybeHead, hts, false); err != nil { span.AddAttributes(trace.StringAttribute("col_error", err.Error())) span.SetStatus(trace.Status{ Code: 13, @@ -1247,7 +1237,7 @@ func extractSyncState(ctx context.Context) *SyncerState { // // All throughout the process, we keep checking if the received blocks are in // the deny list, and short-circuit the process if so. -func (syncer *Syncer) collectHeaders(ctx context.Context, incoming *types.TipSet, known *types.TipSet) ([]*types.TipSet, error) { +func (syncer *Syncer) collectHeaders(ctx context.Context, incoming *types.TipSet, known *types.TipSet, ignoreCheckpoint bool) ([]*types.TipSet, error) { ctx, span := trace.StartSpan(ctx, "collectHeaders") defer span.End() ss := extractSyncState(ctx) @@ -1416,7 +1406,7 @@ loop: // We have now ascertained that this is *not* a 'fast forward' log.Warnf("(fork detected) synced header chain (%s - %d) does not link to our best block (%s - %d)", incoming.Cids(), incoming.Height(), known.Cids(), known.Height()) - fork, err := syncer.syncFork(ctx, base, known) + fork, err := syncer.syncFork(ctx, base, known, ignoreCheckpoint) if err != nil { if xerrors.Is(err, ErrForkTooLong) || xerrors.Is(err, ErrForkCheckpoint) { // TODO: we're marking this block bad in the same way that we mark invalid blocks bad. Maybe distinguish? @@ -1442,11 +1432,14 @@ var ErrForkCheckpoint = fmt.Errorf("fork would require us to diverge from checkp // If the fork is too long (build.ForkLengthThreshold), or would cause us to diverge from the checkpoint (ErrForkCheckpoint), // we add the entire subchain to the denylist. Else, we find the common ancestor, and add the missing chain // fragment until the fork point to the returned []TipSet. -func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, known *types.TipSet) ([]*types.TipSet, error) { +func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, known *types.TipSet, ignoreCheckpoint bool) ([]*types.TipSet, error) { - chkpt := syncer.GetCheckpoint() - if known.Key() == chkpt { - return nil, ErrForkCheckpoint + var chkpt *types.TipSet + if !ignoreCheckpoint { + chkpt = syncer.store.GetCheckpoint() + if known.Equals(chkpt) { + return nil, ErrForkCheckpoint + } } // TODO: Does this mean we always ask for ForkLengthThreshold blocks from the network, even if we just need, like, 2? Yes. @@ -1488,7 +1481,7 @@ func (syncer *Syncer) syncFork(ctx context.Context, incoming *types.TipSet, know } // We will be forking away from nts, check that it isn't checkpointed - if nts.Key() == chkpt { + if nts.Equals(chkpt) { return nil, ErrForkCheckpoint } @@ -1699,14 +1692,14 @@ func persistMessages(ctx context.Context, bs bstore.Blockstore, bst *exchange.Co // // 3. StageMessages: having acquired the headers and found a common tipset, // we then move forward, requesting the full blocks, including the messages. -func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet, hts *types.TipSet) error { +func (syncer *Syncer) collectChain(ctx context.Context, ts *types.TipSet, hts *types.TipSet, ignoreCheckpoint bool) error { ctx, span := trace.StartSpan(ctx, "collectChain") defer span.End() ss := extractSyncState(ctx) ss.Init(hts, ts) - headers, err := syncer.collectHeaders(ctx, ts, hts) + headers, err := syncer.collectHeaders(ctx, ts, hts, ignoreCheckpoint) if err != nil { ss.Error(err) return err diff --git a/chain/sync_test.go b/chain/sync_test.go index fb2528c59..3176d9ec3 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -793,6 +793,11 @@ func TestSyncCheckpointHead(t *testing.T) { p1Head := tu.getHead(p1) require.True(tu.t, p1Head.Equals(a.TipSet())) tu.assertBad(p1, b.TipSet()) + + // Should be able to switch forks. + tu.checkpointTs(p1, b.TipSet().Key()) + p1Head = tu.getHead(p1) + require.True(tu.t, p1Head.Equals(b.TipSet())) } func TestSyncCheckpointEarlierThanHead(t *testing.T) { @@ -835,4 +840,9 @@ func TestSyncCheckpointEarlierThanHead(t *testing.T) { p1Head := tu.getHead(p1) require.True(tu.t, p1Head.Equals(a.TipSet())) tu.assertBad(p1, b.TipSet()) + + // Should be able to switch forks. + tu.checkpointTs(p1, b.TipSet().Key()) + p1Head = tu.getHead(p1) + require.True(tu.t, p1Head.Equals(b.TipSet())) } From c8fcab5d22ae832488aeaf50f9f034aaee946ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 18 Mar 2021 23:12:57 +0100 Subject: [PATCH 27/97] shed: Command to list duplicate messages in tipsets --- cmd/lotus-shed/balances.go | 133 +++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/cmd/lotus-shed/balances.go b/cmd/lotus-shed/balances.go index bce7be3d5..76ca9d6ae 100644 --- a/cmd/lotus-shed/balances.go +++ b/cmd/lotus-shed/balances.go @@ -8,6 +8,7 @@ import ( "os" "strconv" "strings" + "sync" "time" "github.com/filecoin-project/lotus/build" @@ -70,6 +71,138 @@ var auditsCmd = &cli.Command{ chainBalanceStateCmd, chainPledgeCmd, fillBalancesCmd, + duplicatedMessagesCmd, + }, +} + +var duplicatedMessagesCmd = &cli.Command{ + Name: "duplicate-messages", + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "count", + Required: true, + }, + }, + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + + defer closer() + ctx := lcli.ReqContext(cctx) + + head, err := api.ChainHead(ctx) + if err != nil { + return err + } + + var printLk sync.Mutex + + threads := 64 + mcount := 0 + + throttle := make(chan struct{}, threads) + + for i := 0; i < cctx.Int("count"); i++ { + select { + case throttle <- struct{}{}: + case <-ctx.Done(): + return ctx.Err() + } + + go func(ts *types.TipSet) { + defer func() { + <-throttle + }() + + type addrNonce struct { + s address.Address + n uint64 + } + anonce := func(m *types.Message) addrNonce { + return addrNonce{ + s: m.From, + n: m.Nonce, + } + } + + type mc struct { + m abi.MethodNum + c cid.Cid + } + + msgs := map[addrNonce]mc{} + + for _, bh := range ts.Blocks() { + bms, err := api.ChainGetBlockMessages(ctx, bh.Cid()) + if err != nil { + fmt.Println("ERROR: ", err) + return + } + + for _, m := range bms.SecpkMessages { + c, found := msgs[anonce(&m.Message)] + if found { + if c.c == m.Cid() { + continue + } + printLk.Lock() + fmt.Printf("DUPE: M:%d %s / %s ; val: %s\tto: %s\n", c.m, c.c, m.Message.Cid(), types.FIL(m.Message.Value), m.Message.To) + printLk.Unlock() + } + msgs[anonce(&m.Message)] = mc{ + m: m.Message.Method, + c: m.Cid(), + } + } + + for _, m := range bms.BlsMessages { + c, found := msgs[anonce(m)] + if found { + if c.c == m.Cid() { + continue + } + printLk.Lock() + fmt.Printf("DUPE: M:%d %s / %s ; val: %s\tto: %s\n", c.m, c.c, m.Cid(), types.FIL(m.Value), m.To) + printLk.Unlock() + } + msgs[anonce(m)] = mc{ + m: m.Method, + c: m.Cid(), + } + } + + mcount += len(bms.SecpkMessages) + len(bms.BlsMessages) + } + }(head) + head, err = api.ChainGetTipSet(ctx, head.Parents()) + if err != nil { + return err + } + + if head.Height() % 20 == 0 { + printLk.Lock() + //fmt.Printf("H:%d; Ms: %d\n", head.Height(), mcount) + printLk.Unlock() + } + } + + for i := 0; i < threads; i++ { + select { + case throttle <- struct{}{}: + case <-ctx.Done(): + return ctx.Err() + } + + if head.Height() % 20 == 0 { + printLk.Lock() + //fmt.Printf("finH:%d; Ms: %d\n", head.Height(), mcount) + printLk.Unlock() + } + } + + return nil }, } From 77eefcd6d8905e0cd0c2386c650c4d94582e6d80 Mon Sep 17 00:00:00 2001 From: lotus Date: Fri, 19 Mar 2021 03:30:22 +0000 Subject: [PATCH 28/97] feat(lotus-shed): improve duplicate-messages audit command - Allow a start/end epoch. - Print one line per duplicate set. - Allow filtering duplicate messages by method number. - Make the number of threads configurable. --- cmd/lotus-shed/balances.go | 160 ++++++++++++++++++++++++++----------- 1 file changed, 114 insertions(+), 46 deletions(-) diff --git a/cmd/lotus-shed/balances.go b/cmd/lotus-shed/balances.go index 76ca9d6ae..248257c7b 100644 --- a/cmd/lotus-shed/balances.go +++ b/cmd/lotus-shed/balances.go @@ -3,9 +3,11 @@ package main import ( "context" "encoding/csv" + "encoding/json" "fmt" "io" "os" + "runtime" "strconv" "strings" "sync" @@ -76,11 +78,37 @@ var auditsCmd = &cli.Command{ } var duplicatedMessagesCmd = &cli.Command{ - Name: "duplicate-messages", + Name: "duplicate-messages", + Usage: "Check for duplicate messages included in a tipset.", + UsageText: `Check for duplicate messages included in a tipset. + +Due to Filecoin's expected consensus, a tipset may include the same message multiple times in +different blocks. The message will only be executed once. + +This command will find such duplicate messages and print them to standard out as newline-delimited +JSON. Status messages in the form of "H: $HEIGHT ($PROGRESS%)" will be printed to standard error for +every day of chain processed. +`, Flags: []cli.Flag{ &cli.IntFlag{ - Name: "count", - Required: true, + Name: "parallel", + Usage: "the number of parallel threads for block processing", + DefaultText: "half the number of cores", + }, + &cli.IntFlag{ + Name: "start", + Usage: "the first epoch to check", + DefaultText: "genesis", + }, + &cli.IntFlag{ + Name: "end", + Usage: "the last epoch to check", + DefaultText: "the current head", + }, + &cli.IntSliceFlag{ + Name: "method", + Usage: "Filter results by method number.", + DefaultText: "all methods", }, }, Action: func(cctx *cli.Context) error { @@ -92,19 +120,43 @@ var duplicatedMessagesCmd = &cli.Command{ defer closer() ctx := lcli.ReqContext(cctx) - head, err := api.ChainHead(ctx) + var head *types.TipSet + if cctx.IsSet("end") { + epoch := abi.ChainEpoch(cctx.Int("end")) + head, err = api.ChainGetTipSetByHeight(ctx, epoch, types.EmptyTSK) + } else { + head, err = api.ChainHead(ctx) + } if err != nil { return err } var printLk sync.Mutex - threads := 64 - mcount := 0 + threads := runtime.NumCPU() / 2 + if cctx.IsSet("parallel") { + threads = cctx.Int("int") + if threads <= 0 { + return fmt.Errorf("parallelism needs to be at least 1") + } + } else if threads == 0 { + threads = 1 // if we have one core, but who are we kidding... + } throttle := make(chan struct{}, threads) - for i := 0; i < cctx.Int("count"); i++ { + methods := make(map[abi.MethodNum]bool) + for _, m := range cctx.IntSlice("method") { + if m < 0 { + return fmt.Errorf("expected method numbers to be non-negative") + } + methods[abi.MethodNum(m)] = true + } + + target := abi.ChainEpoch(cctx.Int("start")) + totalEpochs := head.Height() - target + + for target <= head.Height() { select { case throttle <- struct{}{}: case <-ctx.Done(): @@ -127,63 +179,80 @@ var duplicatedMessagesCmd = &cli.Command{ } } - type mc struct { - m abi.MethodNum - c cid.Cid - } + msgs := map[addrNonce]map[cid.Cid]*types.Message{} - msgs := map[addrNonce]mc{} + encoder := json.NewEncoder(os.Stdout) for _, bh := range ts.Blocks() { bms, err := api.ChainGetBlockMessages(ctx, bh.Cid()) if err != nil { - fmt.Println("ERROR: ", err) + fmt.Fprintln(os.Stderr, "ERROR: ", err) return } - for _, m := range bms.SecpkMessages { - c, found := msgs[anonce(&m.Message)] - if found { - if c.c == m.Cid() { - continue - } - printLk.Lock() - fmt.Printf("DUPE: M:%d %s / %s ; val: %s\tto: %s\n", c.m, c.c, m.Message.Cid(), types.FIL(m.Message.Value), m.Message.To) - printLk.Unlock() + for i, m := range bms.BlsMessages { + if len(methods) > 0 && !methods[m.Method] { + continue } - msgs[anonce(&m.Message)] = mc{ - m: m.Message.Method, - c: m.Cid(), + c, ok := msgs[anonce(m)] + if !ok { + c = make(map[cid.Cid]*types.Message, 1) + msgs[anonce(m)] = c } + c[bms.Cids[i]] = m } - for _, m := range bms.BlsMessages { - c, found := msgs[anonce(m)] - if found { - if c.c == m.Cid() { - continue - } - printLk.Lock() - fmt.Printf("DUPE: M:%d %s / %s ; val: %s\tto: %s\n", c.m, c.c, m.Cid(), types.FIL(m.Value), m.To) - printLk.Unlock() + for i, m := range bms.SecpkMessages { + if len(methods) > 0 && !methods[m.Message.Method] { + continue } - msgs[anonce(m)] = mc{ - m: m.Method, - c: m.Cid(), + c, ok := msgs[anonce(&m.Message)] + if !ok { + c = make(map[cid.Cid]*types.Message, 1) + msgs[anonce(&m.Message)] = c } + c[bms.Cids[len(bms.BlsMessages)+i]] = &m.Message } - - mcount += len(bms.SecpkMessages) + len(bms.BlsMessages) + } + for _, ms := range msgs { + if len(ms) == 1 { + continue + } + type Msg struct { + Cid string + Value string + Method uint64 + } + grouped := map[string][]Msg{} + for c, m := range ms { + addr := m.To.String() + grouped[addr] = append(grouped[addr], Msg{ + Cid: c.String(), + Value: types.FIL(m.Value).String(), + Method: uint64(m.Method), + }) + } + printLk.Lock() + err := encoder.Encode(grouped) + if err != nil { + fmt.Fprintln(os.Stderr, "ERROR: ", err) + } + printLk.Unlock() } }(head) + + if head.Parents().IsEmpty() { + break + } + head, err = api.ChainGetTipSet(ctx, head.Parents()) if err != nil { return err } - if head.Height() % 20 == 0 { + if head.Height()%2880 == 0 { printLk.Lock() - //fmt.Printf("H:%d; Ms: %d\n", head.Height(), mcount) + fmt.Fprintf(os.Stderr, "H: %s (%d%%)\n", head.Height(), (100*(head.Height()-target))/totalEpochs) printLk.Unlock() } } @@ -195,13 +264,12 @@ var duplicatedMessagesCmd = &cli.Command{ return ctx.Err() } - if head.Height() % 20 == 0 { - printLk.Lock() - //fmt.Printf("finH:%d; Ms: %d\n", head.Height(), mcount) - printLk.Unlock() - } } + printLk.Lock() + fmt.Fprintf(os.Stderr, "H: %s (100%%)\n", head.Height()) + printLk.Unlock() + return nil }, } From 895e968ff9ab634dd6cbc73d7011ea5b10a57843 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 28 Apr 2021 19:33:40 -0400 Subject: [PATCH 29/97] Add a CLI tool for miner proving deadline --- cli/state.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/cli/state.go b/cli/state.go index 60bb0b59f..b5b696712 100644 --- a/cli/state.go +++ b/cli/state.go @@ -75,6 +75,50 @@ var StateCmd = &cli.Command{ StateMarketCmd, StateExecTraceCmd, StateNtwkVersionCmd, + StateMinerProvingDeadlineCmd, + }, +} + +var StateMinerProvingDeadlineCmd = &cli.Command{ + Name: "miner-proving-deadline", + Usage: "Retrieve information about a given miner's proving deadline", + ArgsUsage: "[minerAddress]", + Action: func(cctx *cli.Context) error { + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := ReqContext(cctx) + + if !cctx.Args().Present() { + return fmt.Errorf("must specify miner to get information for") + } + + addr, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + ts, err := LoadTipSet(ctx, cctx, api) + if err != nil { + return err + } + + cd, err := api.StateMinerProvingDeadline(ctx, addr, ts.Key()) + if err != nil { + return xerrors.Errorf("getting miner info: %w", err) + } + + fmt.Printf("Period Start:\t%s\n", cd.PeriodStart) + fmt.Printf("Index:\t\t%d\n", cd.Index) + fmt.Printf("Open:\t\t%s\n", cd.Open) + fmt.Printf("Close:\t\t%s\n", cd.Close) + fmt.Printf("Challenge:\t%s\n", cd.Challenge) + fmt.Printf("FaultCutoff:\t%s\n", cd.FaultCutoff) + + return nil }, } From 701682c98ad12ef90f82c35c7fe76c50e829cbeb Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Apr 2021 17:41:29 -0700 Subject: [PATCH 30/97] feat(lotus-shed): make it possible to filter by to/from when checking dups --- cmd/lotus-shed/balances.go | 99 ++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/cmd/lotus-shed/balances.go b/cmd/lotus-shed/balances.go index 248257c7b..669e8590d 100644 --- a/cmd/lotus-shed/balances.go +++ b/cmd/lotus-shed/balances.go @@ -107,9 +107,27 @@ every day of chain processed. }, &cli.IntSliceFlag{ Name: "method", - Usage: "Filter results by method number.", + Usage: "filter results by method number", DefaultText: "all methods", }, + &cli.StringSliceFlag{ + Name: "include-to", + Usage: "include only messages to the given address (does not perform address resolution)", + DefaultText: "all recipients", + }, + &cli.StringSliceFlag{ + Name: "include-from", + Usage: "include only messages from the given address (does not perform address resolution)", + DefaultText: "all senders", + }, + &cli.StringSliceFlag{ + Name: "exclude-to", + Usage: "exclude messages to the given address (does not perform address resolution)", + }, + &cli.StringSliceFlag{ + Name: "exclude-from", + Usage: "exclude messages from the given address (does not perform address resolution)", + }, }, Action: func(cctx *cli.Context) error { api, closer, err := lcli.GetFullNodeAPI(cctx) @@ -145,7 +163,7 @@ every day of chain processed. throttle := make(chan struct{}, threads) - methods := make(map[abi.MethodNum]bool) + methods := map[abi.MethodNum]bool{} for _, m := range cctx.IntSlice("method") { if m < 0 { return fmt.Errorf("expected method numbers to be non-negative") @@ -153,6 +171,39 @@ every day of chain processed. methods[abi.MethodNum(m)] = true } + addressSet := func(flag string) (map[address.Address]bool, error) { + if !cctx.IsSet(flag) { + return nil, nil + } + addrs := cctx.StringSlice(flag) + set := make(map[address.Address]bool, len(addrs)) + for _, addrStr := range addrs { + addr, err := address.NewFromString(addrStr) + if err != nil { + return nil, fmt.Errorf("failed to parse address %s: %w", addrStr, err) + } + set[addr] = true + } + return set, nil + } + + onlyFrom, err := addressSet("include-from") + if err != nil { + return err + } + onlyTo, err := addressSet("include-to") + if err != nil { + return err + } + excludeFrom, err := addressSet("exclude-from") + if err != nil { + return err + } + excludeTo, err := addressSet("exclude-to") + if err != nil { + return err + } + target := abi.ChainEpoch(cctx.Int("start")) totalEpochs := head.Height() - target @@ -181,6 +232,30 @@ every day of chain processed. msgs := map[addrNonce]map[cid.Cid]*types.Message{} + processMessage := func(c cid.Cid, m *types.Message) { + // Filter + if len(methods) > 0 && !methods[m.Method] { + return + } + if len(onlyFrom) > 0 && !onlyFrom[m.From] { + return + } + if len(onlyTo) > 0 && !onlyTo[m.To] { + return + } + if excludeFrom[m.From] || excludeTo[m.To] { + return + } + + // Record + msgSet, ok := msgs[anonce(m)] + if !ok { + msgSet = make(map[cid.Cid]*types.Message, 1) + msgs[anonce(m)] = msgSet + } + msgSet[c] = m + } + encoder := json.NewEncoder(os.Stdout) for _, bh := range ts.Blocks() { @@ -191,27 +266,11 @@ every day of chain processed. } for i, m := range bms.BlsMessages { - if len(methods) > 0 && !methods[m.Method] { - continue - } - c, ok := msgs[anonce(m)] - if !ok { - c = make(map[cid.Cid]*types.Message, 1) - msgs[anonce(m)] = c - } - c[bms.Cids[i]] = m + processMessage(bms.Cids[i], m) } for i, m := range bms.SecpkMessages { - if len(methods) > 0 && !methods[m.Message.Method] { - continue - } - c, ok := msgs[anonce(&m.Message)] - if !ok { - c = make(map[cid.Cid]*types.Message, 1) - msgs[anonce(&m.Message)] = c - } - c[bms.Cids[len(bms.BlsMessages)+i]] = &m.Message + processMessage(bms.Cids[len(bms.BlsMessages)+i], &m.Message) } } for _, ms := range msgs { From bcfad6b2bbba7b345d671e18e8c8016f89d18a9a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Apr 2021 17:51:25 -0700 Subject: [PATCH 31/97] fix(lotus-shed): sanity check start height for dup check --- cmd/lotus-shed/balances.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/lotus-shed/balances.go b/cmd/lotus-shed/balances.go index 669e8590d..87530c666 100644 --- a/cmd/lotus-shed/balances.go +++ b/cmd/lotus-shed/balances.go @@ -205,6 +205,9 @@ every day of chain processed. } target := abi.ChainEpoch(cctx.Int("start")) + if target < 0 || target > head.Height() { + return fmt.Errorf("start height must be greater than 0 and less than the end height") + } totalEpochs := head.Height() - target for target <= head.Height() { From 63db9e1633cc2f1ed09c8eda511cefd72395220f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Apr 2021 19:55:18 -0700 Subject: [PATCH 32/97] fix(splitstore): fix a panic on revert-only head changes Calling, e.g., `lotus chain sethead` on an ancestor tipset won't apply any new blocks, it'll just revert a bunch. This will lead to HeadChange calls with no new blocks to apply. fixes #6125 --- blockstore/splitstore/splitstore.go | 5 +++++ blockstore/splitstore/splitstore_test.go | 28 ++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/blockstore/splitstore/splitstore.go b/blockstore/splitstore/splitstore.go index 23b2d3427..f6d26bbdd 100644 --- a/blockstore/splitstore/splitstore.go +++ b/blockstore/splitstore/splitstore.go @@ -406,6 +406,11 @@ func (s *SplitStore) Close() error { } func (s *SplitStore) HeadChange(_, apply []*types.TipSet) error { + // Revert only. + if len(apply) == 0 { + return nil + } + s.mx.Lock() curTs := apply[len(apply)-1] epoch := curTs.Height() diff --git a/blockstore/splitstore/splitstore_test.go b/blockstore/splitstore/splitstore_test.go index e5314b80f..9dbc56eba 100644 --- a/blockstore/splitstore/splitstore_test.go +++ b/blockstore/splitstore/splitstore_test.go @@ -27,7 +27,7 @@ func init() { } func testSplitStore(t *testing.T, cfg *Config) { - chain := &mockChain{} + chain := &mockChain{t: t} // genesis genBlock := mock.MkBlock(nil, 0, 0) genTs := mock.TipSet(genBlock) @@ -169,6 +169,9 @@ func testSplitStore(t *testing.T, cfg *Config) { t.Errorf("expected %d hot blocks, but got %d", 7, hotCnt) } } + + // Make sure we can revert without panicing. + chain.revert(2) } func TestSplitStoreSimpleCompaction(t *testing.T) { @@ -191,6 +194,8 @@ func TestSplitStoreFullCompactionWithGC(t *testing.T) { } type mockChain struct { + t testing.TB + sync.Mutex tipsets []*types.TipSet listener func(revert []*types.TipSet, apply []*types.TipSet) error @@ -204,7 +209,26 @@ func (c *mockChain) push(ts *types.TipSet) { if c.listener != nil { err := c.listener(nil, []*types.TipSet{ts}) if err != nil { - log.Errorf("mockchain: error dispatching listener: %s", err) + c.t.Errorf("mockchain: error dispatching listener: %s", err) + } + } +} + +func (c *mockChain) revert(count int) { + c.Lock() + revert := make([]*types.TipSet, count) + if count > len(c.tipsets) { + c.Unlock() + c.t.Fatalf("not enough tipsets to revert") + } + copy(revert, c.tipsets[len(c.tipsets)-count:]) + c.tipsets = c.tipsets[:len(c.tipsets)-count] + c.Unlock() + + if c.listener != nil { + err := c.listener(revert, nil) + if err != nil { + c.t.Errorf("mockchain: error dispatching listener: %s", err) } } } From d794b49df352ce9f909baa5abb520848c1aa26b6 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 29 Apr 2021 00:56:16 -0400 Subject: [PATCH 33/97] Use EmptyTSK where appropriate --- chain/types/tipset_key.go | 2 +- chain/types/tipset_key_test.go | 2 +- cmd/lotus-shed/sectors.go | 2 +- node/impl/full/gas.go | 4 ++-- storage/wdpost_run.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chain/types/tipset_key.go b/chain/types/tipset_key.go index e5bc7750d..9f9887796 100644 --- a/chain/types/tipset_key.go +++ b/chain/types/tipset_key.go @@ -47,7 +47,7 @@ func NewTipSetKey(cids ...cid.Cid) TipSetKey { func TipSetKeyFromBytes(encoded []byte) (TipSetKey, error) { _, err := decodeKey(encoded) if err != nil { - return TipSetKey{}, err + return EmptyTSK, err } return TipSetKey{string(encoded)}, nil } diff --git a/chain/types/tipset_key_test.go b/chain/types/tipset_key_test.go index 7b3ce439d..73c1ca9df 100644 --- a/chain/types/tipset_key_test.go +++ b/chain/types/tipset_key_test.go @@ -19,7 +19,7 @@ func TestTipSetKey(t *testing.T) { fmt.Println(len(c1.Bytes())) t.Run("zero value", func(t *testing.T) { - assert.Equal(t, TipSetKey{}, NewTipSetKey()) + assert.Equal(t, EmptyTSK, NewTipSetKey()) }) t.Run("CID extraction", func(t *testing.T) { diff --git a/cmd/lotus-shed/sectors.go b/cmd/lotus-shed/sectors.go index 6cf6ee86e..cf40e1152 100644 --- a/cmd/lotus-shed/sectors.go +++ b/cmd/lotus-shed/sectors.go @@ -254,7 +254,7 @@ var terminateSectorPenaltyEstimationCmd = &cli.Command{ //TODO: 4667 add an option to give a more precise estimation with pending termination penalty excluded - invocResult, err := nodeApi.StateCall(ctx, msg, types.TipSetKey{}) + invocResult, err := nodeApi.StateCall(ctx, msg, types.EmptyTSK) if err != nil { return xerrors.Errorf("fail to state call: %w", err) } diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index a3bbc8d78..7b624d39b 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -324,7 +324,7 @@ func gasEstimateGasLimit( func (m *GasModule) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, _ types.TipSetKey) (*types.Message, error) { if msg.GasLimit == 0 { - gasLimit, err := m.GasEstimateGasLimit(ctx, msg, types.TipSetKey{}) + gasLimit, err := m.GasEstimateGasLimit(ctx, msg, types.EmptyTSK) if err != nil { return nil, xerrors.Errorf("estimating gas used: %w", err) } @@ -332,7 +332,7 @@ func (m *GasModule) GasEstimateMessageGas(ctx context.Context, msg *types.Messag } if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 { - gasPremium, err := m.GasEstimateGasPremium(ctx, 10, msg.From, msg.GasLimit, types.TipSetKey{}) + gasPremium, err := m.GasEstimateGasPremium(ctx, 10, msg.From, msg.GasLimit, types.EmptyTSK) if err != nil { return nil, xerrors.Errorf("estimating gas price: %w", err) } diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index 4218daea1..cec86a09b 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -821,7 +821,7 @@ func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message, // estimate minGasFeeMsg := *msg - minGasFeeMsg.GasPremium, err = s.api.GasEstimateGasPremium(ctx, 5, msg.From, msg.GasLimit, types.TipSetKey{}) + minGasFeeMsg.GasPremium, err = s.api.GasEstimateGasPremium(ctx, 5, msg.From, msg.GasLimit, types.EmptyTSK) if err != nil { log.Errorf("failed to estimate minimum gas premium: %+v", err) minGasFeeMsg.GasPremium = msg.GasPremium From f353a794cb23d1ff822a152278c3f749be420274 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Apr 2021 22:08:37 -0700 Subject: [PATCH 34/97] fix: spelling Co-authored-by: Aayush Rajasekaran --- blockstore/splitstore/splitstore_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockstore/splitstore/splitstore_test.go b/blockstore/splitstore/splitstore_test.go index 9dbc56eba..dcaf27647 100644 --- a/blockstore/splitstore/splitstore_test.go +++ b/blockstore/splitstore/splitstore_test.go @@ -170,7 +170,7 @@ func testSplitStore(t *testing.T, cfg *Config) { } } - // Make sure we can revert without panicing. + // Make sure we can revert without panicking. chain.revert(2) } From 486742568521467c41694925e27e19f8da8dcd6f Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 28 Apr 2021 23:35:27 -0700 Subject: [PATCH 35/97] fix: use the parent state when listing actors To be consistent with other commands. --- chain/stmgr/stmgr.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index ad72444e8..afc98a32a 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -854,10 +854,7 @@ func (sm *StateManager) ListAllActors(ctx context.Context, ts *types.TipSet) ([] if ts == nil { ts = sm.cs.GetHeaviestTipSet() } - st, _, err := sm.TipSetState(ctx, ts) - if err != nil { - return nil, err - } + st := ts.ParentState() stateTree, err := sm.StateTree(st) if err != nil { From c7d74ef16e4ef944f9847500b30f6f0795cc387e Mon Sep 17 00:00:00 2001 From: Anton Evangelatov Date: Thu, 29 Apr 2021 17:39:00 +0200 Subject: [PATCH 36/97] separate TestBatchDealInput from TestAPIDealFlow --- node/node_test.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/node/node_test.go b/node/node_test.go index 91348647d..45a5b7f57 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -58,9 +58,23 @@ func TestAPIDealFlow(t *testing.T) { t.Run("TestPublishDealsBatching", func(t *testing.T) { test.TestPublishDealsBatching(t, builder.MockSbBuilder, blockTime, dealStartEpoch) }) - t.Run("TestBatchDealInput", func(t *testing.T) { - test.TestBatchDealInput(t, builder.MockSbBuilder, blockTime, dealStartEpoch) - }) +} + +func TestBatchDealInput(t *testing.T) { + logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("chainstore", "ERROR") + logging.SetLogLevel("chain", "ERROR") + logging.SetLogLevel("sub", "ERROR") + logging.SetLogLevel("storageminer", "ERROR") + + blockTime := 10 * time.Millisecond + + // For these tests where the block time is artificially short, just use + // a deal start epoch that is guaranteed to be far enough in the future + // so that the deal starts sealing in time + dealStartEpoch := abi.ChainEpoch(2 << 12) + + test.TestBatchDealInput(t, builder.MockSbBuilder, blockTime, dealStartEpoch) } func TestAPIDealFlowReal(t *testing.T) { From 2857f6c0edc9a6e31ebe3a646a6a3dc8063e2bd8 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 29 Apr 2021 08:46:38 -0700 Subject: [PATCH 37/97] fix: use a consistent tipset in commands It's very easy to write an incorrect command that operates over different heads by using the "empty" tipset. This change makes the `LoadTipSet` command helper get the latest head from the lotus daemon if its unset. The cost is an extra call to get the head. That should be trivial in most cases. --- cli/chain.go | 6 ------ cli/state.go | 27 ++++----------------------- cmd/lotus-shed/frozen-miners.go | 6 ------ cmd/lotus-shed/postfind.go | 6 ------ cmd/lotus-shed/stateroot-stats.go | 14 -------------- 5 files changed, 4 insertions(+), 55 deletions(-) diff --git a/cli/chain.go b/cli/chain.go index 9954813de..cc2fe50ec 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -723,12 +723,6 @@ var ChainGetCmd = &cli.Command{ return err } - if ts == nil { - ts, err = api.ChainHead(ctx) - if err != nil { - return err - } - } p = "/ipfs/" + ts.ParentState().String() + p if cctx.Bool("verbose") { fmt.Println(p) diff --git a/cli/state.go b/cli/state.go index 60bb0b59f..ae1553dd8 100644 --- a/cli/state.go +++ b/cli/state.go @@ -180,10 +180,13 @@ func ParseTipSetString(ts string) ([]cid.Cid, error) { return cids, nil } +// LoadTipSet gets the tipset from the context, or the head from the API. +// +// It always gets the head from the API so commands use a consistent tipset even if time pases. func LoadTipSet(ctx context.Context, cctx *cli.Context, api v0api.FullNode) (*types.TipSet, error) { tss := cctx.String("tipset") if tss == "" { - return nil, nil + return api.ChainHead(ctx) } return ParseTipSetRef(ctx, api, tss) @@ -850,14 +853,6 @@ var StateListMessagesCmd = &cli.Command{ return err } - if ts == nil { - head, err := api.ChainHead(ctx) - if err != nil { - return err - } - ts = head - } - windowSize := abi.ChainEpoch(100) cur := ts @@ -957,13 +952,6 @@ var StateComputeStateCmd = &cli.Command{ } h := abi.ChainEpoch(cctx.Uint64("vm-height")) - if ts == nil { - head, err := api.ChainHead(ctx) - if err != nil { - return err - } - ts = head - } if h == 0 { h = ts.Height() } @@ -1765,13 +1753,6 @@ var StateSectorCmd = &cli.Command{ return err } - if ts == nil { - ts, err = api.ChainHead(ctx) - if err != nil { - return err - } - } - maddr, err := address.NewFromString(cctx.Args().Get(0)) if err != nil { return err diff --git a/cmd/lotus-shed/frozen-miners.go b/cmd/lotus-shed/frozen-miners.go index 6b843f0d6..ed09c00c5 100644 --- a/cmd/lotus-shed/frozen-miners.go +++ b/cmd/lotus-shed/frozen-miners.go @@ -35,12 +35,6 @@ var frozenMinersCmd = &cli.Command{ if err != nil { return err } - if ts == nil { - ts, err = api.ChainHead(ctx) - if err != nil { - return err - } - } queryEpoch := ts.Height() diff --git a/cmd/lotus-shed/postfind.go b/cmd/lotus-shed/postfind.go index 83006fd09..c8a4c9907 100644 --- a/cmd/lotus-shed/postfind.go +++ b/cmd/lotus-shed/postfind.go @@ -49,12 +49,6 @@ var postFindCmd = &cli.Command{ if err != nil { return err } - if startTs == nil { - startTs, err = api.ChainHead(ctx) - if err != nil { - return err - } - } stopEpoch := startTs.Height() - abi.ChainEpoch(c.Int("lookback")) if verbose { fmt.Printf("Collecting messages between %d and %d\n", startTs.Height(), stopEpoch) diff --git a/cmd/lotus-shed/stateroot-stats.go b/cmd/lotus-shed/stateroot-stats.go index 023f782bd..6d5d57708 100644 --- a/cmd/lotus-shed/stateroot-stats.go +++ b/cmd/lotus-shed/stateroot-stats.go @@ -56,13 +56,6 @@ var staterootDiffsCmd = &cli.Command{ return err } - if ts == nil { - ts, err = api.ChainHead(ctx) - if err != nil { - return err - } - } - fn := func(ts *types.TipSet) (cid.Cid, []cid.Cid) { blk := ts.Blocks()[0] strt := blk.ParentStateRoot @@ -134,13 +127,6 @@ var staterootStatCmd = &cli.Command{ return err } - if ts == nil { - ts, err = api.ChainHead(ctx) - if err != nil { - return err - } - } - var addrs []address.Address for _, inp := range cctx.Args().Slice() { From 05dbe5cbbe4ff9b6db9fa5a0d0e430240c4ec63c Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Fri, 26 Mar 2021 09:25:51 -0700 Subject: [PATCH 38/97] checks on push --- .github/workflows/testground-on-push.yml | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/testground-on-push.yml diff --git a/.github/workflows/testground-on-push.yml b/.github/workflows/testground-on-push.yml new file mode 100644 index 000000000..73fa267c3 --- /dev/null +++ b/.github/workflows/testground-on-push.yml @@ -0,0 +1,33 @@ +--- +name: Testground PR Checker + +on: [push] + +jobs: + testground: + runs-on: ubuntu-latest + name: ${{ matrix.composition_file }} + strategy: + matrix: + include: + - backend_addr: ci.testground.ipfs.team + backend_proto: https + plan_directory: testplans/graphsync + composition_file: testplans/graphsync/_compositions/stress-k8s.toml + - backend_addr: ci.testground.ipfs.team + backend_proto: https + plan_directory: testplans/lotus-soup + composition_file: testplans/lotus-soup/_compositions/baseline-k8s-3-1.toml + - backend_addr: ci.testground.ipfs.team + backend_proto: https + plan_directory: testplans/lotus-soup + composition_file: testplans/lotus-soup/_compositions/paych-stress-k8s.toml + steps: + - uses: actions/checkout@v2 + - name: testground run + uses: coryschwartz/testground-github-action@v1.0 + with: + backend_addr: ${{ matrix.backend_addr }} + backend_proto: ${{ matrix.backend_proto }} + plan_directory: ${{ matrix.plan_directory }} + composition_file: ${{ matrix.composition_file }} From dd2b44351cd2832762dc89d63ca2b492128e4ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 29 Apr 2021 18:50:13 +0200 Subject: [PATCH 39/97] Drop graphsync testplan from CI --- .github/workflows/testground-on-push.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/testground-on-push.yml b/.github/workflows/testground-on-push.yml index 73fa267c3..9d58db962 100644 --- a/.github/workflows/testground-on-push.yml +++ b/.github/workflows/testground-on-push.yml @@ -10,10 +10,6 @@ jobs: strategy: matrix: include: - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/graphsync - composition_file: testplans/graphsync/_compositions/stress-k8s.toml - backend_addr: ci.testground.ipfs.team backend_proto: https plan_directory: testplans/lotus-soup From ac77c51d5e23f3b5ed318cde4362d82e46d5aaed Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 29 Apr 2021 10:24:16 -0700 Subject: [PATCH 40/97] address nit --- extern/sector-storage/ffiwrapper/sealer_cgo.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extern/sector-storage/ffiwrapper/sealer_cgo.go b/extern/sector-storage/ffiwrapper/sealer_cgo.go index 51f70e132..36fbacb30 100644 --- a/extern/sector-storage/ffiwrapper/sealer_cgo.go +++ b/extern/sector-storage/ffiwrapper/sealer_cgo.go @@ -212,6 +212,11 @@ func (sb *Sealer) AddPiece(ctx context.Context, sector storage.SectorRef, existi return abi.PieceInfo{}, xerrors.Errorf("generate unsealed CID: %w", err) } + // validate that the pieceCID was properly formed + if _, err := commcid.CIDToPieceCommitmentV1(pieceCID); err != nil { + return abi.PieceInfo{}, err + } + if payloadRoundedBytes < pieceSize.Padded() { paddedCid, err := commpffi.ZeroPadPieceCommitment(pieceCID, payloadRoundedBytes.Unpadded(), pieceSize) if err != nil { @@ -221,11 +226,6 @@ func (sb *Sealer) AddPiece(ctx context.Context, sector storage.SectorRef, existi pieceCID = paddedCid } - // validate that the pieceCID was properly formed - if _, err := commcid.CIDToPieceCommitmentV1(pieceCID); err != nil { - return abi.PieceInfo{}, err - } - return abi.PieceInfo{ Size: pieceSize.Padded(), PieceCID: pieceCID, From a590fb6a12668ec291a14b7d01594bdf2029daab Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 29 Apr 2021 10:28:33 -0700 Subject: [PATCH 41/97] mod tidy for the magik --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index 7ce7ede20..3e548b523 100644 --- a/go.sum +++ b/go.sum @@ -260,8 +260,6 @@ github.com/filecoin-project/go-bitfield v0.2.4/go.mod h1:CNl9WG8hgR5mttCnUErjcQj github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:av5fw6wmm58FYMgJeoB/lK9XXrgdugYiTqkdxjTy9k8= github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg= github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= -github.com/filecoin-project/go-commp-utils v0.1.0 h1:PaDxoXYh1TXnnz5kA/xSObpAQwcJSUs4Szb72nuaNdk= -github.com/filecoin-project/go-commp-utils v0.1.0/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427180530-4606b1a6cbdd h1:cpwbE1z6a+0fp62P0Qv7Z7ZqIy8Jiay0SWR0xcuZ0qs= github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427180530-4606b1a6cbdd/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus= From e6779b0b6f6d66bc0be8b7aec9a49cef80c82946 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 5 Jan 2021 01:08:19 -0500 Subject: [PATCH 42/97] Add a command to get the fees of a deal --- chain/actors/builtin/market/market.go | 17 +++++ cmd/lotus-shed/main.go | 1 + cmd/lotus-shed/market.go | 102 ++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 cmd/lotus-shed/market.go diff --git a/chain/actors/builtin/market/market.go b/chain/actors/builtin/market/market.go index 738151503..33729bdf9 100644 --- a/chain/actors/builtin/market/market.go +++ b/chain/actors/builtin/market/market.go @@ -1,6 +1,7 @@ package market import ( + "github.com/filecoin-project/go-state-types/big" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -153,3 +154,19 @@ func EmptyDealState() *DealState { LastUpdatedEpoch: -1, } } + +// returns the earned fees and pending fees for a given deal +func (deal DealProposal) GetDealFees(height abi.ChainEpoch) (abi.TokenAmount, abi.TokenAmount) { + tf := big.Mul(deal.StoragePricePerEpoch, big.NewInt(int64(deal.EndEpoch-deal.StartEpoch))) + + ef := big.Mul(deal.StoragePricePerEpoch, big.NewInt(int64(height-deal.StartEpoch))) + if ef.LessThan(big.Zero()) { + ef = big.Zero() + } + + if ef.GreaterThan(tf) { + ef = tf + } + + return ef, big.Sub(tf, ef) +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index ebe4f014a..3aa667459 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -34,6 +34,7 @@ func main() { postFindCmd, proofsCmd, verifRegCmd, + marketCmd, miscCmd, mpoolCmd, genesisVerifyCmd, diff --git a/cmd/lotus-shed/market.go b/cmd/lotus-shed/market.go new file mode 100644 index 000000000..e2e322784 --- /dev/null +++ b/cmd/lotus-shed/market.go @@ -0,0 +1,102 @@ +package main + +import ( + "fmt" + + lcli "github.com/filecoin-project/lotus/cli" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var marketCmd = &cli.Command{ + Name: "market", + Usage: "Interact with the market actor", + Flags: []cli.Flag{}, + Subcommands: []*cli.Command{ + marketDealFeesCmd, + }, +} + +var marketDealFeesCmd = &cli.Command{ + Name: "get-deal-fees", + Usage: "View the storage fees associated with a particular deal or storage provider", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "provider", + Usage: "provider whose outstanding fees you'd like to calculate", + }, + &cli.IntFlag{ + Name: "dealId", + Usage: "deal whose outstanding fees you'd like to calculate", + }, + }, + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.ReqContext(cctx) + + ts, err := lcli.LoadTipSet(ctx, cctx, api) + if err != nil { + return err + } + + ht := ts.Height() + + if cctx.IsSet("provider") { + p, err := address.NewFromString(cctx.String("provider")) + if err != nil { + return fmt.Errorf("failed to parse provider: %w", err) + } + + deals, err := api.StateMarketDeals(ctx, ts.Key()) + if err != nil { + return err + } + + ef := big.Zero() + pf := big.Zero() + count := 0 + + for _, deal := range deals { + if deal.Proposal.Provider == p { + e, p := deal.Proposal.GetDealFees(ht) + ef = big.Add(ef, e) + pf = big.Add(pf, p) + count++ + } + } + + fmt.Println("Total deals: ", count) + fmt.Println("Total earned fees: ", ef) + fmt.Println("Total pending fees: ", pf) + fmt.Println("Total fees: ", big.Add(ef, pf)) + + return nil + } + + if dealid := cctx.Int("dealId"); dealid != 0 { + deal, err := api.StateMarketStorageDeal(ctx, abi.DealID(dealid), ts.Key()) + if err != nil { + return err + } + + ef, pf := deal.Proposal.GetDealFees(ht) + + fmt.Println("Earned fees: ", ef) + fmt.Println("Pending fees: ", pf) + fmt.Println("Total fees: ", big.Add(ef, pf)) + + return nil + } + + return xerrors.New("must provide either --provider or --dealId flag") + }, +} From 51ca7aa79acc3baba5be5e4f2e44e399239fffb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 29 Apr 2021 19:44:39 +0200 Subject: [PATCH 43/97] scripts: Some improvements to cli gen script --- scripts/generate-lotus-cli.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/generate-lotus-cli.py b/scripts/generate-lotus-cli.py index 8fb27026b..fc436d6ad 100644 --- a/scripts/generate-lotus-cli.py +++ b/scripts/generate-lotus-cli.py @@ -5,21 +5,20 @@ import os -def generate_lotus_cli(): - output_folder = '../documentation/en' - txt_file = open('%s/lotus-cli.txt' % output_folder, 'w') # set the name of txt output - md_file = open('%s/lotus-cli.md' % output_folder, 'w') # set the name of md output +def generate_lotus_cli(prog): + output_folder = 'documentation/en' + md_file = open('%s/cli-%s.md' % (output_folder, prog), 'w') # set the name of md output def get_cmd_recursively(cur_cmd): - txt_file.writelines('\n\n%s\n' % cur_cmd[2:]) - md_file.writelines('#' * cur_cmd.count(' ') + '# ' + cur_cmd[2:] + '\n') + depth = cur_cmd.count(' ') + md_file.writelines(('\n' * min(depth, 1)) + ('#' * depth) + '# ' + cur_cmd[2:] + '\n') cmd_flag = False - cmd_help_output = os.popen('cd ..' + ' && ' + cur_cmd + ' -h') + print('> ' + cur_cmd) + cmd_help_output = os.popen(cur_cmd + ' -h') cmd_help_output_lines = cmd_help_output.readlines() - txt_file.writelines(cmd_help_output_lines) md_file.writelines('```\n') md_file.writelines(cmd_help_output_lines) md_file.writelines('```\n') @@ -42,10 +41,11 @@ def generate_lotus_cli(): except Exception as e: print('Fail to deal with "%s" with error:\n%s' % (line, e)) - get_cmd_recursively('./lotus') - txt_file.close() + get_cmd_recursively('./' + prog) md_file.close() if __name__ == "__main__": - generate_lotus_cli() + generate_lotus_cli('lotus') + generate_lotus_cli('lotus-miner') + generate_lotus_cli('lotus-worker') From 483478d5113e4a792c0b5eaa7f60e5e296a8313d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 29 Apr 2021 19:45:01 +0200 Subject: [PATCH 44/97] Generate cli docs --- documentation/en/cli-lotus-miner.md | 1816 ++++++++++++++++++ documentation/en/cli-lotus-worker.md | 171 ++ documentation/en/cli-lotus.md | 2659 ++++++++++++++++++++++++++ 3 files changed, 4646 insertions(+) create mode 100644 documentation/en/cli-lotus-miner.md create mode 100644 documentation/en/cli-lotus-worker.md create mode 100644 documentation/en/cli-lotus.md diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md new file mode 100644 index 000000000..bc06573ad --- /dev/null +++ b/documentation/en/cli-lotus-miner.md @@ -0,0 +1,1816 @@ +# lotus-miner +``` +NAME: + lotus-miner - Filecoin decentralized storage network miner + +USAGE: + lotus-miner [global options] command [command options] [arguments...] + +VERSION: + 1.11.0-dev+mainnet+git.12867a567.dirty + +COMMANDS: + init Initialize a lotus miner repo + run Start a lotus miner process + stop Stop a running lotus miner + config Output default configuration + backup Create node metadata backup + version Print version + help, h Shows a list of commands or help for one command + CHAIN: + actor manipulate the miner actor + info Print miner info + DEVELOPER: + auth Manage RPC permissions + log Manage logging + wait-api Wait for lotus api to come online + fetch-params Fetch proving parameters + MARKET: + storage-deals Manage storage deals and related configuration + retrieval-deals Manage retrieval deals and related configuration + data-transfers Manage data transfers + NETWORK: + net Manage P2P Network + RETRIEVAL: + pieces interact with the piecestore + STORAGE: + sectors interact with sector store + proving View proving information + storage manage sector storage + sealing interact with sealing pipeline + +GLOBAL OPTIONS: + --actor value, -a value specify other actor to check state for (read only) + --color (default: false) + --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] + --help, -h show help (default: false) + --version, -v print the version (default: false) +``` + +## lotus-miner init +``` +NAME: + lotus-miner init - Initialize a lotus miner repo + +USAGE: + lotus-miner init command [command options] [arguments...] + +COMMANDS: + restore Initialize a lotus miner repo from a backup + help, h Shows a list of commands or help for one command + +OPTIONS: + --actor value specify the address of an already created miner actor + --create-worker-key create separate worker key (default: false) + --worker value, -w value worker key to use (overrides --create-worker-key) + --owner value, -o value owner key to use + --sector-size value specify sector size to use (default: "32GiB") + --pre-sealed-sectors value specify set of presealed sectors for starting as a genesis miner + --pre-sealed-metadata value specify the metadata file for the presealed sectors + --nosync don't check full-node sync status (default: false) + --symlink-imported-sectors attempt to symlink to presealed sectors instead of copying them into place (default: false) + --no-local-storage don't use storageminer repo for sector storage (default: false) + --gas-premium value set gas premium for initialization messages in AttoFIL (default: "0") + --from value select which address to send actor creation message from + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner init restore +``` +NAME: + lotus-miner init restore - Initialize a lotus miner repo from a backup + +USAGE: + lotus-miner init restore [command options] [backupFile] + +OPTIONS: + --nosync don't check full-node sync status (default: false) + --config value config file (config.toml) + --storage-config value storage paths config (storage.json) + --help, -h show help (default: false) + +``` + +## lotus-miner run +``` +NAME: + lotus-miner run - Start a lotus miner process + +USAGE: + lotus-miner run [command options] [arguments...] + +OPTIONS: + --miner-api value 2345 + --enable-gpu-proving enable use of GPU for mining operations (default: true) + --nosync don't check full-node sync status (default: false) + --manage-fdlimit manage open file limit (default: true) + --help, -h show help (default: false) + +``` + +## lotus-miner stop +``` +NAME: + lotus-miner stop - Stop a running lotus miner + +USAGE: + lotus-miner stop [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner config +``` +NAME: + lotus-miner config - Output default configuration + +USAGE: + lotus-miner config [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner backup +``` +NAME: + lotus-miner backup - Create node metadata backup + +USAGE: + lotus-miner backup [command options] [backup file path] + +DESCRIPTION: + The backup command writes a copy of node metadata under the specified path + +Online backups: +For security reasons, the daemon must be have LOTUS_BACKUP_BASE_PATH env var set +to a path where backup files are supposed to be saved, and the path specified in +this command must be within this base path + +OPTIONS: + --offline create backup without the node running (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner version +``` +NAME: + lotus-miner version - Print version + +USAGE: + lotus-miner version [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner actor +``` +NAME: + lotus-miner actor - manipulate the miner actor + +USAGE: + lotus-miner actor command [command options] [arguments...] + +COMMANDS: + set-addrs set addresses that your miner can be publicly dialed on + withdraw withdraw available balance + repay-debt pay down a miner's debt + set-peer-id set the peer id of your miner + set-owner Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner) + control Manage control addresses + propose-change-worker Propose a worker address change + confirm-change-worker Confirm a worker address change + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner actor set-addrs +``` +NAME: + lotus-miner actor set-addrs - set addresses that your miner can be publicly dialed on + +USAGE: + lotus-miner actor set-addrs [command options] [arguments...] + +OPTIONS: + --gas-limit value set gas limit (default: 0) + --unset unset address (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner actor withdraw +``` +NAME: + lotus-miner actor withdraw - withdraw available balance + +USAGE: + lotus-miner actor withdraw [command options] [amount (FIL)] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner actor repay-debt +``` +NAME: + lotus-miner actor repay-debt - pay down a miner's debt + +USAGE: + lotus-miner actor repay-debt [command options] [amount (FIL)] + +OPTIONS: + --from value optionally specify the account to send funds from + --help, -h show help (default: false) + +``` + +### lotus-miner actor set-peer-id +``` +NAME: + lotus-miner actor set-peer-id - set the peer id of your miner + +USAGE: + lotus-miner actor set-peer-id [command options] [arguments...] + +OPTIONS: + --gas-limit value set gas limit (default: 0) + --help, -h show help (default: false) + +``` + +### lotus-miner actor set-owner +``` +NAME: + lotus-miner actor set-owner - Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner) + +USAGE: + lotus-miner actor set-owner [command options] [newOwnerAddress senderAddress] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner actor control +``` +NAME: + lotus-miner actor control - Manage control addresses + +USAGE: + lotus-miner actor control command [command options] [arguments...] + +COMMANDS: + list Get currently set control addresses + set Set control address(-es) + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner actor control list +``` +NAME: + lotus-miner actor control list - Get currently set control addresses + +USAGE: + lotus-miner actor control list [command options] [arguments...] + +OPTIONS: + --verbose (default: false) + --color (default: true) + --help, -h show help (default: false) + +``` + +#### lotus-miner actor control set +``` +NAME: + lotus-miner actor control set - Set control address(-es) + +USAGE: + lotus-miner actor control set [command options] [...address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner actor propose-change-worker +``` +NAME: + lotus-miner actor propose-change-worker - Propose a worker address change + +USAGE: + lotus-miner actor propose-change-worker [command options] [address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner actor confirm-change-worker +``` +NAME: + lotus-miner actor confirm-change-worker - Confirm a worker address change + +USAGE: + lotus-miner actor confirm-change-worker [command options] [address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner info +``` +NAME: + lotus-miner info - Print miner info + +USAGE: + lotus-miner info command [command options] [arguments...] + +COMMANDS: + all dump all related miner info + help, h Shows a list of commands or help for one command + +OPTIONS: + --hide-sectors-info hide sectors info (default: false) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner info all +``` +NAME: + lotus-miner info all - dump all related miner info + +USAGE: + lotus-miner info all [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner auth +``` +NAME: + lotus-miner auth - Manage RPC permissions + +USAGE: + lotus-miner auth command [command options] [arguments...] + +COMMANDS: + create-token Create token + api-info Get token with API info required to connect to this node + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner auth create-token +``` +NAME: + lotus-miner auth create-token - Create token + +USAGE: + lotus-miner auth create-token [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help (default: false) + +``` + +### lotus-miner auth api-info +``` +NAME: + lotus-miner auth api-info - Get token with API info required to connect to this node + +USAGE: + lotus-miner auth api-info [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help (default: false) + +``` + +## lotus-miner log +``` +NAME: + lotus-miner log - Manage logging + +USAGE: + lotus-miner log command [command options] [arguments...] + +COMMANDS: + list List log systems + set-level Set log level + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner log list +``` +NAME: + lotus-miner log list - List log systems + +USAGE: + lotus-miner log list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner log set-level +``` +NAME: + lotus-miner log set-level - Set log level + +USAGE: + lotus-miner log set-level [command options] [level] + +DESCRIPTION: + Set the log level for logging systems: + + The system flag can be specified multiple times. + + eg) log set-level --system chain --system chainxchg debug + + Available Levels: + debug + info + warn + error + + Environment Variables: + GOLOG_LOG_LEVEL - Default log level for all log systems + GOLOG_LOG_FMT - Change output log format (json, nocolor) + GOLOG_FILE - Write logs to file + GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr + + +OPTIONS: + --system value limit to log system + --help, -h show help (default: false) + +``` + +## lotus-miner wait-api +``` +NAME: + lotus-miner wait-api - Wait for lotus api to come online + +USAGE: + lotus-miner wait-api [command options] [arguments...] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner fetch-params +``` +NAME: + lotus-miner fetch-params - Fetch proving parameters + +USAGE: + lotus-miner fetch-params [command options] [sectorSize] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner storage-deals +``` +NAME: + lotus-miner storage-deals - Manage storage deals and related configuration + +USAGE: + lotus-miner storage-deals command [command options] [arguments...] + +COMMANDS: + import-data Manually import data for a deal + list List all deals for this miner + selection Configure acceptance criteria for storage deal proposals + set-ask Configure the miner's ask + get-ask Print the miner's ask + set-blocklist Set the miner's list of blocklisted piece CIDs + get-blocklist List the contents of the miner's piece CID blocklist + reset-blocklist Remove all entries from the miner's piece CID blocklist + set-seal-duration Set the expected time, in minutes, that you expect sealing sectors to take. Deals that start before this duration will be rejected. + pending-publish list deals waiting in publish queue + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner storage-deals import-data +``` +NAME: + lotus-miner storage-deals import-data - Manually import data for a deal + +USAGE: + lotus-miner storage-deals import-data [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals list +``` +NAME: + lotus-miner storage-deals list - List all deals for this miner + +USAGE: + lotus-miner storage-deals list [command options] [arguments...] + +OPTIONS: + --verbose, -v (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals selection +``` +NAME: + lotus-miner storage-deals selection - Configure acceptance criteria for storage deal proposals + +USAGE: + lotus-miner storage-deals selection command [command options] [arguments...] + +COMMANDS: + list List storage deal proposal selection criteria + reset Reset storage deal proposal selection criteria to default values + reject Configure criteria which necessitate automatic rejection + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner storage-deals selection list +``` +NAME: + lotus-miner storage-deals selection list - List storage deal proposal selection criteria + +USAGE: + lotus-miner storage-deals selection list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner storage-deals selection reset +``` +NAME: + lotus-miner storage-deals selection reset - Reset storage deal proposal selection criteria to default values + +USAGE: + lotus-miner storage-deals selection reset [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner storage-deals selection reject +``` +NAME: + lotus-miner storage-deals selection reject - Configure criteria which necessitate automatic rejection + +USAGE: + lotus-miner storage-deals selection reject [command options] [arguments...] + +OPTIONS: + --online (default: false) + --offline (default: false) + --verified (default: false) + --unverified (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals set-ask +``` +NAME: + lotus-miner storage-deals set-ask - Configure the miner's ask + +USAGE: + lotus-miner storage-deals set-ask [command options] [arguments...] + +OPTIONS: + --price PRICE Set the price of the ask for unverified deals (specified as FIL / GiB / Epoch) to PRICE. + --verified-price PRICE Set the price of the ask for verified deals (specified as FIL / GiB / Epoch) to PRICE + --min-piece-size SIZE Set minimum piece size (w/bit-padding, in bytes) in ask to SIZE (default: 256B) + --max-piece-size SIZE Set maximum piece size (w/bit-padding, in bytes) in ask to SIZE (default: miner sector size) + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals get-ask +``` +NAME: + lotus-miner storage-deals get-ask - Print the miner's ask + +USAGE: + lotus-miner storage-deals get-ask [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals set-blocklist +``` +NAME: + lotus-miner storage-deals set-blocklist - Set the miner's list of blocklisted piece CIDs + +USAGE: + lotus-miner storage-deals set-blocklist [command options] [ (optional, will read from stdin if omitted)] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals get-blocklist +``` +NAME: + lotus-miner storage-deals get-blocklist - List the contents of the miner's piece CID blocklist + +USAGE: + lotus-miner storage-deals get-blocklist [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals reset-blocklist +``` +NAME: + lotus-miner storage-deals reset-blocklist - Remove all entries from the miner's piece CID blocklist + +USAGE: + lotus-miner storage-deals reset-blocklist [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals set-seal-duration +``` +NAME: + lotus-miner storage-deals set-seal-duration - Set the expected time, in minutes, that you expect sealing sectors to take. Deals that start before this duration will be rejected. + +USAGE: + lotus-miner storage-deals set-seal-duration [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals pending-publish +``` +NAME: + lotus-miner storage-deals pending-publish - list deals waiting in publish queue + +USAGE: + lotus-miner storage-deals pending-publish [command options] [arguments...] + +OPTIONS: + --publish-now send a publish message now (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner retrieval-deals +``` +NAME: + lotus-miner retrieval-deals - Manage retrieval deals and related configuration + +USAGE: + lotus-miner retrieval-deals command [command options] [arguments...] + +COMMANDS: + selection Configure acceptance criteria for retrieval deal proposals + list List all active retrieval deals for this miner + set-ask Configure the provider's retrieval ask + get-ask Get the provider's current retrieval ask + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner retrieval-deals selection +``` +NAME: + lotus-miner retrieval-deals selection - Configure acceptance criteria for retrieval deal proposals + +USAGE: + lotus-miner retrieval-deals selection command [command options] [arguments...] + +COMMANDS: + list List retrieval deal proposal selection criteria + reset Reset retrieval deal proposal selection criteria to default values + reject Configure criteria which necessitate automatic rejection + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner retrieval-deals selection list +``` +NAME: + lotus-miner retrieval-deals selection list - List retrieval deal proposal selection criteria + +USAGE: + lotus-miner retrieval-deals selection list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner retrieval-deals selection reset +``` +NAME: + lotus-miner retrieval-deals selection reset - Reset retrieval deal proposal selection criteria to default values + +USAGE: + lotus-miner retrieval-deals selection reset [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner retrieval-deals selection reject +``` +NAME: + lotus-miner retrieval-deals selection reject - Configure criteria which necessitate automatic rejection + +USAGE: + lotus-miner retrieval-deals selection reject [command options] [arguments...] + +OPTIONS: + --online (default: false) + --offline (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner retrieval-deals list +``` +NAME: + lotus-miner retrieval-deals list - List all active retrieval deals for this miner + +USAGE: + lotus-miner retrieval-deals list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner retrieval-deals set-ask +``` +NAME: + lotus-miner retrieval-deals set-ask - Configure the provider's retrieval ask + +USAGE: + lotus-miner retrieval-deals set-ask [command options] [arguments...] + +OPTIONS: + --price value Set the price of the ask for retrievals (FIL/GiB) + --unseal-price value Set the price to unseal + --payment-interval value Set the payment interval (in bytes) for retrieval (default: 1MiB) + --payment-interval-increase value Set the payment interval increase (in bytes) for retrieval (default: 1MiB) + --help, -h show help (default: false) + +``` + +### lotus-miner retrieval-deals get-ask +``` +NAME: + lotus-miner retrieval-deals get-ask - Get the provider's current retrieval ask + +USAGE: + lotus-miner retrieval-deals get-ask [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner data-transfers +``` +NAME: + lotus-miner data-transfers - Manage data transfers + +USAGE: + lotus-miner data-transfers command [command options] [arguments...] + +COMMANDS: + list List ongoing data transfers for this miner + restart Force restart a stalled data transfer + cancel Force cancel a data transfer + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner data-transfers list +``` +NAME: + lotus-miner data-transfers list - List ongoing data transfers for this miner + +USAGE: + lotus-miner data-transfers list [command options] [arguments...] + +OPTIONS: + --verbose, -v print verbose transfer details (default: false) + --color use color in display output (default: true) + --completed show completed data transfers (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --show-failed show failed/cancelled transfers (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner data-transfers restart +``` +NAME: + lotus-miner data-transfers restart - Force restart a stalled data transfer + +USAGE: + lotus-miner data-transfers restart [command options] [arguments...] + +OPTIONS: + --peerid value narrow to transfer with specific peer + --initiator specify only transfers where peer is/is not initiator (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner data-transfers cancel +``` +NAME: + lotus-miner data-transfers cancel - Force cancel a data transfer + +USAGE: + lotus-miner data-transfers cancel [command options] [arguments...] + +OPTIONS: + --peerid value narrow to transfer with specific peer + --initiator specify only transfers where peer is/is not initiator (default: false) + --cancel-timeout value time to wait for cancel to be sent to client (default: 5s) + --help, -h show help (default: false) + +``` + +## lotus-miner net +``` +NAME: + lotus-miner net - Manage P2P Network + +USAGE: + lotus-miner net command [command options] [arguments...] + +COMMANDS: + peers Print peers + connect Connect to a peer + listen List listen addresses + id Get node identity + findpeer Find the addresses of a given peerID + scores Print peers' pubsub scores + reachability Print information about reachability from the internet + bandwidth Print bandwidth usage information + block Manage network connection gating rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner net peers +``` +NAME: + lotus-miner net peers - Print peers + +USAGE: + lotus-miner net peers [command options] [arguments...] + +OPTIONS: + --agent, -a Print agent name (default: false) + --extended, -x Print extended peer information in json (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner net connect +``` +NAME: + lotus-miner net connect - Connect to a peer + +USAGE: + lotus-miner net connect [command options] [peerMultiaddr|minerActorAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net listen +``` +NAME: + lotus-miner net listen - List listen addresses + +USAGE: + lotus-miner net listen [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net id +``` +NAME: + lotus-miner net id - Get node identity + +USAGE: + lotus-miner net id [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net findpeer +``` +NAME: + lotus-miner net findpeer - Find the addresses of a given peerID + +USAGE: + lotus-miner net findpeer [command options] [peerId] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net scores +``` +NAME: + lotus-miner net scores - Print peers' pubsub scores + +USAGE: + lotus-miner net scores [command options] [arguments...] + +OPTIONS: + --extended, -x print extended peer scores in json (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner net reachability +``` +NAME: + lotus-miner net reachability - Print information about reachability from the internet + +USAGE: + lotus-miner net reachability [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net bandwidth +``` +NAME: + lotus-miner net bandwidth - Print bandwidth usage information + +USAGE: + lotus-miner net bandwidth [command options] [arguments...] + +OPTIONS: + --by-peer list bandwidth usage by peer (default: false) + --by-protocol list bandwidth usage by protocol (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner net block +``` +NAME: + lotus-miner net block - Manage network connection gating rules + +USAGE: + lotus-miner net block command [command options] [arguments...] + +COMMANDS: + add Add connection gating rules + remove Remove connection gating rules + list list connection gating rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner net block add +``` +NAME: + lotus-miner net block add - Add connection gating rules + +USAGE: + lotus-miner net block add command [command options] [arguments...] + +COMMANDS: + peer Block a peer + ip Block an IP address + subnet Block an IP subnet + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +##### lotus-miner net block add peer +``` +NAME: + lotus-miner net block add peer - Block a peer + +USAGE: + lotus-miner net block add peer [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus-miner net block add ip +``` +NAME: + lotus-miner net block add ip - Block an IP address + +USAGE: + lotus-miner net block add ip [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus-miner net block add subnet +``` +NAME: + lotus-miner net block add subnet - Block an IP subnet + +USAGE: + lotus-miner net block add subnet [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner net block remove +``` +NAME: + lotus-miner net block remove - Remove connection gating rules + +USAGE: + lotus-miner net block remove command [command options] [arguments...] + +COMMANDS: + peer Unblock a peer + ip Unblock an IP address + subnet Unblock an IP subnet + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +##### lotus-miner net block remove peer +``` +NAME: + lotus-miner net block remove peer - Unblock a peer + +USAGE: + lotus-miner net block remove peer [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus-miner net block remove ip +``` +NAME: + lotus-miner net block remove ip - Unblock an IP address + +USAGE: + lotus-miner net block remove ip [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus-miner net block remove subnet +``` +NAME: + lotus-miner net block remove subnet - Unblock an IP subnet + +USAGE: + lotus-miner net block remove subnet [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner net block list +``` +NAME: + lotus-miner net block list - list connection gating rules + +USAGE: + lotus-miner net block list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner pieces +``` +NAME: + lotus-miner pieces - interact with the piecestore + +USAGE: + lotus-miner pieces command [command options] [arguments...] + +DESCRIPTION: + The piecestore is a database that tracks and manages data that is made available to the retrieval market + +COMMANDS: + list-pieces list registered pieces + list-cids list registered payload CIDs + piece-info get registered information for a given piece CID + cid-info get registered information for a given payload CID + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner pieces list-pieces +``` +NAME: + lotus-miner pieces list-pieces - list registered pieces + +USAGE: + lotus-miner pieces list-pieces [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner pieces list-cids +``` +NAME: + lotus-miner pieces list-cids - list registered payload CIDs + +USAGE: + lotus-miner pieces list-cids [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner pieces piece-info +``` +NAME: + lotus-miner pieces piece-info - get registered information for a given piece CID + +USAGE: + lotus-miner pieces piece-info [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner pieces cid-info +``` +NAME: + lotus-miner pieces cid-info - get registered information for a given payload CID + +USAGE: + lotus-miner pieces cid-info [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner sectors +``` +NAME: + lotus-miner sectors - interact with sector store + +USAGE: + lotus-miner sectors command [command options] [arguments...] + +COMMANDS: + status Get the seal status of a sector by its number + list List sectors + refs List References to sectors + update-state ADVANCED: manually update the state of a sector, this may aid in error recovery + pledge store random data in a sector + extend Extend sector expiration + terminate Terminate sector on-chain then remove (WARNING: This means losing power and collateral for the removed sector) + remove Forcefully remove a sector (WARNING: This means losing power and collateral for the removed sector (use 'terminate' for lower penalty)) + mark-for-upgrade Mark a committed capacity sector for replacement by a sector with deals + seal Manually start sealing a sector (filling any unused space with junk) + set-seal-delay Set the time, in minutes, that a new sector waits for deals before sealing starts + get-cc-collateral Get the collateral required to pledge a committed capacity sector + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner sectors status +``` +NAME: + lotus-miner sectors status - Get the seal status of a sector by its number + +USAGE: + lotus-miner sectors status [command options] + +OPTIONS: + --log display event log (default: false) + --on-chain-info show sector on chain info (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sectors list +``` +NAME: + lotus-miner sectors list - List sectors + +USAGE: + lotus-miner sectors list [command options] [arguments...] + +OPTIONS: + --show-removed show removed sectors (default: false) + --color, -c (default: true) + --fast don't show on-chain info for better performance (default: false) + --events display number of events the sector has received (default: false) + --seal-time display how long it took for the sector to be sealed (default: false) + --states value filter sectors by a comma-separated list of states + --help, -h show help (default: false) + +``` + +### lotus-miner sectors refs +``` +NAME: + lotus-miner sectors refs - List References to sectors + +USAGE: + lotus-miner sectors refs [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors update-state +``` +NAME: + lotus-miner sectors update-state - ADVANCED: manually update the state of a sector, this may aid in error recovery + +USAGE: + lotus-miner sectors update-state [command options] + +OPTIONS: + --really-do-it pass this flag if you know what you are doing (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sectors pledge +``` +NAME: + lotus-miner sectors pledge - store random data in a sector + +USAGE: + lotus-miner sectors pledge [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors extend +``` +NAME: + lotus-miner sectors extend - Extend sector expiration + +USAGE: + lotus-miner sectors extend [command options] + +OPTIONS: + --new-expiration value new expiration epoch (default: 0) + --v1-sectors renews all v1 sectors up to the maximum possible lifetime (default: false) + --tolerance value when extending v1 sectors, don't try to extend sectors by fewer than this number of epochs (default: 20160) + --expiration-cutoff value when extending v1 sectors, skip sectors whose current expiration is more than epochs from now (infinity if unspecified) (default: 0) + + --help, -h show help (default: false) + +``` + +### lotus-miner sectors terminate +``` +NAME: + lotus-miner sectors terminate - Terminate sector on-chain then remove (WARNING: This means losing power and collateral for the removed sector) + +USAGE: + lotus-miner sectors terminate command [command options] + +COMMANDS: + flush Send a terminate message if there are sectors queued for termination + pending List sector numbers of sectors pending termination + help, h Shows a list of commands or help for one command + +OPTIONS: + --really-do-it pass this flag if you know what you are doing (default: false) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner sectors terminate flush +``` +NAME: + lotus-miner sectors terminate flush - Send a terminate message if there are sectors queued for termination + +USAGE: + lotus-miner sectors terminate flush [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner sectors terminate pending +``` +NAME: + lotus-miner sectors terminate pending - List sector numbers of sectors pending termination + +USAGE: + lotus-miner sectors terminate pending [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors remove +``` +NAME: + lotus-miner sectors remove - Forcefully remove a sector (WARNING: This means losing power and collateral for the removed sector (use 'terminate' for lower penalty)) + +USAGE: + lotus-miner sectors remove [command options] + +OPTIONS: + --really-do-it pass this flag if you know what you are doing (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sectors mark-for-upgrade +``` +NAME: + lotus-miner sectors mark-for-upgrade - Mark a committed capacity sector for replacement by a sector with deals + +USAGE: + lotus-miner sectors mark-for-upgrade [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors seal +``` +NAME: + lotus-miner sectors seal - Manually start sealing a sector (filling any unused space with junk) + +USAGE: + lotus-miner sectors seal [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors set-seal-delay +``` +NAME: + lotus-miner sectors set-seal-delay - Set the time, in minutes, that a new sector waits for deals before sealing starts + +USAGE: + lotus-miner sectors set-seal-delay [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors get-cc-collateral +``` +NAME: + lotus-miner sectors get-cc-collateral - Get the collateral required to pledge a committed capacity sector + +USAGE: + lotus-miner sectors get-cc-collateral [command options] [arguments...] + +OPTIONS: + --expiration value the epoch when the sector will expire (default: 0) + --help, -h show help (default: false) + +``` + +## lotus-miner proving +``` +NAME: + lotus-miner proving - View proving information + +USAGE: + lotus-miner proving command [command options] [arguments...] + +COMMANDS: + info View current state information + deadlines View the current proving period deadlines information + deadline View the current proving period deadline information by its index + faults View the currently known proving faulty sectors information + check Check sectors provable + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner proving info +``` +NAME: + lotus-miner proving info - View current state information + +USAGE: + lotus-miner proving info [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner proving deadlines +``` +NAME: + lotus-miner proving deadlines - View the current proving period deadlines information + +USAGE: + lotus-miner proving deadlines [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner proving deadline +``` +NAME: + lotus-miner proving deadline - View the current proving period deadline information by its index + +USAGE: + lotus-miner proving deadline [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner proving faults +``` +NAME: + lotus-miner proving faults - View the currently known proving faulty sectors information + +USAGE: + lotus-miner proving faults [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner proving check +``` +NAME: + lotus-miner proving check - Check sectors provable + +USAGE: + lotus-miner proving check [command options] + +OPTIONS: + --only-bad print only bad sectors (default: false) + --slow run slower checks (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner storage +``` +NAME: + lotus-miner storage - manage sector storage + +USAGE: + lotus-miner storage command [command options] [arguments...] + +DESCRIPTION: + Sectors can be stored across many filesystem paths. These +commands provide ways to manage the storage the miner will used to store sectors +long term for proving (references as 'store') as well as how sectors will be +stored while moving through the sealing pipeline (references as 'seal'). + +COMMANDS: + attach attach local storage path + list list local storage paths + find find sector in the storage system + cleanup trigger cleanup actions + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner storage attach +``` +NAME: + lotus-miner storage attach - attach local storage path + +USAGE: + lotus-miner storage attach [command options] [arguments...] + +DESCRIPTION: + Storage can be attached to the miner using this command. The storage volume +list is stored local to the miner in $LOTUS_MINER_PATH/storage.json. We do not +recommend manually modifying this value without further understanding of the +storage system. + +Each storage volume contains a configuration file which describes the +capabilities of the volume. When the '--init' flag is provided, this file will +be created using the additional flags. + +Weight +A high weight value means data will be more likely to be stored in this path + +Seal +Data for the sealing process will be stored here + +Store +Finalized sectors that will be moved here for long term storage and be proven +over time + + +OPTIONS: + --init initialize the path first (default: false) + --weight value (for init) path weight (default: 10) + --seal (for init) use path for sealing (default: false) + --store (for init) use path for long-term storage (default: false) + --max-storage value (for init) limit storage space for sectors (expensive for very large paths!) + --help, -h show help (default: false) + +``` + +### lotus-miner storage list +``` +NAME: + lotus-miner storage list - list local storage paths + +USAGE: + lotus-miner storage list command [command options] [arguments...] + +COMMANDS: + sectors get list of all sector files + help, h Shows a list of commands or help for one command + +OPTIONS: + --color (default: false) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner storage list sectors +``` +NAME: + lotus-miner storage list sectors - get list of all sector files + +USAGE: + lotus-miner storage list sectors [command options] [arguments...] + +OPTIONS: + --color (default: true) + --help, -h show help (default: false) + +``` + +### lotus-miner storage find +``` +NAME: + lotus-miner storage find - find sector in the storage system + +USAGE: + lotus-miner storage find [command options] [sector number] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage cleanup +``` +NAME: + lotus-miner storage cleanup - trigger cleanup actions + +USAGE: + lotus-miner storage cleanup [command options] [arguments...] + +OPTIONS: + --removed cleanup remaining files from removed sectors (default: true) + --help, -h show help (default: false) + +``` + +## lotus-miner sealing +``` +NAME: + lotus-miner sealing - interact with sealing pipeline + +USAGE: + lotus-miner sealing command [command options] [arguments...] + +COMMANDS: + jobs list running jobs + workers list workers + sched-diag Dump internal scheduler state + abort Abort a running job + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner sealing jobs +``` +NAME: + lotus-miner sealing jobs - list running jobs + +USAGE: + lotus-miner sealing jobs [command options] [arguments...] + +OPTIONS: + --color (default: false) + --show-ret-done show returned but not consumed calls (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sealing workers +``` +NAME: + lotus-miner sealing workers - list workers + +USAGE: + lotus-miner sealing workers [command options] [arguments...] + +OPTIONS: + --color (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sealing sched-diag +``` +NAME: + lotus-miner sealing sched-diag - Dump internal scheduler state + +USAGE: + lotus-miner sealing sched-diag [command options] [arguments...] + +OPTIONS: + --force-sched (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sealing abort +``` +NAME: + lotus-miner sealing abort - Abort a running job + +USAGE: + lotus-miner sealing abort [command options] [callid] + +OPTIONS: + --help, -h show help (default: false) + +``` diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md new file mode 100644 index 000000000..58f84dfa7 --- /dev/null +++ b/documentation/en/cli-lotus-worker.md @@ -0,0 +1,171 @@ +# lotus-worker +``` +NAME: + lotus-worker - Remote miner worker + +USAGE: + lotus-worker [global options] command [command options] [arguments...] + +VERSION: + 1.11.0-dev+2k+git.ac39417f4.dirty + +COMMANDS: + run Start lotus worker + info Print worker info + storage manage sector storage + set Manage worker settings + wait-quiet Block until all running tasks exit + tasks Manage task processing + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --worker-repo value, --workerrepo value Specify worker repo path. flag workerrepo and env WORKER_PATH are DEPRECATION, will REMOVE SOON (default: "~/.lotusworker") [$LOTUS_WORKER_PATH, $WORKER_PATH] + --miner-repo value, --storagerepo value Specify miner repo path. flag storagerepo and env LOTUS_STORAGE_PATH are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] + --enable-gpu-proving enable use of GPU for mining operations (default: true) + --help, -h show help (default: false) + --version, -v print the version (default: false) +``` + +## lotus-worker run +``` +NAME: + lotus-worker run - Start lotus worker + +USAGE: + lotus-worker run [command options] [arguments...] + +OPTIONS: + --listen value host address and port the worker api will listen on (default: "0.0.0.0:3456") + --no-local-storage don't use storageminer repo for sector storage (default: false) + --no-swap don't use swap (default: false) + --addpiece enable addpiece (default: true) + --precommit1 enable precommit1 (32G sectors: 1 core, 128GiB Memory) (default: true) + --unseal enable unsealing (32G sectors: 1 core, 128GiB Memory) (default: true) + --precommit2 enable precommit2 (32G sectors: all cores, 96GiB Memory) (default: true) + --commit enable commit (32G sectors: all cores or GPUs, 128GiB Memory + 64GiB swap) (default: true) + --parallel-fetch-limit value maximum fetch operations to run in parallel (default: 5) + --timeout value used when 'listen' is unspecified. must be a valid duration recognized by golang's time.ParseDuration function (default: "30m") + --help, -h show help (default: false) + +``` + +## lotus-worker info +``` +NAME: + lotus-worker info - Print worker info + +USAGE: + lotus-worker info [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-worker storage +``` +NAME: + lotus-worker storage - manage sector storage + +USAGE: + lotus-worker storage command [command options] [arguments...] + +COMMANDS: + attach attach local storage path + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-worker storage attach +``` +NAME: + lotus-worker storage attach - attach local storage path + +USAGE: + lotus-worker storage attach [command options] [arguments...] + +OPTIONS: + --init initialize the path first (default: false) + --weight value (for init) path weight (default: 10) + --seal (for init) use path for sealing (default: false) + --store (for init) use path for long-term storage (default: false) + --max-storage value (for init) limit storage space for sectors (expensive for very large paths!) + --help, -h show help (default: false) + +``` + +## lotus-worker set +``` +NAME: + lotus-worker set - Manage worker settings + +USAGE: + lotus-worker set [command options] [arguments...] + +OPTIONS: + --enabled enable/disable new task processing (default: true) + --help, -h show help (default: false) + +``` + +## lotus-worker wait-quiet +``` +NAME: + lotus-worker wait-quiet - Block until all running tasks exit + +USAGE: + lotus-worker wait-quiet [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-worker tasks +``` +NAME: + lotus-worker tasks - Manage task processing + +USAGE: + lotus-worker tasks command [command options] [arguments...] + +COMMANDS: + enable Enable a task type + disable Disable a task type + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-worker tasks enable +``` +NAME: + lotus-worker tasks enable - Enable a task type + +USAGE: + lotus-worker tasks enable [command options] [UNS|C2|PC2|PC1|AP] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-worker tasks disable +``` +NAME: + lotus-worker tasks disable - Disable a task type + +USAGE: + lotus-worker tasks disable [command options] [UNS|C2|PC2|PC1|AP] + +OPTIONS: + --help, -h show help (default: false) + +``` diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md new file mode 100644 index 000000000..d2fe7377a --- /dev/null +++ b/documentation/en/cli-lotus.md @@ -0,0 +1,2659 @@ +# lotus +``` +NAME: + lotus - Filecoin decentralized storage network client + +USAGE: + lotus [global options] command [command options] [arguments...] + +VERSION: + 1.11.0-dev+mainnet+git.12867a567.dirty + +COMMANDS: + daemon Start a lotus daemon process + backup Create node metadata backup + version Print version + help, h Shows a list of commands or help for one command + BASIC: + send Send funds between accounts + wallet Manage wallet + client Make deals, store data, retrieve data + msig Interact with a multisig wallet + paych Manage payment channels + DEVELOPER: + auth Manage RPC permissions + mpool Manage message pool + state Interact with and query filecoin chain state + chain Interact with filecoin blockchain + log Manage logging + wait-api Wait for lotus api to come online + fetch-params Fetch proving parameters + NETWORK: + net Manage P2P Network + sync Inspect or interact with the chain syncer + +GLOBAL OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) +``` + +## lotus daemon +``` +NAME: + lotus daemon - Start a lotus daemon process + +USAGE: + lotus daemon command [command options] [arguments...] + +COMMANDS: + stop Stop a running lotus daemon + help, h Shows a list of commands or help for one command + +OPTIONS: + --api value (default: "1234") + --genesis value genesis file to use for first node run + --bootstrap (default: true) + --import-chain value on first run, load chain from given file or url and validate + --import-snapshot value import chain state from a given chain export file or url + --halt-after-import halt the process after importing chain from file (default: false) + --pprof value specify name of file for writing cpu profile to + --profile value specify type of node + --manage-fdlimit manage open file limit (default: true) + --config value specify path of config file to use + --api-max-req-size value maximum API request size accepted by the JSON RPC server (default: 0) + --restore value restore from backup file + --restore-config value config file to use when restoring from backup + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus daemon stop +``` +NAME: + lotus daemon stop - Stop a running lotus daemon + +USAGE: + lotus daemon stop [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus backup +``` +NAME: + lotus backup - Create node metadata backup + +USAGE: + lotus backup [command options] [backup file path] + +DESCRIPTION: + The backup command writes a copy of node metadata under the specified path + +Online backups: +For security reasons, the daemon must be have LOTUS_BACKUP_BASE_PATH env var set +to a path where backup files are supposed to be saved, and the path specified in +this command must be within this base path + +OPTIONS: + --offline create backup without the node running (default: false) + --help, -h show help (default: false) + +``` + +## lotus version +``` +NAME: + lotus version - Print version + +USAGE: + lotus version [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus send +``` +NAME: + lotus send - Send funds between accounts + +USAGE: + lotus send [command options] [targetAddress] [amount] + +CATEGORY: + BASIC + +OPTIONS: + --from value optionally specify the account to send funds from + --gas-premium value specify gas price to use in AttoFIL (default: "0") + --gas-feecap value specify gas fee cap to use in AttoFIL (default: "0") + --gas-limit value specify gas limit (default: 0) + --nonce value specify the nonce to use (default: 0) + --method value specify method to invoke (default: 0) + --params-json value specify invocation parameters in json + --params-hex value specify invocation parameters in hex + --force must be specified for the action to take effect if maybe SysErrInsufficientFunds etc (default: false) + --help, -h show help (default: false) + +``` + +## lotus wallet +``` +NAME: + lotus wallet - Manage wallet + +USAGE: + lotus wallet command [command options] [arguments...] + +COMMANDS: + new Generate a new key of the given type + list List wallet address + balance Get account balance + export export keys + import import keys + default Get default wallet address + set-default Set default wallet address + sign sign a message + verify verify the signature of a message + delete Delete an account from the wallet + market Interact with market balances + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus wallet new +``` +NAME: + lotus wallet new - Generate a new key of the given type + +USAGE: + lotus wallet new [command options] [bls|secp256k1 (default secp256k1)] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet list +``` +NAME: + lotus wallet list - List wallet address + +USAGE: + lotus wallet list [command options] [arguments...] + +OPTIONS: + --addr-only, -a Only print addresses (default: false) + --id, -i Output ID addresses (default: false) + --market, -m Output market balances (default: false) + --help, -h show help (default: false) + +``` + +### lotus wallet balance +``` +NAME: + lotus wallet balance - Get account balance + +USAGE: + lotus wallet balance [command options] [address] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet export +``` +NAME: + lotus wallet export - export keys + +USAGE: + lotus wallet export [command options] [address] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet import +``` +NAME: + lotus wallet import - import keys + +USAGE: + lotus wallet import [command options] [ (optional, will read from stdin if omitted)] + +OPTIONS: + --format value specify input format for key (default: "hex-lotus") + --as-default import the given key as your new default key (default: false) + --help, -h show help (default: false) + +``` + +### lotus wallet default +``` +NAME: + lotus wallet default - Get default wallet address + +USAGE: + lotus wallet default [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet set-default +``` +NAME: + lotus wallet set-default - Set default wallet address + +USAGE: + lotus wallet set-default [command options] [address] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet sign +``` +NAME: + lotus wallet sign - sign a message + +USAGE: + lotus wallet sign [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet verify +``` +NAME: + lotus wallet verify - verify the signature of a message + +USAGE: + lotus wallet verify [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet delete +``` +NAME: + lotus wallet delete - Delete an account from the wallet + +USAGE: + lotus wallet delete [command options]
+ +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet market +``` +NAME: + lotus wallet market - Interact with market balances + +USAGE: + lotus wallet market command [command options] [arguments...] + +COMMANDS: + withdraw Withdraw funds from the Storage Market Actor + add Add funds to the Storage Market Actor + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus wallet market withdraw +``` +NAME: + lotus wallet market withdraw - Withdraw funds from the Storage Market Actor + +USAGE: + lotus wallet market withdraw [command options] [amount (FIL) optional, otherwise will withdraw max available] + +OPTIONS: + --wallet value, -w value Specify address to withdraw funds to, otherwise it will use the default wallet address + --address value, -a value Market address to withdraw from (account or miner actor address, defaults to --wallet address) + --help, -h show help (default: false) + +``` + +#### lotus wallet market add +``` +NAME: + lotus wallet market add - Add funds to the Storage Market Actor + +USAGE: + lotus wallet market add [command options] + +OPTIONS: + --from value, -f value Specify address to move funds from, otherwise it will use the default wallet address + --address value, -a value Market address to move funds to (account or miner actor address, defaults to --from address) + --help, -h show help (default: false) + +``` + +## lotus client +``` +NAME: + lotus client - Make deals, store data, retrieve data + +USAGE: + lotus client command [command options] [arguments...] + +COMMANDS: + help, h Shows a list of commands or help for one command + DATA: + import Import data + drop Remove import + local List locally imported data + stat Print information about a locally stored file (piece size, etc) + RETRIEVAL: + find Find data in the network + retrieve Retrieve data from network + cancel-retrieval Cancel a retrieval deal by deal ID; this also cancels the associated transfer + STORAGE: + deal Initialize storage deal with a miner + query-ask Find a miners ask + list-deals List storage market deals + get-deal Print detailed deal information + list-asks List asks for top miners + deal-stats Print statistics about local storage deals + inspect-deal Inspect detailed information about deal's lifecycle and the various stages it goes through + UTIL: + commP Calculate the piece-cid (commP) of a CAR file + generate-car Generate a car file from input + balances Print storage market client balances + list-transfers List ongoing data transfers for deals + restart-transfer Force restart a stalled data transfer + cancel-transfer Force cancel a data transfer + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus client import +``` +NAME: + lotus client import - Import data + +USAGE: + lotus client import [command options] [inputPath] + +CATEGORY: + DATA + +OPTIONS: + --car import from a car file instead of a regular file (default: false) + --quiet, -q Output root CID only (default: false) + --help, -h show help (default: false) + +``` + +### lotus client drop +``` +NAME: + lotus client drop - Remove import + +USAGE: + lotus client drop [command options] [import ID...] + +CATEGORY: + DATA + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client local +``` +NAME: + lotus client local - List locally imported data + +USAGE: + lotus client local [command options] [arguments...] + +CATEGORY: + DATA + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client stat +``` +NAME: + lotus client stat - Print information about a locally stored file (piece size, etc) + +USAGE: + lotus client stat [command options] + +CATEGORY: + DATA + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client find +``` +NAME: + lotus client find - Find data in the network + +USAGE: + lotus client find [command options] [dataCid] + +CATEGORY: + RETRIEVAL + +OPTIONS: + --pieceCid value require data to be retrieved from a specific Piece CID + --help, -h show help (default: false) + +``` + +### lotus client retrieve +``` +NAME: + lotus client retrieve - Retrieve data from network + +USAGE: + lotus client retrieve [command options] [dataCid outputPath] + +CATEGORY: + RETRIEVAL + +OPTIONS: + --from value address to send transactions from + --car export to a car file instead of a regular file (default: false) + --miner value miner address for retrieval, if not present it'll use local discovery + --maxPrice value maximum price the client is willing to consider (default: 0.01 FIL) + --pieceCid value require data to be retrieved from a specific Piece CID + --allow-local (default: false) + --help, -h show help (default: false) + +``` + +### lotus client cancel-retrieval +``` +NAME: + lotus client cancel-retrieval - Cancel a retrieval deal by deal ID; this also cancels the associated transfer + +USAGE: + lotus client cancel-retrieval [command options] [arguments...] + +CATEGORY: + RETRIEVAL + +OPTIONS: + --deal-id value specify retrieval deal by deal ID (default: 0) + --help, -h show help (default: false) + +``` + +### lotus client deal +``` +NAME: + lotus client deal - Initialize storage deal with a miner + +USAGE: + lotus client deal [command options] [dataCid miner price duration] + +CATEGORY: + STORAGE + +DESCRIPTION: + Make a deal with a miner. +dataCid comes from running 'lotus client import'. +miner is the address of the miner you wish to make a deal with. +price is measured in FIL/GB/Epoch. Miners usually don't accept a bid +lower than their advertised ask. You can check a miners listed price +with 'lotus client query-ask '. +duration is how long the miner should store the data for, in blocks. +The minimum value is 518400 (6 months). + +OPTIONS: + --manual-piece-cid value manually specify piece commitment for data (dataCid must be to a car file) + --manual-piece-size value if manually specifying piece cid, used to specify size (dataCid must be to a car file) (default: 0) + --from value specify address to fund the deal with + --start-epoch value specify the epoch that the deal should start at (default: -1) + --fast-retrieval indicates that data should be available for fast retrieval (default: true) + --verified-deal indicate that the deal counts towards verified client total (default: true if client is verified, false otherwise) + --provider-collateral value specify the requested provider collateral the miner should put up + --help, -h show help (default: false) + +``` + +### lotus client query-ask +``` +NAME: + lotus client query-ask - Find a miners ask + +USAGE: + lotus client query-ask [command options] [minerAddress] + +CATEGORY: + STORAGE + +OPTIONS: + --peerid value specify peer ID of node to make query against + --size value data size in bytes (default: 0) + --duration value deal duration (default: 0) + --help, -h show help (default: false) + +``` + +### lotus client list-deals +``` +NAME: + lotus client list-deals - List storage market deals + +USAGE: + lotus client list-deals [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --verbose, -v print verbose deal details (default: false) + --color use color in display output (default: true) + --show-failed show failed/failing deals (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --help, -h show help (default: false) + +``` + +### lotus client get-deal +``` +NAME: + lotus client get-deal - Print detailed deal information + +USAGE: + lotus client get-deal [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client list-asks +``` +NAME: + lotus client list-asks - List asks for top miners + +USAGE: + lotus client list-asks [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --by-ping (default: false) + --output-format value Either 'text' or 'csv' (default: "text") + --help, -h show help (default: false) + +``` + +### lotus client deal-stats +``` +NAME: + lotus client deal-stats - Print statistics about local storage deals + +USAGE: + lotus client deal-stats [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --newer-than value (default: 0s) + --help, -h show help (default: false) + +``` + +### lotus client inspect-deal +``` +NAME: + lotus client inspect-deal - Inspect detailed information about deal's lifecycle and the various stages it goes through + +USAGE: + lotus client inspect-deal [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --deal-id value (default: 0) + --proposal-cid value + --help, -h show help (default: false) + +``` + +### lotus client commP +``` +NAME: + lotus client commP - Calculate the piece-cid (commP) of a CAR file + +USAGE: + lotus client commP [command options] [inputFile] + +CATEGORY: + UTIL + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client generate-car +``` +NAME: + lotus client generate-car - Generate a car file from input + +USAGE: + lotus client generate-car [command options] [inputPath outputPath] + +CATEGORY: + UTIL + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client balances +``` +NAME: + lotus client balances - Print storage market client balances + +USAGE: + lotus client balances [command options] [arguments...] + +CATEGORY: + UTIL + +OPTIONS: + --client value specify storage client address + --help, -h show help (default: false) + +``` + +### lotus client list-transfers +``` +NAME: + lotus client list-transfers - List ongoing data transfers for deals + +USAGE: + lotus client list-transfers [command options] [arguments...] + +CATEGORY: + UTIL + +OPTIONS: + --verbose, -v print verbose transfer details (default: false) + --color use color in display output (default: true) + --completed show completed data transfers (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --show-failed show failed/cancelled transfers (default: false) + --help, -h show help (default: false) + +``` + +### lotus client restart-transfer +``` +NAME: + lotus client restart-transfer - Force restart a stalled data transfer + +USAGE: + lotus client restart-transfer [command options] [arguments...] + +CATEGORY: + UTIL + +OPTIONS: + --peerid value narrow to transfer with specific peer + --initiator specify only transfers where peer is/is not initiator (default: true) + --help, -h show help (default: false) + +``` + +### lotus client cancel-transfer +``` +NAME: + lotus client cancel-transfer - Force cancel a data transfer + +USAGE: + lotus client cancel-transfer [command options] [arguments...] + +CATEGORY: + UTIL + +OPTIONS: + --peerid value narrow to transfer with specific peer + --initiator specify only transfers where peer is/is not initiator (default: true) + --cancel-timeout value time to wait for cancel to be sent to storage provider (default: 5s) + --help, -h show help (default: false) + +``` + +## lotus msig +``` +NAME: + lotus msig - Interact with a multisig wallet + +USAGE: + lotus msig command [command options] [arguments...] + +COMMANDS: + create Create a new multisig wallet + inspect Inspect a multisig wallet + propose Propose a multisig transaction + propose-remove Propose to remove a signer + approve Approve a multisig message + add-propose Propose to add a signer + add-approve Approve a message to add a signer + add-cancel Cancel a message to add a signer + swap-propose Propose to swap signers + swap-approve Approve a message to swap signers + swap-cancel Cancel a message to swap signers + lock-propose Propose to lock up some balance + lock-approve Approve a message to lock up some balance + lock-cancel Cancel a message to lock up some balance + vested Gets the amount vested in an msig between two epochs + propose-threshold Propose setting a different signing threshold on the account + help, h Shows a list of commands or help for one command + +OPTIONS: + --confidence value number of block confirmations to wait for (default: 5) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus msig create +``` +NAME: + lotus msig create - Create a new multisig wallet + +USAGE: + lotus msig create [command options] [address1 address2 ...] + +OPTIONS: + --required value number of required approvals (uses number of signers provided if omitted) (default: 0) + --value value initial funds to give to multisig (default: "0") + --duration value length of the period over which funds unlock (default: "0") + --from value account to send the create message from + --help, -h show help (default: false) + +``` + +### lotus msig inspect +``` +NAME: + lotus msig inspect - Inspect a multisig wallet + +USAGE: + lotus msig inspect [command options] [address] + +OPTIONS: + --vesting Include vesting details (default: false) + --decode-params Decode parameters of transaction proposals (default: false) + --help, -h show help (default: false) + +``` + +### lotus msig propose +``` +NAME: + lotus msig propose - Propose a multisig transaction + +USAGE: + lotus msig propose [command options] [multisigAddress destinationAddress value (optional)] + +OPTIONS: + --from value account to send the propose message from + --help, -h show help (default: false) + +``` + +### lotus msig propose-remove +``` +NAME: + lotus msig propose-remove - Propose to remove a signer + +USAGE: + lotus msig propose-remove [command options] [multisigAddress signer] + +OPTIONS: + --decrease-threshold whether the number of required signers should be decreased (default: false) + --from value account to send the propose message from + --help, -h show help (default: false) + +``` + +### lotus msig approve +``` +NAME: + lotus msig approve - Approve a multisig message + +USAGE: + lotus msig approve [command options] [proposerAddress destination value [methodId methodParams]] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig add-propose +``` +NAME: + lotus msig add-propose - Propose to add a signer + +USAGE: + lotus msig add-propose [command options] [multisigAddress signer] + +OPTIONS: + --increase-threshold whether the number of required signers should be increased (default: false) + --from value account to send the propose message from + --help, -h show help (default: false) + +``` + +### lotus msig add-approve +``` +NAME: + lotus msig add-approve - Approve a message to add a signer + +USAGE: + lotus msig add-approve [command options] [multisigAddress proposerAddress txId newAddress increaseThreshold] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig add-cancel +``` +NAME: + lotus msig add-cancel - Cancel a message to add a signer + +USAGE: + lotus msig add-cancel [command options] [multisigAddress txId newAddress increaseThreshold] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig swap-propose +``` +NAME: + lotus msig swap-propose - Propose to swap signers + +USAGE: + lotus msig swap-propose [command options] [multisigAddress oldAddress newAddress] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig swap-approve +``` +NAME: + lotus msig swap-approve - Approve a message to swap signers + +USAGE: + lotus msig swap-approve [command options] [multisigAddress proposerAddress txId oldAddress newAddress] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig swap-cancel +``` +NAME: + lotus msig swap-cancel - Cancel a message to swap signers + +USAGE: + lotus msig swap-cancel [command options] [multisigAddress txId oldAddress newAddress] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig lock-propose +``` +NAME: + lotus msig lock-propose - Propose to lock up some balance + +USAGE: + lotus msig lock-propose [command options] [multisigAddress startEpoch unlockDuration amount] + +OPTIONS: + --from value account to send the propose message from + --help, -h show help (default: false) + +``` + +### lotus msig lock-approve +``` +NAME: + lotus msig lock-approve - Approve a message to lock up some balance + +USAGE: + lotus msig lock-approve [command options] [multisigAddress proposerAddress txId startEpoch unlockDuration amount] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig lock-cancel +``` +NAME: + lotus msig lock-cancel - Cancel a message to lock up some balance + +USAGE: + lotus msig lock-cancel [command options] [multisigAddress txId startEpoch unlockDuration amount] + +OPTIONS: + --from value account to send the cancel message from + --help, -h show help (default: false) + +``` + +### lotus msig vested +``` +NAME: + lotus msig vested - Gets the amount vested in an msig between two epochs + +USAGE: + lotus msig vested [command options] [multisigAddress] + +OPTIONS: + --start-epoch value start epoch to measure vesting from (default: 0) + --end-epoch value end epoch to stop measure vesting at (default: -1) + --help, -h show help (default: false) + +``` + +### lotus msig propose-threshold +``` +NAME: + lotus msig propose-threshold - Propose setting a different signing threshold on the account + +USAGE: + lotus msig propose-threshold [command options] + +OPTIONS: + --from value account to send the proposal from + --help, -h show help (default: false) + +``` + +## lotus paych +``` +NAME: + lotus paych - Manage payment channels + +USAGE: + lotus paych command [command options] [arguments...] + +COMMANDS: + add-funds Add funds to the payment channel between fromAddress and toAddress. Creates the payment channel if it doesn't already exist. + list List all locally registered payment channels + voucher Interact with payment channel vouchers + settle Settle a payment channel + status Show the status of an outbound payment channel + status-by-from-to Show the status of an active outbound payment channel by from/to addresses + collect Collect funds for a payment channel + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus paych add-funds +``` +NAME: + lotus paych add-funds - Add funds to the payment channel between fromAddress and toAddress. Creates the payment channel if it doesn't already exist. + +USAGE: + lotus paych add-funds [command options] [fromAddress toAddress amount] + +OPTIONS: + --restart-retrievals restart stalled retrieval deals on this payment channel (default: true) + --help, -h show help (default: false) + +``` + +### lotus paych list +``` +NAME: + lotus paych list - List all locally registered payment channels + +USAGE: + lotus paych list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych voucher +``` +NAME: + lotus paych voucher - Interact with payment channel vouchers + +USAGE: + lotus paych voucher command [command options] [arguments...] + +COMMANDS: + create Create a signed payment channel voucher + check Check validity of payment channel voucher + add Add payment channel voucher to local datastore + list List stored vouchers for a given payment channel + best-spendable Print vouchers with highest value that is currently spendable for each lane + submit Submit voucher to chain to update payment channel state + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus paych voucher create +``` +NAME: + lotus paych voucher create - Create a signed payment channel voucher + +USAGE: + lotus paych voucher create [command options] [channelAddress amount] + +OPTIONS: + --lane value specify payment channel lane to use (default: 0) + --help, -h show help (default: false) + +``` + +#### lotus paych voucher check +``` +NAME: + lotus paych voucher check - Check validity of payment channel voucher + +USAGE: + lotus paych voucher check [command options] [channelAddress voucher] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus paych voucher add +``` +NAME: + lotus paych voucher add - Add payment channel voucher to local datastore + +USAGE: + lotus paych voucher add [command options] [channelAddress voucher] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus paych voucher list +``` +NAME: + lotus paych voucher list - List stored vouchers for a given payment channel + +USAGE: + lotus paych voucher list [command options] [channelAddress] + +OPTIONS: + --export Print voucher as serialized string (default: false) + --help, -h show help (default: false) + +``` + +#### lotus paych voucher best-spendable +``` +NAME: + lotus paych voucher best-spendable - Print vouchers with highest value that is currently spendable for each lane + +USAGE: + lotus paych voucher best-spendable [command options] [channelAddress] + +OPTIONS: + --export Print voucher as serialized string (default: false) + --help, -h show help (default: false) + +``` + +#### lotus paych voucher submit +``` +NAME: + lotus paych voucher submit - Submit voucher to chain to update payment channel state + +USAGE: + lotus paych voucher submit [command options] [channelAddress voucher] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych settle +``` +NAME: + lotus paych settle - Settle a payment channel + +USAGE: + lotus paych settle [command options] [channelAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych status +``` +NAME: + lotus paych status - Show the status of an outbound payment channel + +USAGE: + lotus paych status [command options] [channelAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych status-by-from-to +``` +NAME: + lotus paych status-by-from-to - Show the status of an active outbound payment channel by from/to addresses + +USAGE: + lotus paych status-by-from-to [command options] [fromAddress toAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych collect +``` +NAME: + lotus paych collect - Collect funds for a payment channel + +USAGE: + lotus paych collect [command options] [channelAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus auth +``` +NAME: + lotus auth - Manage RPC permissions + +USAGE: + lotus auth command [command options] [arguments...] + +COMMANDS: + create-token Create token + api-info Get token with API info required to connect to this node + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus auth create-token +``` +NAME: + lotus auth create-token - Create token + +USAGE: + lotus auth create-token [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help (default: false) + +``` + +### lotus auth api-info +``` +NAME: + lotus auth api-info - Get token with API info required to connect to this node + +USAGE: + lotus auth api-info [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help (default: false) + +``` + +## lotus mpool +``` +NAME: + lotus mpool - Manage message pool + +USAGE: + lotus mpool command [command options] [arguments...] + +COMMANDS: + pending Get pending messages + sub Subscribe to mpool changes + stat print mempool stats + replace replace a message in the mempool + find find a message in the mempool + config get or set current mpool configuration + gas-perf Check gas performance of messages in mempool + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus mpool pending +``` +NAME: + lotus mpool pending - Get pending messages + +USAGE: + lotus mpool pending [command options] [arguments...] + +OPTIONS: + --local print pending messages for addresses in local wallet only (default: false) + --cids only print cids of messages in output (default: false) + --to value return messages to a given address + --from value return messages from a given address + --help, -h show help (default: false) + +``` + +### lotus mpool sub +``` +NAME: + lotus mpool sub - Subscribe to mpool changes + +USAGE: + lotus mpool sub [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus mpool stat +``` +NAME: + lotus mpool stat - print mempool stats + +USAGE: + lotus mpool stat [command options] [arguments...] + +OPTIONS: + --local print stats for addresses in local wallet only (default: false) + --basefee-lookback value number of blocks to look back for minimum basefee (default: 60) + --help, -h show help (default: false) + +``` + +### lotus mpool replace +``` +NAME: + lotus mpool replace - replace a message in the mempool + +USAGE: + lotus mpool replace [command options] | + +OPTIONS: + --gas-feecap value gas feecap for new message (burn and pay to miner, attoFIL/GasUnit) + --gas-premium value gas price for new message (pay to miner, attoFIL/GasUnit) + --gas-limit value gas limit for new message (GasUnit) (default: 0) + --auto automatically reprice the specified message (default: false) + --max-fee value Spend up to X attoFIL for this message (applicable for auto mode) + --help, -h show help (default: false) + +``` + +### lotus mpool find +``` +NAME: + lotus mpool find - find a message in the mempool + +USAGE: + lotus mpool find [command options] [arguments...] + +OPTIONS: + --from value search for messages with given 'from' address + --to value search for messages with given 'to' address + --method value search for messages with given method (default: 0) + --help, -h show help (default: false) + +``` + +### lotus mpool config +``` +NAME: + lotus mpool config - get or set current mpool configuration + +USAGE: + lotus mpool config [command options] [new-config] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus mpool gas-perf +``` +NAME: + lotus mpool gas-perf - Check gas performance of messages in mempool + +USAGE: + lotus mpool gas-perf [command options] [arguments...] + +OPTIONS: + --all print gas performance for all mempool messages (default only prints for local) (default: false) + --help, -h show help (default: false) + +``` + +## lotus state +``` +NAME: + lotus state - Interact with and query filecoin chain state + +USAGE: + lotus state command [command options] [arguments...] + +COMMANDS: + power Query network or miner power + sectors Query the sector set of a miner + active-sectors Query the active sector set of a miner + list-actors list all actors in the network + list-miners list all miners in the network + circulating-supply Get the exact current circulating supply of Filecoin + sector Get miner sector info + get-actor Print actor information + lookup Find corresponding ID address + replay Replay a particular message + sector-size Look up miners sector size + read-state View a json representation of an actors state + list-messages list messages on chain matching given criteria + compute-state Perform state computations + call Invoke a method on an actor locally + get-deal View on-chain deal info + wait-msg Wait for a message to appear on chain + search-msg Search to see whether a message has appeared on chain + miner-info Retrieve miner information + market Inspect the storage market actor + exec-trace Get the execution trace of a given message + network-version Returns the network version + help, h Shows a list of commands or help for one command + +OPTIONS: + --tipset value specify tipset to call method on (pass comma separated array of cids) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus state power +``` +NAME: + lotus state power - Query network or miner power + +USAGE: + lotus state power [command options] [ (optional)] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state sectors +``` +NAME: + lotus state sectors - Query the sector set of a miner + +USAGE: + lotus state sectors [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state active-sectors +``` +NAME: + lotus state active-sectors - Query the active sector set of a miner + +USAGE: + lotus state active-sectors [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state list-actors +``` +NAME: + lotus state list-actors - list all actors in the network + +USAGE: + lotus state list-actors [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state list-miners +``` +NAME: + lotus state list-miners - list all miners in the network + +USAGE: + lotus state list-miners [command options] [arguments...] + +OPTIONS: + --sort-by value criteria to sort miners by (none, num-deals) + --help, -h show help (default: false) + +``` + +### lotus state circulating-supply +``` +NAME: + lotus state circulating-supply - Get the exact current circulating supply of Filecoin + +USAGE: + lotus state circulating-supply [command options] [arguments...] + +OPTIONS: + --vm-supply calculates the approximation of the circulating supply used internally by the VM (instead of the exact amount) (default: false) + --help, -h show help (default: false) + +``` + +### lotus state sector +``` +NAME: + lotus state sector - Get miner sector info + +USAGE: + lotus state sector [command options] [minerAddress] [sectorNumber] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state get-actor +``` +NAME: + lotus state get-actor - Print actor information + +USAGE: + lotus state get-actor [command options] [actorrAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state lookup +``` +NAME: + lotus state lookup - Find corresponding ID address + +USAGE: + lotus state lookup [command options] [address] + +OPTIONS: + --reverse, -r Perform reverse lookup (default: false) + --help, -h show help (default: false) + +``` + +### lotus state replay +``` +NAME: + lotus state replay - Replay a particular message + +USAGE: + lotus state replay [command options] + +OPTIONS: + --show-trace print out full execution trace for given message (default: false) + --detailed-gas print out detailed gas costs for given message (default: false) + --help, -h show help (default: false) + +``` + +### lotus state sector-size +``` +NAME: + lotus state sector-size - Look up miners sector size + +USAGE: + lotus state sector-size [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state read-state +``` +NAME: + lotus state read-state - View a json representation of an actors state + +USAGE: + lotus state read-state [command options] [actorAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state list-messages +``` +NAME: + lotus state list-messages - list messages on chain matching given criteria + +USAGE: + lotus state list-messages [command options] [arguments...] + +OPTIONS: + --to value return messages to a given address + --from value return messages from a given address + --toheight value don't look before given block height (default: 0) + --cids print message CIDs instead of messages (default: false) + --help, -h show help (default: false) + +``` + +### lotus state compute-state +``` +NAME: + lotus state compute-state - Perform state computations + +USAGE: + lotus state compute-state [command options] [arguments...] + +OPTIONS: + --vm-height value set the height that the vm will see (default: 0) + --apply-mpool-messages apply messages from the mempool to the computed state (default: false) + --show-trace print out full execution trace for given tipset (default: false) + --html generate html report (default: false) + --json generate json output (default: false) + --compute-state-output value a json file containing pre-existing compute-state output, to generate html reports without rerunning state changes + --no-timing don't show timing information in html traces (default: false) + --help, -h show help (default: false) + +``` + +### lotus state call +``` +NAME: + lotus state call - Invoke a method on an actor locally + +USAGE: + lotus state call [command options] [toAddress methodId (optional)] + +OPTIONS: + --from value (default: "f00") + --value value specify value field for invocation (default: "0") + --ret value specify how to parse output (auto, raw, addr, big) (default: "auto") + --help, -h show help (default: false) + +``` + +### lotus state get-deal +``` +NAME: + lotus state get-deal - View on-chain deal info + +USAGE: + lotus state get-deal [command options] [dealId] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state wait-msg +``` +NAME: + lotus state wait-msg - Wait for a message to appear on chain + +USAGE: + lotus state wait-msg [command options] [messageCid] + +OPTIONS: + --timeout value (default: "10m") + --help, -h show help (default: false) + +``` + +### lotus state search-msg +``` +NAME: + lotus state search-msg - Search to see whether a message has appeared on chain + +USAGE: + lotus state search-msg [command options] [messageCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state miner-info +``` +NAME: + lotus state miner-info - Retrieve miner information + +USAGE: + lotus state miner-info [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state market +``` +NAME: + lotus state market - Inspect the storage market actor + +USAGE: + lotus state market command [command options] [arguments...] + +COMMANDS: + balance Get the market balance (locked and escrowed) for a given account + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus state market balance +``` +NAME: + lotus state market balance - Get the market balance (locked and escrowed) for a given account + +USAGE: + lotus state market balance [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state exec-trace +``` +NAME: + lotus state exec-trace - Get the execution trace of a given message + +USAGE: + lotus state exec-trace [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state network-version +``` +NAME: + lotus state network-version - Returns the network version + +USAGE: + lotus state network-version [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus chain +``` +NAME: + lotus chain - Interact with filecoin blockchain + +USAGE: + lotus chain command [command options] [arguments...] + +COMMANDS: + head Print chain head + getblock Get a block and print its details + read-obj Read the raw bytes of an object + delete-obj Delete an object from the chain blockstore + stat-obj Collect size and ipld link counts for objs + getmessage Get and print a message by its cid + sethead manually set the local nodes head tipset (Caution: normally only used for recovery) + list, love View a segment of the chain + get Get chain DAG node by path + bisect bisect chain for an event + export export chain to a car file + slash-consensus Report consensus fault + gas-price Estimate gas prices + inspect-usage Inspect block space usage of a given tipset + decode decode various types + encode encode various types + disputer interact with the window post disputer + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus chain head +``` +NAME: + lotus chain head - Print chain head + +USAGE: + lotus chain head [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain getblock +``` +NAME: + lotus chain getblock - Get a block and print its details + +USAGE: + lotus chain getblock [command options] [blockCid] + +OPTIONS: + --raw print just the raw block header (default: false) + --help, -h show help (default: false) + +``` + +### lotus chain read-obj +``` +NAME: + lotus chain read-obj - Read the raw bytes of an object + +USAGE: + lotus chain read-obj [command options] [objectCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain delete-obj +``` +NAME: + lotus chain delete-obj - Delete an object from the chain blockstore + +USAGE: + lotus chain delete-obj [command options] [objectCid] + +DESCRIPTION: + WARNING: Removing wrong objects from the chain blockstore may lead to sync issues + +OPTIONS: + --really-do-it (default: false) + --help, -h show help (default: false) + +``` + +### lotus chain stat-obj +``` +NAME: + lotus chain stat-obj - Collect size and ipld link counts for objs + +USAGE: + lotus chain stat-obj [command options] [cid] + +DESCRIPTION: + Collect object size and ipld link count for an object. + + When a base is provided it will be walked first, and all links visisted + will be ignored when the passed in object is walked. + + +OPTIONS: + --base value ignore links found in this obj + --help, -h show help (default: false) + +``` + +### lotus chain getmessage +``` +NAME: + lotus chain getmessage - Get and print a message by its cid + +USAGE: + lotus chain getmessage [command options] [messageCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain sethead +``` +NAME: + lotus chain sethead - manually set the local nodes head tipset (Caution: normally only used for recovery) + +USAGE: + lotus chain sethead [command options] [tipsetkey] + +OPTIONS: + --genesis reset head to genesis (default: false) + --epoch value reset head to given epoch (default: 0) + --help, -h show help (default: false) + +``` + +#### lotus chain list, love +``` +``` + +### lotus chain get +``` +NAME: + lotus chain get - Get chain DAG node by path + +USAGE: + lotus chain get [command options] [path] + +DESCRIPTION: + Get ipld node under a specified path: + + lotus chain get /ipfs/[cid]/some/path + + Path prefixes: + - /ipfs/[cid], /ipld/[cid] - traverse IPLD path + - /pstate - traverse from head.ParentStateRoot + + Note: + You can use special path elements to traverse through some data structures: + - /ipfs/[cid]/@H:elem - get 'elem' from hamt + - /ipfs/[cid]/@Hi:123 - get varint elem 123 from hamt + - /ipfs/[cid]/@Hu:123 - get uvarint elem 123 from hamt + - /ipfs/[cid]/@Ha:t01 - get element under Addr(t01).Bytes + - /ipfs/[cid]/@A:10 - get 10th amt element + - .../@Ha:t01/@state - get pretty map-based actor state + + List of --as-type types: + - raw + - block + - message + - smessage, signedmessage + - actor + - amt + - hamt-epoch + - hamt-address + - cronevent + - account-state + + +OPTIONS: + --as-type value specify type to interpret output as + --verbose (default: false) + --tipset value specify tipset for /pstate (pass comma separated array of cids) + --help, -h show help (default: false) + +``` + +### lotus chain bisect +``` +NAME: + lotus chain bisect - bisect chain for an event + +USAGE: + lotus chain bisect [command options] [minHeight maxHeight path shellCommand ] + +DESCRIPTION: + Bisect the chain state tree: + + lotus chain bisect [min height] [max height] '1/2/3/state/path' 'shell command' 'args' + + Returns the first tipset in which condition is true + v + [start] FFFFFFFTTT [end] + + Example: find height at which deal ID 100 000 appeared + - lotus chain bisect 1 32000 '@Ha:t03/1' jq -e '.[2] > 100000' + + For special path elements see 'chain get' help + + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain export +``` +NAME: + lotus chain export - export chain to a car file + +USAGE: + lotus chain export [command options] [outputPath] + +OPTIONS: + --tipset value + --recent-stateroots value specify the number of recent state roots to include in the export (default: 0) + --skip-old-msgs (default: false) + --help, -h show help (default: false) + +``` + +### lotus chain slash-consensus +``` +NAME: + lotus chain slash-consensus - Report consensus fault + +USAGE: + lotus chain slash-consensus [command options] [blockCid1 blockCid2] + +OPTIONS: + --from value optionally specify the account to report consensus from + --extra value Extra block cid + --help, -h show help (default: false) + +``` + +### lotus chain gas-price +``` +NAME: + lotus chain gas-price - Estimate gas prices + +USAGE: + lotus chain gas-price [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain inspect-usage +``` +NAME: + lotus chain inspect-usage - Inspect block space usage of a given tipset + +USAGE: + lotus chain inspect-usage [command options] [arguments...] + +OPTIONS: + --tipset value specify tipset to view block space usage of (default: "@head") + --length value length of chain to inspect block space usage for (default: 1) + --num-results value number of results to print per category (default: 10) + --help, -h show help (default: false) + +``` + +### lotus chain decode +``` +NAME: + lotus chain decode - decode various types + +USAGE: + lotus chain decode command [command options] [arguments...] + +COMMANDS: + params Decode message params + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus chain decode params +``` +NAME: + lotus chain decode params - Decode message params + +USAGE: + lotus chain decode params [command options] [toAddr method params] + +OPTIONS: + --tipset value + --encoding value specify input encoding to parse (default: "base64") + --help, -h show help (default: false) + +``` + +### lotus chain encode +``` +NAME: + lotus chain encode - encode various types + +USAGE: + lotus chain encode command [command options] [arguments...] + +COMMANDS: + params Encodes the given JSON params + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus chain encode params +``` +NAME: + lotus chain encode params - Encodes the given JSON params + +USAGE: + lotus chain encode params [command options] [toAddr method params] + +OPTIONS: + --tipset value + --encoding value specify input encoding to parse (default: "base64") + --help, -h show help (default: false) + +``` + +### lotus chain disputer +``` +NAME: + lotus chain disputer - interact with the window post disputer + +USAGE: + lotus chain disputer command [command options] [arguments...] + +COMMANDS: + start Start the window post disputer + dispute Send a specific DisputeWindowedPoSt message + help, h Shows a list of commands or help for one command + +OPTIONS: + --max-fee value Spend up to X FIL per DisputeWindowedPoSt message + --from value optionally specify the account to send messages from + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus chain disputer start +``` +NAME: + lotus chain disputer start - Start the window post disputer + +USAGE: + lotus chain disputer start [command options] [minerAddress] + +OPTIONS: + --start-epoch value only start disputing PoSts after this epoch (default: 0) + --help, -h show help (default: false) + +``` + +#### lotus chain disputer dispute +``` +NAME: + lotus chain disputer dispute - Send a specific DisputeWindowedPoSt message + +USAGE: + lotus chain disputer dispute [command options] [minerAddress index postIndex] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus log +``` +NAME: + lotus log - Manage logging + +USAGE: + lotus log command [command options] [arguments...] + +COMMANDS: + list List log systems + set-level Set log level + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus log list +``` +NAME: + lotus log list - List log systems + +USAGE: + lotus log list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus log set-level +``` +NAME: + lotus log set-level - Set log level + +USAGE: + lotus log set-level [command options] [level] + +DESCRIPTION: + Set the log level for logging systems: + + The system flag can be specified multiple times. + + eg) log set-level --system chain --system chainxchg debug + + Available Levels: + debug + info + warn + error + + Environment Variables: + GOLOG_LOG_LEVEL - Default log level for all log systems + GOLOG_LOG_FMT - Change output log format (json, nocolor) + GOLOG_FILE - Write logs to file + GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr + + +OPTIONS: + --system value limit to log system + --help, -h show help (default: false) + +``` + +## lotus wait-api +``` +NAME: + lotus wait-api - Wait for lotus api to come online + +USAGE: + lotus wait-api [command options] [arguments...] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus fetch-params +``` +NAME: + lotus fetch-params - Fetch proving parameters + +USAGE: + lotus fetch-params [command options] [sectorSize] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus net +``` +NAME: + lotus net - Manage P2P Network + +USAGE: + lotus net command [command options] [arguments...] + +COMMANDS: + peers Print peers + connect Connect to a peer + listen List listen addresses + id Get node identity + findpeer Find the addresses of a given peerID + scores Print peers' pubsub scores + reachability Print information about reachability from the internet + bandwidth Print bandwidth usage information + block Manage network connection gating rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus net peers +``` +NAME: + lotus net peers - Print peers + +USAGE: + lotus net peers [command options] [arguments...] + +OPTIONS: + --agent, -a Print agent name (default: false) + --extended, -x Print extended peer information in json (default: false) + --help, -h show help (default: false) + +``` + +### lotus net connect +``` +NAME: + lotus net connect - Connect to a peer + +USAGE: + lotus net connect [command options] [peerMultiaddr|minerActorAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net listen +``` +NAME: + lotus net listen - List listen addresses + +USAGE: + lotus net listen [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net id +``` +NAME: + lotus net id - Get node identity + +USAGE: + lotus net id [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net findpeer +``` +NAME: + lotus net findpeer - Find the addresses of a given peerID + +USAGE: + lotus net findpeer [command options] [peerId] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net scores +``` +NAME: + lotus net scores - Print peers' pubsub scores + +USAGE: + lotus net scores [command options] [arguments...] + +OPTIONS: + --extended, -x print extended peer scores in json (default: false) + --help, -h show help (default: false) + +``` + +### lotus net reachability +``` +NAME: + lotus net reachability - Print information about reachability from the internet + +USAGE: + lotus net reachability [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net bandwidth +``` +NAME: + lotus net bandwidth - Print bandwidth usage information + +USAGE: + lotus net bandwidth [command options] [arguments...] + +OPTIONS: + --by-peer list bandwidth usage by peer (default: false) + --by-protocol list bandwidth usage by protocol (default: false) + --help, -h show help (default: false) + +``` + +### lotus net block +``` +NAME: + lotus net block - Manage network connection gating rules + +USAGE: + lotus net block command [command options] [arguments...] + +COMMANDS: + add Add connection gating rules + remove Remove connection gating rules + list list connection gating rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus net block add +``` +NAME: + lotus net block add - Add connection gating rules + +USAGE: + lotus net block add command [command options] [arguments...] + +COMMANDS: + peer Block a peer + ip Block an IP address + subnet Block an IP subnet + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +##### lotus net block add peer +``` +NAME: + lotus net block add peer - Block a peer + +USAGE: + lotus net block add peer [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus net block add ip +``` +NAME: + lotus net block add ip - Block an IP address + +USAGE: + lotus net block add ip [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus net block add subnet +``` +NAME: + lotus net block add subnet - Block an IP subnet + +USAGE: + lotus net block add subnet [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus net block remove +``` +NAME: + lotus net block remove - Remove connection gating rules + +USAGE: + lotus net block remove command [command options] [arguments...] + +COMMANDS: + peer Unblock a peer + ip Unblock an IP address + subnet Unblock an IP subnet + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +##### lotus net block remove peer +``` +NAME: + lotus net block remove peer - Unblock a peer + +USAGE: + lotus net block remove peer [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus net block remove ip +``` +NAME: + lotus net block remove ip - Unblock an IP address + +USAGE: + lotus net block remove ip [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus net block remove subnet +``` +NAME: + lotus net block remove subnet - Unblock an IP subnet + +USAGE: + lotus net block remove subnet [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus net block list +``` +NAME: + lotus net block list - list connection gating rules + +USAGE: + lotus net block list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus sync +``` +NAME: + lotus sync - Inspect or interact with the chain syncer + +USAGE: + lotus sync command [command options] [arguments...] + +COMMANDS: + status check sync status + wait Wait for sync to be complete + mark-bad Mark the given block as bad, will prevent syncing to a chain that contains it + unmark-bad Unmark the given block as bad, makes it possible to sync to a chain containing it + check-bad check if the given block was marked bad, and for what reason + checkpoint mark a certain tipset as checkpointed; the node will never fork away from this tipset + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus sync status +``` +NAME: + lotus sync status - check sync status + +USAGE: + lotus sync status [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus sync wait +``` +NAME: + lotus sync wait - Wait for sync to be complete + +USAGE: + lotus sync wait [command options] [arguments...] + +OPTIONS: + --watch don't exit after node is synced (default: false) + --help, -h show help (default: false) + +``` + +### lotus sync mark-bad +``` +NAME: + lotus sync mark-bad - Mark the given block as bad, will prevent syncing to a chain that contains it + +USAGE: + lotus sync mark-bad [command options] [blockCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus sync unmark-bad +``` +NAME: + lotus sync unmark-bad - Unmark the given block as bad, makes it possible to sync to a chain containing it + +USAGE: + lotus sync unmark-bad [command options] [blockCid] + +OPTIONS: + --all drop the entire bad block cache (default: false) + --help, -h show help (default: false) + +``` + +### lotus sync check-bad +``` +NAME: + lotus sync check-bad - check if the given block was marked bad, and for what reason + +USAGE: + lotus sync check-bad [command options] [blockCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus sync checkpoint +``` +NAME: + lotus sync checkpoint - mark a certain tipset as checkpointed; the node will never fork away from this tipset + +USAGE: + lotus sync checkpoint [command options] [tipsetKey] + +OPTIONS: + --epoch value checkpoint the tipset at the given epoch (default: 0) + --help, -h show help (default: false) + +``` From 7e4d906f0667283e4697df61eb2fb626a8e2d884 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 29 Apr 2021 10:59:28 -0700 Subject: [PATCH 45/97] update commp utils import --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3c9cfdfc0..9d5accbf1 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20201006184820-924ee87a1349 // indirect github.com/filecoin-project/go-bitfield v0.2.4 github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 - github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427180530-4606b1a6cbdd + github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427191551-70bf140d31c7 github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 github.com/filecoin-project/go-data-transfer v1.4.3 github.com/filecoin-project/go-fil-commcid v0.0.0-20201016201715-d41df56b4f6a diff --git a/go.sum b/go.sum index 3e548b523..54c5da743 100644 --- a/go.sum +++ b/go.sum @@ -260,8 +260,8 @@ github.com/filecoin-project/go-bitfield v0.2.4/go.mod h1:CNl9WG8hgR5mttCnUErjcQj github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:av5fw6wmm58FYMgJeoB/lK9XXrgdugYiTqkdxjTy9k8= github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg= github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= -github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427180530-4606b1a6cbdd h1:cpwbE1z6a+0fp62P0Qv7Z7ZqIy8Jiay0SWR0xcuZ0qs= -github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427180530-4606b1a6cbdd/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= +github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427191551-70bf140d31c7 h1:U9Z+76pHCKBmtdxFV7JFZJj7OVm12I6dEKwtMVbq5p0= +github.com/filecoin-project/go-commp-utils v0.1.1-0.20210427191551-70bf140d31c7/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ= github.com/filecoin-project/go-data-transfer v1.0.1/go.mod h1:UxvfUAY9v3ub0a21BSK9u3pB2aq30Y0KMsG+w9/ysyo= From ed08366cac0db0fd2a33df3af6a371740a40d3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 29 Apr 2021 20:01:23 +0200 Subject: [PATCH 46/97] Run cli docsgen in CI --- .circleci/config.yml | 9 ++++++--- Makefile | 6 ++++++ build/version.go | 8 ++++++++ documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- scripts/generate-lotus-cli.py | 1 + 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c2f937a9d..c3deab6ad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -436,7 +436,7 @@ jobs: - run: command: "! go fmt ./... 2>&1 | read" - cbor-gen-check: + gen-check: executor: golang steps: - install-deps @@ -444,7 +444,10 @@ jobs: - run: make deps - run: go install golang.org/x/tools/cmd/goimports - run: go install github.com/hannahhoward/cbor-gen-for - - run: make type-gen + - run: make gen + - run: git --no-pager diff + - run: git --no-pager diff --quiet + - run: make docsgen-cli - run: git --no-pager diff - run: git --no-pager diff --quiet @@ -701,7 +704,7 @@ workflows: concurrency: "16" # expend all docker 2xlarge CPUs. - mod-tidy-check - gofmt - - cbor-gen-check + - gen-check - docs-check - test: codecov-upload: true diff --git a/Makefile b/Makefile index e2d4e3764..1ccce11ed 100644 --- a/Makefile +++ b/Makefile @@ -368,7 +368,13 @@ docsgen-openrpc-worker: docsgen-openrpc-bin .PHONY: docsgen docsgen-md-bin docsgen-openrpc-bin gen: type-gen method-gen docsgen api-gen + @echo ">>> IF YOU'VE MODIFIED THE CLI, REMEMBER TO ALSO MAKE docsgen-cli" .PHONY: gen +# separate from gen because it needs binaries +docsgen-cli: lotus lotus-miner lotus-worker + python ./scripts/generate-lotus-cli.py +.PHONY: docsgen-cli + print-%: @echo $*=$($*) diff --git a/build/version.go b/build/version.go index 84f49ead8..57582119b 100644 --- a/build/version.go +++ b/build/version.go @@ -1,8 +1,16 @@ package build +import "os" + var CurrentCommit string var BuildType int +func init() { + if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { + CurrentCommit = "" + } +} + const ( BuildDefault = 0 BuildMainnet = 0x1 diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index bc06573ad..bd8ed424e 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.11.0-dev+mainnet+git.12867a567.dirty + 1.11.0-dev+mainnet COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index 58f84dfa7..b04587c4f 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.11.0-dev+2k+git.ac39417f4.dirty + 1.11.0-dev+mainnet COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index d2fe7377a..c93a866ff 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.11.0-dev+mainnet+git.12867a567.dirty + 1.11.0-dev+mainnet COMMANDS: daemon Start a lotus daemon process diff --git a/scripts/generate-lotus-cli.py b/scripts/generate-lotus-cli.py index fc436d6ad..8018962e9 100644 --- a/scripts/generate-lotus-cli.py +++ b/scripts/generate-lotus-cli.py @@ -46,6 +46,7 @@ def generate_lotus_cli(prog): if __name__ == "__main__": + os.putenv("LOTUS_VERSION_IGNORE_COMMIT", "1") generate_lotus_cli('lotus') generate_lotus_cli('lotus-miner') generate_lotus_cli('lotus-worker') From ab811e2e19e76d92cf011c10202e2cfd25cf5143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Apr 2021 09:35:38 +0200 Subject: [PATCH 47/97] drand: fix beacon cache --- chain/beacon/drand/drand.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/chain/beacon/drand/drand.go b/chain/beacon/drand/drand.go index 847091858..e7f673d7f 100644 --- a/chain/beacon/drand/drand.go +++ b/chain/beacon/drand/drand.go @@ -168,8 +168,8 @@ func (db *DrandBeacon) getCachedValue(round uint64) *types.BeaconEntry { if !ok { return nil } - e, _ := v.(*types.BeaconEntry) - return e + e, _ := v.(types.BeaconEntry) + return &e } func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntry) error { @@ -178,6 +178,9 @@ func (db *DrandBeacon) VerifyEntry(curr types.BeaconEntry, prev types.BeaconEntr return nil } if be := db.getCachedValue(curr.Round); be != nil { + if !bytes.Equal(curr.Data, be.Data) { + return xerrors.New("invalid beacon value, does not match cached good value") + } // return no error if the value is in the cache already return nil } From 04c71b2c6ba125a69bb3f5e3c91c107b6103e08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Apr 2021 10:09:35 +0200 Subject: [PATCH 48/97] mpool: Cleanup pre-nv12 selection logic --- chain/messagepool/selection.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 0a836804f..05acc5667 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -11,9 +11,7 @@ import ( "github.com/filecoin-project/go-address" tbig "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/specs-actors/v3/actors/builtin" - "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/messagepool/gasguess" "github.com/filecoin-project/lotus/chain/types" @@ -700,17 +698,6 @@ func (*MessagePool) getGasPerf(gasReward *big.Int, gasLimit int64) float64 { return r } -func isMessageMute(m *types.Message, ts *types.TipSet) bool { - if api.RunningNodeType != api.NodeFull || ts.Height() > build.UpgradeActorsV4Height { - return false - } - - if m.To == builtin.StoragePowerActorAddr { - return m.Method == builtin.MethodsPower.CreateMiner - } - return false -} - func (mp *MessagePool) createMessageChains(actor address.Address, mset map[uint64]*types.SignedMessage, baseFee types.BigInt, ts *types.TipSet) []*msgChain { // collect all messages msgs := make([]*types.SignedMessage, 0, len(mset)) @@ -772,10 +759,6 @@ func (mp *MessagePool) createMessageChains(actor address.Address, mset map[uint6 break } - if isMessageMute(&m.Message, ts) { - break - } - balance = new(big.Int).Sub(balance, required) value := m.Message.Value.Int From 3574ec3d9dd7983c9d3f973371ad645865681f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Apr 2021 11:18:15 +0200 Subject: [PATCH 49/97] cli docsgen: Ignore build type too --- build/version.go | 10 ++++------ documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/build/version.go b/build/version.go index 57582119b..12b1058b3 100644 --- a/build/version.go +++ b/build/version.go @@ -5,12 +5,6 @@ import "os" var CurrentCommit string var BuildType int -func init() { - if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { - CurrentCommit = "" - } -} - const ( BuildDefault = 0 BuildMainnet = 0x1 @@ -40,5 +34,9 @@ func buildType() string { const BuildVersion = "1.11.0-dev" func UserVersion() string { + if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { + return BuildVersion + } + return BuildVersion + buildType() + CurrentCommit } diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index bd8ed424e..1b9b80ee9 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.11.0-dev+mainnet + 1.11.0-dev COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index b04587c4f..0b29da503 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.11.0-dev+mainnet + 1.11.0-dev COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index c93a866ff..2c155aae9 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.11.0-dev+mainnet + 1.11.0-dev COMMANDS: daemon Start a lotus daemon process From 19ced50d816e49d58b8c2af02b3031c465c0b311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 29 Apr 2021 21:23:41 +0200 Subject: [PATCH 50/97] Update ffi to proofs v7 --- extern/filecoin-ffi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index d82899449..3db17a0a0 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit d82899449741ce190e950a3582ebe33806f018a9 +Subproject commit 3db17a0a0f24ce6a04e946f86bf18b0e1d8c0007 From cad781c8eee67d9c5cb63c039367748a5088827f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Apr 2021 17:59:01 +0200 Subject: [PATCH 51/97] Update cli gen --- documentation/en/cli-lotus.md | 62 +++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 2c155aae9..ebd3300f0 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -616,7 +616,7 @@ CATEGORY: STORAGE OPTIONS: - --by-ping (default: false) + --by-ping sort by ping (default: false) --output-format value Either 'text' or 'csv' (default: "text") --help, -h show help (default: false) @@ -1422,29 +1422,30 @@ USAGE: lotus state command [command options] [arguments...] COMMANDS: - power Query network or miner power - sectors Query the sector set of a miner - active-sectors Query the active sector set of a miner - list-actors list all actors in the network - list-miners list all miners in the network - circulating-supply Get the exact current circulating supply of Filecoin - sector Get miner sector info - get-actor Print actor information - lookup Find corresponding ID address - replay Replay a particular message - sector-size Look up miners sector size - read-state View a json representation of an actors state - list-messages list messages on chain matching given criteria - compute-state Perform state computations - call Invoke a method on an actor locally - get-deal View on-chain deal info - wait-msg Wait for a message to appear on chain - search-msg Search to see whether a message has appeared on chain - miner-info Retrieve miner information - market Inspect the storage market actor - exec-trace Get the execution trace of a given message - network-version Returns the network version - help, h Shows a list of commands or help for one command + power Query network or miner power + sectors Query the sector set of a miner + active-sectors Query the active sector set of a miner + list-actors list all actors in the network + list-miners list all miners in the network + circulating-supply Get the exact current circulating supply of Filecoin + sector Get miner sector info + get-actor Print actor information + lookup Find corresponding ID address + replay Replay a particular message + sector-size Look up miners sector size + read-state View a json representation of an actors state + list-messages list messages on chain matching given criteria + compute-state Perform state computations + call Invoke a method on an actor locally + get-deal View on-chain deal info + wait-msg Wait for a message to appear on chain + search-msg Search to see whether a message has appeared on chain + miner-info Retrieve miner information + market Inspect the storage market actor + exec-trace Get the execution trace of a given message + network-version Returns the network version + miner-proving-deadline Retrieve information about a given miner's proving deadline + help, h Shows a list of commands or help for one command OPTIONS: --tipset value specify tipset to call method on (pass comma separated array of cids) @@ -1777,6 +1778,19 @@ OPTIONS: ``` +### lotus state miner-proving-deadline +``` +NAME: + lotus state miner-proving-deadline - Retrieve information about a given miner's proving deadline + +USAGE: + lotus state miner-proving-deadline [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + ## lotus chain ``` NAME: From 4193235b5903e04b3ebb656342811d7e20080402 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 30 Apr 2021 22:41:02 -0400 Subject: [PATCH 52/97] Allow creation of state tree v3s --- chain/state/statetree.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/state/statetree.go b/chain/state/statetree.go index 33a8116df..2a7b436b8 100644 --- a/chain/state/statetree.go +++ b/chain/state/statetree.go @@ -154,7 +154,7 @@ func NewStateTree(cst cbor.IpldStore, ver types.StateTreeVersion) (*StateTree, e switch ver { case types.StateTreeVersion0: // info is undefined - case types.StateTreeVersion1, types.StateTreeVersion2: + case types.StateTreeVersion1, types.StateTreeVersion2, types.StateTreeVersion3: var err error info, err = cst.Put(context.TODO(), new(types.StateInfo0)) if err != nil { From edc6a63e938d699bc86dcf808e179d908f37a93d Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sat, 1 May 2021 00:15:45 -0400 Subject: [PATCH 53/97] Add a shed util to count miners by post type --- cmd/lotus-shed/main.go | 1 + cmd/lotus-shed/miner-types.go | 123 ++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 cmd/lotus-shed/miner-types.go diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 3aa667459..99f533dde 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -55,6 +55,7 @@ func main() { cidCmd, blockmsgidCmd, signaturesCmd, + minerTypesCmd, } app := &cli.App{ diff --git a/cmd/lotus-shed/miner-types.go b/cmd/lotus-shed/miner-types.go new file mode 100644 index 000000000..bee478195 --- /dev/null +++ b/cmd/lotus-shed/miner-types.go @@ -0,0 +1,123 @@ +package main + +import ( + "context" + "fmt" + "io" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/state" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" + "github.com/filecoin-project/lotus/node/repo" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + "github.com/filecoin-project/specs-actors/v4/actors/util/adt" + "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var minerTypesCmd = &cli.Command{ + Name: "miner-types", + Usage: "Scrape state to report on how many miners of each WindowPoStProofType exist", Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + }, + }, + Action: func(cctx *cli.Context) error { + ctx := context.TODO() + + if !cctx.Args().Present() { + return fmt.Errorf("must pass state root") + } + + sroot, err := cid.Decode(cctx.Args().First()) + if err != nil { + return fmt.Errorf("failed to parse input: %w", err) + } + + fsrepo, err := repo.NewFS(cctx.String("repo")) + if err != nil { + return err + } + + lkrepo, err := fsrepo.Lock(repo.FullNode) + if err != nil { + return err + } + + defer lkrepo.Close() //nolint:errcheck + + bs, err := lkrepo.Blockstore(ctx, repo.UniversalBlockstore) + if err != nil { + return fmt.Errorf("failed to open blockstore: %w", err) + } + + defer func() { + if c, ok := bs.(io.Closer); ok { + if err := c.Close(); err != nil { + log.Warnf("failed to close blockstore: %s", err) + } + } + }() + + mds, err := lkrepo.Datastore(context.Background(), "/metadata") + if err != nil { + return err + } + + cs := store.NewChainStore(bs, bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), nil) + defer cs.Close() //nolint:errcheck + + cst := cbor.NewCborStore(bs) + store := adt.WrapStore(ctx, cst) + + tree, err := state.LoadStateTree(cst, sroot) + if err != nil { + return err + } + + typeMap := make(map[abi.RegisteredPoStProof]int64) + + err = tree.ForEach(func(addr address.Address, act *types.Actor) error { + if act.Code == builtin4.StorageMinerActorCodeID { + ms, err := miner.Load(store, act) + if err != nil { + return err + } + + mi, err := ms.Info() + if err != nil { + return err + } + + if mi.WindowPoStProofType < abi.RegisteredPoStProof_StackedDrgWindow32GiBV1 { + fmt.Println(addr) + } + + c, f := typeMap[mi.WindowPoStProofType] + if !f { + typeMap[mi.WindowPoStProofType] = 1 + } else { + typeMap[mi.WindowPoStProofType] = c + 1 + } + } + return nil + }) + if err != nil { + return xerrors.Errorf("failed to loop over actors: %w", err) + } + + for k, v := range typeMap { + fmt.Println("Type:", k, " Count: ", v) + } + + return nil + }, +} From b14c467fb485e4ee7f5500dddeb85dc724331e7d Mon Sep 17 00:00:00 2001 From: Jennifer <42981373+jennijuju@users.noreply.github.com> Date: Wed, 5 May 2021 14:28:48 -0400 Subject: [PATCH 54/97] Update documentation/misc/RELEASE_ISSUE_TEMPLATE.md --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 7692058cb..36eecbbee 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -4,7 +4,9 @@ We're happy to announce Lotus X.Y.Z... -## 🗺 What's left for release +## 🗺 Must-dos for the release + +## 🌟 Nice-to-haves for the release From 72134ff458534d1740f3e27a8fc4b54b0f28df6b Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Tue, 4 May 2021 18:18:26 +0200 Subject: [PATCH 55/97] Add a mining-heartbeat INFO line at every epoch --- chain/gen/gen.go | 9 +++++++++ miner/miner.go | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index d06c755fa..a6bf1b2b1 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -3,6 +3,7 @@ package gen import ( "bytes" "context" + "encoding/base64" "fmt" "io" "io/ioutil" @@ -610,6 +611,8 @@ func (wpp *wppProvider) ComputeProof(context.Context, []proof2.SectorInfo, abi.P return ValidWpostForTesting, nil } +var b64 = base64.URLEncoding.WithPadding(base64.NoPadding) + func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch, miner address.Address, brand types.BeaconEntry, mbi *api.MiningBaseInfo, a MiningCheckAPI) (*types.ElectionProof, error) { @@ -631,6 +634,12 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch, ep := &types.ElectionProof{VRFProof: vrfout} j := ep.ComputeWinCount(mbi.MinerPower, mbi.NetworkPower) ep.WinCount = j + + log.Infow("completed winAttemptVRF", + "VRFb64", b64.EncodeToString(vrfout), + "winCount", j, + ) + if j < 1 { return nil, nil } diff --git a/miner/miner.go b/miner/miner.go index e7e012d7c..15adfb993 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -13,6 +13,7 @@ import ( proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/gen/slashfilter" "github.com/filecoin-project/go-address" @@ -425,8 +426,37 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, return nil, xerrors.Errorf("failed to get mining base info: %w", err) } if mbi == nil { + log.Warnf("mineOne: unexpectedly nil MiningBaseInfo for round %d, off tipset %d/%s", round, base.TipSet.Height(), base.TipSet.Key().String()) return nil, nil } + + // always write out a log from this point out + var winner *types.ElectionProof + lookBack := make(chan int64) + + // figure this out in the background, instead of slowing down the main loop + go func() { + lb := int64(-1) + if netVer, err := m.api.StateNetworkVersion(ctx, base.TipSet.Key()); err == nil { + lb = int64(policy.GetWinningPoStSectorSetLookback(netVer)) + } + lookBack <- lb + }() + + defer func() { + + log.Infow( + "completed mineOne", + "forRound", int64(round), + "baseEpoch", int64(base.TipSet.Height()), + "lookbackEpochs", <-lookBack, + "networkPowerAtLookback", mbi.NetworkPower.String(), + "minerPowerAtLookback", mbi.MinerPower.String(), + "isEligible", mbi.EligibleForMining, + "isWinner", (winner != nil), + ) + }() + if !mbi.EligibleForMining { // slashed or just have no power yet return nil, nil @@ -453,7 +483,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, return nil, xerrors.Errorf("scratching ticket failed: %w", err) } - winner, err := gen.IsRoundWinner(ctx, base.TipSet, round, m.address, rbase, mbi, m.api) + winner, err = gen.IsRoundWinner(ctx, base.TipSet, round, m.address, rbase, mbi, m.api) if err != nil { return nil, xerrors.Errorf("failed to check if we win next round: %w", err) } @@ -505,7 +535,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, for i, header := range base.TipSet.Blocks() { parentMiners[i] = header.Miner } - log.Infow("mined new block", "cid", b.Cid(), "height", b.Header.Height, "miner", b.Header.Miner, "parents", parentMiners, "took", dur) + log.Infow("mined new block", "cid", b.Cid(), "height", int64(b.Header.Height), "miner", b.Header.Miner, "parents", parentMiners, "parentTipset", base.TipSet.Key().String(), "took", dur) if dur > time.Second*time.Duration(build.BlockDelaySecs) { log.Warnw("CAUTION: block production took longer than the block delay. Your computer may not be fast enough to keep up", "tMinerBaseInfo ", tMBI.Sub(start), From b1db3fee78ca1ab9f6d6c000ae6000317e178cbb Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Wed, 5 May 2021 20:39:16 +0200 Subject: [PATCH 56/97] Log more ComputeVRF() inputs as per Why's request --- chain/gen/gen.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index a6bf1b2b1..b4e04424c 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -636,7 +636,10 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch, ep.WinCount = j log.Infow("completed winAttemptVRF", - "VRFb64", b64.EncodeToString(vrfout), + "beaconRound", brand.Round, + "beaconDataB64", b64.EncodeToString(brand.Data), + "electionRandB64", b64.EncodeToString(electionRand), + "vrfB64", b64.EncodeToString(vrfout), "winCount", j, ) From de60229957e038c2a20c2463165a624177c38104 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Wed, 5 May 2021 22:54:01 +0200 Subject: [PATCH 57/97] mining lookback is effectively a constant - make it so --- chain/actors/policy/policy.go | 1 + miner/miner.go | 14 +------------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index f210b8d94..07f489b11 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -187,6 +187,7 @@ func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { return 10 } + // NOTE: if this ever changes, adjust it in a (*Miner).mineOne() logline as well return ChainFinality } diff --git a/miner/miner.go b/miner/miner.go index 15adfb993..a77e1c18b 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -432,24 +432,12 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, // always write out a log from this point out var winner *types.ElectionProof - lookBack := make(chan int64) - - // figure this out in the background, instead of slowing down the main loop - go func() { - lb := int64(-1) - if netVer, err := m.api.StateNetworkVersion(ctx, base.TipSet.Key()); err == nil { - lb = int64(policy.GetWinningPoStSectorSetLookback(netVer)) - } - lookBack <- lb - }() - defer func() { - log.Infow( "completed mineOne", "forRound", int64(round), "baseEpoch", int64(base.TipSet.Height()), - "lookbackEpochs", <-lookBack, + "lookbackEpochs", int64(policy.ChainFinality), // hardcoded as it is unlikely to change again: https://github.com/filecoin-project/lotus/blob/v1.8.0/chain/actors/policy/policy.go#L180-L186 "networkPowerAtLookback", mbi.NetworkPower.String(), "minerPowerAtLookback", mbi.MinerPower.String(), "isEligible", mbi.EligibleForMining, From e1185dd5b5397961ef647cfc6aaf9a7685d99889 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Thu, 22 Apr 2021 23:03:53 -0400 Subject: [PATCH 58/97] cron-wc --- cmd/lotus-shed/cron-count.go | 99 ++++++++++++++++++++++++++++++++++++ cmd/lotus-shed/main.go | 1 + 2 files changed, 100 insertions(+) create mode 100644 cmd/lotus-shed/cron-count.go diff --git a/cmd/lotus-shed/cron-count.go b/cmd/lotus-shed/cron-count.go new file mode 100644 index 000000000..91e7ab313 --- /dev/null +++ b/cmd/lotus-shed/cron-count.go @@ -0,0 +1,99 @@ +package main + +import ( + "fmt" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/build" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var cronWcCmd = &cli.Command{ + Name: "cron-wc", + Description: "cron stats", + Subcommands: []*cli.Command{ + minerDeadlineCronCountCmd, + }, +} + +var minerDeadlineCronCountCmd = &cli.Command{ + Name: "deadline", + Description: "list all addresses of miners with active deadline crons", + Action: func(c *cli.Context) error { + return countDeadlineCrons(c) + }, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "tipset", + Usage: "specify tipset state to search on (pass comma separated array of cids)", + }, + }, +} + +func findDeadlineCrons(c *cli.Context) (map[address.Address]struct{}, error) { + api, acloser, err := lcli.GetFullNodeAPI(c) + if err != nil { + return nil, err + } + defer acloser() + ctx := lcli.ReqContext(c) + + ts, err := lcli.LoadTipSet(ctx, c, api) + if err != nil { + return nil, err + } + if ts == nil { + ts, err = api.ChainHead(ctx) + if err != nil { + return nil, err + } + } + + mAddrs, err := api.StateListMiners(ctx, ts.Key()) + if err != nil { + return nil, err + } + activeMiners := make(map[address.Address]struct{}) + for _, mAddr := range mAddrs { + // All miners have active cron before v4. + // v4 upgrade epoch is last epoch running v3 epoch and api.StateReadState reads + // parent state, so v4 state isn't read until upgrade epoch + 2 + if ts.Height() <= build.UpgradeActorsV4Height+1 { + activeMiners[mAddr] = struct{}{} + continue + } + st, err := api.StateReadState(ctx, mAddr, ts.Key()) + if err != nil { + return nil, err + } + minerState, ok := st.State.(map[string]interface{}) + if !ok { + return nil, xerrors.Errorf("internal error: failed to cast miner state to expected map type") + } + + activeDlineIface, ok := minerState["DeadlineCronActive"] + if !ok { + return nil, xerrors.Errorf("miner %s had no deadline state, is this a v3 state root?", mAddr) + } + active := activeDlineIface.(bool) + if active { + activeMiners[mAddr] = struct{}{} + } + } + + return activeMiners, nil +} + +func countDeadlineCrons(c *cli.Context) error { + activeMiners, err := findDeadlineCrons(c) + if err != nil { + return err + } + for addr, _ := range activeMiners { + fmt.Printf("%s\n", addr) + } + + return nil +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 3aa667459..daf878c73 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -20,6 +20,7 @@ func main() { base32Cmd, base16Cmd, bitFieldCmd, + cronWcCmd, frozenMinersCmd, keyinfoCmd, jwtCmd, From 5351db9da723adafc02e8dfd861e6153ad74b8c6 Mon Sep 17 00:00:00 2001 From: ZenGround0 Date: Thu, 6 May 2021 10:17:25 -0400 Subject: [PATCH 59/97] Lint --- cmd/lotus-shed/cron-count.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lotus-shed/cron-count.go b/cmd/lotus-shed/cron-count.go index 91e7ab313..79fd6ec42 100644 --- a/cmd/lotus-shed/cron-count.go +++ b/cmd/lotus-shed/cron-count.go @@ -91,7 +91,7 @@ func countDeadlineCrons(c *cli.Context) error { if err != nil { return err } - for addr, _ := range activeMiners { + for addr := range activeMiners { fmt.Printf("%s\n", addr) } From 25b4ffd09b4e7a4bd478a558c6a3b8b48af4a3b8 Mon Sep 17 00:00:00 2001 From: Jennifer <42981373+jennijuju@users.noreply.github.com> Date: Fri, 7 May 2021 00:06:46 -0400 Subject: [PATCH 60/97] Update documentation/misc/RELEASE_ISSUE_TEMPLATE.md --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 36eecbbee..fcde01876 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -93,7 +93,7 @@ Testing an RC: - [ ] Tag this merge commit (on the `releases` branch) with `vX.Y.Z`. - [ ] Cut the release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=true&target=releases). - [ ] Final announcements - - [ ] Update network.filecoin.io + - [ ] Update network.filecoin.io for mainnet, calib and nerpa. - [ ] Add a comment when the final release is tagged, example [here](https://github.com/filecoin-project/lotus/discussions/5905#discussioncomment-571752) - [ ] repost in #fil-lotus in filecoin slack From fac9994c02e34ca1f59787706b9b70389e8852dc Mon Sep 17 00:00:00 2001 From: Jennifer <42981373+jennijuju@users.noreply.github.com> Date: Fri, 7 May 2021 00:08:23 -0400 Subject: [PATCH 61/97] Update documentation/misc/RELEASE_ISSUE_TEMPLATE.md --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index fcde01876..12618bd4c 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -96,6 +96,7 @@ Testing an RC: - [ ] Update network.filecoin.io for mainnet, calib and nerpa. - [ ] Add a comment when the final release is tagged, example [here](https://github.com/filecoin-project/lotus/discussions/5905#discussioncomment-571752) - [ ] repost in #fil-lotus in filecoin slack + - [ ] Inform node provides (Protofire, Digital Ocean..) - [ ] **Post-Release** - [ ] Merge the `releases` branch back into `master`, ignoring the changes to `version.go` (keep the `-dev` version from master). From e3948c851fe2fa19f7b39c24bb3aa279500b723b Mon Sep 17 00:00:00 2001 From: Jennifer <42981373+jennijuju@users.noreply.github.com> Date: Fri, 7 May 2021 00:09:11 -0400 Subject: [PATCH 62/97] Update documentation/misc/RELEASE_ISSUE_TEMPLATE.md --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 12618bd4c..df5e8d1c5 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -63,7 +63,7 @@ Testing an RC: - [ ] Submit a PoSt - [ ] (optional) let a sector go faulty, and see it be recovered -- [ ] **Stage 2 - Community Dev Testing** +- [ ] **Stage 2 - Community Testing** - [ ] Inform beta miners (@lotus-early-testers-miner in Filecoin Slack #fil-lotus) - [ ] Ask close ecosystem partners to test their projects (@lotus-early-testers-eco-dev in Filecoin slack #fil-lotus) - [ ] Powergate From ed61642b3ab68755d485ed04559514db5cc2cae5 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 10 Mar 2021 19:22:35 +0200 Subject: [PATCH 63/97] implement NodeStatus API Signed-off-by: Jakub Sztandera --- api/api_full.go | 5 ++ api/mocks/mock_full.go | 15 ++++ api/proxy_gen.go | 10 +++ api/types.go | 21 +++++ build/openrpc/full.json.gz | Bin 22483 -> 22681 bytes build/openrpc/miner.json.gz | Bin 7848 -> 7849 bytes build/openrpc/worker.json.gz | Bin 2577 -> 2579 bytes cli/cmd.go | 1 + cli/status.go | 60 ++++++++++++++ documentation/en/api-v1-unstable-methods.md | 36 +++++++++ documentation/en/cli-lotus.md | 19 +++++ node/impl/full.go | 82 +++++++++++++++++++- node/modules/lp2p/pubsub.go | 19 +++-- 13 files changed, 262 insertions(+), 6 deletions(-) create mode 100644 cli/status.go diff --git a/api/api_full.go b/api/api_full.go index a90b0c89f..8631ec4b7 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -664,6 +664,11 @@ type FullNode interface { PaychVoucherList(context.Context, address.Address) ([]*paych.SignedVoucher, error) //perm:write PaychVoucherSubmit(context.Context, address.Address, *paych.SignedVoucher, []byte, []byte) (cid.Cid, error) //perm:sign + // MethodGroup: Node + // These methods are general node management and status commands + + NodeStatus(ctx context.Context, inclChainStatus bool) (NodeStatus, error) //perm:read + // CreateBackup creates node backup onder the specified file name. The // method requires that the lotus daemon is running with the // LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 4336a56f9..ede04fa20 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1692,6 +1692,21 @@ func (mr *MockFullNodeMockRecorder) NetPubsubScores(arg0 interface{}) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetPubsubScores", reflect.TypeOf((*MockFullNode)(nil).NetPubsubScores), arg0) } +// NodeStatus mocks base method +func (m *MockFullNode) NodeStatus(arg0 context.Context, arg1 bool) (api.NodeStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NodeStatus", arg0, arg1) + ret0, _ := ret[0].(api.NodeStatus) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NodeStatus indicates an expected call of NodeStatus +func (mr *MockFullNodeMockRecorder) NodeStatus(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeStatus", reflect.TypeOf((*MockFullNode)(nil).NodeStatus), arg0, arg1) +} + // PaychAllocateLane mocks base method func (m *MockFullNode) PaychAllocateLane(arg0 context.Context, arg1 address.Address) (uint64, error) { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index bfaaade94..b743a2ddb 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -287,6 +287,8 @@ type FullNodeStruct struct { MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) `perm:"sign"` + NodeStatus func(p0 context.Context, p1 bool) (NodeStatus, error) `perm:"read"` + PaychAllocateLane func(p0 context.Context, p1 address.Address) (uint64, error) `perm:"sign"` PaychAvailableFunds func(p0 context.Context, p1 address.Address) (*ChannelAvailableFunds, error) `perm:"sign"` @@ -1715,6 +1717,14 @@ func (s *FullNodeStub) MsigSwapPropose(p0 context.Context, p1 address.Address, p return *new(cid.Cid), xerrors.New("method not supported") } +func (s *FullNodeStruct) NodeStatus(p0 context.Context, p1 bool) (NodeStatus, error) { + return s.Internal.NodeStatus(p0, p1) +} + +func (s *FullNodeStub) NodeStatus(p0 context.Context, p1 bool) (NodeStatus, error) { + return *new(NodeStatus), xerrors.New("method not supported") +} + func (s *FullNodeStruct) PaychAllocateLane(p0 context.Context, p1 address.Address) (uint64, error) { return s.Internal.PaychAllocateLane(p0, p1) } diff --git a/api/types.go b/api/types.go index 6417ce756..bbcfa5c20 100644 --- a/api/types.go +++ b/api/types.go @@ -116,3 +116,24 @@ type ConnMgrInfo struct { Tags map[string]int Conns map[string]time.Time } + +type NodeStatus struct { + SyncStatus NodeSyncStatus + PeerStatus NodePeerStatus + ChainStatus NodeChainStatus +} + +type NodeSyncStatus struct { + Epoch uint64 + Behind uint64 +} + +type NodePeerStatus struct { + PeersToPublishMsgs int + PeersToPublishBlocks int +} + +type NodeChainStatus struct { + BlocksPerTipsetLast100 float64 + BlocksPerTipsetLastFinality float64 +} diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index df2b87f1330c334cb1a830105c124265d0b69486..7763e4af8ba241fea0cb4d20a1fc61790c2646f4 100644 GIT binary patch delta 20255 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 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index a3407e7caa660f07c67af1ac5ff9477f29bdd95e..501b238282ef3d11fb1e356a867206a563054b2b 100644 GIT binary patch delta 6894 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 z_catK≫HXDx^sAZ>UX73c+MO9^WL|RLlm8Q1X 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 diff --git a/cli/cmd.go b/cli/cmd.go index 6ecd236f4..09bf5c461 100644 --- a/cli/cmd.go +++ b/cli/cmd.go @@ -81,6 +81,7 @@ var Commands = []*cli.Command{ WithCategory("developer", FetchParamCmd), WithCategory("network", NetCmd), WithCategory("network", SyncCmd), + WithCategory("status", StatusCmd), PprofCmd, VersionCmd, } diff --git a/cli/status.go b/cli/status.go new file mode 100644 index 000000000..75f91196a --- /dev/null +++ b/cli/status.go @@ -0,0 +1,60 @@ +package cli + +import ( + "fmt" + + "github.com/urfave/cli/v2" + + "github.com/filecoin-project/lotus/build" +) + +var StatusCmd = &cli.Command{ + Name: "status", + Usage: "Check node status", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "chain", + Usage: "include chain health status", + }, + }, + + Action: func(cctx *cli.Context) error { + apic, closer, err := GetFullNodeAPIV1(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + inclChainStatus := cctx.Bool("chain") + + status, err := apic.NodeStatus(ctx, inclChainStatus) + if err != nil { + return err + } + + fmt.Printf("Sync Epoch: %d\n", status.SyncStatus.Epoch) + fmt.Printf("Epochs Behind: %d\n", status.SyncStatus.Behind) + fmt.Printf("Peers to Publish Messages: %d\n", status.PeerStatus.PeersToPublishMsgs) + fmt.Printf("Peers to Publish Blocks: %d\n", status.PeerStatus.PeersToPublishBlocks) + + if inclChainStatus && status.SyncStatus.Epoch > uint64(build.Finality) { + var ok100, okFin string + if status.ChainStatus.BlocksPerTipsetLast100 >= 4.75 { + ok100 = "[OK]" + } else { + ok100 = "[UNHEALTHY]" + } + if status.ChainStatus.BlocksPerTipsetLastFinality >= 4.75 { + okFin = "[OK]" + } else { + okFin = "[UNHEALTHY]" + } + + fmt.Printf("Blocks per TipSet in last 100 epochs: %f %s\n", status.ChainStatus.BlocksPerTipsetLast100, ok100) + fmt.Printf("Blocks per TipSet in last finality: %f %s\n", status.ChainStatus.BlocksPerTipsetLastFinality, okFin) + } + + return nil + }, +} diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 261c0d51b..a0ee6fcf3 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -126,6 +126,8 @@ * [NetPeerInfo](#NetPeerInfo) * [NetPeers](#NetPeers) * [NetPubsubScores](#NetPubsubScores) +* [Node](#Node) + * [NodeStatus](#NodeStatus) * [Paych](#Paych) * [PaychAllocateLane](#PaychAllocateLane) * [PaychAvailableFunds](#PaychAvailableFunds) @@ -3000,6 +3002,40 @@ Inputs: `null` Response: `null` +## Node +These methods are general node management and status commands + + +### NodeStatus +There are not yet any comments for this method. + +Perms: read + +Inputs: +```json +[ + true +] +``` + +Response: +```json +{ + "SyncStatus": { + "Epoch": 42, + "Behind": 42 + }, + "PeerStatus": { + "PeersToPublishMsgs": 123, + "PeersToPublishBlocks": 123 + }, + "ChainStatus": { + "BlocksPerTipsetLast100": 12.3, + "BlocksPerTipsetLastFinality": 12.3 + } +} +``` + ## Paych The Paych methods are for interacting with and managing payment channels diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index ebd3300f0..a3ac4d487 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -31,6 +31,8 @@ COMMANDS: NETWORK: net Manage P2P Network sync Inspect or interact with the chain syncer + STATUS: + status Check node status GLOBAL OPTIONS: --help, -h show help (default: false) @@ -2671,3 +2673,20 @@ OPTIONS: --help, -h show help (default: false) ``` + +## lotus status +``` +NAME: + lotus status - Check node status + +USAGE: + lotus status [command options] [arguments...] + +CATEGORY: + STATUS + +OPTIONS: + --chain include chain health status (default: false) + --help, -h show help (default: false) + +``` diff --git a/node/impl/full.go b/node/impl/full.go index add40917c..50fd09cdf 100644 --- a/node/impl/full.go +++ b/node/impl/full.go @@ -2,16 +2,21 @@ package impl import ( "context" + "time" + + "github.com/libp2p/go-libp2p-core/peer" logging "github.com/ipfs/go-log/v2" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/node/impl/client" "github.com/filecoin-project/lotus/node/impl/common" "github.com/filecoin-project/lotus/node/impl/full" "github.com/filecoin-project/lotus/node/impl/market" "github.com/filecoin-project/lotus/node/impl/paych" "github.com/filecoin-project/lotus/node/modules/dtypes" + "github.com/filecoin-project/lotus/node/modules/lp2p" ) var log = logging.Logger("node") @@ -30,11 +35,86 @@ type FullNodeAPI struct { full.SyncAPI full.BeaconAPI - DS dtypes.MetadataDS + DS dtypes.MetadataDS + NetworkName dtypes.NetworkName } func (n *FullNodeAPI) CreateBackup(ctx context.Context, fpath string) error { return backup(n.DS, fpath) } +func (n *FullNodeAPI) NodeStatus(ctx context.Context, inclChainStatus bool) (status api.NodeStatus, err error) { + curTs, err := n.ChainHead(ctx) + if err != nil { + return status, err + } + + status.SyncStatus.Epoch = uint64(curTs.Height()) + timestamp := time.Unix(int64(curTs.MinTimestamp()), 0) + delta := time.Since(timestamp).Seconds() + status.SyncStatus.Behind = uint64(delta / 30) + + // get peers in the messages and blocks topics + peersMsgs := make(map[peer.ID]struct{}) + peersBlocks := make(map[peer.ID]struct{}) + + for _, p := range n.PubSub.ListPeers(build.MessagesTopic(n.NetworkName)) { + peersMsgs[p] = struct{}{} + } + + for _, p := range n.PubSub.ListPeers(build.BlocksTopic(n.NetworkName)) { + peersBlocks[p] = struct{}{} + } + + // get scores for all connected and recent peers + scores, err := n.NetPubsubScores(ctx) + if err != nil { + return status, err + } + + for _, score := range scores { + if score.Score.Score > lp2p.PublishScoreThreshold { + _, inMsgs := peersMsgs[score.ID] + if inMsgs { + status.PeerStatus.PeersToPublishMsgs++ + } + + _, inBlocks := peersBlocks[score.ID] + if inBlocks { + status.PeerStatus.PeersToPublishBlocks++ + } + } + } + + if inclChainStatus && status.SyncStatus.Epoch > uint64(build.Finality) { + blockCnt := 0 + ts := curTs + + for i := 0; i < 100; i++ { + blockCnt += len(ts.Blocks()) + tsk := ts.Parents() + ts, err = n.ChainGetTipSet(ctx, tsk) + if err != nil { + return status, err + } + } + + status.ChainStatus.BlocksPerTipsetLast100 = float64(blockCnt) / 100 + + for i := 100; i < int(build.Finality); i++ { + blockCnt += len(ts.Blocks()) + tsk := ts.Parents() + ts, err = n.ChainGetTipSet(ctx, tsk) + if err != nil { + return status, err + } + } + + status.ChainStatus.BlocksPerTipsetLastFinality = float64(blockCnt) / float64(build.Finality) + + } + + return status, nil +} + var _ api.FullNode = &FullNodeAPI{} diff --git a/node/modules/lp2p/pubsub.go b/node/modules/lp2p/pubsub.go index 748167d95..32b85daf3 100644 --- a/node/modules/lp2p/pubsub.go +++ b/node/modules/lp2p/pubsub.go @@ -36,6 +36,15 @@ func init() { pubsub.GossipSubHistoryLength = 10 pubsub.GossipSubGossipFactor = 0.1 } + +const ( + GossipScoreThreshold = -500 + PublishScoreThreshold = -1000 + GraylistScoreThreshold = -2500 + AcceptPXScoreThreshold = 1000 + OpportunisticGraftScoreThreshold = 3.5 +) + func ScoreKeeper() *dtypes.ScoreKeeper { return new(dtypes.ScoreKeeper) } @@ -256,11 +265,11 @@ func GossipSub(in GossipIn) (service *pubsub.PubSub, err error) { Topics: topicParams, }, &pubsub.PeerScoreThresholds{ - GossipThreshold: -500, - PublishThreshold: -1000, - GraylistThreshold: -2500, - AcceptPXThreshold: 1000, - OpportunisticGraftThreshold: 3.5, + GossipThreshold: GossipScoreThreshold, + PublishThreshold: PublishScoreThreshold, + GraylistThreshold: GraylistScoreThreshold, + AcceptPXThreshold: AcceptPXScoreThreshold, + OpportunisticGraftThreshold: OpportunisticGraftScoreThreshold, }, ), pubsub.WithPeerScoreInspect(in.Sk.Update, 10*time.Second), From 91e774063e5d30934108c59876e9c0a35e6df404 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 12 Mar 2021 17:10:12 +0200 Subject: [PATCH 64/97] implement MessagePool.CheckMessages Signed-off-by: Jakub Sztandera --- api/api_full.go | 5 + api/checkstatuscode_string.go | 35 +++ api/docgen/docgen.go | 3 + api/mocks/mock_full.go | 30 +++ api/proxy_gen.go | 20 ++ api/types.go | 32 +++ build/tools.go | 1 + chain/messagepool/check.go | 374 +++++++++++++++++++++++++++++ documentation/en/api-v0-methods.md | 32 +++ go.mod | 1 + node/impl/full/mpool.go | 8 + 11 files changed, 541 insertions(+) create mode 100644 api/checkstatuscode_string.go create mode 100644 chain/messagepool/check.go diff --git a/api/api_full.go b/api/api_full.go index 8631ec4b7..3b69ca5e6 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -252,6 +252,11 @@ type FullNode interface { // MpoolBatchPushMessage batch pushes a unsigned message to mempool. MpoolBatchPushMessage(context.Context, []*types.Message, *MessageSendSpec) ([]*types.SignedMessage, error) //perm:sign + // MpoolCheckMessages performs logical checks on a batch of messages + MpoolCheckMessages(context.Context, []*types.Message) ([][]MessageCheckStatus, error) //perm:read + // MpoolCheckPendingMessages performs logical checks for all pending messages from a given address + MpoolCheckPendingMessages(context.Context, address.Address) ([][]MessageCheckStatus, error) //perm:read + // MpoolGetNonce gets next nonce for the specified sender. // Note that this method may not be atomic. Use MpoolPushMessage instead. MpoolGetNonce(context.Context, address.Address) (uint64, error) //perm:read diff --git a/api/checkstatuscode_string.go b/api/checkstatuscode_string.go new file mode 100644 index 000000000..072f77989 --- /dev/null +++ b/api/checkstatuscode_string.go @@ -0,0 +1,35 @@ +// Code generated by "stringer -type=CheckStatusCode -trimprefix=CheckStatus"; DO NOT EDIT. + +package api + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[CheckStatusMessageSerialize-1] + _ = x[CheckStatusMessageSize-2] + _ = x[CheckStatusMessageValidity-3] + _ = x[CheckStatusMessageMinGas-4] + _ = x[CheckStatusMessageMinBaseFee-5] + _ = x[CheckStatusMessageBaseFee-6] + _ = x[CheckStatusMessageBaseFeeLowerBound-7] + _ = x[CheckStatusMessageBaseFeeUpperBound-8] + _ = x[CheckStatusMessageGetStateNonce-9] + _ = x[CheckStatusMessageNonce-10] + _ = x[CheckStatusMessageGetStateBalance-11] + _ = x[CheckStatusMessageBalance-12] +} + +const _CheckStatusCode_name = "MessageSerializeMessageSizeMessageValidityMessageMinGasMessageMinBaseFeeMessageBaseFeeMessageBaseFeeLowerBoundMessageBaseFeeUpperBoundMessageGetStateNonceMessageNonceMessageGetStateBalanceMessageBalance" + +var _CheckStatusCode_index = [...]uint8{0, 16, 27, 42, 55, 72, 86, 110, 134, 154, 166, 188, 202} + +func (i CheckStatusCode) String() string { + i -= 1 + if i < 0 || i >= CheckStatusCode(len(_CheckStatusCode_index)-1) { + return "CheckStatusCode(" + strconv.FormatInt(int64(i+1), 10) + ")" + } + return _CheckStatusCode_name[_CheckStatusCode_index[i]:_CheckStatusCode_index[i+1]] +} diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 8357ff9b5..4f9bc637e 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -261,6 +261,9 @@ func init() { }, "methods": []interface{}{}}, ) + + addExample(api.CheckStatusCode(0)) + addExample(map[string]interface{}{"abc": 123}) } func GetAPIType(name, pkg string) (i interface{}, t, permStruct, commonPermStruct reflect.Type) { diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index ede04fa20..891a3637f 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1068,6 +1068,36 @@ func (mr *MockFullNodeMockRecorder) MpoolBatchPushUntrusted(arg0, arg1 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolBatchPushUntrusted", reflect.TypeOf((*MockFullNode)(nil).MpoolBatchPushUntrusted), arg0, arg1) } +// MpoolCheckMessages mocks base method +func (m *MockFullNode) MpoolCheckMessages(arg0 context.Context, arg1 []*types.Message) ([][]api.MessageCheckStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MpoolCheckMessages", arg0, arg1) + ret0, _ := ret[0].([][]api.MessageCheckStatus) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MpoolCheckMessages indicates an expected call of MpoolCheckMessages +func (mr *MockFullNodeMockRecorder) MpoolCheckMessages(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolCheckMessages", reflect.TypeOf((*MockFullNode)(nil).MpoolCheckMessages), arg0, arg1) +} + +// MpoolCheckPendingMessages mocks base method +func (m *MockFullNode) MpoolCheckPendingMessages(arg0 context.Context, arg1 address.Address) ([][]api.MessageCheckStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MpoolCheckPendingMessages", arg0, arg1) + ret0, _ := ret[0].([][]api.MessageCheckStatus) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MpoolCheckPendingMessages indicates an expected call of MpoolCheckPendingMessages +func (mr *MockFullNodeMockRecorder) MpoolCheckPendingMessages(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolCheckPendingMessages", reflect.TypeOf((*MockFullNode)(nil).MpoolCheckPendingMessages), arg0, arg1) +} + // MpoolClear mocks base method func (m *MockFullNode) MpoolClear(arg0 context.Context, arg1 bool) error { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index b743a2ddb..08a9c0dd8 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -235,6 +235,10 @@ type FullNodeStruct struct { MpoolBatchPushUntrusted func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` + MpoolCheckMessages func(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) `perm:"read"` + + MpoolCheckPendingMessages func(p0 context.Context, p1 address.Address) ([][]MessageCheckStatus, error) `perm:"read"` + MpoolClear func(p0 context.Context, p1 bool) error `perm:"write"` MpoolGetConfig func(p0 context.Context) (*types.MpoolConfig, error) `perm:"read"` @@ -1509,6 +1513,22 @@ func (s *FullNodeStub) MpoolBatchPushUntrusted(p0 context.Context, p1 []*types.S return *new([]cid.Cid), xerrors.New("method not supported") } +func (s *FullNodeStruct) MpoolCheckMessages(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) { + return s.Internal.MpoolCheckMessages(p0, p1) +} + +func (s *FullNodeStub) MpoolCheckMessages(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) { + return *new([][]MessageCheckStatus), xerrors.New("method not supported") +} + +func (s *FullNodeStruct) MpoolCheckPendingMessages(p0 context.Context, p1 address.Address) ([][]MessageCheckStatus, error) { + return s.Internal.MpoolCheckPendingMessages(p0, p1) +} + +func (s *FullNodeStub) MpoolCheckPendingMessages(p0 context.Context, p1 address.Address) ([][]MessageCheckStatus, error) { + return *new([][]MessageCheckStatus), xerrors.New("method not supported") +} + func (s *FullNodeStruct) MpoolClear(p0 context.Context, p1 bool) error { return s.Internal.MpoolClear(p0, p1) } diff --git a/api/types.go b/api/types.go index bbcfa5c20..ae8bbe958 100644 --- a/api/types.go +++ b/api/types.go @@ -137,3 +137,35 @@ type NodeChainStatus struct { BlocksPerTipsetLast100 float64 BlocksPerTipsetLastFinality float64 } + +type CheckStatusCode int + +//go:generate go run golang.org/x/tools/cmd/stringer -type=CheckStatusCode -trimprefix=CheckStatus +const ( + _ CheckStatusCode = iota + // Message Checks + CheckStatusMessageSerialize + CheckStatusMessageSize + CheckStatusMessageValidity + CheckStatusMessageMinGas + CheckStatusMessageMinBaseFee + CheckStatusMessageBaseFee + CheckStatusMessageBaseFeeLowerBound + CheckStatusMessageBaseFeeUpperBound + CheckStatusMessageGetStateNonce + CheckStatusMessageNonce + CheckStatusMessageGetStateBalance + CheckStatusMessageBalance +) + +type CheckStatus struct { + Code CheckStatusCode + OK bool + Err string + Hint map[string]interface{} +} + +type MessageCheckStatus struct { + Cid cid.Cid + CheckStatus +} diff --git a/build/tools.go b/build/tools.go index ad45397bb..57b6e7d1f 100644 --- a/build/tools.go +++ b/build/tools.go @@ -6,4 +6,5 @@ import ( _ "github.com/GeertJohan/go.rice/rice" _ "github.com/golang/mock/mockgen" _ "github.com/whyrusleeping/bencher" + _ "golang.org/x/tools/cmd/stringer" ) diff --git a/chain/messagepool/check.go b/chain/messagepool/check.go new file mode 100644 index 000000000..9a8e32248 --- /dev/null +++ b/chain/messagepool/check.go @@ -0,0 +1,374 @@ +package messagepool + +import ( + "context" + "fmt" + stdbig "math/big" + "sort" + + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" +) + +var baseFeeUpperBoundFactor = types.NewInt(10) + +// CheckMessages performs a set of logic checks for a list of messages, prior to submitting it to the mpool +func (mp *MessagePool) CheckMessages(msgs []*types.Message) ([][]api.MessageCheckStatus, error) { + return mp.checkMessages(msgs, false) +} + +// CheckPendingMessages performs a set of logical sets for all messages pending from a given actor +func (mp *MessagePool) CheckPendingMessages(from address.Address) ([][]api.MessageCheckStatus, error) { + var msgs []*types.Message + mp.lk.Lock() + mset, ok := mp.pending[from] + if ok { + for _, sm := range mset.msgs { + msgs = append(msgs, &sm.Message) + } + } + mp.lk.Unlock() + + if len(msgs) == 0 { + return nil, nil + } + + sort.Slice(msgs, func(i, j int) bool { + return msgs[i].Nonce < msgs[j].Nonce + }) + + return mp.checkMessages(msgs, true) +} + +func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (result [][]api.MessageCheckStatus, err error) { + mp.curTsLk.Lock() + curTs := mp.curTs + mp.curTsLk.Unlock() + + epoch := curTs.Height() + + var baseFee big.Int + if len(curTs.Blocks()) > 0 { + baseFee = curTs.Blocks()[0].ParentBaseFee + } else { + baseFee, err = mp.api.ChainComputeBaseFee(context.Background(), curTs) + if err != nil { + return nil, xerrors.Errorf("error computing basefee: %w", err) + } + } + + baseFeeLowerBound := getBaseFeeLowerBound(baseFee, baseFeeLowerBoundFactor) + baseFeeUpperBound := types.BigMul(baseFee, baseFeeUpperBoundFactor) + + type actorState struct { + nextNonce uint64 + requiredFunds *stdbig.Int + } + + state := make(map[address.Address]*actorState) + balances := make(map[address.Address]big.Int) + + result = make([][]api.MessageCheckStatus, len(msgs)) + + for i, m := range msgs { + // pre-check: actor nonce + check := api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageGetStateNonce, + }, + } + + st, ok := state[m.From] + if !ok { + mp.lk.Lock() + mset, ok := mp.pending[m.From] + if ok && !interned { + st = &actorState{nextNonce: mset.nextNonce, requiredFunds: mset.requiredFunds} + for _, m := range mset.msgs { + st.requiredFunds = new(stdbig.Int).Add(st.requiredFunds, m.Message.Value.Int) + } + state[m.From] = st + mp.lk.Unlock() + + check.OK = true + check.Hint = map[string]interface{}{ + "nonce": st.nextNonce, + } + } else { + mp.lk.Unlock() + + stateNonce, err := mp.getStateNonce(m.From, curTs) + if err != nil { + check.OK = false + check.Err = fmt.Sprintf("error retrieving state nonce: %s", err.Error()) + } else { + check.OK = true + check.Hint = map[string]interface{}{ + "nonce": stateNonce, + } + } + + st = &actorState{nextNonce: stateNonce, requiredFunds: new(stdbig.Int)} + state[m.From] = st + } + } + + result[i] = append(result[i], check) + if !check.OK { + continue + } + + // pre-check: actor balance + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageGetStateBalance, + }, + } + + balance, ok := balances[m.From] + if !ok { + balance, err = mp.getStateBalance(m.From, curTs) + if err != nil { + check.OK = false + check.Err = fmt.Sprintf("error retrieving state balance: %s", err) + } else { + check.OK = true + check.Hint = map[string]interface{}{ + "balance": balance, + } + } + + balances[m.From] = balance + } else { + check.OK = true + check.Hint = map[string]interface{}{ + "balance": balance, + } + } + + result[i] = append(result[i], check) + if !check.OK { + continue + } + + // 1. Serialization + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageSerialize, + }, + } + + bytes, err := m.Serialize() + if err != nil { + check.OK = false + check.Err = err.Error() + } else { + check.OK = true + } + + result[i] = append(result[i], check) + + // 2. Message size + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageSize, + }, + } + + if len(bytes) > 32*1024-128 { // 128 bytes to account for signature size + check.OK = false + check.Err = "message too big" + } else { + check.OK = true + } + + result[i] = append(result[i], check) + + // 3. Syntactic validation + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageValidity, + }, + } + + if err := m.ValidForBlockInclusion(0, build.NewestNetworkVersion); err != nil { + check.OK = false + check.Err = fmt.Sprintf("syntactically invalid message: %s", err.Error()) + } else { + check.OK = true + } + + result[i] = append(result[i], check) + if !check.OK { + // skip remaining checks if it is a syntatically invalid message + continue + } + + // gas checks + + // 4. Min Gas + minGas := vm.PricelistByEpoch(epoch).OnChainMessage(m.ChainLength()) + + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageMinGas, + Hint: map[string]interface{}{ + "minGas": minGas, + }, + }, + } + + if m.GasLimit < minGas.Total() { + check.OK = false + check.Err = "GasLimit less than epoch minimum gas" + } else { + check.OK = true + } + + result[i] = append(result[i], check) + + // 5. Min Base Fee + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageMinBaseFee, + }, + } + + if m.GasFeeCap.LessThan(minimumBaseFee) { + check.OK = false + check.Err = "GasFeeCap less than minimum base fee" + } else { + check.OK = true + } + + result[i] = append(result[i], check) + if !check.OK { + goto checkState + } + + // 6. Base Fee + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageBaseFee, + Hint: map[string]interface{}{ + "baseFee": baseFee, + }, + }, + } + + if m.GasFeeCap.LessThan(baseFee) { + check.OK = false + check.Err = "GasFeeCap less than current base fee" + } else { + check.OK = true + } + + result[i] = append(result[i], check) + + // 7. Base Fee lower bound + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageBaseFeeLowerBound, + Hint: map[string]interface{}{ + "baseFeeLowerBound": baseFeeLowerBound, + }, + }, + } + + if m.GasFeeCap.LessThan(baseFeeLowerBound) { + check.OK = false + check.Err = "GasFeeCap less than base fee lower bound for inclusion in next 20 epochs" + } else { + check.OK = true + } + + result[i] = append(result[i], check) + if !check.OK { + goto checkState + } + + // 8. Base Fee upper bound + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageBaseFeeUpperBound, + Hint: map[string]interface{}{ + "baseFeeUpperBound": baseFeeUpperBound, + }, + }, + } + + if m.GasFeeCap.LessThan(baseFeeUpperBound) { + check.OK = false + check.Err = "GasFeeCap less than base fee upper bound for inclusion in next 20 epochs" + } else { + check.OK = true + } + + result[i] = append(result[i], check) + + // stateful checks + checkState: + // 9. Message Nonce + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageNonce, + Hint: map[string]interface{}{ + "nextNonce": st.nextNonce, + }, + }, + } + + if st.nextNonce != m.Nonce { + check.OK = false + check.Err = fmt.Sprintf("message nonce doesn't match next nonce (%d)", st.nextNonce) + } else { + check.OK = true + st.nextNonce++ + } + + result[i] = append(result[i], check) + + // check required funds -vs- balance + st.requiredFunds = new(stdbig.Int).Add(st.requiredFunds, m.RequiredFunds().Int) + st.requiredFunds.Add(st.requiredFunds, m.Value.Int) + + // 10. Balance + check = api.MessageCheckStatus{ + Cid: m.Cid(), + CheckStatus: api.CheckStatus{ + Code: api.CheckStatusMessageBalance, + Hint: map[string]interface{}{ + "requiredFunds": big.Int{Int: stdbig.NewInt(0).Set(st.requiredFunds)}, + }, + }, + } + + if balance.Int.Cmp(st.requiredFunds) < 0 { + check.OK = false + check.Err = "insufficient balance" + } else { + check.OK = true + } + + result[i] = append(result[i], check) + } + + return result, nil +} diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 3c5356a56..2b75212c2 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -82,6 +82,8 @@ * [MpoolBatchPush](#MpoolBatchPush) * [MpoolBatchPushMessage](#MpoolBatchPushMessage) * [MpoolBatchPushUntrusted](#MpoolBatchPushUntrusted) + * [MpoolCheckMessages](#MpoolCheckMessages) + * [MpoolCheckPendingMessages](#MpoolCheckPendingMessages) * [MpoolClear](#MpoolClear) * [MpoolGetConfig](#MpoolGetConfig) * [MpoolGetNonce](#MpoolGetNonce) @@ -1994,6 +1996,36 @@ Inputs: Response: `null` +### MpoolCheckMessages +MpoolCheckMessages performs logical checks on a batch of messages + + +Perms: read + +Inputs: +```json +[ + null +] +``` + +Response: `null` + +### MpoolCheckPendingMessages +MpoolCheckPendingMessages performs logical checks for all pending messages from a given address + + +Perms: read + +Inputs: +```json +[ + "f01234" +] +``` + +Response: `null` + ### MpoolClear MpoolClear clears pending messages from the mpool diff --git a/go.mod b/go.mod index 9d5accbf1..07afe91d3 100644 --- a/go.mod +++ b/go.mod @@ -150,6 +150,7 @@ require ( golang.org/x/sync v0.0.0-20201207232520-09787c993a3a golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 + golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index 31c8bc4f7..4916af894 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -225,6 +225,14 @@ func (a *MpoolAPI) MpoolBatchPushMessage(ctx context.Context, msgs []*types.Mess return smsgs, nil } +func (a *MpoolAPI) MpoolCheckMessages(ctx context.Context, msgs []*types.Message) ([][]api.MessageCheckStatus, error) { + return a.Mpool.CheckMessages(msgs) +} + +func (a *MpoolAPI) MpoolCheckPendingMessages(ctx context.Context, from address.Address) ([][]api.MessageCheckStatus, error) { + return a.Mpool.CheckPendingMessages(from) +} + func (a *MpoolAPI) MpoolGetNonce(ctx context.Context, addr address.Address) (uint64, error) { return a.Mpool.GetNonce(ctx, addr, types.EmptyTSK) } From d782250abaa89b047e415e28a050230950b6ebb2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 23 Mar 2021 16:55:38 +0200 Subject: [PATCH 65/97] implement MessagePool.CheckReplaceMessages Signed-off-by: Jakub Sztandera --- api/api_full.go | 2 ++ api/mocks/mock_full.go | 15 ++++++++++ api/proxy_gen.go | 10 +++++++ chain/messagepool/check.go | 45 ++++++++++++++++++++++++++++++ documentation/en/api-v0-methods.md | 16 +++++++++++ node/impl/full/mpool.go | 4 +++ 6 files changed, 92 insertions(+) diff --git a/api/api_full.go b/api/api_full.go index 3b69ca5e6..148f4ac92 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -256,6 +256,8 @@ type FullNode interface { MpoolCheckMessages(context.Context, []*types.Message) ([][]MessageCheckStatus, error) //perm:read // MpoolCheckPendingMessages performs logical checks for all pending messages from a given address MpoolCheckPendingMessages(context.Context, address.Address) ([][]MessageCheckStatus, error) //perm:read + // MpoolCheckReplaceMessages performs logical checks on pending messages with replacement + MpoolCheckReplaceMessages(context.Context, []*types.Message) ([][]MessageCheckStatus, error) //perm:read // MpoolGetNonce gets next nonce for the specified sender. // Note that this method may not be atomic. Use MpoolPushMessage instead. diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 891a3637f..ee89a1d23 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1098,6 +1098,21 @@ func (mr *MockFullNodeMockRecorder) MpoolCheckPendingMessages(arg0, arg1 interfa return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolCheckPendingMessages", reflect.TypeOf((*MockFullNode)(nil).MpoolCheckPendingMessages), arg0, arg1) } +// MpoolCheckReplaceMessages mocks base method +func (m *MockFullNode) MpoolCheckReplaceMessages(arg0 context.Context, arg1 []*types.Message) ([][]api.MessageCheckStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MpoolCheckReplaceMessages", arg0, arg1) + ret0, _ := ret[0].([][]api.MessageCheckStatus) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MpoolCheckReplaceMessages indicates an expected call of MpoolCheckReplaceMessages +func (mr *MockFullNodeMockRecorder) MpoolCheckReplaceMessages(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolCheckReplaceMessages", reflect.TypeOf((*MockFullNode)(nil).MpoolCheckReplaceMessages), arg0, arg1) +} + // MpoolClear mocks base method func (m *MockFullNode) MpoolClear(arg0 context.Context, arg1 bool) error { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 08a9c0dd8..dfb9f3731 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -239,6 +239,8 @@ type FullNodeStruct struct { MpoolCheckPendingMessages func(p0 context.Context, p1 address.Address) ([][]MessageCheckStatus, error) `perm:"read"` + MpoolCheckReplaceMessages func(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) `perm:"read"` + MpoolClear func(p0 context.Context, p1 bool) error `perm:"write"` MpoolGetConfig func(p0 context.Context) (*types.MpoolConfig, error) `perm:"read"` @@ -1529,6 +1531,14 @@ func (s *FullNodeStub) MpoolCheckPendingMessages(p0 context.Context, p1 address. return *new([][]MessageCheckStatus), xerrors.New("method not supported") } +func (s *FullNodeStruct) MpoolCheckReplaceMessages(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) { + return s.Internal.MpoolCheckReplaceMessages(p0, p1) +} + +func (s *FullNodeStub) MpoolCheckReplaceMessages(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) { + return *new([][]MessageCheckStatus), xerrors.New("method not supported") +} + func (s *FullNodeStruct) MpoolClear(p0 context.Context, p1 bool) error { return s.Internal.MpoolClear(p0, p1) } diff --git a/chain/messagepool/check.go b/chain/messagepool/check.go index 9a8e32248..5b0761a62 100644 --- a/chain/messagepool/check.go +++ b/chain/messagepool/check.go @@ -46,6 +46,51 @@ func (mp *MessagePool) CheckPendingMessages(from address.Address) ([][]api.Messa return mp.checkMessages(msgs, true) } +// CheckReplaceMessages performs a set of logical checks for related messages while performing a +// replacement. +func (mp *MessagePool) CheckReplaceMessages(replace []*types.Message) ([][]api.MessageCheckStatus, error) { + msgMap := make(map[address.Address]map[uint64]*types.Message) + count := 0 + + mp.lk.Lock() + for _, m := range replace { + mmap, ok := msgMap[m.From] + if !ok { + mmap = make(map[uint64]*types.Message) + msgMap[m.From] = mmap + mset, ok := mp.pending[m.From] + if ok { + count += len(mset.msgs) + for _, sm := range mset.msgs { + mmap[sm.Message.Nonce] = &sm.Message + } + } else { + count++ + } + } + mmap[m.Nonce] = m + } + mp.lk.Unlock() + + msgs := make([]*types.Message, 0, count) + start := 0 + for _, mmap := range msgMap { + end := start + len(mmap) + + for _, m := range mmap { + msgs = append(msgs, m) + } + + sort.Slice(msgs[start:end], func(i, j int) bool { + return msgs[start+i].Nonce < msgs[start+j].Nonce + }) + + start = end + } + + return mp.checkMessages(msgs, true) +} + func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (result [][]api.MessageCheckStatus, err error) { mp.curTsLk.Lock() curTs := mp.curTs diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 2b75212c2..e90ba7d6a 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -84,6 +84,7 @@ * [MpoolBatchPushUntrusted](#MpoolBatchPushUntrusted) * [MpoolCheckMessages](#MpoolCheckMessages) * [MpoolCheckPendingMessages](#MpoolCheckPendingMessages) + * [MpoolCheckReplaceMessages](#MpoolCheckReplaceMessages) * [MpoolClear](#MpoolClear) * [MpoolGetConfig](#MpoolGetConfig) * [MpoolGetNonce](#MpoolGetNonce) @@ -2026,6 +2027,21 @@ Inputs: Response: `null` +### MpoolCheckReplaceMessages +MpoolCheckMessages performs logical checks on pending messages with replacement + + +Perms: read + +Inputs: +```json +[ + null +] +``` + +Response: `null` + ### MpoolClear MpoolClear clears pending messages from the mpool diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index 4916af894..099fb45b8 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -233,6 +233,10 @@ func (a *MpoolAPI) MpoolCheckPendingMessages(ctx context.Context, from address.A return a.Mpool.CheckPendingMessages(from) } +func (a *MpoolAPI) MpoolCheckReplaceMessages(ctx context.Context, msgs []*types.Message) ([][]api.MessageCheckStatus, error) { + return a.Mpool.CheckReplaceMessages(msgs) +} + func (a *MpoolAPI) MpoolGetNonce(ctx context.Context, addr address.Address) (uint64, error) { return a.Mpool.GetNonce(ctx, addr, types.EmptyTSK) } From 86e90dc6f11e9e5cade15a6a1e81552fd5450d01 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 24 Mar 2021 15:12:35 +0100 Subject: [PATCH 66/97] Message sending UI Signed-off-by: Jakub Sztandera --- chain/messagepool/check.go | 5 +- cli/cmd.go | 2 +- cli/send.go | 24 ++-- cli/send_test.go | 15 +-- cli/sending_ui.go | 235 +++++++++++++++++++++++++++++++++++++ cli/services.go | 146 +++++++++++++++-------- cli/services_send_test.go | 85 +++----------- cli/servicesmock_test.go | 64 ++++++++-- go.mod | 4 +- go.sum | 18 ++- 10 files changed, 445 insertions(+), 153 deletions(-) create mode 100644 cli/sending_ui.go diff --git a/chain/messagepool/check.go b/chain/messagepool/check.go index 5b0761a62..6800b0ef4 100644 --- a/chain/messagepool/check.go +++ b/chain/messagepool/check.go @@ -331,6 +331,7 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu Code: api.CheckStatusMessageBaseFeeLowerBound, Hint: map[string]interface{}{ "baseFeeLowerBound": baseFeeLowerBound, + "baseFee": baseFee, }, }, } @@ -343,9 +344,6 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu } result[i] = append(result[i], check) - if !check.OK { - goto checkState - } // 8. Base Fee upper bound check = api.MessageCheckStatus{ @@ -354,6 +352,7 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu Code: api.CheckStatusMessageBaseFeeUpperBound, Hint: map[string]interface{}{ "baseFeeUpperBound": baseFeeUpperBound, + "baseFee": baseFee, }, }, } diff --git a/cli/cmd.go b/cli/cmd.go index 09bf5c461..fad9ee668 100644 --- a/cli/cmd.go +++ b/cli/cmd.go @@ -34,7 +34,7 @@ func GetFullNodeServices(ctx *cli.Context) (ServicesAPI, error) { return tn.(ServicesAPI), nil } - api, c, err := GetFullNodeAPI(ctx) + api, c, err := GetFullNodeAPIV1(ctx) if err != nil { return nil, err } diff --git a/cli/send.go b/cli/send.go index daf73ccad..4056a2d61 100644 --- a/cli/send.go +++ b/cli/send.go @@ -2,7 +2,6 @@ package cli import ( "encoding/hex" - "errors" "fmt" "github.com/urfave/cli/v2" @@ -137,23 +136,30 @@ var sendCmd = &cli.Command{ params.Params = decparams } - params.Force = cctx.Bool("force") - if cctx.IsSet("nonce") { n := cctx.Uint64("nonce") params.Nonce = &n } - msgCid, err := srv.Send(ctx, params) - + proto, err := srv.MessageForSend(ctx, params) if err != nil { - if errors.Is(err, ErrSendBalanceTooLow) { - return fmt.Errorf("--force must be specified for this action to have an effect; you have been warned: %w", err) + return xerrors.Errorf("creating message prototype: %w", err) + } + msg, checks, err := srv.PublishMessage(ctx, proto, cctx.Bool("force")) + if xerrors.Is(err, ErrCheckFailed) { + proto, err = resolveChecks(ctx, srv, cctx.App.Writer, proto, checks, true) + if err != nil { + return xerrors.Errorf("from UI: %w", err) } - return xerrors.Errorf("executing send: %w", err) + + msg, _, err = srv.PublishMessage(ctx, proto, true) } - fmt.Fprintf(cctx.App.Writer, "%s\n", msgCid) + if err != nil { + return xerrors.Errorf("publishing message: %w", err) + } + + fmt.Fprintf(cctx.App.Writer, "%s\n", msg.Cid()) return nil }, } diff --git a/cli/send_test.go b/cli/send_test.go index ff258346a..b16e3c57e 100644 --- a/cli/send_test.go +++ b/cli/send_test.go @@ -1,18 +1,6 @@ package cli -import ( - "bytes" - "errors" - "testing" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - types "github.com/filecoin-project/lotus/chain/types" - gomock "github.com/golang/mock/gomock" - cid "github.com/ipfs/go-cid" - "github.com/stretchr/testify/assert" - ucli "github.com/urfave/cli/v2" -) +/* var arbtCid = (&types.Message{ From: mustAddr(address.NewIDAddress(2)), @@ -126,3 +114,4 @@ func TestSendCLI(t *testing.T) { }) } +*/ diff --git a/cli/sending_ui.go b/cli/sending_ui.go new file mode 100644 index 000000000..c05f67a97 --- /dev/null +++ b/cli/sending_ui.go @@ -0,0 +1,235 @@ +package cli + +import ( + "context" + "errors" + "fmt" + "io" + "strings" + + "github.com/Kubuxu/imtui" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/api" + types "github.com/filecoin-project/lotus/chain/types" + "github.com/gdamore/tcell/v2" + cid "github.com/ipfs/go-cid" +) + +var interactiveSolves = map[api.CheckStatusCode]bool{ + api.CheckStatusMessageBaseFee: true, + api.CheckStatusMessageBaseFeeLowerBound: true, + api.CheckStatusMessageBaseFeeUpperBound: true, +} + +func baseFeeFromHints(hint map[string]interface{}) big.Int { + bHint, ok := hint["baseFee"] + if !ok { + return big.Zero() + } + bHintS, ok := bHint.(string) + if !ok { + return big.Zero() + } + + var err error + baseFee, err := big.FromString(bHintS) + if err != nil { + return big.Zero() + } + return baseFee +} + +func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer, + proto *types.Message, checkGroups [][]api.MessageCheckStatus, + interactive bool) (*types.Message, error) { + + fmt.Fprintf(printer, "Following checks have failed:\n") + printChecks(printer, checkGroups, proto.Cid()) + if !interactive { + return nil, ErrCheckFailed + } + + if interactive { + if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Cid()); feeCapBad { + fmt.Fprintf(printer, "Fee of the message can be adjusted\n") + if askUser(printer, "Do you wish to do that? [Yes/no]: ", true) { + var err error + proto, err = runFeeCapAdjustmentUI(proto, baseFee) + if err != nil { + return nil, err + } + } + checks, err := s.RunChecksForPrototype(ctx, proto) + if err != nil { + return nil, err + } + fmt.Fprintf(printer, "Following checks still failed:\n") + printChecks(printer, checks, proto.Cid()) + } + + if !askUser(printer, "Do you wish to send this message? [yes/No]: ", false) { + return nil, ErrAbortedByUser + } + } + return proto, nil +} + +var ErrAbortedByUser = errors.New("aborted by user") + +func printChecks(printer io.Writer, checkGroups [][]api.MessageCheckStatus, protoCid cid.Cid) { + for _, checks := range checkGroups { + for _, c := range checks { + if c.OK { + continue + } + aboutProto := c.Cid.Equals(protoCid) + msgName := "current" + if !aboutProto { + msgName = c.Cid.String() + } + fmt.Fprintf(printer, "%s message failed a check: %s\n", msgName, c.Err) + } + } +} + +func askUser(printer io.Writer, q string, def bool) bool { + var resp string + fmt.Fprint(printer, q) + fmt.Scanln(&resp) + resp = strings.ToLower(resp) + if len(resp) == 0 { + return def + } + return resp[0] == 'y' +} + +func isFeeCapProblem(checkGroups [][]api.MessageCheckStatus, protoCid cid.Cid) (bool, big.Int) { + baseFee := big.Zero() + yes := false + for _, checks := range checkGroups { + for _, c := range checks { + if c.OK { + continue + } + aboutProto := c.Cid.Equals(protoCid) + if aboutProto && interactiveSolves[c.Code] { + yes = true + if baseFee.IsZero() { + baseFee = baseFeeFromHints(c.Hint) + } + } + } + } + + return yes, baseFee +} + +func runFeeCapAdjustmentUI(proto *types.Message, baseFee abi.TokenAmount) (*types.Message, error) { + t, err := imtui.NewTui() + if err != nil { + return nil, err + } + + maxFee := big.Mul(proto.GasFeeCap, big.NewInt(proto.GasLimit)) + send := false + t.SetScene(ui(baseFee, proto.GasLimit, &maxFee, &send)) + + err = t.Run() + if err != nil { + return nil, err + } + if !send { + return nil, fmt.Errorf("aborted by user") + } + + proto.GasFeeCap = big.Div(maxFee, big.NewInt(proto.GasLimit)) + + return proto, nil +} + +func ui(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, send *bool) func(*imtui.Tui) error { + orignalMaxFee := *maxFee + required := big.Mul(baseFee, big.NewInt(gasLimit)) + safe := big.Mul(required, big.NewInt(10)) + + price := fmt.Sprintf("%s", types.FIL(*maxFee).Unitless()) + + return func(t *imtui.Tui) error { + if t.CurrentKey != nil { + if t.CurrentKey.Key() == tcell.KeyRune { + pF, err := types.ParseFIL(price) + switch t.CurrentKey.Rune() { + case 's', 'S': + price = types.FIL(safe).Unitless() + case '+': + if err == nil { + p := big.Mul(big.Int(pF), types.NewInt(11)) + p = big.Div(p, types.NewInt(10)) + price = fmt.Sprintf("%s", types.FIL(p).Unitless()) + } + case '-': + if err == nil { + p := big.Mul(big.Int(pF), types.NewInt(10)) + p = big.Div(p, types.NewInt(11)) + price = fmt.Sprintf("%s", types.FIL(p).Unitless()) + } + default: + } + } + + if t.CurrentKey.Key() == tcell.KeyEnter { + *send = true + return imtui.ErrNormalExit + } + } + + defS := tcell.StyleDefault + + row := 0 + t.Label(0, row, "Fee of the message is too low.", defS) + row++ + + t.Label(0, row, fmt.Sprintf("Your configured maximum fee is: %s FIL", + types.FIL(orignalMaxFee).Unitless()), defS) + row++ + t.Label(0, row, fmt.Sprintf("Required maximum fee for the message: %s FIL", + types.FIL(required).Unitless()), defS) + row++ + w := t.Label(0, row, fmt.Sprintf("Safe maximum fee for the message: %s FIL", + types.FIL(safe).Unitless()), defS) + t.Label(w, row, " Press S to use it", defS) + row++ + + w = t.Label(0, row, "Current Maximum Fee: ", defS) + + w += t.EditFieldFiltered(w, row, 14, &price, imtui.FilterDecimal, defS.Foreground(tcell.ColorWhite).Background(tcell.ColorBlack)) + + w += t.Label(w, row, " FIL", defS) + + pF, err := types.ParseFIL(price) + *maxFee = abi.TokenAmount(pF) + if err != nil { + w += t.Label(w, row, " invalid price", defS.Foreground(tcell.ColorMaroon).Bold(true)) + } else if maxFee.GreaterThanEqual(safe) { + w += t.Label(w, row, " SAFE", defS.Foreground(tcell.ColorDarkGreen).Bold(true)) + } else if maxFee.GreaterThanEqual(required) { + w += t.Label(w, row, " low", defS.Foreground(tcell.ColorYellow).Bold(true)) + over := big.Div(big.Mul(*maxFee, big.NewInt(100)), required) + w += t.Label(w, row, + fmt.Sprintf(" %.1fx over the minimum", float64(over.Int64())/100.0), defS) + } else { + w += t.Label(w, row, " too low", defS.Foreground(tcell.ColorRed).Bold(true)) + } + row += 2 + + t.Label(0, row, fmt.Sprintf("Current Base Fee is: %s", types.FIL(baseFee)), defS) + row++ + t.Label(0, row, fmt.Sprintf("Resulting FeeCap is: %s", + types.FIL(big.Div(*maxFee, big.NewInt(gasLimit)))), defS) + row++ + t.Label(0, row, "You can use '+' and '-' to adjust the fee.", defS) + + return nil + } +} diff --git a/cli/services.go b/cli/services.go index 3de0b567b..6fc4afe58 100644 --- a/cli/services.go +++ b/cli/services.go @@ -4,14 +4,14 @@ import ( "bytes" "context" "encoding/json" - "errors" "fmt" "reflect" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/api/v0api" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/stmgr" types "github.com/filecoin-project/lotus/chain/types" cid "github.com/ipfs/go-cid" @@ -22,12 +22,23 @@ import ( //go:generate go run github.com/golang/mock/mockgen -destination=servicesmock_test.go -package=cli -self_package github.com/filecoin-project/lotus/cli . ServicesAPI type ServicesAPI interface { - // Sends executes a send given SendParams - Send(ctx context.Context, params SendParams) (cid.Cid, error) + GetBaseFee(ctx context.Context) (abi.TokenAmount, error) + + // MessageForSend creates a prototype of a message based on SendParams + MessageForSend(ctx context.Context, params SendParams) (*types.Message, error) + // DecodeTypedParamsFromJSON takes in information needed to identify a method and converts JSON // parameters to bytes of their CBOR encoding DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error) + RunChecksForPrototype(ctx context.Context, prototype *types.Message) ([][]api.MessageCheckStatus, error) + + // PublishMessage takes in a message prototype and publishes it + // before publishing the message, it runs checks on the node, message and mpool to verify that + // message is valid and won't be stuck. + // if `force` is true, it skips the checks + PublishMessage(ctx context.Context, prototype *types.Message, interactive bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) + // Close ends the session of services and disconnects from RPC, using Services after Close is called // most likely will result in an error // Should not be called concurrently @@ -35,7 +46,7 @@ type ServicesAPI interface { } type ServicesImpl struct { - api v0api.FullNode + api api.FullNode closer jsonrpc.ClientCloser } @@ -48,6 +59,16 @@ func (s *ServicesImpl) Close() error { return nil } +func (s *ServicesImpl) GetBaseFee(ctx context.Context) (abi.TokenAmount, error) { + // not used but useful + + ts, err := s.api.ChainHead(ctx) + if err != nil { + return big.Zero(), xerrors.Errorf("getting head: %w", err) + } + return ts.MinTicketBlock().ParentBaseFee, nil +} + func (s *ServicesImpl) DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error) { act, err := s.api.StateGetActor(ctx, to, types.EmptyTSK) if err != nil { @@ -72,6 +93,76 @@ func (s *ServicesImpl) DecodeTypedParamsFromJSON(ctx context.Context, to address return buf.Bytes(), nil } +type CheckInfo struct { + MessageTie cid.Cid + CurrentMessageTie bool + + Check api.MessageCheckStatus +} + +var ErrCheckFailed = fmt.Errorf("check has failed") + +func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *types.Message) ([][]api.MessageCheckStatus, error) { + var outChecks [][]api.MessageCheckStatus + checks, err := s.api.MpoolCheckMessages(ctx, []*types.Message{prototype}) + if err != nil { + return nil, xerrors.Errorf("message check: %w", err) + } + outChecks = append(outChecks, checks...) + + checks, err = s.api.MpoolCheckPendingMessages(ctx, prototype.From) + if err != nil { + return nil, xerrors.Errorf("pending mpool check: %w", err) + } + outChecks = append(outChecks, checks...) + + return outChecks, nil +} + +// PublishMessage modifies prototype to include gas estimation +// Errors with ErrCheckFailed if any of the checks fail +// First group of checks is related to the message prototype +func (s *ServicesImpl) PublishMessage(ctx context.Context, + prototype *types.Message, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { + + gasedMsg, err := s.api.GasEstimateMessageGas(ctx, prototype, nil, types.EmptyTSK) + if err != nil { + return nil, nil, xerrors.Errorf("estimating gas: %w", err) + } + *prototype = *gasedMsg + + if !force { + checks, err := s.RunChecksForPrototype(ctx, prototype) + if err != nil { + return nil, nil, xerrors.Errorf("running checks: %w", err) + } + if len(checks) != 0 { + return nil, checks, ErrCheckFailed + } + } + + //TODO: message prototype needs to have "IsNonceSet" + if prototype.Nonce != 0 { + sm, err := s.api.WalletSignMessage(ctx, prototype.From, prototype) + if err != nil { + return nil, nil, err + } + + _, err = s.api.MpoolPush(ctx, sm) + if err != nil { + return nil, nil, err + } + return sm, nil, nil + } + + sm, err := s.api.MpoolPushMessage(ctx, prototype, nil) + if err != nil { + return nil, nil, err + } + + return sm, nil, nil +} + type SendParams struct { To address.Address From address.Address @@ -84,21 +175,13 @@ type SendParams struct { Nonce *uint64 Method abi.MethodNum Params []byte - - Force bool } -// This is specialised Send for Send command -// There might be room for generic Send that other commands can use to send their messages -// We will see - -var ErrSendBalanceTooLow = errors.New("balance too low") - -func (s *ServicesImpl) Send(ctx context.Context, params SendParams) (cid.Cid, error) { +func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*types.Message, error) { if params.From == address.Undef { defaddr, err := s.api.WalletDefaultAddress(ctx) if err != nil { - return cid.Undef, err + return nil, err } params.From = defaddr } @@ -127,40 +210,9 @@ func (s *ServicesImpl) Send(ctx context.Context, params SendParams) (cid.Cid, er } else { msg.GasLimit = 0 } - - if !params.Force { - // Funds insufficient check - fromBalance, err := s.api.WalletBalance(ctx, msg.From) - if err != nil { - return cid.Undef, err - } - totalCost := types.BigAdd(types.BigMul(msg.GasFeeCap, types.NewInt(uint64(msg.GasLimit))), msg.Value) - - if fromBalance.LessThan(totalCost) { - return cid.Undef, xerrors.Errorf("From balance %s less than total cost %s: %w", types.FIL(fromBalance), types.FIL(totalCost), ErrSendBalanceTooLow) - - } - } - if params.Nonce != nil { msg.Nonce = *params.Nonce - sm, err := s.api.WalletSignMessage(ctx, params.From, msg) - if err != nil { - return cid.Undef, err - } - - _, err = s.api.MpoolPush(ctx, sm) - if err != nil { - return cid.Undef, err - } - - return sm.Cid(), nil } - sm, err := s.api.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, err - } - - return sm.Cid(), nil + return msg, nil } diff --git a/cli/services_send_test.go b/cli/services_send_test.go index 713e81b2a..faa052e0c 100644 --- a/cli/services_send_test.go +++ b/cli/services_send_test.go @@ -151,47 +151,12 @@ func TestSendService(t *testing.T) { t.Run("happy", func(t *testing.T) { params := params - srvcs, mockApi := setupMockSrvcs(t) + srvcs, _ := setupMockSrvcs(t) defer srvcs.Close() //nolint:errcheck - msgCid, sign := makeMessageSigner() - gomock.InOrder( - mockApi.EXPECT().WalletBalance(ctxM, params.From).Return(types.NewInt(balance), nil), - mockApi.EXPECT().MpoolPushMessage(ctxM, MessageMatcher(params), nil).DoAndReturn(sign), - ) - c, err := srvcs.Send(ctx, params) + proto, err := srvcs.MessageForSend(ctx, params) assert.NoError(t, err) - assert.Equal(t, *msgCid, c) - }) - - t.Run("balance-too-low", func(t *testing.T) { - params := params - srvcs, mockApi := setupMockSrvcs(t) - defer srvcs.Close() //nolint:errcheck - gomock.InOrder( - mockApi.EXPECT().WalletBalance(ctxM, a1).Return(types.NewInt(balance-200), nil), - // no MpoolPushMessage - ) - - c, err := srvcs.Send(ctx, params) - assert.Equal(t, c, cid.Undef) - assert.ErrorIs(t, err, ErrSendBalanceTooLow) - }) - - t.Run("force", func(t *testing.T) { - params := params - params.Force = true - srvcs, mockApi := setupMockSrvcs(t) - defer srvcs.Close() //nolint:errcheck - msgCid, sign := makeMessageSigner() - gomock.InOrder( - mockApi.EXPECT().WalletBalance(ctxM, a1).Return(types.NewInt(balance-200), nil).AnyTimes(), - mockApi.EXPECT().MpoolPushMessage(ctxM, MessageMatcher(params), nil).DoAndReturn(sign), - ) - - c, err := srvcs.Send(ctx, params) - assert.NoError(t, err) - assert.Equal(t, *msgCid, c) + assert.True(t, MessageMatcher(params).Matches(proto)) }) t.Run("default-from", func(t *testing.T) { @@ -202,16 +167,14 @@ func TestSendService(t *testing.T) { srvcs, mockApi := setupMockSrvcs(t) defer srvcs.Close() //nolint:errcheck - msgCid, sign := makeMessageSigner() + gomock.InOrder( mockApi.EXPECT().WalletDefaultAddress(ctxM).Return(a1, nil), - mockApi.EXPECT().WalletBalance(ctxM, a1).Return(types.NewInt(balance), nil), - mockApi.EXPECT().MpoolPushMessage(ctxM, mm, nil).DoAndReturn(sign), ) - c, err := srvcs.Send(ctx, params) + proto, err := srvcs.MessageForSend(ctx, params) assert.NoError(t, err) - assert.Equal(t, *msgCid, c) + assert.True(t, mm.Matches(proto)) }) t.Run("set-nonce", func(t *testing.T) { @@ -220,26 +183,12 @@ func TestSendService(t *testing.T) { params.Nonce = &n mm := MessageMatcher(params) - srvcs, mockApi := setupMockSrvcs(t) + srvcs, _ := setupMockSrvcs(t) defer srvcs.Close() //nolint:errcheck - _, _ = mm, mockApi - var sm *types.SignedMessage - gomock.InOrder( - mockApi.EXPECT().WalletBalance(ctxM, a1).Return(types.NewInt(balance), nil), - mockApi.EXPECT().WalletSignMessage(ctxM, a1, mm).DoAndReturn( - func(_ context.Context, _ address.Address, msg *types.Message) (*types.SignedMessage, error) { - sm = fakeSign(msg) - - // now we expect MpoolPush with that SignedMessage - mockApi.EXPECT().MpoolPush(ctxM, sm).Return(sm.Cid(), nil) - return sm, nil - }), - ) - - c, err := srvcs.Send(ctx, params) + proto, err := srvcs.MessageForSend(ctx, params) assert.NoError(t, err) - assert.Equal(t, sm.Cid(), c) + assert.True(t, mm.Matches(proto)) }) t.Run("gas-params", func(t *testing.T) { @@ -251,16 +200,14 @@ func TestSendService(t *testing.T) { gp := big.NewInt(10) params.GasPremium = &gp - srvcs, mockApi := setupMockSrvcs(t) - defer srvcs.Close() //nolint:errcheck - msgCid, sign := makeMessageSigner() - gomock.InOrder( - mockApi.EXPECT().WalletBalance(ctxM, params.From).Return(types.NewInt(balance), nil), - mockApi.EXPECT().MpoolPushMessage(ctxM, MessageMatcher(params), nil).DoAndReturn(sign), - ) + mm := MessageMatcher(params) - c, err := srvcs.Send(ctx, params) + srvcs, _ := setupMockSrvcs(t) + defer srvcs.Close() //nolint:errcheck + + proto, err := srvcs.MessageForSend(ctx, params) assert.NoError(t, err) - assert.Equal(t, *msgCid, c) + assert.True(t, mm.Matches(proto)) + }) } diff --git a/cli/servicesmock_test.go b/cli/servicesmock_test.go index 48f1a95ec..4d1f589cd 100644 --- a/cli/servicesmock_test.go +++ b/cli/servicesmock_test.go @@ -8,8 +8,10 @@ import ( context "context" go_address "github.com/filecoin-project/go-address" abi "github.com/filecoin-project/go-state-types/abi" + big "github.com/filecoin-project/go-state-types/big" + api "github.com/filecoin-project/lotus/api" + types "github.com/filecoin-project/lotus/chain/types" gomock "github.com/golang/mock/gomock" - go_cid "github.com/ipfs/go-cid" reflect "reflect" ) @@ -65,17 +67,63 @@ func (mr *MockServicesAPIMockRecorder) DecodeTypedParamsFromJSON(arg0, arg1, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeTypedParamsFromJSON", reflect.TypeOf((*MockServicesAPI)(nil).DecodeTypedParamsFromJSON), arg0, arg1, arg2, arg3) } -// Send mocks base method -func (m *MockServicesAPI) Send(arg0 context.Context, arg1 SendParams) (go_cid.Cid, error) { +// GetBaseFee mocks base method +func (m *MockServicesAPI) GetBaseFee(arg0 context.Context) (big.Int, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Send", arg0, arg1) - ret0, _ := ret[0].(go_cid.Cid) + ret := m.ctrl.Call(m, "GetBaseFee", arg0) + ret0, _ := ret[0].(big.Int) ret1, _ := ret[1].(error) return ret0, ret1 } -// Send indicates an expected call of Send -func (mr *MockServicesAPIMockRecorder) Send(arg0, arg1 interface{}) *gomock.Call { +// GetBaseFee indicates an expected call of GetBaseFee +func (mr *MockServicesAPIMockRecorder) GetBaseFee(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockServicesAPI)(nil).Send), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseFee", reflect.TypeOf((*MockServicesAPI)(nil).GetBaseFee), arg0) +} + +// MessageForSend mocks base method +func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*types.Message, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MessageForSend", arg0, arg1) + ret0, _ := ret[0].(*types.Message) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MessageForSend indicates an expected call of MessageForSend +func (mr *MockServicesAPIMockRecorder) MessageForSend(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageForSend", reflect.TypeOf((*MockServicesAPI)(nil).MessageForSend), arg0, arg1) +} + +// PublishMessage mocks base method +func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *types.Message, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PublishMessage", arg0, arg1, arg2) + ret0, _ := ret[0].(*types.SignedMessage) + ret1, _ := ret[1].([][]api.MessageCheckStatus) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// PublishMessage indicates an expected call of PublishMessage +func (mr *MockServicesAPIMockRecorder) PublishMessage(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublishMessage", reflect.TypeOf((*MockServicesAPI)(nil).PublishMessage), arg0, arg1, arg2) +} + +// RunChecksForPrototype mocks base method +func (m *MockServicesAPI) RunChecksForPrototype(arg0 context.Context, arg1 *types.Message) ([][]api.MessageCheckStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunChecksForPrototype", arg0, arg1) + ret0, _ := ret[0].([][]api.MessageCheckStatus) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RunChecksForPrototype indicates an expected call of RunChecksForPrototype +func (mr *MockServicesAPIMockRecorder) RunChecksForPrototype(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunChecksForPrototype", reflect.TypeOf((*MockServicesAPI)(nil).RunChecksForPrototype), arg0, arg1) } diff --git a/go.mod b/go.mod index 07afe91d3..e1fe8c764 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/BurntSushi/toml v0.3.1 github.com/GeertJohan/go.rice v1.0.0 github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee + github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921 @@ -50,6 +51,7 @@ require ( github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 + github.com/gdamore/tcell/v2 v2.2.0 github.com/go-kit/kit v0.10.0 github.com/go-ole/go-ole v1.2.4 // indirect github.com/golang/mock v1.4.4 @@ -86,7 +88,7 @@ require ( github.com/ipfs/go-ipfs-util v0.0.2 github.com/ipfs/go-ipld-cbor v0.0.5 github.com/ipfs/go-ipld-format v0.2.0 - github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 + github.com/ipfs/go-log/v2 v2.1.2 github.com/ipfs/go-merkledag v0.3.2 github.com/ipfs/go-metrics-interface v0.0.1 github.com/ipfs/go-metrics-prometheus v0.0.2 diff --git a/go.sum b/go.sum index 54c5da743..bfb498886 100644 --- a/go.sum +++ b/go.sum @@ -45,6 +45,8 @@ github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETF github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7 h1:oaKenk0p5Pg7k2YRflJtiai4weJN+VsABO3zSaUVU6w= +github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7/go.mod h1:WUmMvh9wMtqj1Xhf1hf3kp9RvL+y6odtdYxpyZjb90U= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -330,6 +332,10 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 h1:EzDjxMg43q1tA2c0MV3tNbaontnHLplHyFF6M5KiVP0= github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1/go.mod h1:0eHX/BVySxPc6SE2mZRoppGq7qcEagxdmQnA3dzork8= +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.2.0 h1:vSyEgKwraXPSOkvCk7IwOSyX+Pv3V2cV9CikJMXg4U4= +github.com/gdamore/tcell/v2 v2.2.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= @@ -672,8 +678,9 @@ github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBW github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= -github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 h1:3bijxqzQ1O9yg7gd7Aqk80oaEvsJ+uXw0zSvi2qR3Jw= github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= +github.com/ipfs/go-log/v2 v2.1.2 h1:a0dRiL098zY23vay1h3dimx6y94XchEUyt5h0l4VvQU= +github.com/ipfs/go-log/v2 v2.1.2/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA= github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= @@ -1110,6 +1117,8 @@ github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdf github.com/lucas-clemente/quic-go v0.16.0/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE= github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys= github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg= +github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/iostat v1.1.0/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= @@ -1143,8 +1152,9 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-xmlrpc v0.0.3/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -1369,6 +1379,8 @@ github.com/raulk/go-watchdog v1.0.1/go.mod h1:lzSbAl5sh4rtI8tYHU01BWIDzgzqaQLj6R github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1814,6 +1826,8 @@ golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From e2d0047a2aef2f209bf1af0518d7201e7c5f37b1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 27 Mar 2021 16:35:46 +0200 Subject: [PATCH 67/97] introduce message prototypes This introduces message prototypes to applicable API endpoints, which allows us to invert control of message sending and give the user a chance to intervene with an interactive ui. Signed-off-by: Jakub Sztandera --- api/api_full.go | 31 ++- api/mocks/mock_full.go | 48 ++-- api/proxy_gen.go | 96 ++++---- api/types.go | 7 + api/v0api/v1_wrapper.go | 127 ++++++++++ build/openrpc/full.json.gz | Bin 22681 -> 23192 bytes build/openrpc/miner.json.gz | Bin 7849 -> 7847 bytes build/openrpc/worker.json.gz | Bin 2579 -> 2579 bytes cli/init_test.go | 9 + cli/multisig.go | 247 ++++++++++++++----- cli/send.go | 4 +- cli/sending_ui.go | 18 +- cli/services.go | 60 +++-- cli/services_send_test.go | 37 +-- cli/servicesmock_test.go | 22 +- cmd/lotus-gateway/endtoend_test.go | 35 ++- cmd/lotus-shed/verifreg.go | 19 +- documentation/en/api-v0-methods.md | 48 ---- documentation/en/api-v1-unstable-methods.md | 252 +++++++++++++++++++- node/impl/full/multisig.go | 115 ++++----- 20 files changed, 852 insertions(+), 323 deletions(-) create mode 100644 cli/init_test.go diff --git a/api/api_full.go b/api/api_full.go index 148f4ac92..e8e8dcb2e 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -586,15 +586,16 @@ type FullNode interface { // MsigCreate creates a multisig wallet // It takes the following params: , , //, , - MsigCreate(context.Context, uint64, []address.Address, abi.ChainEpoch, types.BigInt, address.Address, types.BigInt) (cid.Cid, error) //perm:sign + MsigCreate(context.Context, uint64, []address.Address, abi.ChainEpoch, types.BigInt, address.Address, types.BigInt) (*MessagePrototype, error) //perm:sign + // MsigPropose proposes a multisig message // It takes the following params: , , , // , , - MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) //perm:sign + MsigPropose(context.Context, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (*MessagePrototype, error) //perm:sign // MsigApprove approves a previously-proposed multisig message by transaction ID // It takes the following params: , - MsigApprove(context.Context, address.Address, uint64, address.Address) (cid.Cid, error) //perm:sign + MsigApprove(context.Context, address.Address, uint64, address.Address) (*MessagePrototype, error) //perm:sign // MsigApproveTxnHash approves a previously-proposed multisig message, specified // using both transaction ID and a hash of the parameters used in the @@ -602,43 +603,49 @@ type FullNode interface { // exactly the transaction you think you are. // It takes the following params: , , , , , // , , - MsigApproveTxnHash(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) //perm:sign + MsigApproveTxnHash(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (*MessagePrototype, error) //perm:sign // MsigCancel cancels a previously-proposed multisig message // It takes the following params: , , , , // , , - MsigCancel(context.Context, address.Address, uint64, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) //perm:sign + MsigCancel(context.Context, address.Address, uint64, address.Address, types.BigInt, address.Address, uint64, []byte) (*MessagePrototype, error) //perm:sign + // MsigAddPropose proposes adding a signer in the multisig // It takes the following params: , , // , - MsigAddPropose(context.Context, address.Address, address.Address, address.Address, bool) (cid.Cid, error) //perm:sign + MsigAddPropose(context.Context, address.Address, address.Address, address.Address, bool) (*MessagePrototype, error) //perm:sign + // MsigAddApprove approves a previously proposed AddSigner message // It takes the following params: , , , // , , - MsigAddApprove(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, bool) (cid.Cid, error) //perm:sign + MsigAddApprove(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, bool) (*MessagePrototype, error) //perm:sign + // MsigAddCancel cancels a previously proposed AddSigner message // It takes the following params: , , , // , - MsigAddCancel(context.Context, address.Address, address.Address, uint64, address.Address, bool) (cid.Cid, error) //perm:sign + MsigAddCancel(context.Context, address.Address, address.Address, uint64, address.Address, bool) (*MessagePrototype, error) //perm:sign + // MsigSwapPropose proposes swapping 2 signers in the multisig // It takes the following params: , , // , - MsigSwapPropose(context.Context, address.Address, address.Address, address.Address, address.Address) (cid.Cid, error) //perm:sign + MsigSwapPropose(context.Context, address.Address, address.Address, address.Address, address.Address) (*MessagePrototype, error) //perm:sign + // MsigSwapApprove approves a previously proposed SwapSigner // It takes the following params: , , , // , , - MsigSwapApprove(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, address.Address) (cid.Cid, error) //perm:sign + MsigSwapApprove(context.Context, address.Address, address.Address, uint64, address.Address, address.Address, address.Address) (*MessagePrototype, error) //perm:sign + // MsigSwapCancel cancels a previously proposed SwapSigner message // It takes the following params: , , , // , - MsigSwapCancel(context.Context, address.Address, address.Address, uint64, address.Address, address.Address) (cid.Cid, error) //perm:sign + MsigSwapCancel(context.Context, address.Address, address.Address, uint64, address.Address, address.Address) (*MessagePrototype, error) //perm:sign // MsigRemoveSigner proposes the removal of a signer from the multisig. // It accepts the multisig to make the change on, the proposer address to // send the message from, the address to be removed, and a boolean // indicating whether or not the signing threshold should be lowered by one // along with the address removal. - MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error) //perm:sign + MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (*MessagePrototype, error) //perm:sign // MarketAddBalance adds funds to the market actor MarketAddBalance(ctx context.Context, wallet, addr address.Address, amt types.BigInt) (cid.Cid, error) //perm:sign diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index ee89a1d23..a14336537 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1262,10 +1262,10 @@ func (mr *MockFullNodeMockRecorder) MpoolSub(arg0 interface{}) *gomock.Call { } // MsigAddApprove mocks base method -func (m *MockFullNode) MsigAddApprove(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5 address.Address, arg6 bool) (cid.Cid, error) { +func (m *MockFullNode) MsigAddApprove(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5 address.Address, arg6 bool) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigAddApprove", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1277,10 +1277,10 @@ func (mr *MockFullNodeMockRecorder) MsigAddApprove(arg0, arg1, arg2, arg3, arg4, } // MsigAddCancel mocks base method -func (m *MockFullNode) MsigAddCancel(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4 address.Address, arg5 bool) (cid.Cid, error) { +func (m *MockFullNode) MsigAddCancel(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4 address.Address, arg5 bool) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigAddCancel", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1292,10 +1292,10 @@ func (mr *MockFullNodeMockRecorder) MsigAddCancel(arg0, arg1, arg2, arg3, arg4, } // MsigAddPropose mocks base method -func (m *MockFullNode) MsigAddPropose(arg0 context.Context, arg1, arg2, arg3 address.Address, arg4 bool) (cid.Cid, error) { +func (m *MockFullNode) MsigAddPropose(arg0 context.Context, arg1, arg2, arg3 address.Address, arg4 bool) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigAddPropose", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1307,10 +1307,10 @@ func (mr *MockFullNodeMockRecorder) MsigAddPropose(arg0, arg1, arg2, arg3, arg4 } // MsigApprove mocks base method -func (m *MockFullNode) MsigApprove(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3 address.Address) (cid.Cid, error) { +func (m *MockFullNode) MsigApprove(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3 address.Address) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigApprove", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1322,10 +1322,10 @@ func (mr *MockFullNodeMockRecorder) MsigApprove(arg0, arg1, arg2, arg3 interface } // MsigApproveTxnHash mocks base method -func (m *MockFullNode) MsigApproveTxnHash(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3, arg4 address.Address, arg5 big.Int, arg6 address.Address, arg7 uint64, arg8 []byte) (cid.Cid, error) { +func (m *MockFullNode) MsigApproveTxnHash(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3, arg4 address.Address, arg5 big.Int, arg6 address.Address, arg7 uint64, arg8 []byte) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigApproveTxnHash", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1337,10 +1337,10 @@ func (mr *MockFullNodeMockRecorder) MsigApproveTxnHash(arg0, arg1, arg2, arg3, a } // MsigCancel mocks base method -func (m *MockFullNode) MsigCancel(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3 address.Address, arg4 big.Int, arg5 address.Address, arg6 uint64, arg7 []byte) (cid.Cid, error) { +func (m *MockFullNode) MsigCancel(arg0 context.Context, arg1 address.Address, arg2 uint64, arg3 address.Address, arg4 big.Int, arg5 address.Address, arg6 uint64, arg7 []byte) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigCancel", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1352,10 +1352,10 @@ func (mr *MockFullNodeMockRecorder) MsigCancel(arg0, arg1, arg2, arg3, arg4, arg } // MsigCreate mocks base method -func (m *MockFullNode) MsigCreate(arg0 context.Context, arg1 uint64, arg2 []address.Address, arg3 abi.ChainEpoch, arg4 big.Int, arg5 address.Address, arg6 big.Int) (cid.Cid, error) { +func (m *MockFullNode) MsigCreate(arg0 context.Context, arg1 uint64, arg2 []address.Address, arg3 abi.ChainEpoch, arg4 big.Int, arg5 address.Address, arg6 big.Int) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigCreate", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1427,10 +1427,10 @@ func (mr *MockFullNodeMockRecorder) MsigGetVestingSchedule(arg0, arg1, arg2 inte } // MsigPropose mocks base method -func (m *MockFullNode) MsigPropose(arg0 context.Context, arg1, arg2 address.Address, arg3 big.Int, arg4 address.Address, arg5 uint64, arg6 []byte) (cid.Cid, error) { +func (m *MockFullNode) MsigPropose(arg0 context.Context, arg1, arg2 address.Address, arg3 big.Int, arg4 address.Address, arg5 uint64, arg6 []byte) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigPropose", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1442,10 +1442,10 @@ func (mr *MockFullNodeMockRecorder) MsigPropose(arg0, arg1, arg2, arg3, arg4, ar } // MsigRemoveSigner mocks base method -func (m *MockFullNode) MsigRemoveSigner(arg0 context.Context, arg1, arg2, arg3 address.Address, arg4 bool) (cid.Cid, error) { +func (m *MockFullNode) MsigRemoveSigner(arg0 context.Context, arg1, arg2, arg3 address.Address, arg4 bool) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigRemoveSigner", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1457,10 +1457,10 @@ func (mr *MockFullNodeMockRecorder) MsigRemoveSigner(arg0, arg1, arg2, arg3, arg } // MsigSwapApprove mocks base method -func (m *MockFullNode) MsigSwapApprove(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5, arg6 address.Address) (cid.Cid, error) { +func (m *MockFullNode) MsigSwapApprove(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5, arg6 address.Address) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigSwapApprove", arg0, arg1, arg2, arg3, arg4, arg5, arg6) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1472,10 +1472,10 @@ func (mr *MockFullNodeMockRecorder) MsigSwapApprove(arg0, arg1, arg2, arg3, arg4 } // MsigSwapCancel mocks base method -func (m *MockFullNode) MsigSwapCancel(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5 address.Address) (cid.Cid, error) { +func (m *MockFullNode) MsigSwapCancel(arg0 context.Context, arg1, arg2 address.Address, arg3 uint64, arg4, arg5 address.Address) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigSwapCancel", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1487,10 +1487,10 @@ func (mr *MockFullNodeMockRecorder) MsigSwapCancel(arg0, arg1, arg2, arg3, arg4, } // MsigSwapPropose mocks base method -func (m *MockFullNode) MsigSwapPropose(arg0 context.Context, arg1, arg2, arg3, arg4 address.Address) (cid.Cid, error) { +func (m *MockFullNode) MsigSwapPropose(arg0 context.Context, arg1, arg2, arg3, arg4 address.Address) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MsigSwapPropose", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(cid.Cid) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/api/proxy_gen.go b/api/proxy_gen.go index dfb9f3731..f285f1ce6 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -261,19 +261,19 @@ type FullNodeStruct struct { MpoolSub func(p0 context.Context) (<-chan MpoolUpdate, error) `perm:"read"` - MsigAddApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (cid.Cid, error) `perm:"sign"` + MsigAddApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) `perm:"sign"` - MsigAddCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (cid.Cid, error) `perm:"sign"` + MsigAddCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) `perm:"sign"` - MsigAddPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) `perm:"sign"` + MsigAddPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) `perm:"sign"` - MsigApprove func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (cid.Cid, error) `perm:"sign"` + MsigApprove func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigApproveTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (cid.Cid, error) `perm:"sign"` + MsigApproveTxnHash func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigCancel func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (cid.Cid, error) `perm:"sign"` + MsigCancel func(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigCreate func(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (cid.Cid, error) `perm:"sign"` + MsigCreate func(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) `perm:"sign"` MsigGetAvailableBalance func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) `perm:"read"` @@ -283,15 +283,15 @@ type FullNodeStruct struct { MsigGetVestingSchedule func(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (MsigVesting, error) `perm:"read"` - MsigPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (cid.Cid, error) `perm:"sign"` + MsigPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) `perm:"sign"` - MsigRemoveSigner func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) `perm:"sign"` + MsigRemoveSigner func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) `perm:"sign"` - MsigSwapApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (cid.Cid, error) `perm:"sign"` + MsigSwapApprove func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigSwapCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (cid.Cid, error) `perm:"sign"` + MsigSwapCancel func(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) `perm:"sign"` - MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) `perm:"sign"` + MsigSwapPropose func(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) `perm:"sign"` NodeStatus func(p0 context.Context, p1 bool) (NodeStatus, error) `perm:"read"` @@ -1619,60 +1619,60 @@ func (s *FullNodeStub) MpoolSub(p0 context.Context) (<-chan MpoolUpdate, error) return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (cid.Cid, error) { +func (s *FullNodeStruct) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) { return s.Internal.MsigAddApprove(p0, p1, p2, p3, p4, p5, p6) } -func (s *FullNodeStub) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigAddApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 bool) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (cid.Cid, error) { +func (s *FullNodeStruct) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) { return s.Internal.MsigAddCancel(p0, p1, p2, p3, p4, p5) } -func (s *FullNodeStub) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigAddCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 bool) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) { +func (s *FullNodeStruct) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) { return s.Internal.MsigAddPropose(p0, p1, p2, p3, p4) } -func (s *FullNodeStub) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigAddPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (cid.Cid, error) { +func (s *FullNodeStruct) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) { return s.Internal.MsigApprove(p0, p1, p2, p3) } -func (s *FullNodeStub) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigApprove(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (cid.Cid, error) { +func (s *FullNodeStruct) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) { return s.Internal.MsigApproveTxnHash(p0, p1, p2, p3, p4, p5, p6, p7, p8) } -func (s *FullNodeStub) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigApproveTxnHash(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 address.Address, p5 types.BigInt, p6 address.Address, p7 uint64, p8 []byte) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (cid.Cid, error) { +func (s *FullNodeStruct) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) { return s.Internal.MsigCancel(p0, p1, p2, p3, p4, p5, p6, p7) } -func (s *FullNodeStub) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigCancel(p0 context.Context, p1 address.Address, p2 uint64, p3 address.Address, p4 types.BigInt, p5 address.Address, p6 uint64, p7 []byte) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (cid.Cid, error) { +func (s *FullNodeStruct) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) { return s.Internal.MsigCreate(p0, p1, p2, p3, p4, p5, p6) } -func (s *FullNodeStub) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigCreate(p0 context.Context, p1 uint64, p2 []address.Address, p3 abi.ChainEpoch, p4 types.BigInt, p5 address.Address, p6 types.BigInt) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } func (s *FullNodeStruct) MsigGetAvailableBalance(p0 context.Context, p1 address.Address, p2 types.TipSetKey) (types.BigInt, error) { @@ -1707,44 +1707,44 @@ func (s *FullNodeStub) MsigGetVestingSchedule(p0 context.Context, p1 address.Add return *new(MsigVesting), xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (cid.Cid, error) { +func (s *FullNodeStruct) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) { return s.Internal.MsigPropose(p0, p1, p2, p3, p4, p5, p6) } -func (s *FullNodeStub) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 types.BigInt, p4 address.Address, p5 uint64, p6 []byte) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) { +func (s *FullNodeStruct) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) { return s.Internal.MsigRemoveSigner(p0, p1, p2, p3, p4) } -func (s *FullNodeStub) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigRemoveSigner(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 bool) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (cid.Cid, error) { +func (s *FullNodeStruct) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) { return s.Internal.MsigSwapApprove(p0, p1, p2, p3, p4, p5, p6) } -func (s *FullNodeStub) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigSwapApprove(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address, p6 address.Address) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (cid.Cid, error) { +func (s *FullNodeStruct) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) { return s.Internal.MsigSwapCancel(p0, p1, p2, p3, p4, p5) } -func (s *FullNodeStub) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigSwapCancel(p0 context.Context, p1 address.Address, p2 address.Address, p3 uint64, p4 address.Address, p5 address.Address) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } -func (s *FullNodeStruct) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) { +func (s *FullNodeStruct) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) { return s.Internal.MsigSwapPropose(p0, p1, p2, p3, p4) } -func (s *FullNodeStub) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (cid.Cid, error) { - return *new(cid.Cid), xerrors.New("method not supported") +func (s *FullNodeStub) MsigSwapPropose(p0 context.Context, p1 address.Address, p2 address.Address, p3 address.Address, p4 address.Address) (*MessagePrototype, error) { + return nil, xerrors.New("method not supported") } func (s *FullNodeStruct) NodeStatus(p0 context.Context, p1 bool) (NodeStatus, error) { diff --git a/api/types.go b/api/types.go index ae8bbe958..83de131a2 100644 --- a/api/types.go +++ b/api/types.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "github.com/filecoin-project/lotus/chain/types" + datatransfer "github.com/filecoin-project/go-data-transfer" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" @@ -169,3 +171,8 @@ type MessageCheckStatus struct { Cid cid.Cid CheckStatus } + +type MessagePrototype struct { + Message types.Message + ValidNonce bool +} diff --git a/api/v0api/v1_wrapper.go b/api/v0api/v1_wrapper.go index e977c6b67..ff4474fe5 100644 --- a/api/v0api/v1_wrapper.go +++ b/api/v0api/v1_wrapper.go @@ -3,7 +3,9 @@ package v0api import ( "context" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/chain/types" + "golang.org/x/xerrors" "github.com/ipfs/go-cid" @@ -57,4 +59,129 @@ func (w *WrapperV1Full) Version(ctx context.Context) (api.APIVersion, error) { return ver, nil } +func (w *WrapperV1Full) executePrototype(ctx context.Context, p *api.MessagePrototype) (cid.Cid, error) { + sm, err := w.FullNode.MpoolPushMessage(ctx, &p.Message, nil) + if err != nil { + return cid.Undef, xerrors.Errorf("pushing message: %w", err) + } + + return sm.Cid(), nil +} +func (w *WrapperV1Full) MsigCreate(ctx context.Context, req uint64, addrs []address.Address, duration abi.ChainEpoch, val types.BigInt, src address.Address, gp types.BigInt) (cid.Cid, error) { + + p, err := w.FullNode.MsigCreate(ctx, req, addrs, duration, val, src, gp) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigPropose(ctx context.Context, msig address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { + + p, err := w.FullNode.MsigPropose(ctx, msig, to, amt, src, method, params) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} +func (w *WrapperV1Full) MsigApprove(ctx context.Context, msig address.Address, txID uint64, src address.Address) (cid.Cid, error) { + + p, err := w.FullNode.MsigApprove(ctx, msig, txID, src) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigApproveTxnHash(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { + p, err := w.FullNode.MsigApproveTxnHash(ctx, msig, txID, proposer, to, amt, src, method, params) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigCancel(ctx context.Context, msig address.Address, txID uint64, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { + p, err := w.FullNode.MsigCancel(ctx, msig, txID, to, amt, src, method, params) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigAddPropose(ctx context.Context, msig address.Address, src address.Address, newAdd address.Address, inc bool) (cid.Cid, error) { + + p, err := w.FullNode.MsigAddPropose(ctx, msig, src, newAdd, inc) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigAddApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, newAdd address.Address, inc bool) (cid.Cid, error) { + + p, err := w.FullNode.MsigAddApprove(ctx, msig, src, txID, proposer, newAdd, inc) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigAddCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, newAdd address.Address, inc bool) (cid.Cid, error) { + + p, err := w.FullNode.MsigAddCancel(ctx, msig, src, txID, newAdd, inc) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigSwapPropose(ctx context.Context, msig address.Address, src address.Address, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { + + p, err := w.FullNode.MsigSwapPropose(ctx, msig, src, oldAdd, newAdd) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigSwapApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { + + p, err := w.FullNode.MsigSwapApprove(ctx, msig, src, txID, proposer, oldAdd, newAdd) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigSwapCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { + + p, err := w.FullNode.MsigSwapCancel(ctx, msig, src, txID, oldAdd, newAdd) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + +func (w *WrapperV1Full) MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error) { + + p, err := w.FullNode.MsigRemoveSigner(ctx, msig, proposer, toRemove, decrease) + if err != nil { + return cid.Undef, xerrors.Errorf("creating prototype: %w", err) + } + + return w.executePrototype(ctx, p) +} + var _ FullNode = &WrapperV1Full{} diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 7763e4af8ba241fea0cb4d20a1fc61790c2646f4..cb781bec9169de220365f7d8cfbac0850a948f70 100644 GIT binary patch literal 23192 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 Npp+;_KoSPZe*qz{wEF-6 literal 22681 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 BZlVAH diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 501b238282ef3d11fb1e356a867206a563054b2b..0d52dac870e8b5a1854f566261221e5616a6839f 100644 GIT binary patch delta 7234 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 Q2LJ&7|C@$z=;D0=07z*TkN^Mx delta 7236 zcmV-K9J}MEJ*hpg6?KWa>N@A@d%~kDS7}s4?gnW*}eZ3mYaW4Y9mNuCD+H2RW$uw zYwK@%7HT5>bYNIA=xo4}2@?Ot|Df_fvZ9fJA}!!{!5Z#!qd- zo4DQmS5=w%Ht`kHV_1K!7(!_Hkfk$4UW&|b;xWKJfFLRo8+L!te|>t=ANG%jx2wSg zC7-$M46|kDe1U(=yRk>_IS`hToCF1>+)Q!%8qG29AF{@D!p0t@vleiB;Pb67$p%w= zjrj9E;OP4jyl|lVc{TrX3U7VpVUnxJ*Sp5M!kgsew<~km!`&L=mS@-1D0-<@GbTn0 zJ-88Z+X{8q=Nu)LGzu;+aPZ%h};P+E2_=;OPWuG#1tC+zX@w5Pe7&gY{ zNGj;UQ`ApQ5tk9IIpNe}9SAL(@`$}bEK|MQTvo@d?e_Y{$S@)K9|oW)`U^Y+Enrjg z&fpz%A>&d%PB2INrIVIA+`G%F9=NW!v};~2u$TON-X37wm`slk;&1<0{pW9KKM4cS)eaOMwUS^p*~;> z$V3(KIgcDZL@tTmk`PSh;45Ye{{9QO`u*nP++b>L}y5BFnV`D&<$M`le@ zz$o={Rh($OJheu-;iw&Plp;!(oFcLnpvs_H7f)rAZkTE}Om(V=4`j?y%_x7=7@aS}81k=!FsSljioR5sn3*h)#fVXfV;R#(SR|6?nRl|{8Cwo-qPZkTE} zOx4?DrC&NO_1FbuusjQ`Xs7}+Cbrt=*b;GZtsqUB?oQ&~K|k}CDKq2DNm}RF0v=}w z9}(sQ2z+GEj%1lBCUg0r2n$mxkOgo=tqm?2z&@B!&p{TLJ^~2-kjaZJ0Kqx6kO^LV zU0`zoFtM;Hf>RJUOPgVnzde6)p?Qa_s1?rBFGW}gf)BjZ0{V+~auxNP@k)d*XOF$< zKhQ+5|JAn^$B%v3KW5Xue}^3RKlI_h?(&g3?tiAv3_Z^JgBk8GACH$yH0a$AjHj}p znvJ3HCUEp!pktFec7=Ub9yECEZ2j{2_5<|p5aY$P^h`i%EBYkfnT&rmi^R(hma*7-zD zkuasZzH`s*mEuDUYgYMO69zq2;DZH{HTJNH#>k6m7Hc)M%>)Z)Bz|Tkf=n1>s303& zL7#neEe>!+#wb(Xihe&C8~O|F^63#C{xaQ3e^W?A3LLiH*wnnUE1`UflnU}A3sD2_ zvxsg9{8t9yI`Ze5iaHDQ2V_R3jI0Bbn8A~v2Ood0HwBo|yiiYwX*1pWIe3kF!@q~a zzsH+mS#eINb%`*CaGe~SO}=5_17hJ#F_jc6)Q`YAYm(L2x53|oW!{rbeq)Rg>R$fL zC0!#ED;sDS`EVF{sOiqtyV#mTxJD*ckZ)M{a9H>>$Cs-yjF&}Cy&)pUeYjh-2MNB1_7G#hGW!hok%L|c| z;UeU<#ov7$ZL^{2{4M6H%D@zlIREmlg0QdAo)|F4_lT^XUtS1Crfh*cF@oB*sVF|x z_Rd|{%J%hOQuUH1y1k43uatgz|Ifoezy5#u?|by~|1tOd@tN;^{N;`L>EY)$?i*aNW3$r96??j+`^Up6MJz&!=zGM@{JRR+Bnf0M`TZ0et>B1~?FpWf z$XY$~@A4J`*BaQ$@?TCDQy(V3yv|O4UQ(ocV3T*EW2_eGpyGtXRh~b422dQ(%9BC2N)1UEMMYA5O9{<>NU6druMGh+s2XVyKMp zaY_oP_rf9pG8V!2HS*g5`iu^y(LKA6-kF3lN9)Za3*q}C@x^f$D?lH^M+XrmzQ8@S zIRK{~UJ}zo&_@EDi(zewyjDh>s(HP1y7;JzVqM|`mBgb15oXn~e^N$*EO~zf?b2EN zS%hj!8fH%-Dml}WRUlPnPWYo(_`{o5Ehy=;->9-${@54FD%-8>&Es!@xiK?-) zw-}tG{F8mxjs}Y=>75I_wgrDeoN!}tm#DZMauJ{9jPYF2dFhT_(Rn7#AW^Cvvju!YJCpw{w%>C#+ea_!-72pFo)P>f-1>80;A8r64w zr3p5BznZ;Y88w@|U(MdH>Vd!mgL>5wjVn~WBqrss#VX`GDXFR8Ke>N+B4`!U1?hIS zq!koBRUaRoQF6Qb;>@z%Es5oyW2-oFp4~%i!>NtlQ7?+VX1f6^1(Tv8Up=#BfR^+X zFePQ31!(Kqr|+Q8az3w^!)xRt?_L_Q#d@>JN&A2pv>=0H$QIcJqGRPVHLJ2-W2!W! z%6^zCAt#ms!SQlxV}E~9s_r$K73BUf2XgDYa}B?WJj((#C}qXF6*}J$6e}F+#Nu|b zL)nYkdNJEok84rUNJ9|xyUd5`tSdkA+sTU9#ktOR=~~22{k?wxJ6Cn;@ycSXVSk{C zn99msUqlWenO740sc-M@<~&i2E@v3NI(4>d0*As_^g5mIA&h@5<9z9Pg;(bT^Uy@N zlzFH-(knEF=Hu9-jG9zoscteg$MDgnu-r`@-yv(Eni+Q)+(28z*Ad`TatWb`$8m+o zoN4OH-)A!)u_BQgy?Vq~m!EcFr6?{(7d z0$`O_`UVc&7>+# zpnV7cX;~UcLD%~D894IIm{HGxOzs>*M+`e?gL!fwX{mpzyE+235q_t7bX%HQri#XM^uZBUqezt>Xp#Q3Os=9J1g2kG4%1&~3$_izWwaZi&z}M(+ z{Sc*QN9>{Rh;0z!s&}foKkEY~n9Ses*)IhPQFVXCu6b#4++cTtSZ=63PHa1S@?g;5W+gGza9n9<1AwNSIi`#bLO&P0kd-=y&(vy2vM$OV=<1|2NC2<*9{rpWsa zFsOf^DN%maXT9-qON9kd*JBuZXkpPgfiGkv)g?lhT- zm*iyi>|`L1urK zScYABylx--lq~mF3c|+4wN(50NnWc)K2h%ud6GnJIa%vt)s%)wv{ywT7ji?G(K(U- zKg#%5_ibz&PSlc$~*jYkV|S_jxGH*Pj- z#uYT8>YhZY3*I3nva`ynLs7qwE-bD2!lrg|Tyvw^8r7Dk+8U5;KsEqb)g7Jza$y2( z{7)6nI*ZVbV0XRE27ep;ZSc3j-+jVg)djQ?!amUZ@Fw^Q;2Wc|8{|zk*uZ~o1G^3E zHn6)tu-osbE*F;gb&X)ntU09p!Q z<8tdYvJv!=+%1OEnI}cmK!Vb4?C*4=jSb#tfxXZI4Foq396)e)i)iD)vSN=WIr0ayg}r>LF6>6S4*cYatKH2$c?i3s*Tm(s2mNI zqq@?69i-DzQby(UN~xT&N9n9CZPRPCO`~o0QQHVEZxl_VXbw}+^fxZ+PQ)2Rs2=xb zMX02@L3KJgiQY7tr_nrnsCiK1>NFbY&^6Ft;|g~Is;hcCD?+6-kVJo?b8PuFs-MR3 zY1GeNs-G3NYjR1n2wB_lE|Xg66=b|sUS-FLY=FyAA9Hu=`q(JiyHhOy*}(>ZMOu)jZdx1G1aJl^Uly`zu24yvDtQQ8> z(s6x&17g6J?*X4+YiT17_zW^s+i5!A?~<+97ftml`A!1>)sQ8-RL-8+Q@J=%EKo@x ze2>`M2N#(PS)92pmL3ckqKuGipqPEu8p951heh&5Wix%1%xr(C1?GtP;+8-Zyal{V z;-oeRd{GrJDg>+Yk!^{=rti#Jd%K+*kYcwpm%hzuPZ?cxZFs~0V&ahvAFFY9M5a!b zjuXw{WI1)$$f5UDSXvUOm6aoso6PoALGk_Wk;Lu5z?gZ|i55x0B?A^kKKUO8 z81fuUAQKo($&tB$n6!XALD+=k3z;HdQG(=c$FiN9uu^{;h$gv10Eq<%T@mEZYn!qHn)u1wYd1Wah#$O1y;NNWuf@YZqJBcLAm1Thnr zG09l3|AjCLn1|t)vEhC&!Pyulh}?tvwg}{F$qYh-43*Tepv|*NT)*6ZWu_Q`$ zW91qzzO$Epxf9E?2q`YS*1Wt)K~YPlL}7p(2U!?0WOLJwSbj`;5O8GHiwr=7`bh{B zvVse5B0+0^Au{XC2b>uIo~^1w5%BO4VU?rLKdG*f`soM1Gc(xU5(;FY8!z`&Bvsc9 zuCage7!j+Qg}6>ib*WmB>!XyEwZt3-B6$DDu&zB-O zBYKh)lFSw#{t`gj=8n=l3PSj`GW&1YW?6q`k$k5A-0k&S-QM7Ke>NrVDXXgP3ykr_ zC7B2Zxhl(sbW}di=iA7YXQ(PO#~M?+LV~elu>lu)44X?EdQnWECO;)Hwvto=W1zsDpovW|bt6*UwfO4s<_Drn=>93TNJ~{5RqO0q~??{dvGdu7XE*&=0DY`#4G)+D;zFPTZXhkM#k6^SuRG)IH!(t zVT`!ZA%9$lKjbk~Bjex1?)*7(Fp|~{CT`fi3it+c>*;Dy_Ru9!=%07`(8nxz4g5HEo^@+5FJ4*UnxTXaWxzzAik1(Q{@YWzFqsLqt03fQskO;YO{ zp@M1C`Rs8H(kZ{jG(|r^#Pxrvjg$f7m$OQK#l*^_souEB;lM#YgN~~=@*5AD8+U*z z?U9vO>9mu?mZIDlz{NL11ortKQ%s=u_?DPdL`g@+6#D3R5bNfub_7{^=3RtNTd)QL z6fVF70Y>Xk53qf^a+t*!fm+Mt#!xvO*pOqIFB*1ecsymRr(&pbXy$*u>Nd~Nf(}fn zD)zo4q4u?7k)T^ln67OfkyRV@!7qTxjB>NSu0U-@uQR>ybQvAfG_*oT-Cifhsjfk) z4vnEz*CPgO^U<}crK$uJ3i+>zIIc$G%Bqc>9HjEh9nzwaOQdv zIvh@q(5&A%8dxXjXxc~S(J4Icoy-PP_!wYx z);&54y3oYzQ3O}d?jg3}l=u9!WxSgte_taXdH1|8r{ZIZ|92Z>XVSLMf$Ep9{_=Znv`S$ZkQ!A^JZJDiK09Q4*)lI~VqlH~Mkt@Hgs z|Mlrff7m}B)-a6pp|xq2(lly=ZB%vIvss*+k(5)?bg6%m75d{D6^E5+)KlH@70kf} zGHvLg;K08Mke?uw<3|MN@g|eN6DXh33WFd|LdVBg?@AotIjOCKycnV$nrQhGrMDyK z!MOI3n2Liii&?Te%QLT0Lp&pWJ=mk1mLJZxR^4+I0+UDG8jv=kQZvkIqHDbQo!ZU~ ztTwQ^Q&@j(Fzu?}Q{9{tz`;9Atm>GyqLRP%7`rb?VJ!C3A%xI=j0FvXm9?UwgG5(% zYFakXqI0Xz^MIC(2>C1{Ec;}5s)c+*R3)S6bBnEvJuLg1Ri_E0RMUf-mtZrQtAGaN z^>gj{m*7R|kay6)POjV77fU@08#E%V%@Xm!M00Y2->Vt26UW` z-oje&OIDH9^^k3?shwRYyf1EfF37BXRo!C`7-Z7RttUgbWn>&HP!QZzTeQQzHYvar zQ-GU$L2JR-&B4teJ-#%y^7=Z#%|t%Frt5zb(Ej1$n(KQRnw<`Z1ak-Cx+-rjld^o{ zJy!Kz0}|VIU|&or)eVNOy43ixjW65yvMZexWC(nZw1GWE$iC`Mxlh!)L*6+hgbP3! zfJ{)RwC#MIlG^r4lmrafw!CKDZvA*SOcW*{r^M7FONpCYgWX z7dkC>#OA8Hdii9z#8&&`$7rojLSs}jPhlsT*xFi3PA4;$R`ToU=y)_6TBoP5cXT{F zetk6P4o{DUvl%=(ouOH0HtZZDgrY=-@EpmQUQaD<28jX2$#G}cox;x1ad&DR4PL|7 zM^m#iJc6^%>sigci@iZ_4IzvIG-p#AE;^NO>$IL7T2*W%Yp@(E&Qj5;ES z;xS`n^g6xHQMY^4?cH=vN4?&tbMmLrGJd8$3qJdT`pnqMvg@jmj3|%%j0LF43iTc= z=D>$bNw9m|G8{}|zt)p2R`atrMqfI>6I+XpxIzEiy!{+p#LVA}8TA~<+1h`C3>`7- zpseEwrI3_3L-D{|%D(F6Ly3(O23Zw#BkJqun{`kY74`H@*v1YNPG&E26ppG+eG2EB zA#&^zXeuFF#8v3hGXr5n&Uo?YV-woOsCP0Lo}8R^j!y@J7JoJQ3SDE=?G1;6(@u9d z9CpOxRb-~u9h?k?{o}!~Wt@MHKl;Y#c+fK5i@!b_@csBB==S?3#w{0;w**e@l|Xzz z&PN|vA21QBC;SN?#LxHcQigN_xi}ZUjZx<*N97br-V11I9Yz~=J}Z?k>TG77UoaDO zsldei6&bCV6`irZ4@&B%6eie6D+(CrBFzet8|zYZaemY6K*X-QTum6+TP-m(o}O-> S{(k@f0RR7}{E`;peE|UZq(4jm diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index dcb410ce0056baaef6a154a896b1246547580e6a..5f024f24c2b314b106ed981c82c1a08907ed8b3e 100644 GIT binary patch literal 2579 zcmV+u3hebCiwFP!00000|Li?&bJ{xAe?_C`OLNBo0whiO)+gE9%}ig|8pEY|QcC z2`j2xzyth|t3aa5;Td{AyucQ|2O%kEA;i&9t8?hUJ)T?G12yH2#Dniom=RBd{z+Z1 zIc2T)jx(fS1GcaO(iYTL2F2~|?Sx;malmZ!MxsByad8LYN{PT$2WSEI#_u4TGX;;T zb!Psxkbi`v;(|<|bp^KK;u6Ld^1JB!4H?naSLPGP0fXR*{sas8Jyi5fKgM-V6gfa| z^hY`-Hnh%&>t38&2!!C`06kr^Yb8Jhf+5+uUL0-=|2G&}UUq~Vf9U#FJYKgz6YW@S=i>ksx6 zJfim*9*O6>U@%^s>!weXfxv^KPN!qx&w)46pWe+aJQSRd|C;%L2dDEJ3lql)$s+Dh za36%C8qb)x5-jW|niiNV07OJA8Ic)6EUZET7N6%!64&>g$1QyH18jxUld6E=`vSww z+`4yXVJ50nT%Z-y_xCQ7%2lbc)1-Rfp`tAJxm?HLPP_r}6hLBtf98KlQDYlWyp z1g%W+`vT@h@9mn!|(k&z2dMPDI;%5fDA(3=tG?m7#;M38T=D_G_{Y1JfTrW7@tuc5T=@+xKxj(U5~HbfY4D~emNv+8a4M5&F}4S**|dyYPgFF za?AOOjZ^pvox&pa+@8m@0e30u^<@RA6xU%1#HDP46GTZR!j{eWa2YW!N0-O%p*B&C z8`rpT`{u^=P7AC!WyGaFA=-QqK}|H-9_htjSyx+iX+_2(r>9tb;PS@4F_nWQ#x@u1(ebi>sH@#7Dd zAVJ(~hS3dY!>`i(??d=Mt!d{KMSDrv(}J@j=YGCITm__turiYOA?g|HmJAK6x*)Yi zWrj#CTS)-3=#4t)^^g1c>fjT`CS!yoDyfKh=FK1sBYL$M66>w+O68*pejnw;rO$i}2fOzt(!^O)RyW zvD9;i;Th@JVj*O|(>vWxE#%87>1<-dCN|u+*zmOAeBu&ZAQHYZ38Z=<$t3 zX98D1+%xWZRAss6dHF~{R`d4^Qxf+w%uO-#xr@TPTPxdal`I)QU>kdQcFKbQYcIMT-(@ zOs3F<15D8|O>*+`Nbi3TA|zJsc#YE1inpZJic<`OC*0NwlZ`mgUs4AC(SKH6zGE{-w zq!-l0driFelAYS4f|LEFKbJM%Wu4k?_3BF98o7~Q0`*INXt&8>?wauIg72B-vX|$N zHHN4$M0;n53d(A3&Xl#6A z<6n`D?-o?fq%2~ccykF{FXUXB+ein}3?h&zeY6c+qUBm(nYd%@Q&^qDK9rbPcmC6b^NWs_^> zkr`6t4iG_Oct8?t&K!vpM_P%J2~nUwC?fBWW>hC|H&{pZd)}OF3bj7=j*pAZw2TM# z-f_aKzg1a24P6{)2+AhwI6s4{nEf{vEQHC7a*u!}UkKXdqnodf6$`T3e-U|iZWLBU zHcUVkvLFywYcUxPV;-n^mL1bLx6g_h`pUme+rIC#?J#_5cN^CH71lf{NXFgj;mcRSmv0M#vBjQLeSRf|%MZvr;(yO>{EZBupXTerv?TU?=3MkZhu1-; z(*tB)7Ww7yYYpLzZ>G>^^u3EB<_<^r8$?8Hv^(Yf~6|o?JLBh`mhp zVx1Qw5|DM@ugiNS;<=yc$~_D}MmBX?XVj^YF;CB-xi8S9&P%<8_Y&W4*xR4v+Oq4?CJ3cghMmpNIBOJtW1V{-EqA|7>x=?xn#$0&d zpwSEw^l$TnkQO57j5}bXHxE;4PZ4Df)l<}Cc!8^^t-n3=iG2r7JcmBvFr?su#uCh--Ht`#w>R$l`Rgr;WxLg)`HZzk1&F7rB2O7EDzLO|min>TmK>#8G p5Hn{j5{ZDZ@Py_UneDZuN=`Z_cs{?G|2F^t|NpL25vosm008}t0%8CF delta 2573 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 diff --git a/cli/init_test.go b/cli/init_test.go new file mode 100644 index 000000000..8c343bcfa --- /dev/null +++ b/cli/init_test.go @@ -0,0 +1,9 @@ +package cli + +import ( + logging "github.com/ipfs/go-log/v2" +) + +func init() { + logging.SetLogLevel("watchdog", "ERROR") +} diff --git a/cli/multisig.go b/cli/multisig.go index f6caa6ee0..6ec0dcd5d 100644 --- a/cli/multisig.go +++ b/cli/multisig.go @@ -95,11 +95,13 @@ var msigCreateCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("multisigs must have at least one signer")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) var addrs []address.Address @@ -146,13 +148,20 @@ var msigCreateCmd = &cli.Command{ gp := types.NewInt(1) - msgCid, err := api.MsigCreate(ctx, required, addrs, d, intVal, sendAddr, gp) + proto, err := api.MsigCreate(ctx, required, addrs, d, intVal, sendAddr, gp) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -364,11 +373,13 @@ var msigProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must either pass three or five arguments")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -426,14 +437,21 @@ var msigProposeCmd = &cli.Command{ return fmt.Errorf("actor %s is not a multisig actor", msig) } - msgCid, err := api.MsigPropose(ctx, msig, dest, types.BigInt(value), from, method, params) + proto, err := api.MsigPropose(ctx, msig, dest, types.BigInt(value), from, method, params) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("send proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -481,11 +499,13 @@ var msigApproveCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("usage: msig approve [ ]")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -515,10 +535,17 @@ var msigApproveCmd = &cli.Command{ var msgCid cid.Cid if cctx.Args().Len() == 2 { - msgCid, err = api.MsigApprove(ctx, msig, txid, from) + proto, err := api.MsigApprove(ctx, msig, txid, from) if err != nil { return err } + + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid = sm.Cid() } else { proposer, err := address.NewFromString(cctx.Args().Get(2)) if err != nil { @@ -558,15 +585,22 @@ var msigApproveCmd = &cli.Command{ params = p } - msgCid, err = api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params) + proto, err := api.MsigApproveTxnHash(ctx, msig, txid, proposer, dest, types.BigInt(value), from, method, params) if err != nil { return err } + + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid = sm.Cid() } fmt.Println("sent approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -598,11 +632,13 @@ var msigRemoveProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address and signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -630,14 +666,21 @@ var msigRemoveProposeCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigRemoveSigner(ctx, msig, from, addr, cctx.Bool("decrease-threshold")) + proto, err := api.MsigRemoveSigner(ctx, msig, from, addr, cctx.Bool("decrease-threshold")) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent remove proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -676,11 +719,13 @@ var msigAddProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address and signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -708,14 +753,21 @@ var msigAddProposeCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigAddPropose(ctx, msig, from, addr, cctx.Bool("increase-threshold")) + proto, err := api.MsigAddPropose(ctx, msig, from, addr, cctx.Bool("increase-threshold")) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Fprintln(cctx.App.Writer, "sent add proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -743,11 +795,13 @@ var msigAddApproveCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, transaction id, new signer address, whether to increase threshold")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -790,14 +844,21 @@ var msigAddApproveCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigAddApprove(ctx, msig, from, txid, prop, newAdd, inc) + proto, err := api.MsigAddApprove(ctx, msig, from, txid, prop, newAdd, inc) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent add approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -825,11 +886,13 @@ var msigAddCancelCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, transaction id, new signer address, whether to increase threshold")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -867,14 +930,21 @@ var msigAddCancelCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigAddCancel(ctx, msig, from, txid, newAdd, inc) + proto, err := api.MsigAddCancel(ctx, msig, from, txid, newAdd, inc) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent add cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -902,11 +972,13 @@ var msigSwapProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, old signer address, new signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -939,14 +1011,21 @@ var msigSwapProposeCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigSwapPropose(ctx, msig, from, oldAdd, newAdd) + proto, err := api.MsigSwapPropose(ctx, msig, from, oldAdd, newAdd) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent swap proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -974,11 +1053,13 @@ var msigSwapApproveCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, transaction id, old signer address, new signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1021,14 +1102,21 @@ var msigSwapApproveCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigSwapApprove(ctx, msig, from, txid, prop, oldAdd, newAdd) + proto, err := api.MsigSwapApprove(ctx, msig, from, txid, prop, oldAdd, newAdd) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent swap approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1056,11 +1144,13 @@ var msigSwapCancelCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, transaction id, old signer address, new signer address")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1098,14 +1188,21 @@ var msigSwapCancelCmd = &cli.Command{ from = defaddr } - msgCid, err := api.MsigSwapCancel(ctx, msig, from, txid, oldAdd, newAdd) + proto, err := api.MsigSwapCancel(ctx, msig, from, txid, oldAdd, newAdd) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent swap cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1133,11 +1230,13 @@ var msigLockProposeCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, start epoch, unlock duration, and amount")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1185,14 +1284,21 @@ var msigLockProposeCmd = &cli.Command{ return actErr } - msgCid, err := api.MsigPropose(ctx, msig, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) + proto, err := api.MsigPropose(ctx, msig, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent lock proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1220,11 +1326,13 @@ var msigLockApproveCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, proposer address, tx id, start epoch, unlock duration, and amount")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1282,14 +1390,21 @@ var msigLockApproveCmd = &cli.Command{ return actErr } - msgCid, err := api.MsigApproveTxnHash(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) + proto, err := api.MsigApproveTxnHash(ctx, msig, txid, prop, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent lock approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1317,11 +1432,13 @@ var msigLockCancelCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address, tx id, start epoch, unlock duration, and amount")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1374,14 +1491,21 @@ var msigLockCancelCmd = &cli.Command{ return actErr } - msgCid, err := api.MsigCancel(ctx, msig, txid, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) + proto, err := api.MsigCancel(ctx, msig, txid, msig, big.Zero(), from, uint64(multisig.Methods.LockBalance), params) if err != nil { return err } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent lock cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } @@ -1471,11 +1595,13 @@ var msigProposeThresholdCmd = &cli.Command{ return ShowHelp(cctx, fmt.Errorf("must pass multisig address and new threshold value")) } - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := ReqContext(cctx) msig, err := address.NewFromString(cctx.Args().Get(0)) @@ -1511,14 +1637,21 @@ var msigProposeThresholdCmd = &cli.Command{ return actErr } - msgCid, err := api.MsigPropose(ctx, msig, msig, types.NewInt(0), from, uint64(multisig.Methods.ChangeNumApprovalsThreshold), params) + proto, err := api.MsigPropose(ctx, msig, msig, types.NewInt(0), from, uint64(multisig.Methods.ChangeNumApprovalsThreshold), params) if err != nil { return fmt.Errorf("failed to propose change of threshold: %w", err) } + sm, _, err := srv.PublishMessage(ctx, proto, true) + if err != nil { + return err + } + + msgCid := sm.Cid() + fmt.Println("sent change threshold proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } diff --git a/cli/send.go b/cli/send.go index 4056a2d61..9efed458a 100644 --- a/cli/send.go +++ b/cli/send.go @@ -147,12 +147,12 @@ var sendCmd = &cli.Command{ } msg, checks, err := srv.PublishMessage(ctx, proto, cctx.Bool("force")) if xerrors.Is(err, ErrCheckFailed) { - proto, err = resolveChecks(ctx, srv, cctx.App.Writer, proto, checks, true) + proto, err := resolveChecks(ctx, srv, cctx.App.Writer, proto, checks, true) if err != nil { return xerrors.Errorf("from UI: %w", err) } - msg, _, err = srv.PublishMessage(ctx, proto, true) + msg, _, err = srv.PublishMessage(ctx, proto, true) //nolint } if err != nil { diff --git a/cli/sending_ui.go b/cli/sending_ui.go index c05f67a97..d58e93c74 100644 --- a/cli/sending_ui.go +++ b/cli/sending_ui.go @@ -41,17 +41,17 @@ func baseFeeFromHints(hint map[string]interface{}) big.Int { } func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer, - proto *types.Message, checkGroups [][]api.MessageCheckStatus, - interactive bool) (*types.Message, error) { + proto *api.MessagePrototype, checkGroups [][]api.MessageCheckStatus, + interactive bool) (*api.MessagePrototype, error) { fmt.Fprintf(printer, "Following checks have failed:\n") - printChecks(printer, checkGroups, proto.Cid()) + printChecks(printer, checkGroups, proto.Message.Cid()) if !interactive { return nil, ErrCheckFailed } if interactive { - if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Cid()); feeCapBad { + if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Message.Cid()); feeCapBad { fmt.Fprintf(printer, "Fee of the message can be adjusted\n") if askUser(printer, "Do you wish to do that? [Yes/no]: ", true) { var err error @@ -65,7 +65,7 @@ func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer, return nil, err } fmt.Fprintf(printer, "Following checks still failed:\n") - printChecks(printer, checks, proto.Cid()) + printChecks(printer, checks, proto.Message.Cid()) } if !askUser(printer, "Do you wish to send this message? [yes/No]: ", false) { @@ -125,15 +125,15 @@ func isFeeCapProblem(checkGroups [][]api.MessageCheckStatus, protoCid cid.Cid) ( return yes, baseFee } -func runFeeCapAdjustmentUI(proto *types.Message, baseFee abi.TokenAmount) (*types.Message, error) { +func runFeeCapAdjustmentUI(proto *api.MessagePrototype, baseFee abi.TokenAmount) (*api.MessagePrototype, error) { t, err := imtui.NewTui() if err != nil { return nil, err } - maxFee := big.Mul(proto.GasFeeCap, big.NewInt(proto.GasLimit)) + maxFee := big.Mul(proto.Message.GasFeeCap, big.NewInt(proto.Message.GasLimit)) send := false - t.SetScene(ui(baseFee, proto.GasLimit, &maxFee, &send)) + t.SetScene(ui(baseFee, proto.Message.GasLimit, &maxFee, &send)) err = t.Run() if err != nil { @@ -143,7 +143,7 @@ func runFeeCapAdjustmentUI(proto *types.Message, baseFee abi.TokenAmount) (*type return nil, fmt.Errorf("aborted by user") } - proto.GasFeeCap = big.Div(maxFee, big.NewInt(proto.GasLimit)) + proto.Message.GasFeeCap = big.Div(maxFee, big.NewInt(proto.Message.GasLimit)) return proto, nil } diff --git a/cli/services.go b/cli/services.go index 6fc4afe58..82d95397b 100644 --- a/cli/services.go +++ b/cli/services.go @@ -22,22 +22,24 @@ import ( //go:generate go run github.com/golang/mock/mockgen -destination=servicesmock_test.go -package=cli -self_package github.com/filecoin-project/lotus/cli . ServicesAPI type ServicesAPI interface { + FullNodeAPI() api.FullNode + GetBaseFee(ctx context.Context) (abi.TokenAmount, error) // MessageForSend creates a prototype of a message based on SendParams - MessageForSend(ctx context.Context, params SendParams) (*types.Message, error) + MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, error) // DecodeTypedParamsFromJSON takes in information needed to identify a method and converts JSON // parameters to bytes of their CBOR encoding DecodeTypedParamsFromJSON(ctx context.Context, to address.Address, method abi.MethodNum, paramstr string) ([]byte, error) - RunChecksForPrototype(ctx context.Context, prototype *types.Message) ([][]api.MessageCheckStatus, error) + RunChecksForPrototype(ctx context.Context, prototype *api.MessagePrototype) ([][]api.MessageCheckStatus, error) // PublishMessage takes in a message prototype and publishes it // before publishing the message, it runs checks on the node, message and mpool to verify that // message is valid and won't be stuck. // if `force` is true, it skips the checks - PublishMessage(ctx context.Context, prototype *types.Message, interactive bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) + PublishMessage(ctx context.Context, prototype *api.MessagePrototype, interactive bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) // Close ends the session of services and disconnects from RPC, using Services after Close is called // most likely will result in an error @@ -50,6 +52,10 @@ type ServicesImpl struct { closer jsonrpc.ClientCloser } +func (s *ServicesImpl) FullNodeAPI() api.FullNode { + return s.api +} + func (s *ServicesImpl) Close() error { if s.closer == nil { return xerrors.Errorf("Services already closed") @@ -102,15 +108,24 @@ type CheckInfo struct { var ErrCheckFailed = fmt.Errorf("check has failed") -func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *types.Message) ([][]api.MessageCheckStatus, error) { +func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *api.MessagePrototype) ([][]api.MessageCheckStatus, error) { + if !prototype.ValidNonce { + nonce, err := s.api.MpoolGetNonce(ctx, prototype.Message.From) + if err != nil { + return nil, xerrors.Errorf("mpool get nonce: %w", err) + } + prototype.Message.Nonce = nonce + prototype.ValidNonce = true + } + var outChecks [][]api.MessageCheckStatus - checks, err := s.api.MpoolCheckMessages(ctx, []*types.Message{prototype}) + checks, err := s.api.MpoolCheckMessages(ctx, []*types.Message{&prototype.Message}) if err != nil { return nil, xerrors.Errorf("message check: %w", err) } outChecks = append(outChecks, checks...) - checks, err = s.api.MpoolCheckPendingMessages(ctx, prototype.From) + checks, err = s.api.MpoolCheckPendingMessages(ctx, prototype.Message.From) if err != nil { return nil, xerrors.Errorf("pending mpool check: %w", err) } @@ -123,27 +138,30 @@ func (s *ServicesImpl) RunChecksForPrototype(ctx context.Context, prototype *typ // Errors with ErrCheckFailed if any of the checks fail // First group of checks is related to the message prototype func (s *ServicesImpl) PublishMessage(ctx context.Context, - prototype *types.Message, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { + prototype *api.MessagePrototype, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { - gasedMsg, err := s.api.GasEstimateMessageGas(ctx, prototype, nil, types.EmptyTSK) + gasedMsg, err := s.api.GasEstimateMessageGas(ctx, &prototype.Message, nil, types.EmptyTSK) if err != nil { return nil, nil, xerrors.Errorf("estimating gas: %w", err) } - *prototype = *gasedMsg + prototype.Message = *gasedMsg if !force { checks, err := s.RunChecksForPrototype(ctx, prototype) if err != nil { return nil, nil, xerrors.Errorf("running checks: %w", err) } - if len(checks) != 0 { - return nil, checks, ErrCheckFailed + for _, chks := range checks { + for _, c := range chks { + if !c.OK { + return nil, checks, ErrCheckFailed + } + } } } - //TODO: message prototype needs to have "IsNonceSet" - if prototype.Nonce != 0 { - sm, err := s.api.WalletSignMessage(ctx, prototype.From, prototype) + if prototype.ValidNonce { + sm, err := s.api.WalletSignMessage(ctx, prototype.Message.From, &prototype.Message) if err != nil { return nil, nil, err } @@ -155,7 +173,7 @@ func (s *ServicesImpl) PublishMessage(ctx context.Context, return sm, nil, nil } - sm, err := s.api.MpoolPushMessage(ctx, prototype, nil) + sm, err := s.api.MpoolPushMessage(ctx, &prototype.Message, nil) if err != nil { return nil, nil, err } @@ -177,7 +195,7 @@ type SendParams struct { Params []byte } -func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*types.Message, error) { +func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (*api.MessagePrototype, error) { if params.From == address.Undef { defaddr, err := s.api.WalletDefaultAddress(ctx) if err != nil { @@ -186,7 +204,7 @@ func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (* params.From = defaddr } - msg := &types.Message{ + msg := types.Message{ From: params.From, To: params.To, Value: params.Val, @@ -210,9 +228,15 @@ func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (* } else { msg.GasLimit = 0 } + validNonce := false if params.Nonce != nil { msg.Nonce = *params.Nonce + validNonce = true } - return msg, nil + prototype := &api.MessagePrototype{ + Message: msg, + ValidNonce: validNonce, + } + return prototype, nil } diff --git a/cli/services_send_test.go b/cli/services_send_test.go index faa052e0c..3437e90d9 100644 --- a/cli/services_send_test.go +++ b/cli/services_send_test.go @@ -7,12 +7,10 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/api" mocks "github.com/filecoin-project/lotus/api/v0api/v0mocks" types "github.com/filecoin-project/lotus/chain/types" gomock "github.com/golang/mock/gomock" - cid "github.com/ipfs/go-cid" "github.com/stretchr/testify/assert" ) @@ -61,22 +59,23 @@ func setupMockSrvcs(t *testing.T) (*ServicesImpl, *mocks.MockFullNode) { return srvcs, mockApi } -func fakeSign(msg *types.Message) *types.SignedMessage { - return &types.SignedMessage{ - Message: *msg, - Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)}, - } -} +// linter doesn't like dead code, so these are commented out. +// func fakeSign(msg *types.Message) *types.SignedMessage { +// return &types.SignedMessage{ +// Message: *msg, +// Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)}, +// } +// } -func makeMessageSigner() (*cid.Cid, interface{}) { - smCid := cid.Undef - return &smCid, - func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) { - sm := fakeSign(msg) - smCid = sm.Cid() - return sm, nil - } -} +// func makeMessageSigner() (*cid.Cid, interface{}) { +// smCid := cid.Undef +// return &smCid, +// func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) { +// sm := fakeSign(msg) +// smCid = sm.Cid() +// return sm, nil +// } +// } type MessageMatcher SendParams @@ -84,11 +83,13 @@ var _ gomock.Matcher = MessageMatcher{} // Matches returns whether x is a match. func (mm MessageMatcher) Matches(x interface{}) bool { - m, ok := x.(*types.Message) + proto, ok := x.(*api.MessagePrototype) if !ok { return false } + m := &proto.Message + if mm.From != address.Undef && mm.From != m.From { return false } diff --git a/cli/servicesmock_test.go b/cli/servicesmock_test.go index 4d1f589cd..0a353c153 100644 --- a/cli/servicesmock_test.go +++ b/cli/servicesmock_test.go @@ -67,6 +67,20 @@ func (mr *MockServicesAPIMockRecorder) DecodeTypedParamsFromJSON(arg0, arg1, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodeTypedParamsFromJSON", reflect.TypeOf((*MockServicesAPI)(nil).DecodeTypedParamsFromJSON), arg0, arg1, arg2, arg3) } +// FullNodeAPI mocks base method +func (m *MockServicesAPI) FullNodeAPI() api.FullNode { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FullNodeAPI") + ret0, _ := ret[0].(api.FullNode) + return ret0 +} + +// FullNodeAPI indicates an expected call of FullNodeAPI +func (mr *MockServicesAPIMockRecorder) FullNodeAPI() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullNodeAPI", reflect.TypeOf((*MockServicesAPI)(nil).FullNodeAPI)) +} + // GetBaseFee mocks base method func (m *MockServicesAPI) GetBaseFee(arg0 context.Context) (big.Int, error) { m.ctrl.T.Helper() @@ -83,10 +97,10 @@ func (mr *MockServicesAPIMockRecorder) GetBaseFee(arg0 interface{}) *gomock.Call } // MessageForSend mocks base method -func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*types.Message, error) { +func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*api.MessagePrototype, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MessageForSend", arg0, arg1) - ret0, _ := ret[0].(*types.Message) + ret0, _ := ret[0].(*api.MessagePrototype) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -98,7 +112,7 @@ func (mr *MockServicesAPIMockRecorder) MessageForSend(arg0, arg1 interface{}) *g } // PublishMessage mocks base method -func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *types.Message, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { +func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *api.MessagePrototype, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PublishMessage", arg0, arg1, arg2) ret0, _ := ret[0].(*types.SignedMessage) @@ -114,7 +128,7 @@ func (mr *MockServicesAPIMockRecorder) PublishMessage(arg0, arg1, arg2 interface } // RunChecksForPrototype mocks base method -func (m *MockServicesAPI) RunChecksForPrototype(arg0 context.Context, arg1 *types.Message) ([][]api.MessageCheckStatus, error) { +func (m *MockServicesAPI) RunChecksForPrototype(arg0 context.Context, arg1 *api.MessagePrototype) ([][]api.MessageCheckStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RunChecksForPrototype", arg0, arg1) ret0, _ := ret[0].([][]api.MessageCheckStatus) diff --git a/cmd/lotus-gateway/endtoend_test.go b/cmd/lotus-gateway/endtoend_test.go index 084218b24..fa1004df3 100644 --- a/cmd/lotus-gateway/endtoend_test.go +++ b/cmd/lotus-gateway/endtoend_test.go @@ -18,6 +18,8 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/xerrors" + "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-state-types/abi" @@ -102,7 +104,28 @@ func TestWalletMsig(t *testing.T) { // Create an msig with three of the addresses and threshold of two sigs msigAddrs := walletAddrs[:3] amt := types.NewInt(1000) - addProposal, err := lite.MsigCreate(ctx, 2, msigAddrs, abi.ChainEpoch(50), amt, liteWalletAddr, types.NewInt(0)) + proto, err := lite.MsigCreate(ctx, 2, msigAddrs, abi.ChainEpoch(50), amt, liteWalletAddr, types.NewInt(0)) + require.NoError(t, err) + + doSend := func(proto *api.MessagePrototype) (cid.Cid, error) { + if proto.ValidNonce { + sm, err := lite.WalletSignMessage(ctx, proto.Message.From, &proto.Message) + if err != nil { + return cid.Undef, err + } + + return lite.MpoolPush(ctx, sm) + } + + sm, err := lite.MpoolPushMessage(ctx, &proto.Message, nil) + if err != nil { + return cid.Undef, err + } + + return sm.Cid(), nil + } + + addProposal, err := doSend(proto) require.NoError(t, err) res, err := lite.StateWaitMsg(ctx, addProposal, 1, api.LookbackNoLimit, true) @@ -122,7 +145,10 @@ func TestWalletMsig(t *testing.T) { require.Less(t, msigBalance.Int64(), amt.Int64()) // Propose to add a new address to the msig - addProposal, err = lite.MsigAddPropose(ctx, msig, walletAddrs[0], walletAddrs[3], false) + proto, err = lite.MsigAddPropose(ctx, msig, walletAddrs[0], walletAddrs[3], false) + require.NoError(t, err) + + addProposal, err = doSend(proto) require.NoError(t, err) res, err = lite.StateWaitMsg(ctx, addProposal, 1, api.LookbackNoLimit, true) @@ -136,7 +162,10 @@ func TestWalletMsig(t *testing.T) { // Approve proposal (proposer is first (implicit) signer, approver is // second signer txnID := uint64(proposeReturn.TxnID) - approval1, err := lite.MsigAddApprove(ctx, msig, walletAddrs[1], txnID, walletAddrs[0], walletAddrs[3], false) + proto, err = lite.MsigAddApprove(ctx, msig, walletAddrs[1], txnID, walletAddrs[0], walletAddrs[3], false) + require.NoError(t, err) + + approval1, err := doSend(proto) require.NoError(t, err) res, err = lite.StateWaitMsg(ctx, approval1, 1, api.LookbackNoLimit, true) diff --git a/cmd/lotus-shed/verifreg.go b/cmd/lotus-shed/verifreg.go index 426827ad2..1b0b57ca0 100644 --- a/cmd/lotus-shed/verifreg.go +++ b/cmd/lotus-shed/verifreg.go @@ -67,11 +67,13 @@ var verifRegAddVerifierCmd = &cli.Command{ return err } - api, closer, err := lcli.GetFullNodeAPI(cctx) + srv, err := lcli.GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() //nolint:errcheck + + api := srv.FullNodeAPI() ctx := lcli.ReqContext(cctx) vrk, err := api.StateVerifiedRegistryRootKey(ctx, types.EmptyTSK) @@ -79,14 +81,21 @@ var verifRegAddVerifierCmd = &cli.Command{ return err } - smsg, err := api.MsigPropose(ctx, vrk, verifreg.Address, big.Zero(), sender, uint64(verifreg.Methods.AddVerifier), params) + proto, err := api.MsigPropose(ctx, vrk, verifreg.Address, big.Zero(), sender, uint64(verifreg.Methods.AddVerifier), params) if err != nil { return err } - fmt.Printf("message sent, now waiting on cid: %s\n", smsg) + sm, _, err := srv.PublishMessage(ctx, proto, false) + if err != nil { + return err + } - mwait, err := api.StateWaitMsg(ctx, smsg, build.MessageConfidence) + msgCid := sm.Cid() + + fmt.Printf("message sent, now waiting on cid: %s\n", msgCid) + + mwait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence) if err != nil { return err } diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index e90ba7d6a..3c5356a56 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -82,9 +82,6 @@ * [MpoolBatchPush](#MpoolBatchPush) * [MpoolBatchPushMessage](#MpoolBatchPushMessage) * [MpoolBatchPushUntrusted](#MpoolBatchPushUntrusted) - * [MpoolCheckMessages](#MpoolCheckMessages) - * [MpoolCheckPendingMessages](#MpoolCheckPendingMessages) - * [MpoolCheckReplaceMessages](#MpoolCheckReplaceMessages) * [MpoolClear](#MpoolClear) * [MpoolGetConfig](#MpoolGetConfig) * [MpoolGetNonce](#MpoolGetNonce) @@ -1997,51 +1994,6 @@ Inputs: Response: `null` -### MpoolCheckMessages -MpoolCheckMessages performs logical checks on a batch of messages - - -Perms: read - -Inputs: -```json -[ - null -] -``` - -Response: `null` - -### MpoolCheckPendingMessages -MpoolCheckPendingMessages performs logical checks for all pending messages from a given address - - -Perms: read - -Inputs: -```json -[ - "f01234" -] -``` - -Response: `null` - -### MpoolCheckReplaceMessages -MpoolCheckMessages performs logical checks on pending messages with replacement - - -Perms: read - -Inputs: -```json -[ - null -] -``` - -Response: `null` - ### MpoolClear MpoolClear clears pending messages from the mpool diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index a0ee6fcf3..83c2d6d41 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -82,6 +82,9 @@ * [MpoolBatchPush](#MpoolBatchPush) * [MpoolBatchPushMessage](#MpoolBatchPushMessage) * [MpoolBatchPushUntrusted](#MpoolBatchPushUntrusted) + * [MpoolCheckMessages](#MpoolCheckMessages) + * [MpoolCheckPendingMessages](#MpoolCheckPendingMessages) + * [MpoolCheckReplaceMessages](#MpoolCheckReplaceMessages) * [MpoolClear](#MpoolClear) * [MpoolGetConfig](#MpoolGetConfig) * [MpoolGetNonce](#MpoolGetNonce) @@ -1993,6 +1996,51 @@ Inputs: Response: `null` +### MpoolCheckMessages +MpoolCheckMessages performs logical checks on a batch of messages + + +Perms: read + +Inputs: +```json +[ + null +] +``` + +Response: `null` + +### MpoolCheckPendingMessages +MpoolCheckPendingMessages performs logical checks for all pending messages from a given address + + +Perms: read + +Inputs: +```json +[ + "f01234" +] +``` + +Response: `null` + +### MpoolCheckReplaceMessages +MpoolCheckReplaceMessages performs logical checks on pending messages with replacement + + +Perms: read + +Inputs: +```json +[ + null +] +``` + +Response: `null` + ### MpoolClear MpoolClear clears pending messages from the mpool @@ -2326,7 +2374,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2352,7 +2415,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2377,7 +2455,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2400,7 +2493,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2432,7 +2540,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2460,7 +2583,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2487,7 +2625,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2624,7 +2777,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2651,7 +2819,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2678,7 +2861,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2704,7 +2902,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` @@ -2729,7 +2942,22 @@ Inputs: Response: ```json { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + "Message": { + "Version": 42, + "To": "f01234", + "From": "f01234", + "Nonce": 42, + "Value": "0", + "GasLimit": 9, + "GasFeeCap": "0", + "GasPremium": "0", + "Method": 1, + "Params": "Ynl0ZSBhcnJheQ==", + "CID": { + "/": "bafy2bzacebbpdegvr3i4cosewthysg5xkxpqfn2wfcz6mv2hmoktwbdxkax4s" + } + }, + "ValidNonce": true } ``` diff --git a/node/impl/full/multisig.go b/node/impl/full/multisig.go index 9c5f683c4..e44509d7c 100644 --- a/node/impl/full/multisig.go +++ b/node/impl/full/multisig.go @@ -14,7 +14,6 @@ import ( multisig2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig" - "github.com/ipfs/go-cid" "go.uber.org/fx" "golang.org/x/xerrors" ) @@ -37,134 +36,129 @@ func (a *MsigAPI) messageBuilder(ctx context.Context, from address.Address) (mul // TODO: remove gp (gasPrice) from arguments // TODO: Add "vesting start" to arguments. -func (a *MsigAPI) MsigCreate(ctx context.Context, req uint64, addrs []address.Address, duration abi.ChainEpoch, val types.BigInt, src address.Address, gp types.BigInt) (cid.Cid, error) { +func (a *MsigAPI) MsigCreate(ctx context.Context, req uint64, addrs []address.Address, duration abi.ChainEpoch, val types.BigInt, src address.Address, gp types.BigInt) (*api.MessagePrototype, error) { mb, err := a.messageBuilder(ctx, src) if err != nil { - return cid.Undef, err + return nil, err } msg, err := mb.Create(addrs, req, 0, duration, val) if err != nil { - return cid.Undef, err + return nil, err } - // send the message out to the network - smsg, err := a.MpoolAPI.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, err - } - - return smsg.Cid(), nil + return &api.MessagePrototype{ + Message: *msg, + ValidNonce: false, + }, nil } -func (a *MsigAPI) MsigPropose(ctx context.Context, msig address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { +func (a *MsigAPI) MsigPropose(ctx context.Context, msig address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (*api.MessagePrototype, error) { mb, err := a.messageBuilder(ctx, src) if err != nil { - return cid.Undef, err + return nil, err } msg, err := mb.Propose(msig, to, amt, abi.MethodNum(method), params) if err != nil { - return cid.Undef, xerrors.Errorf("failed to create proposal: %w", err) + return nil, xerrors.Errorf("failed to create proposal: %w", err) } - smsg, err := a.MpoolAPI.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to push message: %w", err) - } - - return smsg.Cid(), nil + return &api.MessagePrototype{ + Message: *msg, + ValidNonce: false, + }, nil } -func (a *MsigAPI) MsigAddPropose(ctx context.Context, msig address.Address, src address.Address, newAdd address.Address, inc bool) (cid.Cid, error) { +func (a *MsigAPI) MsigAddPropose(ctx context.Context, msig address.Address, src address.Address, newAdd address.Address, inc bool) (*api.MessagePrototype, error) { enc, actErr := serializeAddParams(newAdd, inc) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigPropose(ctx, msig, msig, big.Zero(), src, uint64(multisig.Methods.AddSigner), enc) } -func (a *MsigAPI) MsigAddApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, newAdd address.Address, inc bool) (cid.Cid, error) { +func (a *MsigAPI) MsigAddApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, newAdd address.Address, inc bool) (*api.MessagePrototype, error) { enc, actErr := serializeAddParams(newAdd, inc) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigApproveTxnHash(ctx, msig, txID, proposer, msig, big.Zero(), src, uint64(multisig.Methods.AddSigner), enc) } -func (a *MsigAPI) MsigAddCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, newAdd address.Address, inc bool) (cid.Cid, error) { +func (a *MsigAPI) MsigAddCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, newAdd address.Address, inc bool) (*api.MessagePrototype, error) { enc, actErr := serializeAddParams(newAdd, inc) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigCancel(ctx, msig, txID, msig, big.Zero(), src, uint64(multisig.Methods.AddSigner), enc) } -func (a *MsigAPI) MsigSwapPropose(ctx context.Context, msig address.Address, src address.Address, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { +func (a *MsigAPI) MsigSwapPropose(ctx context.Context, msig address.Address, src address.Address, oldAdd address.Address, newAdd address.Address) (*api.MessagePrototype, error) { enc, actErr := serializeSwapParams(oldAdd, newAdd) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigPropose(ctx, msig, msig, big.Zero(), src, uint64(multisig.Methods.SwapSigner), enc) } -func (a *MsigAPI) MsigSwapApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { +func (a *MsigAPI) MsigSwapApprove(ctx context.Context, msig address.Address, src address.Address, txID uint64, proposer address.Address, oldAdd address.Address, newAdd address.Address) (*api.MessagePrototype, error) { enc, actErr := serializeSwapParams(oldAdd, newAdd) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigApproveTxnHash(ctx, msig, txID, proposer, msig, big.Zero(), src, uint64(multisig.Methods.SwapSigner), enc) } -func (a *MsigAPI) MsigSwapCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, oldAdd address.Address, newAdd address.Address) (cid.Cid, error) { +func (a *MsigAPI) MsigSwapCancel(ctx context.Context, msig address.Address, src address.Address, txID uint64, oldAdd address.Address, newAdd address.Address) (*api.MessagePrototype, error) { enc, actErr := serializeSwapParams(oldAdd, newAdd) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigCancel(ctx, msig, txID, msig, big.Zero(), src, uint64(multisig.Methods.SwapSigner), enc) } -func (a *MsigAPI) MsigApprove(ctx context.Context, msig address.Address, txID uint64, src address.Address) (cid.Cid, error) { +func (a *MsigAPI) MsigApprove(ctx context.Context, msig address.Address, txID uint64, src address.Address) (*api.MessagePrototype, error) { return a.msigApproveOrCancelSimple(ctx, api.MsigApprove, msig, txID, src) } -func (a *MsigAPI) MsigApproveTxnHash(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { +func (a *MsigAPI) MsigApproveTxnHash(ctx context.Context, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (*api.MessagePrototype, error) { return a.msigApproveOrCancelTxnHash(ctx, api.MsigApprove, msig, txID, proposer, to, amt, src, method, params) } -func (a *MsigAPI) MsigCancel(ctx context.Context, msig address.Address, txID uint64, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { +func (a *MsigAPI) MsigCancel(ctx context.Context, msig address.Address, txID uint64, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (*api.MessagePrototype, error) { return a.msigApproveOrCancelTxnHash(ctx, api.MsigCancel, msig, txID, src, to, amt, src, method, params) } -func (a *MsigAPI) MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (cid.Cid, error) { +func (a *MsigAPI) MsigRemoveSigner(ctx context.Context, msig address.Address, proposer address.Address, toRemove address.Address, decrease bool) (*api.MessagePrototype, error) { enc, actErr := serializeRemoveParams(toRemove, decrease) if actErr != nil { - return cid.Undef, actErr + return nil, actErr } return a.MsigPropose(ctx, msig, msig, types.NewInt(0), proposer, uint64(multisig.Methods.RemoveSigner), enc) } -func (a *MsigAPI) msigApproveOrCancelSimple(ctx context.Context, operation api.MsigProposeResponse, msig address.Address, txID uint64, src address.Address) (cid.Cid, error) { +func (a *MsigAPI) msigApproveOrCancelSimple(ctx context.Context, operation api.MsigProposeResponse, msig address.Address, txID uint64, src address.Address) (*api.MessagePrototype, error) { if msig == address.Undef { - return cid.Undef, xerrors.Errorf("must provide multisig address") + return nil, xerrors.Errorf("must provide multisig address") } if src == address.Undef { - return cid.Undef, xerrors.Errorf("must provide source address") + return nil, xerrors.Errorf("must provide source address") } mb, err := a.messageBuilder(ctx, src) if err != nil { - return cid.Undef, err + return nil, err } var msg *types.Message @@ -174,34 +168,31 @@ func (a *MsigAPI) msigApproveOrCancelSimple(ctx context.Context, operation api.M case api.MsigCancel: msg, err = mb.Cancel(msig, txID, nil) default: - return cid.Undef, xerrors.Errorf("Invalid operation for msigApproveOrCancel") + return nil, xerrors.Errorf("Invalid operation for msigApproveOrCancel") } if err != nil { - return cid.Undef, err + return nil, err } - smsg, err := a.MpoolAPI.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, err - } - - return smsg.Cid(), nil - + return &api.MessagePrototype{ + Message: *msg, + ValidNonce: false, + }, nil } -func (a *MsigAPI) msigApproveOrCancelTxnHash(ctx context.Context, operation api.MsigProposeResponse, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (cid.Cid, error) { +func (a *MsigAPI) msigApproveOrCancelTxnHash(ctx context.Context, operation api.MsigProposeResponse, msig address.Address, txID uint64, proposer address.Address, to address.Address, amt types.BigInt, src address.Address, method uint64, params []byte) (*api.MessagePrototype, error) { if msig == address.Undef { - return cid.Undef, xerrors.Errorf("must provide multisig address") + return nil, xerrors.Errorf("must provide multisig address") } if src == address.Undef { - return cid.Undef, xerrors.Errorf("must provide source address") + return nil, xerrors.Errorf("must provide source address") } if proposer.Protocol() != address.ID { proposerID, err := a.StateAPI.StateLookupID(ctx, proposer, types.EmptyTSK) if err != nil { - return cid.Undef, err + return nil, err } proposer = proposerID } @@ -216,7 +207,7 @@ func (a *MsigAPI) msigApproveOrCancelTxnHash(ctx context.Context, operation api. mb, err := a.messageBuilder(ctx, src) if err != nil { - return cid.Undef, err + return nil, err } var msg *types.Message @@ -226,18 +217,16 @@ func (a *MsigAPI) msigApproveOrCancelTxnHash(ctx context.Context, operation api. case api.MsigCancel: msg, err = mb.Cancel(msig, txID, &p) default: - return cid.Undef, xerrors.Errorf("Invalid operation for msigApproveOrCancel") + return nil, xerrors.Errorf("Invalid operation for msigApproveOrCancel") } if err != nil { - return cid.Undef, err + return nil, err } - smsg, err := a.MpoolAPI.MpoolPushMessage(ctx, msg, nil) - if err != nil { - return cid.Undef, err - } - - return smsg.Cid(), nil + return &api.MessagePrototype{ + Message: *msg, + ValidNonce: false, + }, nil } func serializeAddParams(new address.Address, inc bool) ([]byte, error) { From 87df73a455fac8bab767a85af279200ab7093ff1 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 1 Apr 2021 13:19:00 +0200 Subject: [PATCH 68/97] Fix get nonce check Signed-off-by: Jakub Sztandera --- chain/messagepool/check.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chain/messagepool/check.go b/chain/messagepool/check.go index 6800b0ef4..38273481a 100644 --- a/chain/messagepool/check.go +++ b/chain/messagepool/check.go @@ -163,6 +163,8 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu st = &actorState{nextNonce: stateNonce, requiredFunds: new(stdbig.Int)} state[m.From] = st } + } else { + check.OK = true } result[i] = append(result[i], check) From 7535c5bb53ca6b53e13f1274bdd660db532d799d Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 25 Mar 2021 15:24:09 +0100 Subject: [PATCH 69/97] Add `mpool manage` command Signed-off-by: Jakub Sztandera --- chain/messagepool/check.go | 2 +- cli/mpool.go | 1 + cli/mpool_manage.go | 356 +++++++++++++++++++++++++++++++++++++ cli/send.go | 20 +-- cli/send_test.go | 102 +++-------- cli/sending_ui.go | 71 +++++--- cli/services.go | 45 ++++- cli/services_send_test.go | 31 ++-- cli/servicesmock_test.go | 46 +++++ cmd/lotus/main.go | 14 ++ go.mod | 3 +- go.sum | 4 +- 12 files changed, 566 insertions(+), 129 deletions(-) create mode 100644 cli/mpool_manage.go diff --git a/chain/messagepool/check.go b/chain/messagepool/check.go index 38273481a..7cb99ff15 100644 --- a/chain/messagepool/check.go +++ b/chain/messagepool/check.go @@ -360,7 +360,7 @@ func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool) (resu } if m.GasFeeCap.LessThan(baseFeeUpperBound) { - check.OK = false + check.OK = true // on purpose, the checks is more of a warning check.Err = "GasFeeCap less than base fee upper bound for inclusion in next 20 epochs" } else { check.OK = true diff --git a/cli/mpool.go b/cli/mpool.go index 025a2fc3f..b128ccc15 100644 --- a/cli/mpool.go +++ b/cli/mpool.go @@ -34,6 +34,7 @@ var MpoolCmd = &cli.Command{ MpoolFindCmd, MpoolConfig, MpoolGasPerfCmd, + mpoolManage, }, } diff --git a/cli/mpool_manage.go b/cli/mpool_manage.go new file mode 100644 index 000000000..1ca23e614 --- /dev/null +++ b/cli/mpool_manage.go @@ -0,0 +1,356 @@ +package cli + +import ( + "context" + "fmt" + "sort" + + "github.com/Kubuxu/imtui" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/messagepool" + types "github.com/filecoin-project/lotus/chain/types" + "github.com/gdamore/tcell/v2" + cid "github.com/ipfs/go-cid" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var mpoolManage = &cli.Command{ + Name: "manage", + Action: func(cctx *cli.Context) error { + srv, err := GetFullNodeServices(cctx) + if err != nil { + return err + } + defer srv.Close() + ctx := ReqContext(cctx) + + _, localAddr, err := srv.LocalAddresses(ctx) + + msgs, err := srv.MpoolPendingFilter(ctx, func(sm *types.SignedMessage) bool { + if sm.Message.From.Empty() { + return false + } + for _, a := range localAddr { + if a == sm.Message.From { + return true + } + } + return false + }, types.EmptyTSK) + if err != nil { + return err + } + + t, err := imtui.NewTui() + if err != nil { + panic(err) + } + + mm := &mmUI{ + ctx: ctx, + srv: srv, + addrs: localAddr, + messages: msgs, + } + sort.Slice(mm.addrs, func(i, j int) bool { + return mm.addrs[i].String() < mm.addrs[j].String() + }) + t.PushScene(mm.addrSelect()) + + err = t.Run() + + if err != nil { + panic(err) + } + + return nil + }, +} + +type mmUI struct { + ctx context.Context + srv ServicesAPI + addrs []address.Address + messages []*types.SignedMessage +} + +func (mm *mmUI) addrSelect() func(*imtui.Tui) error { + rows := [][]string{{"Address", "No. Messages"}} + mCount := map[address.Address]int{} + for _, sm := range mm.messages { + mCount[sm.Message.From]++ + } + for _, a := range mm.addrs { + rows = append(rows, []string{a.String(), fmt.Sprintf("%d", mCount[a])}) + } + + flex := []int{4, 1} + sel := 0 + scroll := 0 + return func(t *imtui.Tui) error { + if t.CurrentKey != nil && t.CurrentKey.Key() == tcell.KeyEnter { + if sel > 0 { + t.ReplaceScene(mm.messageLising(mm.addrs[sel-1])) + } + } + t.FlexTable(0, 0, 0, &sel, &scroll, rows, flex, true) + return nil + } +} + +func errUI(err error) func(*imtui.Tui) error { + return func(t *imtui.Tui) error { + return err + } +} + +type msgInfo struct { + sm *types.SignedMessage + checks []api.MessageCheckStatus +} + +func (mi *msgInfo) Row() []string { + cidStr := mi.sm.Cid().String() + failedChecks := 0 + for _, c := range mi.checks { + if !c.OK { + failedChecks++ + } + } + shortAddr := mi.sm.Message.To.String() + if len(shortAddr) > 16 { + shortAddr = "…" + shortAddr[len(shortAddr)-16:] + } + var fCk string + if failedChecks == 0 { + fCk = "[:green:]OK" + } else { + fCk = "[:orange:]" + fmt.Sprintf("%d", failedChecks) + } + return []string{"…" + cidStr[len(cidStr)-32:], shortAddr, + fmt.Sprintf("%d", mi.sm.Message.Nonce), types.FIL(mi.sm.Message.Value).String(), + fmt.Sprintf("%d", mi.sm.Message.Method), fCk} + +} + +func (mm *mmUI) messageLising(a address.Address) func(*imtui.Tui) error { + genMsgInfos := func() ([]msgInfo, error) { + msgs, err := mm.srv.MpoolPendingFilter(mm.ctx, func(sm *types.SignedMessage) bool { + if sm.Message.From.Empty() { + return false + } + if a == sm.Message.From { + return true + } + return false + }, types.EmptyTSK) + + if err != nil { + return nil, xerrors.Errorf("getting pending: %w", err) + } + + msgIdx := map[cid.Cid]*types.SignedMessage{} + for _, sm := range msgs { + if sm.Message.From == a { + msgIdx[sm.Message.Cid()] = sm + msgIdx[sm.Cid()] = sm + } + } + + checks, err := mm.srv.MpoolCheckPendingMessages(mm.ctx, a) + if err != nil { + return nil, xerrors.Errorf("checking pending: %w", err) + } + msgInfos := make([]msgInfo, 0, len(checks)) + for _, msgChecks := range checks { + failingChecks := []api.MessageCheckStatus{} + for _, c := range msgChecks { + if !c.OK { + failingChecks = append(failingChecks, c) + } + } + msgInfos = append(msgInfos, msgInfo{ + sm: msgIdx[msgChecks[0].Cid], + checks: failingChecks, + }) + } + return msgInfos, nil + } + + sel := 0 + scroll := 0 + + var msgInfos []msgInfo + var rows [][]string + flex := []int{3, 2, 1, 1, 1, 1} + refresh := true + + return func(t *imtui.Tui) error { + if refresh { + var err error + msgInfos, err = genMsgInfos() + if err != nil { + return xerrors.Errorf("getting msgInfos: %w", err) + } + + rows = [][]string{{"Message Cid", "To", "Nonce", "Value", "Method", "Checks"}} + for _, mi := range msgInfos { + rows = append(rows, mi.Row()) + } + refresh = false + } + + if t.CurrentKey != nil && t.CurrentKey.Key() == tcell.KeyEnter { + if sel > 0 { + t.PushScene(mm.messageDetail(msgInfos[sel-1])) + refresh = true + return nil + } + } + + t.Label(0, 0, fmt.Sprintf("Address: %s", a), tcell.StyleDefault) + t.FlexTable(1, 0, 0, &sel, &scroll, rows, flex, true) + return nil + } +} + +func (mm *mmUI) messageDetail(mi msgInfo) func(*imtui.Tui) error { + baseFee, err := mm.srv.GetBaseFee(mm.ctx) + if err != nil { + return errUI(err) + } + _ = baseFee + + m := mi.sm.Message + maxFee := big.Mul(m.GasFeeCap, big.NewInt(m.GasLimit)) + + issues := [][]string{} + for _, c := range mi.checks { + issues = append(issues, []string{c.Code.String(), c.Err}) + } + issuesFlex := []int{1, 3} + var sel, scroll int + + executeReprice := false + executeNoop := false + return func(t *imtui.Tui) error { + if executeReprice { + m.GasFeeCap = big.Div(maxFee, big.NewInt(m.GasLimit)) + m.GasPremium = messagepool.ComputeMinRBF(m.GasPremium) + m.GasFeeCap = big.Max(m.GasFeeCap, m.GasPremium) + + _, _, err := mm.srv.PublishMessage(mm.ctx, &api.MessagePrototype{ + Message: m, + ValidNonce: true, + }, true) + if err != nil { + return err + } + t.PopScene() + return nil + } + if executeNoop { + nop := types.Message{ + To: builtin.BurntFundsActorAddr, + From: m.From, + + Nonce: m.Nonce, + Value: big.Zero(), + } + + nop.GasPremium = messagepool.ComputeMinRBF(m.GasPremium) + + _, _, err := mm.srv.PublishMessage(mm.ctx, &api.MessagePrototype{ + Message: nop, + ValidNonce: true, + }, true) + + if err != nil { + return xerrors.Errorf("publishing noop message: %w", err) + } + + t.PopScene() + return nil + } + + if t.CurrentKey != nil { + if t.CurrentKey.Key() == tcell.KeyLeft { + t.PopScene() + return nil + } + if t.CurrentKey.Key() == tcell.KeyRune { + switch t.CurrentKey.Rune() { + case 'R', 'r': + t.PushScene(feeUI(baseFee, m.GasLimit, &maxFee, &executeReprice)) + return nil + case 'N', 'n': + t.PushScene(confirmationScene( + &executeNoop, + "Are you sure you want to cancel the message by", + "replacing it with a message with no effects?")) + return nil + } + } + } + + row := 0 + defS := tcell.StyleDefault + display := func(f string, args ...interface{}) { + t.Label(0, row, fmt.Sprintf(f, args...), defS) + row++ + } + + display("Message CID: %s", m.Cid()) + display("Signed Message CID: %s", mi.sm.Cid()) + row++ + display("From: %s", m.From) + display("To: %s", m.To) + row++ + display("Nonce: %d", m.Nonce) + display("Value: %s", types.FIL(m.Value)) + row++ + display("GasLimit: %d", m.GasLimit) + display("GasPremium: %s", types.FIL(m.GasPremium).Short()) + display("GasFeeCap %s", types.FIL(m.GasFeeCap).Short()) + row++ + display("Press R to reprice this message") + display("Press N to replace this message with no-operation message") + row++ + + t.FlexTable(row, 0, 0, &sel, &scroll, issues, issuesFlex, false) + + return nil + } +} + +func confirmationScene(yes *bool, ask ...string) func(*imtui.Tui) error { + return func(t *imtui.Tui) error { + row := 0 + defS := tcell.StyleDefault + display := func(f string, args ...interface{}) { + t.Label(0, row, fmt.Sprintf(f, args...), defS) + row++ + } + + for _, a := range ask { + display(a) + } + row++ + display("Enter to confirm") + display("Esc to cancel") + + if t.CurrentKey != nil { + if t.CurrentKey.Key() == tcell.KeyEnter { + *yes = true + t.PopScene() + return nil + } + } + + return nil + } +} diff --git a/cli/send.go b/cli/send.go index 9efed458a..0e53d18c1 100644 --- a/cli/send.go +++ b/cli/send.go @@ -58,10 +58,14 @@ var sendCmd = &cli.Command{ }, &cli.BoolFlag{ Name: "force", - Usage: "must be specified for the action to take effect if maybe SysErrInsufficientFunds etc", + Usage: "Deprecated: use global 'force-send'", }, }, Action: func(cctx *cli.Context) error { + if cctx.IsSet("force") { + fmt.Println("'force' flag is deprecated, use global flag 'force-send'") + } + if cctx.Args().Len() != 2 { return ShowHelp(cctx, fmt.Errorf("'send' expects two arguments, target and amount")) } @@ -145,21 +149,13 @@ var sendCmd = &cli.Command{ if err != nil { return xerrors.Errorf("creating message prototype: %w", err) } - msg, checks, err := srv.PublishMessage(ctx, proto, cctx.Bool("force")) - if xerrors.Is(err, ErrCheckFailed) { - proto, err := resolveChecks(ctx, srv, cctx.App.Writer, proto, checks, true) - if err != nil { - return xerrors.Errorf("from UI: %w", err) - } - - msg, _, err = srv.PublishMessage(ctx, proto, true) //nolint - } + c, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { - return xerrors.Errorf("publishing message: %w", err) + return err } - fmt.Fprintf(cctx.App.Writer, "%s\n", msg.Cid()) + fmt.Fprintf(cctx.App.Writer, "%s\n", c) return nil }, } diff --git a/cli/send_test.go b/cli/send_test.go index b16e3c57e..5e7489c43 100644 --- a/cli/send_test.go +++ b/cli/send_test.go @@ -1,6 +1,17 @@ package cli -/* +import ( + "bytes" + "testing" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/api" + types "github.com/filecoin-project/lotus/chain/types" + gomock "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + ucli "github.com/urfave/cli/v2" +) var arbtCid = (&types.Message{ From: mustAddr(address.NewIDAddress(2)), @@ -37,81 +48,26 @@ func TestSendCLI(t *testing.T) { app, mockSrvcs, buf, done := newMockApp(t, sendCmd) defer done() - gomock.InOrder( - mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{ - To: mustAddr(address.NewIDAddress(1)), - Val: oneFil, - }).Return(arbtCid, nil), - mockSrvcs.EXPECT().Close(), - ) - err := app.Run([]string{"lotus", "send", "t01", "1"}) - assert.NoError(t, err) - assert.EqualValues(t, arbtCid.String()+"\n", buf.String()) - }) - t.Run("ErrSendBalanceTooLow", func(t *testing.T) { - app, mockSrvcs, _, done := newMockApp(t, sendCmd) - defer done() - - gomock.InOrder( - mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{ - To: mustAddr(address.NewIDAddress(1)), - Val: oneFil, - }).Return(cid.Undef, ErrSendBalanceTooLow), - mockSrvcs.EXPECT().Close(), - ) - err := app.Run([]string{"lotus", "send", "t01", "1"}) - assert.ErrorIs(t, err, ErrSendBalanceTooLow) - }) - t.Run("generic-err-is-forwarded", func(t *testing.T) { - app, mockSrvcs, _, done := newMockApp(t, sendCmd) - defer done() - - errMark := errors.New("something") - gomock.InOrder( - mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{ - To: mustAddr(address.NewIDAddress(1)), - Val: oneFil, - }).Return(cid.Undef, errMark), - mockSrvcs.EXPECT().Close(), - ) - err := app.Run([]string{"lotus", "send", "t01", "1"}) - assert.ErrorIs(t, err, errMark) - }) - - t.Run("from-specific", func(t *testing.T) { - app, mockSrvcs, buf, done := newMockApp(t, sendCmd) - defer done() - - gomock.InOrder( - mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{ - To: mustAddr(address.NewIDAddress(1)), - From: mustAddr(address.NewIDAddress(2)), - Val: oneFil, - }).Return(arbtCid, nil), - mockSrvcs.EXPECT().Close(), - ) - err := app.Run([]string{"lotus", "send", "--from=t02", "t01", "1"}) - assert.NoError(t, err) - assert.EqualValues(t, arbtCid.String()+"\n", buf.String()) - }) - - t.Run("nonce-specific", func(t *testing.T) { - app, mockSrvcs, buf, done := newMockApp(t, sendCmd) - defer done() - zero := uint64(0) - - gomock.InOrder( - mockSrvcs.EXPECT().Send(gomock.Any(), SendParams{ + arbtProto := &api.MessagePrototype{ + Message: types.Message{ + From: mustAddr(address.NewIDAddress(1)), To: mustAddr(address.NewIDAddress(1)), - Nonce: &zero, - Val: oneFil, - }).Return(arbtCid, nil), + Value: oneFil, + }, + } + sigMsg := fakeSign(&arbtProto.Message) + + gomock.InOrder( + mockSrvcs.EXPECT().MessageForSend(gomock.Any(), SendParams{ + To: mustAddr(address.NewIDAddress(1)), + Val: oneFil, + }).Return(arbtProto, nil), + mockSrvcs.EXPECT().PublishMessage(gomock.Any(), arbtProto, false). + Return(sigMsg, nil, nil), mockSrvcs.EXPECT().Close(), ) - err := app.Run([]string{"lotus", "send", "--nonce=0", "t01", "1"}) + err := app.Run([]string{"lotus", "send", "t01", "1"}) assert.NoError(t, err) - assert.EqualValues(t, arbtCid.String()+"\n", buf.String()) + assert.EqualValues(t, sigMsg.Cid().String()+"\n", buf.String()) }) - } -*/ diff --git a/cli/sending_ui.go b/cli/sending_ui.go index d58e93c74..4024b1f67 100644 --- a/cli/sending_ui.go +++ b/cli/sending_ui.go @@ -14,8 +14,35 @@ import ( types "github.com/filecoin-project/lotus/chain/types" "github.com/gdamore/tcell/v2" cid "github.com/ipfs/go-cid" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" ) +func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI, + proto *api.MessagePrototype) (cid.Cid, error) { + + msg, checks, err := srv.PublishMessage(ctx, proto, cctx.Bool("force") || cctx.Bool("force-send")) + printer := cctx.App.Writer + if xerrors.Is(err, ErrCheckFailed) { + if !cctx.Bool("interactive") { + fmt.Fprintf(printer, "Following checks have failed:\n") + printChecks(printer, checks, proto.Message.Cid()) + } else { + proto, err = resolveChecks(ctx, srv, cctx.App.Writer, proto, checks) + if err != nil { + return cid.Undef, xerrors.Errorf("from UI: %w", err) + } + + msg, _, err = srv.PublishMessage(ctx, proto, true) + } + } + if err != nil { + return cid.Undef, xerrors.Errorf("publishing message: %w", err) + } + + return msg.Cid(), nil +} + var interactiveSolves = map[api.CheckStatusCode]bool{ api.CheckStatusMessageBaseFee: true, api.CheckStatusMessageBaseFeeLowerBound: true, @@ -42,35 +69,30 @@ func baseFeeFromHints(hint map[string]interface{}) big.Int { func resolveChecks(ctx context.Context, s ServicesAPI, printer io.Writer, proto *api.MessagePrototype, checkGroups [][]api.MessageCheckStatus, - interactive bool) (*api.MessagePrototype, error) { +) (*api.MessagePrototype, error) { fmt.Fprintf(printer, "Following checks have failed:\n") printChecks(printer, checkGroups, proto.Message.Cid()) - if !interactive { - return nil, ErrCheckFailed - } - if interactive { - if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Message.Cid()); feeCapBad { - fmt.Fprintf(printer, "Fee of the message can be adjusted\n") - if askUser(printer, "Do you wish to do that? [Yes/no]: ", true) { - var err error - proto, err = runFeeCapAdjustmentUI(proto, baseFee) - if err != nil { - return nil, err - } - } - checks, err := s.RunChecksForPrototype(ctx, proto) + if feeCapBad, baseFee := isFeeCapProblem(checkGroups, proto.Message.Cid()); feeCapBad { + fmt.Fprintf(printer, "Fee of the message can be adjusted\n") + if askUser(printer, "Do you wish to do that? [Yes/no]: ", true) { + var err error + proto, err = runFeeCapAdjustmentUI(proto, baseFee) if err != nil { return nil, err } - fmt.Fprintf(printer, "Following checks still failed:\n") - printChecks(printer, checks, proto.Message.Cid()) } + checks, err := s.RunChecksForPrototype(ctx, proto) + if err != nil { + return nil, err + } + fmt.Fprintf(printer, "Following checks still failed:\n") + printChecks(printer, checks, proto.Message.Cid()) + } - if !askUser(printer, "Do you wish to send this message? [yes/No]: ", false) { - return nil, ErrAbortedByUser - } + if !askUser(printer, "Do you wish to send this message? [yes/No]: ", false) { + return nil, ErrAbortedByUser } return proto, nil } @@ -88,7 +110,7 @@ func printChecks(printer io.Writer, checkGroups [][]api.MessageCheckStatus, prot if !aboutProto { msgName = c.Cid.String() } - fmt.Fprintf(printer, "%s message failed a check: %s\n", msgName, c.Err) + fmt.Fprintf(printer, "%s message failed a check %s: %s\n", msgName, c.Code, c.Err) } } } @@ -133,7 +155,7 @@ func runFeeCapAdjustmentUI(proto *api.MessagePrototype, baseFee abi.TokenAmount) maxFee := big.Mul(proto.Message.GasFeeCap, big.NewInt(proto.Message.GasLimit)) send := false - t.SetScene(ui(baseFee, proto.Message.GasLimit, &maxFee, &send)) + t.PushScene(feeUI(baseFee, proto.Message.GasLimit, &maxFee, &send)) err = t.Run() if err != nil { @@ -148,7 +170,7 @@ func runFeeCapAdjustmentUI(proto *api.MessagePrototype, baseFee abi.TokenAmount) return proto, nil } -func ui(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, send *bool) func(*imtui.Tui) error { +func feeUI(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, send *bool) func(*imtui.Tui) error { orignalMaxFee := *maxFee required := big.Mul(baseFee, big.NewInt(gasLimit)) safe := big.Mul(required, big.NewInt(10)) @@ -180,7 +202,8 @@ func ui(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, send * if t.CurrentKey.Key() == tcell.KeyEnter { *send = true - return imtui.ErrNormalExit + t.PopScene() + return nil } } diff --git a/cli/services.go b/cli/services.go index 82d95397b..a69dab655 100644 --- a/cli/services.go +++ b/cli/services.go @@ -39,7 +39,12 @@ type ServicesAPI interface { // before publishing the message, it runs checks on the node, message and mpool to verify that // message is valid and won't be stuck. // if `force` is true, it skips the checks - PublishMessage(ctx context.Context, prototype *api.MessagePrototype, interactive bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) + PublishMessage(ctx context.Context, prototype *api.MessagePrototype, force bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) + + LocalAddresses(ctx context.Context) (address.Address, []address.Address, error) + + MpoolPendingFilter(ctx context.Context, filter func(*types.SignedMessage) bool, tsk types.TipSetKey) ([]*types.SignedMessage, error) + MpoolCheckPendingMessages(ctx context.Context, a address.Address) ([][]api.MessageCheckStatus, error) // Close ends the session of services and disconnects from RPC, using Services after Close is called // most likely will result in an error @@ -240,3 +245,41 @@ func (s *ServicesImpl) MessageForSend(ctx context.Context, params SendParams) (* } return prototype, nil } + +func (s *ServicesImpl) MpoolPendingFilter(ctx context.Context, filter func(*types.SignedMessage) bool, + tsk types.TipSetKey) ([]*types.SignedMessage, error) { + msgs, err := s.api.MpoolPending(ctx, types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("getting pending messages: %w", err) + } + out := []*types.SignedMessage{} + for _, sm := range msgs { + if filter(sm) { + out = append(out, sm) + } + } + + return out, nil +} + +func (s *ServicesImpl) LocalAddresses(ctx context.Context) (address.Address, []address.Address, error) { + def, err := s.api.WalletDefaultAddress(ctx) + if err != nil { + return address.Undef, nil, xerrors.Errorf("getting default addr: %w", err) + } + + all, err := s.api.WalletList(ctx) + if err != nil { + return address.Undef, nil, xerrors.Errorf("getting list of addrs: %w", err) + } + + return def, all, nil +} + +func (s *ServicesImpl) MpoolCheckPendingMessages(ctx context.Context, a address.Address) ([][]api.MessageCheckStatus, error) { + checks, err := s.api.MpoolCheckPendingMessages(ctx, a) + if err != nil { + return nil, xerrors.Errorf("pending mpool check: %w", err) + } + return checks, nil +} diff --git a/cli/services_send_test.go b/cli/services_send_test.go index 3437e90d9..c6af9866a 100644 --- a/cli/services_send_test.go +++ b/cli/services_send_test.go @@ -7,6 +7,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/api" mocks "github.com/filecoin-project/lotus/api/v0api/v0mocks" types "github.com/filecoin-project/lotus/chain/types" @@ -60,22 +61,22 @@ func setupMockSrvcs(t *testing.T) (*ServicesImpl, *mocks.MockFullNode) { } // linter doesn't like dead code, so these are commented out. -// func fakeSign(msg *types.Message) *types.SignedMessage { -// return &types.SignedMessage{ -// Message: *msg, -// Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)}, -// } -// } +func fakeSign(msg *types.Message) *types.SignedMessage { + return &types.SignedMessage{ + Message: *msg, + Signature: crypto.Signature{Type: crypto.SigTypeSecp256k1, Data: make([]byte, 32)}, + } +} -// func makeMessageSigner() (*cid.Cid, interface{}) { -// smCid := cid.Undef -// return &smCid, -// func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) { -// sm := fakeSign(msg) -// smCid = sm.Cid() -// return sm, nil -// } -// } +//func makeMessageSigner() (*cid.Cid, interface{}) { +//smCid := cid.Undef +//return &smCid, +//func(_ context.Context, msg *types.Message, _ *api.MessageSendSpec) (*types.SignedMessage, error) { +//sm := fakeSign(msg) +//smCid = sm.Cid() +//return sm, nil +//} +//} type MessageMatcher SendParams diff --git a/cli/servicesmock_test.go b/cli/servicesmock_test.go index 0a353c153..4bd4b79c9 100644 --- a/cli/servicesmock_test.go +++ b/cli/servicesmock_test.go @@ -96,6 +96,22 @@ func (mr *MockServicesAPIMockRecorder) GetBaseFee(arg0 interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBaseFee", reflect.TypeOf((*MockServicesAPI)(nil).GetBaseFee), arg0) } +// LocalAddresses mocks base method +func (m *MockServicesAPI) LocalAddresses(arg0 context.Context) (go_address.Address, []go_address.Address, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LocalAddresses", arg0) + ret0, _ := ret[0].(go_address.Address) + ret1, _ := ret[1].([]go_address.Address) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// LocalAddresses indicates an expected call of LocalAddresses +func (mr *MockServicesAPIMockRecorder) LocalAddresses(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddresses", reflect.TypeOf((*MockServicesAPI)(nil).LocalAddresses), arg0) +} + // MessageForSend mocks base method func (m *MockServicesAPI) MessageForSend(arg0 context.Context, arg1 SendParams) (*api.MessagePrototype, error) { m.ctrl.T.Helper() @@ -111,6 +127,36 @@ func (mr *MockServicesAPIMockRecorder) MessageForSend(arg0, arg1 interface{}) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessageForSend", reflect.TypeOf((*MockServicesAPI)(nil).MessageForSend), arg0, arg1) } +// MpoolCheckPendingMessages mocks base method +func (m *MockServicesAPI) MpoolCheckPendingMessages(arg0 context.Context, arg1 go_address.Address) ([][]api.MessageCheckStatus, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MpoolCheckPendingMessages", arg0, arg1) + ret0, _ := ret[0].([][]api.MessageCheckStatus) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MpoolCheckPendingMessages indicates an expected call of MpoolCheckPendingMessages +func (mr *MockServicesAPIMockRecorder) MpoolCheckPendingMessages(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolCheckPendingMessages", reflect.TypeOf((*MockServicesAPI)(nil).MpoolCheckPendingMessages), arg0, arg1) +} + +// MpoolPendingFilter mocks base method +func (m *MockServicesAPI) MpoolPendingFilter(arg0 context.Context, arg1 func(*types.SignedMessage) bool, arg2 types.TipSetKey) ([]*types.SignedMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MpoolPendingFilter", arg0, arg1, arg2) + ret0, _ := ret[0].([]*types.SignedMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MpoolPendingFilter indicates an expected call of MpoolPendingFilter +func (mr *MockServicesAPIMockRecorder) MpoolPendingFilter(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MpoolPendingFilter", reflect.TypeOf((*MockServicesAPI)(nil).MpoolPendingFilter), arg0, arg1, arg2) +} + // PublishMessage mocks base method func (m *MockServicesAPI) PublishMessage(arg0 context.Context, arg1 *api.MessagePrototype, arg2 bool) (*types.SignedMessage, [][]api.MessageCheckStatus, error) { m.ctrl.T.Helper() diff --git a/cmd/lotus/main.go b/cmd/lotus/main.go index af9c56735..c1dab8e94 100644 --- a/cmd/lotus/main.go +++ b/cmd/lotus/main.go @@ -2,7 +2,9 @@ package main import ( "context" + "os" + "github.com/mattn/go-isatty" "github.com/urfave/cli/v2" "go.opencensus.io/trace" @@ -52,6 +54,8 @@ func main() { ctx, span := trace.StartSpan(context.Background(), "/cli") defer span.End() + interactiveDef := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) + app := &cli.App{ Name: "lotus", Usage: "Filecoin decentralized storage network client", @@ -64,10 +68,20 @@ func main() { Hidden: true, Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME }, + &cli.BoolFlag{ + Name: "interactive", + Usage: "setting to false will disable interactive functionality of commands", + Value: interactiveDef, + }, + &cli.BoolFlag{ + Name: "force-send", + Usage: "if true, will ignore pre-send checks", + }, }, Commands: append(local, lcli.Commands...), } + app.Setup() app.Metadata["traceContext"] = ctx app.Metadata["repoType"] = repo.FullNode diff --git a/go.mod b/go.mod index e1fe8c764..384cac442 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/BurntSushi/toml v0.3.1 github.com/GeertJohan/go.rice v1.0.0 github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee - github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7 + github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/alecthomas/jsonschema v0.0.0-20200530073317-71f438968921 @@ -118,6 +118,7 @@ require ( github.com/libp2p/go-libp2p-yamux v0.4.1 github.com/libp2p/go-maddr-filter v0.1.0 github.com/mattn/go-colorable v0.1.6 // indirect + github.com/mattn/go-isatty v0.0.12 github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.0.3 diff --git a/go.sum b/go.sum index bfb498886..ef3ac9678 100644 --- a/go.sum +++ b/go.sum @@ -42,11 +42,11 @@ github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee h1:8doiS7ib3zi6/K1 github.com/Gurpartap/async v0.0.0-20180927173644-4f7f499dd9ee/go.mod h1:W0GbEAA4uFNYOGG2cJpmFJ04E6SD1NLELPYZB57/7AY= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= +github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa h1:1PPxEyGdIGVkX/kqMvLJ95a1dGS1Sz7tpNEgehEYYt0= +github.com/Kubuxu/imtui v0.0.0-20210401140320-41663d68d0fa/go.mod h1:WUmMvh9wMtqj1Xhf1hf3kp9RvL+y6odtdYxpyZjb90U= github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= -github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7 h1:oaKenk0p5Pg7k2YRflJtiai4weJN+VsABO3zSaUVU6w= -github.com/Kubuxu/imtui v0.0.0-20210323145256-9fdaecfdf6b7/go.mod h1:WUmMvh9wMtqj1Xhf1hf3kp9RvL+y6odtdYxpyZjb90U= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= From 3d8f641310d545e31e7d4c82025c08d4822c45bf Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 2 Apr 2021 15:43:28 +0200 Subject: [PATCH 70/97] Plug in InteractiveSend to all adopted commands Signed-off-by: Jakub Sztandera --- cli/chain.go | 30 +++++++++++++---------- cli/mpool_manage.go | 6 ++++- cli/multisig.go | 58 ++++++++++++++++++++++----------------------- cli/send.go | 4 ++-- cli/send_test.go | 6 ----- cli/sending_ui.go | 8 +++---- 6 files changed, 57 insertions(+), 55 deletions(-) diff --git a/cli/chain.go b/cli/chain.go index cc2fe50ec..019b2e91f 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -31,6 +31,7 @@ import ( cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" + "github.com/filecoin-project/lotus/api" lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api/v0api" "github.com/filecoin-project/lotus/build" @@ -1116,11 +1117,12 @@ var SlashConsensusFault = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { - api, closer, err := GetFullNodeAPI(cctx) + srv, err := GetFullNodeServices(cctx) if err != nil { return err } - defer closer() + defer srv.Close() + a := srv.FullNodeAPI() ctx := ReqContext(cctx) c1, err := cid.Parse(cctx.Args().Get(0)) @@ -1128,7 +1130,7 @@ var SlashConsensusFault = &cli.Command{ return xerrors.Errorf("parsing cid 1: %w", err) } - b1, err := api.ChainGetBlock(ctx, c1) + b1, err := a.ChainGetBlock(ctx, c1) if err != nil { return xerrors.Errorf("getting block 1: %w", err) } @@ -1138,7 +1140,7 @@ var SlashConsensusFault = &cli.Command{ return xerrors.Errorf("parsing cid 2: %w", err) } - b2, err := api.ChainGetBlock(ctx, c2) + b2, err := a.ChainGetBlock(ctx, c2) if err != nil { return xerrors.Errorf("getting block 2: %w", err) } @@ -1149,7 +1151,7 @@ var SlashConsensusFault = &cli.Command{ var fromAddr address.Address if from := cctx.String("from"); from == "" { - defaddr, err := api.WalletDefaultAddress(ctx) + defaddr, err := a.WalletDefaultAddress(ctx) if err != nil { return err } @@ -1185,7 +1187,7 @@ var SlashConsensusFault = &cli.Command{ return xerrors.Errorf("parsing cid extra: %w", err) } - bExtra, err := api.ChainGetBlock(ctx, cExtra) + bExtra, err := a.ChainGetBlock(ctx, cExtra) if err != nil { return xerrors.Errorf("getting block extra: %w", err) } @@ -1203,15 +1205,17 @@ var SlashConsensusFault = &cli.Command{ return err } - msg := &types.Message{ - To: b2.Miner, - From: fromAddr, - Value: types.NewInt(0), - Method: builtin.MethodsMiner.ReportConsensusFault, - Params: enc, + proto := &api.MessagePrototype{ + Message: types.Message{ + To: b2.Miner, + From: fromAddr, + Value: types.NewInt(0), + Method: builtin.MethodsMiner.ReportConsensusFault, + Params: enc, + }, } - smsg, err := api.MpoolPushMessage(ctx, msg, nil) + smsg, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } diff --git a/cli/mpool_manage.go b/cli/mpool_manage.go index 1ca23e614..164a05842 100644 --- a/cli/mpool_manage.go +++ b/cli/mpool_manage.go @@ -25,10 +25,14 @@ var mpoolManage = &cli.Command{ if err != nil { return err } - defer srv.Close() + defer srv.Close() //nolint:errcheck + ctx := ReqContext(cctx) _, localAddr, err := srv.LocalAddresses(ctx) + if err != nil { + return xerrors.Errorf("getting local addresses: %w", err) + } msgs, err := srv.MpoolPendingFilter(ctx, func(sm *types.SignedMessage) bool { if sm.Message.From.Empty() { diff --git a/cli/multisig.go b/cli/multisig.go index 6ec0dcd5d..0baed5a93 100644 --- a/cli/multisig.go +++ b/cli/multisig.go @@ -153,7 +153,7 @@ var msigCreateCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -161,7 +161,7 @@ var msigCreateCmd = &cli.Command{ msgCid := sm.Cid() // wait for it to get mined into a block - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -442,7 +442,7 @@ var msigProposeCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -451,7 +451,7 @@ var msigProposeCmd = &cli.Command{ fmt.Println("send proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -540,7 +540,7 @@ var msigApproveCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -590,7 +590,7 @@ var msigApproveCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -600,7 +600,7 @@ var msigApproveCmd = &cli.Command{ fmt.Println("sent approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -671,7 +671,7 @@ var msigRemoveProposeCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -680,7 +680,7 @@ var msigRemoveProposeCmd = &cli.Command{ fmt.Println("sent remove proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -758,7 +758,7 @@ var msigAddProposeCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -767,7 +767,7 @@ var msigAddProposeCmd = &cli.Command{ fmt.Fprintln(cctx.App.Writer, "sent add proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -849,7 +849,7 @@ var msigAddApproveCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -858,7 +858,7 @@ var msigAddApproveCmd = &cli.Command{ fmt.Println("sent add approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -935,7 +935,7 @@ var msigAddCancelCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -944,7 +944,7 @@ var msigAddCancelCmd = &cli.Command{ fmt.Println("sent add cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -1016,7 +1016,7 @@ var msigSwapProposeCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -1025,7 +1025,7 @@ var msigSwapProposeCmd = &cli.Command{ fmt.Println("sent swap proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -1107,7 +1107,7 @@ var msigSwapApproveCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -1116,7 +1116,7 @@ var msigSwapApproveCmd = &cli.Command{ fmt.Println("sent swap approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -1193,7 +1193,7 @@ var msigSwapCancelCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -1202,7 +1202,7 @@ var msigSwapCancelCmd = &cli.Command{ fmt.Println("sent swap cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -1289,7 +1289,7 @@ var msigLockProposeCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -1298,7 +1298,7 @@ var msigLockProposeCmd = &cli.Command{ fmt.Println("sent lock proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -1395,7 +1395,7 @@ var msigLockApproveCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -1404,7 +1404,7 @@ var msigLockApproveCmd = &cli.Command{ fmt.Println("sent lock approval in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -1496,7 +1496,7 @@ var msigLockCancelCmd = &cli.Command{ return err } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -1505,7 +1505,7 @@ var msigLockCancelCmd = &cli.Command{ fmt.Println("sent lock cancellation in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } @@ -1642,7 +1642,7 @@ var msigProposeThresholdCmd = &cli.Command{ return fmt.Errorf("failed to propose change of threshold: %w", err) } - sm, _, err := srv.PublishMessage(ctx, proto, true) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } @@ -1651,7 +1651,7 @@ var msigProposeThresholdCmd = &cli.Command{ fmt.Println("sent change threshold proposal in message: ", msgCid) - wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) + wait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence"))) if err != nil { return err } diff --git a/cli/send.go b/cli/send.go index 0e53d18c1..a5200d3b8 100644 --- a/cli/send.go +++ b/cli/send.go @@ -150,12 +150,12 @@ var sendCmd = &cli.Command{ return xerrors.Errorf("creating message prototype: %w", err) } - c, err := InteractiveSend(ctx, cctx, srv, proto) + sm, err := InteractiveSend(ctx, cctx, srv, proto) if err != nil { return err } - fmt.Fprintf(cctx.App.Writer, "%s\n", c) + fmt.Fprintf(cctx.App.Writer, "%s\n", sm.Cid()) return nil }, } diff --git a/cli/send_test.go b/cli/send_test.go index 5e7489c43..52eafda67 100644 --- a/cli/send_test.go +++ b/cli/send_test.go @@ -13,12 +13,6 @@ import ( ucli "github.com/urfave/cli/v2" ) -var arbtCid = (&types.Message{ - From: mustAddr(address.NewIDAddress(2)), - To: mustAddr(address.NewIDAddress(1)), - Value: types.NewInt(1000), -}).Cid() - func mustAddr(a address.Address, err error) address.Address { if err != nil { panic(err) diff --git a/cli/sending_ui.go b/cli/sending_ui.go index 4024b1f67..881aa3aac 100644 --- a/cli/sending_ui.go +++ b/cli/sending_ui.go @@ -19,7 +19,7 @@ import ( ) func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI, - proto *api.MessagePrototype) (cid.Cid, error) { + proto *api.MessagePrototype) (*types.SignedMessage, error) { msg, checks, err := srv.PublishMessage(ctx, proto, cctx.Bool("force") || cctx.Bool("force-send")) printer := cctx.App.Writer @@ -30,17 +30,17 @@ func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI, } else { proto, err = resolveChecks(ctx, srv, cctx.App.Writer, proto, checks) if err != nil { - return cid.Undef, xerrors.Errorf("from UI: %w", err) + return nil, xerrors.Errorf("from UI: %w", err) } msg, _, err = srv.PublishMessage(ctx, proto, true) } } if err != nil { - return cid.Undef, xerrors.Errorf("publishing message: %w", err) + return nil, xerrors.Errorf("publishing message: %w", err) } - return msg.Cid(), nil + return msg, nil } var interactiveSolves = map[api.CheckStatusCode]bool{ From 8d75da1629c8d1c67a192ec3ba1447db7802ec80 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 2 Apr 2021 16:12:34 +0200 Subject: [PATCH 71/97] Use MessagePrototype for check API Signed-off-by: Jakub Sztandera --- api/api_full.go | 2 +- api/mocks/mock_full.go | 2 +- api/proxy_gen.go | 6 +++--- build/openrpc/full.json.gz | Bin 23192 -> 23212 bytes chain/messagepool/check.go | 20 ++++++++++++++------ cli/multisig.go | 28 ++++++++++++++-------------- cli/services.go | 11 +---------- documentation/en/cli-lotus.md | 8 +++++++- node/impl/full/mpool.go | 4 ++-- 9 files changed, 43 insertions(+), 38 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index e8e8dcb2e..fed9cece5 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -253,7 +253,7 @@ type FullNode interface { MpoolBatchPushMessage(context.Context, []*types.Message, *MessageSendSpec) ([]*types.SignedMessage, error) //perm:sign // MpoolCheckMessages performs logical checks on a batch of messages - MpoolCheckMessages(context.Context, []*types.Message) ([][]MessageCheckStatus, error) //perm:read + MpoolCheckMessages(context.Context, []*MessagePrototype) ([][]MessageCheckStatus, error) //perm:read // MpoolCheckPendingMessages performs logical checks for all pending messages from a given address MpoolCheckPendingMessages(context.Context, address.Address) ([][]MessageCheckStatus, error) //perm:read // MpoolCheckReplaceMessages performs logical checks on pending messages with replacement diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index a14336537..6feee64f7 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1069,7 +1069,7 @@ func (mr *MockFullNodeMockRecorder) MpoolBatchPushUntrusted(arg0, arg1 interface } // MpoolCheckMessages mocks base method -func (m *MockFullNode) MpoolCheckMessages(arg0 context.Context, arg1 []*types.Message) ([][]api.MessageCheckStatus, error) { +func (m *MockFullNode) MpoolCheckMessages(arg0 context.Context, arg1 []*api.MessagePrototype) ([][]api.MessageCheckStatus, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MpoolCheckMessages", arg0, arg1) ret0, _ := ret[0].([][]api.MessageCheckStatus) diff --git a/api/proxy_gen.go b/api/proxy_gen.go index f285f1ce6..2d4d41503 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -235,7 +235,7 @@ type FullNodeStruct struct { MpoolBatchPushUntrusted func(p0 context.Context, p1 []*types.SignedMessage) ([]cid.Cid, error) `perm:"write"` - MpoolCheckMessages func(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) `perm:"read"` + MpoolCheckMessages func(p0 context.Context, p1 []*MessagePrototype) ([][]MessageCheckStatus, error) `perm:"read"` MpoolCheckPendingMessages func(p0 context.Context, p1 address.Address) ([][]MessageCheckStatus, error) `perm:"read"` @@ -1515,11 +1515,11 @@ func (s *FullNodeStub) MpoolBatchPushUntrusted(p0 context.Context, p1 []*types.S return *new([]cid.Cid), xerrors.New("method not supported") } -func (s *FullNodeStruct) MpoolCheckMessages(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) { +func (s *FullNodeStruct) MpoolCheckMessages(p0 context.Context, p1 []*MessagePrototype) ([][]MessageCheckStatus, error) { return s.Internal.MpoolCheckMessages(p0, p1) } -func (s *FullNodeStub) MpoolCheckMessages(p0 context.Context, p1 []*types.Message) ([][]MessageCheckStatus, error) { +func (s *FullNodeStub) MpoolCheckMessages(p0 context.Context, p1 []*MessagePrototype) ([][]MessageCheckStatus, error) { return *new([][]MessageCheckStatus), xerrors.New("method not supported") } diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index cb781bec9169de220365f7d8cfbac0850a948f70..d628685e57299f0100424b40692559317a058007 100644 GIT binary patch delta 22540 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 delta 22540 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∓_Ll)(Lu^CHRc+S#AeRy(7V#7w# zHreWOt>`%C5TA8ey6CcT3O;ycv7>mV;yW9;8iER>1OVd%bFznM^sbi(_MnRh-emz@ zQ=lQpw>TlZQCrt-pg**)kwTWwKw%!O?PMeeyHZ@YtXYxLn_1Bqil?yd$?Yncl+#q- zJlEOG4~TXT)YWndTzuA_SgS2sv>fiyhosZgvb=u8)zj#R-WRG7Fe|0~`%{aa#B*1r6{Jj27u&#m|}Z&BXyag~ypMXC6Y;%YAD zY^sN={bhdyvbZ!2U{j=t&C@SleSTJx+w;#H?yh z4q?3^6ba@5D;WhS?@vd;k=A17TI5m`S8TFF$ns8wHFSU#k^?kABot#EF?t-%>80#@ zt`#c3@rVaREu`Fa(v^%J{OC!crWLIc$@?>mRZ23%T`R1Wt|=29 zyKA#$2ft@tdH`ywyVGXbG1B4%I%}`urUIz+b8y|Yx8=GSi_jHOv$}JjOS%HiBTcyP z4T>QBE`%BeqjCXLr8cM$2I|xGW^f6usa6KbbXP+I?H$3&h`5OsZB}$Rn52 z+MS}a76x8Ah>dlsVrqCrRVJlV33*nDRW3x(o@`$1hdJ5xgMAoBu!B$DX~c*qZhqA; zfJ>(%XA_pr`5#kB;tMQnE_W}BM_q1jFxC!VO;W0PYVURCNMs8!U(!FWWEM9Xn)w6y zqEtw?_2+&25`UgM*kJ3v$hTZwjkSZjA0m*-dP_CFxS@`ce+Zu}mju_ckm_}x_Dd^7 z{5&~%WysJ#UsxwCrcAsLmrYGuhwsJ_bE1U{)$)|et1~Kn#g9du%h?I}sy?8t<=xSC z-O^+EJ5Ada+D4b62xbFv|CS}^vd&IX%->|s1GmtAILqX^dY=C`nAi4PREpJ zFdvL@EO^cI^H(FOl2!B$PIlFM@f_`ddV*WCLF_>+I)p*RUt3=tzTBmeA67cKf5Ml@ zfWiD5SyaqaETdr_53P8=$$owcU^+on`MOIwR1d5@TFwi7n5A$0V%!Q^w)xi|y9V{@ ziAZw5`NXjk^`&ZyPQyDRM!4NzzIp2+A`G^u?lfuD9Y|a3*kN$r=`QJ6HK85o>=31M zGYygEXK17iEn7w43H7@%AN0~KzF=+v91w+CABELl~#l_IJ<^|7DcHymMa(L@tH@lLSL*X4B6 z^fqE;t};Ai>U!9NZfb>U*+HljF*ic0qNJn)u=)BQDm~xr^Qxa4fGzI@Qemuf34xo) zUn`@L++V0Y{MBE)l07=UZr8m&eO~+GWMa~F#Yypoq;z-mXdzB+|7!PF>7=Xk@0Y-fZaL%V-*Gxd<^7ef)Y_wCJh^-nk@`z>9QX(Jxzzy)1iiIVh(x} zdd;pwYzXl>&2Wo?IiFo|UZ+}SBbwOR2ZL%O{bVEu(9opq`yMoE2x0bdzuS!JQEHFkeDHG2!TrYJVXt!rxS& zrt;0g!Pb@(MgM1~Ue0G!<()=J@>Ek}KMz6w!7w;Yo@a?y0;gNb`@j#e%Ln8ABV?I! zhn|e}1*ROJd5F`yU@?z7qm>BD4dE%?I2q=Z8@%)1TKFfQQ?&v2ljA+JuMPy2O3H?| zaEAZSrs((3^xd_ztNoayc-hOV++k-@gu+Uuz}ZiIXSI~6aUp$w^H=eq94)MKe=+bA z3RY9af%tpntr=?3pdfjujdm(X?}6lXyPfo_b;kA+#YeO{v0-X$zTX(!)btoms#@4dnzsVv}v0c`9c?k1joS5S-`G78V{=pF=jv!|hamu?FdfV3Nt6g%-ZEtES zjaLHM)xQ(Vl>uWC_?x(T9 Date: Fri, 2 Apr 2021 17:25:29 +0200 Subject: [PATCH 72/97] Print more details on test fail Signed-off-by: Jakub Sztandera --- cli/chain.go | 3 ++- cli/test/mockcli.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/chain.go b/cli/chain.go index 019b2e91f..6651a7764 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -1121,7 +1121,8 @@ var SlashConsensusFault = &cli.Command{ if err != nil { return err } - defer srv.Close() + defer srv.Close() //nolint:errcheck + a := srv.FullNodeAPI() ctx := ReqContext(cctx) diff --git a/cli/test/mockcli.go b/cli/test/mockcli.go index e8eb78f1b..aef7808c8 100644 --- a/cli/test/mockcli.go +++ b/cli/test/mockcli.go @@ -56,7 +56,7 @@ type MockCLIClient struct { func (c *MockCLIClient) RunCmd(input ...string) string { out, err := c.RunCmdRaw(input...) - require.NoError(c.t, err) + require.NoError(c.t, err, "output:\n%s", out) return out } From cbfb4770fdfa290e4242b13fd210a83e8735f744 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 6 Apr 2021 18:50:49 +0200 Subject: [PATCH 73/97] Add function to display nanoFIL Signed-off-by: Jakub Sztandera --- chain/types/fil.go | 9 +++++++++ cli/sending_ui.go | 10 ++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/chain/types/fil.go b/chain/types/fil.go index 223ed3c50..7438410c8 100644 --- a/chain/types/fil.go +++ b/chain/types/fil.go @@ -46,6 +46,15 @@ func (f FIL) Short() string { return strings.TrimRight(strings.TrimRight(r.FloatString(3), "0"), ".") + " " + prefix + "FIL" } +func (f FIL) Nano() string { + r := new(big.Rat).SetFrac(f.Int, big.NewInt(int64(1e9))) + if r.Sign() == 0 { + return "0" + } + + return strings.TrimRight(strings.TrimRight(r.FloatString(9), "0"), ".") + " nFIL" +} + func (f FIL) Format(s fmt.State, ch rune) { switch ch { case 's', 'v': diff --git a/cli/sending_ui.go b/cli/sending_ui.go index 881aa3aac..a70abefb9 100644 --- a/cli/sending_ui.go +++ b/cli/sending_ui.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" types "github.com/filecoin-project/lotus/chain/types" "github.com/gdamore/tcell/v2" cid "github.com/ipfs/go-cid" @@ -44,6 +45,7 @@ func InteractiveSend(ctx context.Context, cctx *cli.Context, srv ServicesAPI, } var interactiveSolves = map[api.CheckStatusCode]bool{ + api.CheckStatusMessageMinBaseFee: true, api.CheckStatusMessageBaseFee: true, api.CheckStatusMessageBaseFeeLowerBound: true, api.CheckStatusMessageBaseFeeUpperBound: true, @@ -143,6 +145,10 @@ func isFeeCapProblem(checkGroups [][]api.MessageCheckStatus, protoCid cid.Cid) ( } } } + if baseFee.IsZero() { + // this will only be the case if failing check is: MessageMinBaseFee + baseFee = big.NewInt(build.MinimumBaseFee) + } return yes, baseFee } @@ -246,10 +252,10 @@ func feeUI(baseFee abi.TokenAmount, gasLimit int64, maxFee *abi.TokenAmount, sen } row += 2 - t.Label(0, row, fmt.Sprintf("Current Base Fee is: %s", types.FIL(baseFee)), defS) + t.Label(0, row, fmt.Sprintf("Current Base Fee is: %s", types.FIL(baseFee).Nano()), defS) row++ t.Label(0, row, fmt.Sprintf("Resulting FeeCap is: %s", - types.FIL(big.Div(*maxFee, big.NewInt(gasLimit)))), defS) + types.FIL(big.Div(*maxFee, big.NewInt(gasLimit))).Nano()), defS) row++ t.Label(0, row, "You can use '+' and '-' to adjust the fee.", defS) From 61dbd443b86f88d1e8a6a457f0e741fcbb04b6e5 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 6 May 2021 16:37:46 +0200 Subject: [PATCH 74/97] Fix tests and verifreg Signed-off-by: Jakub Sztandera --- cli/services_send_test.go | 2 +- cmd/lotus-shed/verifreg.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/services_send_test.go b/cli/services_send_test.go index c6af9866a..b7ed78f80 100644 --- a/cli/services_send_test.go +++ b/cli/services_send_test.go @@ -9,7 +9,7 @@ import ( "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/api" - mocks "github.com/filecoin-project/lotus/api/v0api/v0mocks" + mocks "github.com/filecoin-project/lotus/api/mocks" types "github.com/filecoin-project/lotus/chain/types" gomock "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" diff --git a/cmd/lotus-shed/verifreg.go b/cmd/lotus-shed/verifreg.go index 1b0b57ca0..52e1aa1d6 100644 --- a/cmd/lotus-shed/verifreg.go +++ b/cmd/lotus-shed/verifreg.go @@ -95,7 +95,7 @@ var verifRegAddVerifierCmd = &cli.Command{ fmt.Printf("message sent, now waiting on cid: %s\n", msgCid) - mwait, err := api.StateWaitMsg(ctx, msgCid, build.MessageConfidence) + mwait, err := api.StateWaitMsg(ctx, msgCid, uint64(cctx.Int("confidence")), build.Finality, true) if err != nil { return err } From d777680449daf261a7a1109a2d50dc97583bfd6c Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 7 May 2021 15:20:37 +0200 Subject: [PATCH 75/97] Fix mpool.GetActor for lite node Signed-off-by: Jakub Sztandera --- chain/messagepool/messagepool.go | 7 +++++++ chain/messagepool/provider.go | 20 ++++++++++++++++++++ chain/messagesigner/messagesigner.go | 1 + chain/messagesigner/messagesigner_test.go | 3 +++ node/builder.go | 2 ++ node/modules/chain.go | 4 +--- node/modules/mpoolnonceapi.go | 9 +++++++++ 7 files changed, 43 insertions(+), 3 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 40d0c4eaf..fb5f0acba 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -805,6 +805,13 @@ func (mp *MessagePool) GetNonce(_ context.Context, addr address.Address, _ types return mp.getNonceLocked(addr, mp.curTs) } +// GetActor should not be used. It is only here to satisfy interface mess caused by lite node handling +func (mp *MessagePool) GetActor(_ context.Context, addr address.Address, _ types.TipSetKey) (*types.Actor, error) { + mp.curTsLk.Lock() + defer mp.curTsLk.Unlock() + return mp.api.GetActorAfter(addr, mp.curTs) +} + func (mp *MessagePool) getNonceLocked(addr address.Address, curTs *types.TipSet) (uint64, error) { stateNonce, err := mp.getStateNonce(addr, curTs) // sanity check if err != nil { diff --git a/chain/messagepool/provider.go b/chain/messagepool/provider.go index 5a6c751bc..1c64cdcdf 100644 --- a/chain/messagepool/provider.go +++ b/chain/messagepool/provider.go @@ -9,6 +9,7 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/chain/messagesigner" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" @@ -35,12 +36,18 @@ type Provider interface { type mpoolProvider struct { sm *stmgr.StateManager ps *pubsub.PubSub + + lite messagesigner.MpoolNonceAPI } func NewProvider(sm *stmgr.StateManager, ps *pubsub.PubSub) Provider { return &mpoolProvider{sm: sm, ps: ps} } +func NewProviderLite(sm *stmgr.StateManager, ps *pubsub.PubSub, noncer messagesigner.MpoolNonceAPI) Provider { + return &mpoolProvider{sm: sm, ps: ps, lite: noncer} +} + func (mpp *mpoolProvider) SubscribeHeadChanges(cb func(rev, app []*types.TipSet) error) *types.TipSet { mpp.sm.ChainStore().SubscribeHeadChanges( store.WrapHeadChangeCoalescer( @@ -61,6 +68,19 @@ func (mpp *mpoolProvider) PubSubPublish(k string, v []byte) error { } func (mpp *mpoolProvider) GetActorAfter(addr address.Address, ts *types.TipSet) (*types.Actor, error) { + if mpp.lite != nil { + n, err := mpp.lite.GetNonce(context.TODO(), addr, ts.Key()) + if err != nil { + return nil, xerrors.Errorf("getting nonce over lite: %w", err) + } + a, err := mpp.lite.GetActor(context.TODO(), addr, ts.Key()) + if err != nil { + return nil, xerrors.Errorf("getting actor over lite: %w", err) + } + a.Nonce = n + return a, nil + } + stcid, _, err := mpp.sm.TipSetState(context.TODO(), ts) if err != nil { return nil, xerrors.Errorf("computing tipset state for GetActor: %w", err) diff --git a/chain/messagesigner/messagesigner.go b/chain/messagesigner/messagesigner.go index c91f75632..063d1aa7d 100644 --- a/chain/messagesigner/messagesigner.go +++ b/chain/messagesigner/messagesigner.go @@ -24,6 +24,7 @@ var log = logging.Logger("messagesigner") type MpoolNonceAPI interface { GetNonce(context.Context, address.Address, types.TipSetKey) (uint64, error) + GetActor(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) } // MessageSigner keeps track of nonces per address, and increments the nonce diff --git a/chain/messagesigner/messagesigner_test.go b/chain/messagesigner/messagesigner_test.go index 7bba5b3e9..90d16b7ff 100644 --- a/chain/messagesigner/messagesigner_test.go +++ b/chain/messagesigner/messagesigner_test.go @@ -41,6 +41,9 @@ func (mp *mockMpool) GetNonce(_ context.Context, addr address.Address, _ types.T return mp.nonces[addr], nil } +func (mp *mockMpool) GetActor(_ context.Context, addr address.Address, _ types.TipSetKey) (*types.Actor, error) { + panic("don't use it") +} func TestMessageSignerSignMessage(t *testing.T) { ctx := context.Background() diff --git a/node/builder.go b/node/builder.go index c884b169b..34be610f5 100644 --- a/node/builder.go +++ b/node/builder.go @@ -333,6 +333,7 @@ var ChainNode = Options( // Lite node API ApplyIf(isLiteNode, + Override(new(messagepool.Provider), messagepool.NewProviderLite), Override(new(messagesigner.MpoolNonceAPI), From(new(modules.MpoolNonceAPI))), Override(new(full.ChainModuleAPI), From(new(api.Gateway))), Override(new(full.GasModuleAPI), From(new(api.Gateway))), @@ -343,6 +344,7 @@ var ChainNode = Options( // Full node API / service startup ApplyIf(isFullNode, + Override(new(messagepool.Provider), messagepool.NewProvider), Override(new(messagesigner.MpoolNonceAPI), From(new(*messagepool.MessagePool))), Override(new(full.ChainModuleAPI), From(new(full.ChainModule))), Override(new(full.GasModuleAPI), From(new(full.GasModule))), diff --git a/node/modules/chain.go b/node/modules/chain.go index ffdf3aa3a..954322948 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -9,7 +9,6 @@ import ( "github.com/ipfs/go-blockservice" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/routing" - pubsub "github.com/libp2p/go-libp2p-pubsub" "go.uber.org/fx" "golang.org/x/xerrors" @@ -59,8 +58,7 @@ func ChainBlockService(bs dtypes.ExposedBlockstore, rem dtypes.ChainBitswap) dty return blockservice.New(bs, rem) } -func MessagePool(lc fx.Lifecycle, sm *stmgr.StateManager, ps *pubsub.PubSub, ds dtypes.MetadataDS, nn dtypes.NetworkName, j journal.Journal) (*messagepool.MessagePool, error) { - mpp := messagepool.NewProvider(sm, ps) +func MessagePool(lc fx.Lifecycle, mpp messagepool.Provider, ds dtypes.MetadataDS, nn dtypes.NetworkName, j journal.Journal) (*messagepool.MessagePool, error) { mp, err := messagepool.New(mpp, ds, nn, j) if err != nil { return nil, xerrors.Errorf("constructing mpool: %w", err) diff --git a/node/modules/mpoolnonceapi.go b/node/modules/mpoolnonceapi.go index 61b38e821..3b30d24cf 100644 --- a/node/modules/mpoolnonceapi.go +++ b/node/modules/mpoolnonceapi.go @@ -96,4 +96,13 @@ func (a *MpoolNonceAPI) GetNonce(ctx context.Context, addr address.Address, tsk return highestNonce, nil } +func (a *MpoolNonceAPI) GetActor(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error) { + act, err := a.StateModule.StateGetActor(ctx, addr, tsk) + if err != nil { + return nil, xerrors.Errorf("calling StateGetActor: %w", err) + } + + return act, nil +} + var _ messagesigner.MpoolNonceAPI = (*MpoolNonceAPI)(nil) From 18cbdcfc819fba9077561f87a972497e0064905b Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Fri, 7 May 2021 16:38:40 +0200 Subject: [PATCH 76/97] Disable checks API on Lotus Lite Signed-off-by: Jakub Sztandera --- api/test/util.go | 2 +- chain/messagepool/check.go | 3 +++ chain/messagepool/messagepool.go | 4 ++-- chain/messagepool/messagepool_test.go | 3 +++ chain/messagepool/provider.go | 7 ++++++- cli/test/util.go | 2 ++ cmd/lotus-gateway/endtoend_test.go | 2 +- node/impl/full/state.go | 2 +- node/modules/mpoolnonceapi.go | 2 +- 9 files changed, 20 insertions(+), 7 deletions(-) diff --git a/api/test/util.go b/api/test/util.go index f571b48da..219dcf9ed 100644 --- a/api/test/util.go +++ b/api/test/util.go @@ -29,7 +29,7 @@ func SendFunds(ctx context.Context, t *testing.T, sender TestNode, addr address. if err != nil { t.Fatal(err) } - res, err := sender.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) + res, err := sender.StateWaitMsg(ctx, sm.Cid(), 3, lapi.LookbackNoLimit, true) if err != nil { t.Fatal(err) } diff --git a/chain/messagepool/check.go b/chain/messagepool/check.go index 71389d9e2..8d6463691 100644 --- a/chain/messagepool/check.go +++ b/chain/messagepool/check.go @@ -100,6 +100,9 @@ func (mp *MessagePool) CheckReplaceMessages(replace []*types.Message) ([][]api.M // flexibleNonces should be either nil or of len(msgs), it signifies that message at given index // has non-determied nonce at this point func (mp *MessagePool) checkMessages(msgs []*types.Message, interned bool, flexibleNonces []bool) (result [][]api.MessageCheckStatus, err error) { + if mp.api.IsLite() { + return nil, nil + } mp.curTsLk.Lock() curTs := mp.curTs mp.curTsLk.Unlock() diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index fb5f0acba..93dce1df0 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -832,8 +832,8 @@ func (mp *MessagePool) getNonceLocked(addr address.Address, curTs *types.TipSet) return stateNonce, nil } -func (mp *MessagePool) getStateNonce(addr address.Address, curTs *types.TipSet) (uint64, error) { - act, err := mp.api.GetActorAfter(addr, curTs) +func (mp *MessagePool) getStateNonce(addr address.Address, ts *types.TipSet) (uint64, error) { + act, err := mp.api.GetActorAfter(addr, ts) if err != nil { return 0, err } diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 8e4f16a30..925ee438c 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -103,6 +103,9 @@ func (tma *testMpoolAPI) SubscribeHeadChanges(cb func(rev, app []*types.TipSet) func (tma *testMpoolAPI) PutMessage(m types.ChainMsg) (cid.Cid, error) { return cid.Undef, nil } +func (tma *testMpoolAPI) IsLite() bool { + return false +} func (tma *testMpoolAPI) PubSubPublish(string, []byte) error { tma.published++ diff --git a/chain/messagepool/provider.go b/chain/messagepool/provider.go index 1c64cdcdf..565691004 100644 --- a/chain/messagepool/provider.go +++ b/chain/messagepool/provider.go @@ -31,6 +31,7 @@ type Provider interface { MessagesForTipset(*types.TipSet) ([]types.ChainMsg, error) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error) ChainComputeBaseFee(ctx context.Context, ts *types.TipSet) (types.BigInt, error) + IsLite() bool } type mpoolProvider struct { @@ -48,6 +49,10 @@ func NewProviderLite(sm *stmgr.StateManager, ps *pubsub.PubSub, noncer messagesi return &mpoolProvider{sm: sm, ps: ps, lite: noncer} } +func (mpp *mpoolProvider) IsLite() bool { + return mpp.lite != nil +} + func (mpp *mpoolProvider) SubscribeHeadChanges(cb func(rev, app []*types.TipSet) error) *types.TipSet { mpp.sm.ChainStore().SubscribeHeadChanges( store.WrapHeadChangeCoalescer( @@ -68,7 +73,7 @@ func (mpp *mpoolProvider) PubSubPublish(k string, v []byte) error { } func (mpp *mpoolProvider) GetActorAfter(addr address.Address, ts *types.TipSet) (*types.Actor, error) { - if mpp.lite != nil { + if mpp.IsLite() { n, err := mpp.lite.GetNonce(context.TODO(), addr, ts.Key()) if err != nil { return nil, xerrors.Errorf("getting nonce over lite: %w", err) diff --git a/cli/test/util.go b/cli/test/util.go index e3930dc83..d7959b9d5 100644 --- a/cli/test/util.go +++ b/cli/test/util.go @@ -9,4 +9,6 @@ func QuietMiningLogs() { _ = log.SetLogLevel("sub", "ERROR") _ = log.SetLogLevel("storageminer", "ERROR") _ = log.SetLogLevel("pubsub", "ERROR") + _ = log.SetLogLevel("gen", "ERROR") + _ = log.SetLogLevel("dht/RtRefreshManager", "ERROR") } diff --git a/cmd/lotus-gateway/endtoend_test.go b/cmd/lotus-gateway/endtoend_test.go index fa1004df3..3fae221c3 100644 --- a/cmd/lotus-gateway/endtoend_test.go +++ b/cmd/lotus-gateway/endtoend_test.go @@ -333,7 +333,7 @@ func sendFunds(ctx context.Context, fromNode test.TestNode, fromAddr address.Add return err } - res, err := fromNode.StateWaitMsg(ctx, sm.Cid(), 1, api.LookbackNoLimit, true) + res, err := fromNode.StateWaitMsg(ctx, sm.Cid(), 3, api.LookbackNoLimit, true) if err != nil { return err } diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 59fdd8e2e..b3639c5e0 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -426,7 +426,7 @@ func (a *StateAPI) StateReplay(ctx context.Context, tsk types.TipSetKey, mc cid. }, nil } -func (m *StateModule) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*types.Actor, error) { +func (m *StateModule) StateGetActor(ctx context.Context, actor address.Address, tsk types.TipSetKey) (a *types.Actor, err error) { ts, err := m.Chain.GetTipSetFromKey(tsk) if err != nil { return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) diff --git a/node/modules/mpoolnonceapi.go b/node/modules/mpoolnonceapi.go index 3b30d24cf..67f512960 100644 --- a/node/modules/mpoolnonceapi.go +++ b/node/modules/mpoolnonceapi.go @@ -63,7 +63,7 @@ func (a *MpoolNonceAPI) GetNonce(ctx context.Context, addr address.Address, tsk act, err := a.StateModule.StateGetActor(ctx, keyAddr, ts.Key()) if err != nil { if strings.Contains(err.Error(), types.ErrActorNotFound.Error()) { - return 0, types.ErrActorNotFound + return 0, xerrors.Errorf("getting actor converted: %w", types.ErrActorNotFound) } return 0, xerrors.Errorf("getting actor: %w", err) } From 0db070779f45903ba8138a86918edf0383af7e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 26 Apr 2021 16:48:20 +0200 Subject: [PATCH 77/97] wip actor wrapper codegen --- chain/actors/agen/main.go | 87 +++++++++++++++++++ .../actors/builtin/account/actor.go.template | 37 ++++++++ .../actors/builtin/account/state.go.template | 30 +++++++ 3 files changed, 154 insertions(+) create mode 100644 chain/actors/agen/main.go create mode 100644 chain/actors/builtin/account/actor.go.template create mode 100644 chain/actors/builtin/account/state.go.template diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go new file mode 100644 index 000000000..702d309ac --- /dev/null +++ b/chain/actors/agen/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "bytes" + "fmt" + "golang.org/x/xerrors" + "io/ioutil" + "path/filepath" + "text/template" +) + +func main() { + if err := run(); err != nil { + fmt.Println(err) + return + } +} + +func run() error { + versions := []int{0, 2, 3, 4} + + versionImports := map[int]string{ + 0: "/", + 2: "/v2/", + 3: "/v3/", + 4: "/v4/", + } + + actors := map[string][]int{ + "account": versions, + } + + for act, versions := range actors { + actDir := filepath.Join("chain/actors/builtin", act) + + { + af, err := ioutil.ReadFile(filepath.Join(actDir, "state.go.template")) + if err != nil { + return xerrors.Errorf("loading state adapter template: %w", err) + } + + for _, version := range versions { + tpl := template.Must(template.New("").Funcs(template.FuncMap{}).Parse(string(af))) + + var b bytes.Buffer + + err := tpl.Execute(&b, map[string]interface{}{ + "v": version, + "import": versionImports[version], + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("v%d.go", version)), b.Bytes(), 0666); err != nil { + return err + } + } + } + + { + af, err := ioutil.ReadFile(filepath.Join(actDir, "actor.go.template")) + if err != nil { + return xerrors.Errorf("loading actor template: %w", err) + } + + tpl := template.Must(template.New("").Funcs(template.FuncMap{ + "import": func(v int) string { return versionImports[v] }, + }).Parse(string(af))) + + var b bytes.Buffer + + err = tpl.Execute(&b, map[string]interface{}{ + "versions": versions, + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("%s.go", act)), b.Bytes(), 0666); err != nil { + return err + } + } + } + + return nil +} diff --git a/chain/actors/builtin/account/actor.go.template b/chain/actors/builtin/account/actor.go.template new file mode 100644 index 000000000..860dadc1c --- /dev/null +++ b/chain/actors/builtin/account/actor.go.template @@ -0,0 +1,37 @@ +package account + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var Methods = builtin4.MethodsAccount + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.AccountActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + PubkeyAddress() (address.Address, error) +} diff --git a/chain/actors/builtin/account/state.go.template b/chain/actors/builtin/account/state.go.template new file mode 100644 index 000000000..65d874c80 --- /dev/null +++ b/chain/actors/builtin/account/state.go.template @@ -0,0 +1,30 @@ +package account + +import ( + "github.com/filecoin-project/go-address" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + + account{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/account" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + account{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) PubkeyAddress() (address.Address, error) { + return s.Address, nil +} From 1a84bd584207c5a58dd4f196d7d5495df41601db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Apr 2021 20:48:32 +0200 Subject: [PATCH 78/97] chain actors: codegen templates for all actors --- chain/actors/agen/main.go | 130 +++-- chain/actors/builtin/cron/actor.go.template | 10 + chain/actors/builtin/init/actor.go.template | 56 +++ chain/actors/builtin/init/state.go.template | 86 ++++ chain/actors/builtin/market/actor.go.template | 138 ++++++ chain/actors/builtin/market/market.go | 5 +- chain/actors/builtin/market/state.go.template | 209 ++++++++ chain/actors/builtin/market/v0.go | 3 +- chain/actors/builtin/market/v2.go | 32 +- chain/actors/builtin/market/v3.go | 56 +-- chain/actors/builtin/market/v4.go | 48 +- chain/actors/builtin/miner/actor.go.template | 265 +++++++++++ chain/actors/builtin/miner/miner.go | 9 +- chain/actors/builtin/miner/state.go.template | 446 ++++++++++++++++++ chain/actors/builtin/miner/v0.go | 1 + chain/actors/builtin/miner/v3.go | 22 +- chain/actors/builtin/miner/v4.go | 22 +- .../actors/builtin/multisig/actor.go.template | 113 +++++ chain/actors/builtin/multisig/message.go | 79 ---- .../builtin/multisig/message.go.template | 143 ++++++ .../multisig/{state.go => multisig.go} | 70 ++- .../actors/builtin/multisig/state.go.template | 95 ++++ .../builtin/multisig/{state0.go => v0.go} | 6 +- .../builtin/multisig/{state2.go => v2.go} | 3 +- .../builtin/multisig/{state3.go => v3.go} | 4 +- .../builtin/multisig/{state4.go => v4.go} | 6 +- chain/actors/builtin/paych/actor.go.template | 103 ++++ chain/actors/builtin/paych/message.go | 36 -- .../actors/builtin/paych/message.go.template | 74 +++ .../builtin/paych/{state.go => paych.go} | 29 +- chain/actors/builtin/paych/state.go.template | 104 ++++ .../actors/builtin/paych/{state0.go => v0.go} | 0 .../actors/builtin/paych/{state2.go => v2.go} | 0 .../actors/builtin/paych/{state3.go => v3.go} | 0 .../actors/builtin/paych/{state4.go => v4.go} | 0 chain/actors/builtin/power/actor.go.template | 74 +++ chain/actors/builtin/power/power.go | 2 +- chain/actors/builtin/power/state.go.template | 149 ++++++ chain/actors/builtin/power/v0.go | 11 +- chain/actors/builtin/power/v2.go | 6 +- chain/actors/builtin/power/v3.go | 4 +- chain/actors/builtin/power/v4.go | 4 +- chain/actors/builtin/reward/actor.go.template | 56 +++ chain/actors/builtin/reward/reward.go | 3 +- chain/actors/builtin/reward/state.go.template | 99 ++++ chain/actors/builtin/reward/v0.go | 6 +- .../actors/builtin/verifreg/actor.go.template | 46 ++ .../actors/builtin/verifreg/state.go.template | 58 +++ chain/actors/builtin/verifreg/verifreg.go | 1 + 49 files changed, 2647 insertions(+), 275 deletions(-) create mode 100644 chain/actors/builtin/cron/actor.go.template create mode 100644 chain/actors/builtin/init/actor.go.template create mode 100644 chain/actors/builtin/init/state.go.template create mode 100644 chain/actors/builtin/market/actor.go.template create mode 100644 chain/actors/builtin/market/state.go.template create mode 100644 chain/actors/builtin/miner/actor.go.template create mode 100644 chain/actors/builtin/miner/state.go.template create mode 100644 chain/actors/builtin/multisig/actor.go.template delete mode 100644 chain/actors/builtin/multisig/message.go create mode 100644 chain/actors/builtin/multisig/message.go.template rename chain/actors/builtin/multisig/{state.go => multisig.go} (51%) create mode 100644 chain/actors/builtin/multisig/state.go.template rename chain/actors/builtin/multisig/{state0.go => v0.go} (95%) rename chain/actors/builtin/multisig/{state2.go => v2.go} (99%) rename chain/actors/builtin/multisig/{state3.go => v3.go} (96%) rename chain/actors/builtin/multisig/{state4.go => v4.go} (93%) create mode 100644 chain/actors/builtin/paych/actor.go.template delete mode 100644 chain/actors/builtin/paych/message.go create mode 100644 chain/actors/builtin/paych/message.go.template rename chain/actors/builtin/paych/{state.go => paych.go} (80%) create mode 100644 chain/actors/builtin/paych/state.go.template rename chain/actors/builtin/paych/{state0.go => v0.go} (100%) rename chain/actors/builtin/paych/{state2.go => v2.go} (100%) rename chain/actors/builtin/paych/{state3.go => v3.go} (100%) rename chain/actors/builtin/paych/{state4.go => v4.go} (100%) create mode 100644 chain/actors/builtin/power/actor.go.template create mode 100644 chain/actors/builtin/power/state.go.template create mode 100644 chain/actors/builtin/reward/actor.go.template create mode 100644 chain/actors/builtin/reward/state.go.template create mode 100644 chain/actors/builtin/verifreg/actor.go.template create mode 100644 chain/actors/builtin/verifreg/state.go.template diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go index 702d309ac..0d73a064e 100644 --- a/chain/actors/agen/main.go +++ b/chain/actors/agen/main.go @@ -5,10 +5,35 @@ import ( "fmt" "golang.org/x/xerrors" "io/ioutil" + "os" "path/filepath" "text/template" ) +var latestVersion = 4 + +var versions = []int{0, 2, 3, latestVersion} + +var versionImports = map[int]string{ + 0: "/", + 2: "/v2/", + 3: "/v3/", + latestVersion: "/v4/", +} + +var actors = map[string][]int{ + "account": versions, + "cron": versions, + "init": versions, + "market": versions, + "miner": versions, + "multisig": versions, + "paych": versions, + "power": versions, + "reward": versions, + "verifreg": versions, +} + func main() { if err := run(); err != nil { fmt.Println(err) @@ -17,45 +42,15 @@ func main() { } func run() error { - versions := []int{0, 2, 3, 4} - - versionImports := map[int]string{ - 0: "/", - 2: "/v2/", - 3: "/v3/", - 4: "/v4/", - } - - actors := map[string][]int{ - "account": versions, - } - for act, versions := range actors { actDir := filepath.Join("chain/actors/builtin", act) - { - af, err := ioutil.ReadFile(filepath.Join(actDir, "state.go.template")) - if err != nil { - return xerrors.Errorf("loading state adapter template: %w", err) - } + if err := generateState(actDir); err != nil { + return err + } - for _, version := range versions { - tpl := template.Must(template.New("").Funcs(template.FuncMap{}).Parse(string(af))) - - var b bytes.Buffer - - err := tpl.Execute(&b, map[string]interface{}{ - "v": version, - "import": versionImports[version], - }) - if err != nil { - return err - } - - if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("v%d.go", version)), b.Bytes(), 0666); err != nil { - return err - } - } + if err := generateMessages(actDir); err != nil { + return err } { @@ -71,7 +66,8 @@ func run() error { var b bytes.Buffer err = tpl.Execute(&b, map[string]interface{}{ - "versions": versions, + "versions": versions, + "latestVersion": latestVersion, }) if err != nil { return err @@ -85,3 +81,65 @@ func run() error { return nil } + +func generateState(actDir string) error { + af, err := ioutil.ReadFile(filepath.Join(actDir, "state.go.template")) + if err != nil { + if os.IsNotExist(err) { + return nil // skip + } + + return xerrors.Errorf("loading state adapter template: %w", err) + } + + for _, version := range versions { + tpl := template.Must(template.New("").Funcs(template.FuncMap{}).Parse(string(af))) + + var b bytes.Buffer + + err := tpl.Execute(&b, map[string]interface{}{ + "v": version, + "import": versionImports[version], + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("v%d.go", version)), b.Bytes(), 0666); err != nil { + return err + } + } + + return nil +} + +func generateMessages(actDir string) error { + af, err := ioutil.ReadFile(filepath.Join(actDir, "message.go.template")) + if err != nil { + if os.IsNotExist(err) { + return nil // skip + } + + return xerrors.Errorf("loading state adapter template: %w", err) + } + + for _, version := range versions { + tpl := template.Must(template.New("").Funcs(template.FuncMap{}).Parse(string(af))) + + var b bytes.Buffer + + err := tpl.Execute(&b, map[string]interface{}{ + "v": version, + "import": versionImports[version], + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("message%d.go", version)), b.Bytes(), 0666); err != nil { + return err + } + } + + return nil +} diff --git a/chain/actors/builtin/cron/actor.go.template b/chain/actors/builtin/cron/actor.go.template new file mode 100644 index 000000000..6b7449617 --- /dev/null +++ b/chain/actors/builtin/cron/actor.go.template @@ -0,0 +1,10 @@ +package cron + +import ( + builtin{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/builtin" +) + +var ( + Address = builtin{{.latestVersion}}.CronActorAddr + Methods = builtin{{.latestVersion}}.MethodsCron +) diff --git a/chain/actors/builtin/init/actor.go.template b/chain/actors/builtin/init/actor.go.template new file mode 100644 index 000000000..0c9a6c702 --- /dev/null +++ b/chain/actors/builtin/init/actor.go.template @@ -0,0 +1,56 @@ +package init + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/node/modules/dtypes" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.InitActorAddr + Methods = builtin{{.latestVersion}}.MethodsInit +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.InitActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + ResolveAddress(address address.Address) (address.Address, bool, error) + MapAddressToNewID(address address.Address) (address.Address, error) + NetworkName() (dtypes.NetworkName, error) + + ForEachActor(func(id abi.ActorID, address address.Address) error) error + + // Remove exists to support tooling that manipulates state for testing. + // It should not be used in production code, as init actor entries are + // immutable. + Remove(addrs ...address.Address) error + + // Sets the network's name. This should only be used on upgrade/fork. + SetNetworkName(name string) error + + addressMap() (adt.Map, error) +} diff --git a/chain/actors/builtin/init/state.go.template b/chain/actors/builtin/init/state.go.template new file mode 100644 index 000000000..3fd7aaaa7 --- /dev/null +++ b/chain/actors/builtin/init/state.go.template @@ -0,0 +1,86 @@ +package init + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" +{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/node/modules/dtypes" + + init{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/init" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + init{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) ResolveAddress(address address.Address) (address.Address, bool, error) { + return s.State.ResolveAddress(s.store, address) +} + +func (s *state{{.v}}) MapAddressToNewID(address address.Address) (address.Address, error) { + return s.State.MapAddressToNewID(s.store, address) +} + +func (s *state{{.v}}) ForEachActor(cb func(id abi.ActorID, address address.Address) error) error { + addrs, err := adt{{.v}}.AsMap(s.store, s.State.AddressMap{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) + if err != nil { + return err + } + var actorID cbg.CborInt + return addrs.ForEach(&actorID, func(key string) error { + addr, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(abi.ActorID(actorID), addr) + }) +} + +func (s *state{{.v}}) NetworkName() (dtypes.NetworkName, error) { + return dtypes.NetworkName(s.State.NetworkName), nil +} + +func (s *state{{.v}}) SetNetworkName(name string) error { + s.State.NetworkName = name + return nil +} + +func (s *state{{.v}}) Remove(addrs ...address.Address) (err error) { + m, err := adt{{.v}}.AsMap(s.store, s.State.AddressMap{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) + if err != nil { + return err + } + for _, addr := range addrs { + if err = m.Delete(abi.AddrKey(addr)); err != nil { + return xerrors.Errorf("failed to delete entry for address: %s; err: %w", addr, err) + } + } + amr, err := m.Root() + if err != nil { + return xerrors.Errorf("failed to get address map root: %w", err) + } + s.State.AddressMap = amr + return nil +} + +func (s *state{{.v}}) addressMap() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.AddressMap{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} diff --git a/chain/actors/builtin/market/actor.go.template b/chain/actors/builtin/market/actor.go.template new file mode 100644 index 000000000..8c16ffb91 --- /dev/null +++ b/chain/actors/builtin/market/actor.go.template @@ -0,0 +1,138 @@ +package market + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.StorageMarketActorAddr + Methods = builtin{{.latestVersion}}.MethodsMarket +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.StorageMarketActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + BalancesChanged(State) (bool, error) + EscrowTable() (BalanceTable, error) + LockedTable() (BalanceTable, error) + TotalLocked() (abi.TokenAmount, error) + StatesChanged(State) (bool, error) + States() (DealStates, error) + ProposalsChanged(State) (bool, error) + Proposals() (DealProposals, error) + VerifyDealsForActivation( + minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, + ) (weight, verifiedWeight abi.DealWeight, err error) + NextID() (abi.DealID, error) +} + +type BalanceTable interface { + ForEach(cb func(address.Address, abi.TokenAmount) error) error + Get(key address.Address) (abi.TokenAmount, error) +} + +type DealStates interface { + ForEach(cb func(id abi.DealID, ds DealState) error) error + Get(id abi.DealID) (*DealState, bool, error) + + array() adt.Array + decode(*cbg.Deferred) (*DealState, error) +} + +type DealProposals interface { + ForEach(cb func(id abi.DealID, dp DealProposal) error) error + Get(id abi.DealID) (*DealProposal, bool, error) + + array() adt.Array + decode(*cbg.Deferred) (*DealProposal, error) +} + +type PublishStorageDealsParams = market0.PublishStorageDealsParams +type PublishStorageDealsReturn = market0.PublishStorageDealsReturn +type VerifyDealsForActivationParams = market0.VerifyDealsForActivationParams +type WithdrawBalanceParams = market0.WithdrawBalanceParams + +type ClientDealProposal = market0.ClientDealProposal + +type DealState struct { + SectorStartEpoch abi.ChainEpoch // -1 if not yet included in proven sector + LastUpdatedEpoch abi.ChainEpoch // -1 if deal state never updated + SlashEpoch abi.ChainEpoch // -1 if deal never slashed +} + +type DealProposal struct { + PieceCID cid.Cid + PieceSize abi.PaddedPieceSize + VerifiedDeal bool + Client address.Address + Provider address.Address + Label string + StartEpoch abi.ChainEpoch + EndEpoch abi.ChainEpoch + StoragePricePerEpoch abi.TokenAmount + ProviderCollateral abi.TokenAmount + ClientCollateral abi.TokenAmount +} + +type DealStateChanges struct { + Added []DealIDState + Modified []DealStateChange + Removed []DealIDState +} + +type DealIDState struct { + ID abi.DealID + Deal DealState +} + +// DealStateChange is a change in deal state from -> to +type DealStateChange struct { + ID abi.DealID + From *DealState + To *DealState +} + +type DealProposalChanges struct { + Added []ProposalIDState + Removed []ProposalIDState +} + +type ProposalIDState struct { + ID abi.DealID + Proposal DealProposal +} + +func EmptyDealState() *DealState { + return &DealState{ + SectorStartEpoch: -1, + SlashEpoch: -1, + LastUpdatedEpoch: -1, + } +} diff --git a/chain/actors/builtin/market/market.go b/chain/actors/builtin/market/market.go index 33729bdf9..16c44339b 100644 --- a/chain/actors/builtin/market/market.go +++ b/chain/actors/builtin/market/market.go @@ -10,8 +10,9 @@ import ( "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" @@ -41,7 +42,7 @@ var ( Methods = builtin4.MethodsMarket ) -func Load(store adt.Store, act *types.Actor) (st State, err error) { +func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { case builtin0.StorageMarketActorCodeID: return load0(store, act.Head) diff --git a/chain/actors/builtin/market/state.go.template b/chain/actors/builtin/market/state.go.template new file mode 100644 index 000000000..a55743ce5 --- /dev/null +++ b/chain/actors/builtin/market/state.go.template @@ -0,0 +1,209 @@ +package market + +import ( + "bytes" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/types" + + market{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/market" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + market{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) TotalLocked() (abi.TokenAmount, error) { + fml := types.BigAdd(s.TotalClientLockedCollateral, s.TotalProviderLockedCollateral) + fml = types.BigAdd(fml, s.TotalClientStorageFee) + return fml, nil +} + +func (s *state{{.v}}) BalancesChanged(otherState State) (bool, error) { + otherState{{.v}}, ok := otherState.(*state{{.v}}) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.EscrowTable.Equals(otherState{{.v}}.State.EscrowTable) || !s.State.LockedTable.Equals(otherState{{.v}}.State.LockedTable), nil +} + +func (s *state{{.v}}) StatesChanged(otherState State) (bool, error) { + otherState{{.v}}, ok := otherState.(*state{{.v}}) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.States.Equals(otherState{{.v}}.State.States), nil +} + +func (s *state{{.v}}) States() (DealStates, error) { + stateArray, err := adt{{.v}}.AsArray(s.store, s.State.States{{if (ge .v 3)}}, market{{.v}}.StatesAmtBitwidth{{end}}) + if err != nil { + return nil, err + } + return &dealStates{{.v}}{stateArray}, nil +} + +func (s *state{{.v}}) ProposalsChanged(otherState State) (bool, error) { + otherState{{.v}}, ok := otherState.(*state{{.v}}) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.Proposals.Equals(otherState{{.v}}.State.Proposals), nil +} + +func (s *state{{.v}}) Proposals() (DealProposals, error) { + proposalArray, err := adt{{.v}}.AsArray(s.store, s.State.Proposals{{if (ge .v 3)}}, market{{.v}}.ProposalsAmtBitwidth{{end}}) + if err != nil { + return nil, err + } + return &dealProposals{{.v}}{proposalArray}, nil +} + +func (s *state{{.v}}) EscrowTable() (BalanceTable, error) { + bt, err := adt{{.v}}.AsBalanceTable(s.store, s.State.EscrowTable) + if err != nil { + return nil, err + } + return &balanceTable{{.v}}{bt}, nil +} + +func (s *state{{.v}}) LockedTable() (BalanceTable, error) { + bt, err := adt{{.v}}.AsBalanceTable(s.store, s.State.LockedTable) + if err != nil { + return nil, err + } + return &balanceTable{{.v}}{bt}, nil +} + +func (s *state{{.v}}) VerifyDealsForActivation( + minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, +) (weight, verifiedWeight abi.DealWeight, err error) { + w, vw{{if (ge .v 2)}}, _{{end}}, err := market{{.v}}.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + return w, vw, err +} + +func (s *state{{.v}}) NextID() (abi.DealID, error) { + return s.State.NextID, nil +} + +type balanceTable{{.v}} struct { + *adt{{.v}}.BalanceTable +} + +func (bt *balanceTable{{.v}}) ForEach(cb func(address.Address, abi.TokenAmount) error) error { + asMap := (*adt{{.v}}.Map)(bt.BalanceTable) + var ta abi.TokenAmount + return asMap.ForEach(&ta, func(key string) error { + a, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(a, ta) + }) +} + +type dealStates{{.v}} struct { + adt.Array +} + +func (s *dealStates{{.v}}) Get(dealID abi.DealID) (*DealState, bool, error) { + var deal{{.v}} market{{.v}}.DealState + found, err := s.Array.Get(uint64(dealID), &deal{{.v}}) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + deal := fromV{{.v}}DealState(deal{{.v}}) + return &deal, true, nil +} + +func (s *dealStates{{.v}}) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { + var ds{{.v}} market{{.v}}.DealState + return s.Array.ForEach(&ds{{.v}}, func(idx int64) error { + return cb(abi.DealID(idx), fromV{{.v}}DealState(ds{{.v}})) + }) +} + +func (s *dealStates{{.v}}) decode(val *cbg.Deferred) (*DealState, error) { + var ds{{.v}} market{{.v}}.DealState + if err := ds{{.v}}.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + ds := fromV{{.v}}DealState(ds{{.v}}) + return &ds, nil +} + +func (s *dealStates{{.v}}) array() adt.Array { + return s.Array +} + +func fromV{{.v}}DealState(v{{.v}} market{{.v}}.DealState) DealState { + return (DealState)(v{{.v}}) +} + +type dealProposals{{.v}} struct { + adt.Array +} + +func (s *dealProposals{{.v}}) Get(dealID abi.DealID) (*DealProposal, bool, error) { + var proposal{{.v}} market{{.v}}.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal{{.v}}) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + proposal := fromV{{.v}}DealProposal(proposal{{.v}}) + return &proposal, true, nil +} + +func (s *dealProposals{{.v}}) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { + var dp{{.v}} market{{.v}}.DealProposal + return s.Array.ForEach(&dp{{.v}}, func(idx int64) error { + return cb(abi.DealID(idx), fromV{{.v}}DealProposal(dp{{.v}})) + }) +} + +func (s *dealProposals{{.v}}) decode(val *cbg.Deferred) (*DealProposal, error) { + var dp{{.v}} market{{.v}}.DealProposal + if err := dp{{.v}}.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + dp := fromV{{.v}}DealProposal(dp{{.v}}) + return &dp, nil +} + +func (s *dealProposals{{.v}}) array() adt.Array { + return s.Array +} + +func fromV{{.v}}DealProposal(v{{.v}} market{{.v}}.DealProposal) DealProposal { + return (DealProposal)(v{{.v}}) +} diff --git a/chain/actors/builtin/market/v0.go b/chain/actors/builtin/market/v0.go index f3b885995..175c0a2ea 100644 --- a/chain/actors/builtin/market/v0.go +++ b/chain/actors/builtin/market/v0.go @@ -102,7 +102,8 @@ func (s *state0) LockedTable() (BalanceTable, error) { func (s *state0) VerifyDealsForActivation( minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, ) (weight, verifiedWeight abi.DealWeight, err error) { - return market0.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + w, vw, err := market0.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + return w, vw, err } func (s *state0) NextID() (abi.DealID, error) { diff --git a/chain/actors/builtin/market/v2.go b/chain/actors/builtin/market/v2.go index 1ce051c38..dafae8feb 100644 --- a/chain/actors/builtin/market/v2.go +++ b/chain/actors/builtin/market/v2.go @@ -144,18 +144,18 @@ func (s *dealStates2) Get(dealID abi.DealID) (*DealState, bool, error) { } func (s *dealStates2) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { - var ds1 market2.DealState - return s.Array.ForEach(&ds1, func(idx int64) error { - return cb(abi.DealID(idx), fromV2DealState(ds1)) + var ds2 market2.DealState + return s.Array.ForEach(&ds2, func(idx int64) error { + return cb(abi.DealID(idx), fromV2DealState(ds2)) }) } func (s *dealStates2) decode(val *cbg.Deferred) (*DealState, error) { - var ds1 market2.DealState - if err := ds1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var ds2 market2.DealState + if err := ds2.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - ds := fromV2DealState(ds1) + ds := fromV2DealState(ds2) return &ds, nil } @@ -163,8 +163,8 @@ func (s *dealStates2) array() adt.Array { return s.Array } -func fromV2DealState(v1 market2.DealState) DealState { - return (DealState)(v1) +func fromV2DealState(v2 market2.DealState) DealState { + return (DealState)(v2) } type dealProposals2 struct { @@ -185,18 +185,18 @@ func (s *dealProposals2) Get(dealID abi.DealID) (*DealProposal, bool, error) { } func (s *dealProposals2) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { - var dp1 market2.DealProposal - return s.Array.ForEach(&dp1, func(idx int64) error { - return cb(abi.DealID(idx), fromV2DealProposal(dp1)) + var dp2 market2.DealProposal + return s.Array.ForEach(&dp2, func(idx int64) error { + return cb(abi.DealID(idx), fromV2DealProposal(dp2)) }) } func (s *dealProposals2) decode(val *cbg.Deferred) (*DealProposal, error) { - var dp1 market2.DealProposal - if err := dp1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var dp2 market2.DealProposal + if err := dp2.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - dp := fromV2DealProposal(dp1) + dp := fromV2DealProposal(dp2) return &dp, nil } @@ -204,6 +204,6 @@ func (s *dealProposals2) array() adt.Array { return s.Array } -func fromV2DealProposal(v1 market2.DealProposal) DealProposal { - return (DealProposal)(v1) +func fromV2DealProposal(v2 market2.DealProposal) DealProposal { + return (DealProposal)(v2) } diff --git a/chain/actors/builtin/market/v3.go b/chain/actors/builtin/market/v3.go index 15251985b..dec8d6e25 100644 --- a/chain/actors/builtin/market/v3.go +++ b/chain/actors/builtin/market/v3.go @@ -38,23 +38,23 @@ func (s *state3) TotalLocked() (abi.TokenAmount, error) { } func (s *state3) BalancesChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state3) + otherState3, ok := otherState.(*state3) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.EscrowTable.Equals(otherState2.State.EscrowTable) || !s.State.LockedTable.Equals(otherState2.State.LockedTable), nil + return !s.State.EscrowTable.Equals(otherState3.State.EscrowTable) || !s.State.LockedTable.Equals(otherState3.State.LockedTable), nil } func (s *state3) StatesChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state3) + otherState3, ok := otherState.(*state3) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.States.Equals(otherState2.State.States), nil + return !s.State.States.Equals(otherState3.State.States), nil } func (s *state3) States() (DealStates, error) { @@ -66,13 +66,13 @@ func (s *state3) States() (DealStates, error) { } func (s *state3) ProposalsChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state3) + otherState3, ok := otherState.(*state3) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.Proposals.Equals(otherState2.State.Proposals), nil + return !s.State.Proposals.Equals(otherState3.State.Proposals), nil } func (s *state3) Proposals() (DealProposals, error) { @@ -131,31 +131,31 @@ type dealStates3 struct { } func (s *dealStates3) Get(dealID abi.DealID) (*DealState, bool, error) { - var deal2 market3.DealState - found, err := s.Array.Get(uint64(dealID), &deal2) + var deal3 market3.DealState + found, err := s.Array.Get(uint64(dealID), &deal3) if err != nil { return nil, false, err } if !found { return nil, false, nil } - deal := fromV3DealState(deal2) + deal := fromV3DealState(deal3) return &deal, true, nil } func (s *dealStates3) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { - var ds1 market3.DealState - return s.Array.ForEach(&ds1, func(idx int64) error { - return cb(abi.DealID(idx), fromV3DealState(ds1)) + var ds3 market3.DealState + return s.Array.ForEach(&ds3, func(idx int64) error { + return cb(abi.DealID(idx), fromV3DealState(ds3)) }) } func (s *dealStates3) decode(val *cbg.Deferred) (*DealState, error) { - var ds1 market3.DealState - if err := ds1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var ds3 market3.DealState + if err := ds3.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - ds := fromV3DealState(ds1) + ds := fromV3DealState(ds3) return &ds, nil } @@ -163,8 +163,8 @@ func (s *dealStates3) array() adt.Array { return s.Array } -func fromV3DealState(v1 market3.DealState) DealState { - return (DealState)(v1) +func fromV3DealState(v3 market3.DealState) DealState { + return (DealState)(v3) } type dealProposals3 struct { @@ -172,31 +172,31 @@ type dealProposals3 struct { } func (s *dealProposals3) Get(dealID abi.DealID) (*DealProposal, bool, error) { - var proposal2 market3.DealProposal - found, err := s.Array.Get(uint64(dealID), &proposal2) + var proposal3 market3.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal3) if err != nil { return nil, false, err } if !found { return nil, false, nil } - proposal := fromV3DealProposal(proposal2) + proposal := fromV3DealProposal(proposal3) return &proposal, true, nil } func (s *dealProposals3) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { - var dp1 market3.DealProposal - return s.Array.ForEach(&dp1, func(idx int64) error { - return cb(abi.DealID(idx), fromV3DealProposal(dp1)) + var dp3 market3.DealProposal + return s.Array.ForEach(&dp3, func(idx int64) error { + return cb(abi.DealID(idx), fromV3DealProposal(dp3)) }) } func (s *dealProposals3) decode(val *cbg.Deferred) (*DealProposal, error) { - var dp1 market3.DealProposal - if err := dp1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var dp3 market3.DealProposal + if err := dp3.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - dp := fromV3DealProposal(dp1) + dp := fromV3DealProposal(dp3) return &dp, nil } @@ -204,6 +204,6 @@ func (s *dealProposals3) array() adt.Array { return s.Array } -func fromV3DealProposal(v1 market3.DealProposal) DealProposal { - return (DealProposal)(v1) +func fromV3DealProposal(v3 market3.DealProposal) DealProposal { + return (DealProposal)(v3) } diff --git a/chain/actors/builtin/market/v4.go b/chain/actors/builtin/market/v4.go index dede98d1a..22514395c 100644 --- a/chain/actors/builtin/market/v4.go +++ b/chain/actors/builtin/market/v4.go @@ -38,23 +38,23 @@ func (s *state4) TotalLocked() (abi.TokenAmount, error) { } func (s *state4) BalancesChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state4) + otherState4, ok := otherState.(*state4) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.EscrowTable.Equals(otherState2.State.EscrowTable) || !s.State.LockedTable.Equals(otherState2.State.LockedTable), nil + return !s.State.EscrowTable.Equals(otherState4.State.EscrowTable) || !s.State.LockedTable.Equals(otherState4.State.LockedTable), nil } func (s *state4) StatesChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state4) + otherState4, ok := otherState.(*state4) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.States.Equals(otherState2.State.States), nil + return !s.State.States.Equals(otherState4.State.States), nil } func (s *state4) States() (DealStates, error) { @@ -66,13 +66,13 @@ func (s *state4) States() (DealStates, error) { } func (s *state4) ProposalsChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state4) + otherState4, ok := otherState.(*state4) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.Proposals.Equals(otherState2.State.Proposals), nil + return !s.State.Proposals.Equals(otherState4.State.Proposals), nil } func (s *state4) Proposals() (DealProposals, error) { @@ -131,31 +131,31 @@ type dealStates4 struct { } func (s *dealStates4) Get(dealID abi.DealID) (*DealState, bool, error) { - var deal2 market4.DealState - found, err := s.Array.Get(uint64(dealID), &deal2) + var deal4 market4.DealState + found, err := s.Array.Get(uint64(dealID), &deal4) if err != nil { return nil, false, err } if !found { return nil, false, nil } - deal := fromV4DealState(deal2) + deal := fromV4DealState(deal4) return &deal, true, nil } func (s *dealStates4) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { - var ds1 market4.DealState - return s.Array.ForEach(&ds1, func(idx int64) error { - return cb(abi.DealID(idx), fromV4DealState(ds1)) + var ds4 market4.DealState + return s.Array.ForEach(&ds4, func(idx int64) error { + return cb(abi.DealID(idx), fromV4DealState(ds4)) }) } func (s *dealStates4) decode(val *cbg.Deferred) (*DealState, error) { - var ds1 market4.DealState - if err := ds1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var ds4 market4.DealState + if err := ds4.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - ds := fromV4DealState(ds1) + ds := fromV4DealState(ds4) return &ds, nil } @@ -172,31 +172,31 @@ type dealProposals4 struct { } func (s *dealProposals4) Get(dealID abi.DealID) (*DealProposal, bool, error) { - var proposal2 market4.DealProposal - found, err := s.Array.Get(uint64(dealID), &proposal2) + var proposal4 market4.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal4) if err != nil { return nil, false, err } if !found { return nil, false, nil } - proposal := fromV4DealProposal(proposal2) + proposal := fromV4DealProposal(proposal4) return &proposal, true, nil } func (s *dealProposals4) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { - var dp1 market4.DealProposal - return s.Array.ForEach(&dp1, func(idx int64) error { - return cb(abi.DealID(idx), fromV4DealProposal(dp1)) + var dp4 market4.DealProposal + return s.Array.ForEach(&dp4, func(idx int64) error { + return cb(abi.DealID(idx), fromV4DealProposal(dp4)) }) } func (s *dealProposals4) decode(val *cbg.Deferred) (*DealProposal, error) { - var dp1 market4.DealProposal - if err := dp1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var dp4 market4.DealProposal + if err := dp4.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - dp := fromV4DealProposal(dp1) + dp := fromV4DealProposal(dp4) return &dp, nil } diff --git a/chain/actors/builtin/miner/actor.go.template b/chain/actors/builtin/miner/actor.go.template new file mode 100644 index 000000000..4265af7dc --- /dev/null +++ b/chain/actors/builtin/miner/actor.go.template @@ -0,0 +1,265 @@ +package miner + +import ( + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/network" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p-core/peer" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "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/cbor" + "github.com/filecoin-project/go-state-types/dline" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" + + miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" + miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" + miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var Methods = builtin{{.latestVersion}}.MethodsMiner + +// Unchanged between v0, v2, v3, and v4 actors +var WPoStProvingPeriod = miner0.WPoStProvingPeriod +var WPoStPeriodDeadlines = miner0.WPoStPeriodDeadlines +var WPoStChallengeWindow = miner0.WPoStChallengeWindow +var WPoStChallengeLookback = miner0.WPoStChallengeLookback +var FaultDeclarationCutoff = miner0.FaultDeclarationCutoff + +const MinSectorExpiration = miner0.MinSectorExpiration + +// Not used / checked in v0 +// TODO: Abstract over network versions +var DeclarationsMax = miner2.DeclarationsMax +var AddressedSectorsMax = miner2.AddressedSectorsMax + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.StorageMinerActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + // Total available balance to spend. + AvailableBalance(abi.TokenAmount) (abi.TokenAmount, error) + // Funds that will vest by the given epoch. + VestedFunds(abi.ChainEpoch) (abi.TokenAmount, error) + // Funds locked for various reasons. + LockedFunds() (LockedFunds, error) + FeeDebt() (abi.TokenAmount, error) + + GetSector(abi.SectorNumber) (*SectorOnChainInfo, error) + FindSector(abi.SectorNumber) (*SectorLocation, error) + GetSectorExpiration(abi.SectorNumber) (*SectorExpiration, error) + GetPrecommittedSector(abi.SectorNumber) (*SectorPreCommitOnChainInfo, error) + LoadSectors(sectorNos *bitfield.BitField) ([]*SectorOnChainInfo, error) + NumLiveSectors() (uint64, error) + IsAllocated(abi.SectorNumber) (bool, error) + + LoadDeadline(idx uint64) (Deadline, error) + ForEachDeadline(cb func(idx uint64, dl Deadline) error) error + NumDeadlines() (uint64, error) + DeadlinesChanged(State) (bool, error) + + Info() (MinerInfo, error) + MinerInfoChanged(State) (bool, error) + + DeadlineInfo(epoch abi.ChainEpoch) (*dline.Info, error) + DeadlineCronActive() (bool, error) + + // Diff helpers. Used by Diff* functions internally. + sectors() (adt.Array, error) + decodeSectorOnChainInfo(*cbg.Deferred) (SectorOnChainInfo, error) + precommits() (adt.Map, error) + decodeSectorPreCommitOnChainInfo(*cbg.Deferred) (SectorPreCommitOnChainInfo, error) +} + +type Deadline interface { + LoadPartition(idx uint64) (Partition, error) + ForEachPartition(cb func(idx uint64, part Partition) error) error + PartitionsPoSted() (bitfield.BitField, error) + + PartitionsChanged(Deadline) (bool, error) + DisputableProofCount() (uint64, error) +} + +type Partition interface { + AllSectors() (bitfield.BitField, error) + FaultySectors() (bitfield.BitField, error) + RecoveringSectors() (bitfield.BitField, error) + LiveSectors() (bitfield.BitField, error) + ActiveSectors() (bitfield.BitField, error) +} + +type SectorOnChainInfo struct { + SectorNumber abi.SectorNumber + SealProof abi.RegisteredSealProof + SealedCID cid.Cid + DealIDs []abi.DealID + Activation abi.ChainEpoch + Expiration abi.ChainEpoch + DealWeight abi.DealWeight + VerifiedDealWeight abi.DealWeight + InitialPledge abi.TokenAmount + ExpectedDayReward abi.TokenAmount + ExpectedStoragePledge abi.TokenAmount +} + +type SectorPreCommitInfo = miner0.SectorPreCommitInfo + +type SectorPreCommitOnChainInfo struct { + Info SectorPreCommitInfo + PreCommitDeposit abi.TokenAmount + PreCommitEpoch abi.ChainEpoch + DealWeight abi.DealWeight + VerifiedDealWeight abi.DealWeight +} + +type PoStPartition = miner0.PoStPartition +type RecoveryDeclaration = miner0.RecoveryDeclaration +type FaultDeclaration = miner0.FaultDeclaration + +// Params +type DeclareFaultsParams = miner0.DeclareFaultsParams +type DeclareFaultsRecoveredParams = miner0.DeclareFaultsRecoveredParams +type SubmitWindowedPoStParams = miner0.SubmitWindowedPoStParams +type ProveCommitSectorParams = miner0.ProveCommitSectorParams +type DisputeWindowedPoStParams = miner3.DisputeWindowedPoStParams + +func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof) (abi.RegisteredSealProof, error) { + // We added support for the new proofs in network version 7, and removed support for the old + // ones in network version 8. + if nver < network.Version7 { + switch proof { + case abi.RegisteredPoStProof_StackedDrgWindow2KiBV1: + return abi.RegisteredSealProof_StackedDrg2KiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow8MiBV1: + return abi.RegisteredSealProof_StackedDrg8MiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: + return abi.RegisteredSealProof_StackedDrg512MiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow32GiBV1: + return abi.RegisteredSealProof_StackedDrg32GiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow64GiBV1: + return abi.RegisteredSealProof_StackedDrg64GiBV1, nil + default: + return -1, xerrors.Errorf("unrecognized window post type: %d", proof) + } + } + + switch proof { + case abi.RegisteredPoStProof_StackedDrgWindow2KiBV1: + return abi.RegisteredSealProof_StackedDrg2KiBV1_1, nil + case abi.RegisteredPoStProof_StackedDrgWindow8MiBV1: + return abi.RegisteredSealProof_StackedDrg8MiBV1_1, nil + case abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: + return abi.RegisteredSealProof_StackedDrg512MiBV1_1, nil + case abi.RegisteredPoStProof_StackedDrgWindow32GiBV1: + return abi.RegisteredSealProof_StackedDrg32GiBV1_1, nil + case abi.RegisteredPoStProof_StackedDrgWindow64GiBV1: + return abi.RegisteredSealProof_StackedDrg64GiBV1_1, nil + default: + return -1, xerrors.Errorf("unrecognized window post type: %d", proof) + } +} + +func WinningPoStProofTypeFromWindowPoStProofType(nver network.Version, proof abi.RegisteredPoStProof) (abi.RegisteredPoStProof, error) { + switch proof { + case abi.RegisteredPoStProof_StackedDrgWindow2KiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning2KiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow8MiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning8MiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning512MiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow32GiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning32GiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow64GiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning64GiBV1, nil + default: + return -1, xerrors.Errorf("unknown proof type %d", proof) + } +} + +type MinerInfo struct { + Owner address.Address // Must be an ID-address. + Worker address.Address // Must be an ID-address. + NewWorker address.Address // Must be an ID-address. + ControlAddresses []address.Address // Must be an ID-addresses. + WorkerChangeEpoch abi.ChainEpoch + PeerId *peer.ID + Multiaddrs []abi.Multiaddrs + WindowPoStProofType abi.RegisteredPoStProof + SectorSize abi.SectorSize + WindowPoStPartitionSectors uint64 + ConsensusFaultElapsed abi.ChainEpoch +} + +func (mi MinerInfo) IsController(addr address.Address) bool { + if addr == mi.Owner || addr == mi.Worker { + return true + } + + for _, ca := range mi.ControlAddresses { + if addr == ca { + return true + } + } + + return false +} + +type SectorExpiration struct { + OnTime abi.ChainEpoch + + // non-zero if sector is faulty, epoch at which it will be permanently + // removed if it doesn't recover + Early abi.ChainEpoch +} + +type SectorLocation struct { + Deadline uint64 + Partition uint64 +} + +type SectorChanges struct { + Added []SectorOnChainInfo + Extended []SectorExtensions + Removed []SectorOnChainInfo +} + +type SectorExtensions struct { + From SectorOnChainInfo + To SectorOnChainInfo +} + +type PreCommitChanges struct { + Added []SectorPreCommitOnChainInfo + Removed []SectorPreCommitOnChainInfo +} + +type LockedFunds struct { + VestingFunds abi.TokenAmount + InitialPledgeRequirement abi.TokenAmount + PreCommitDeposits abi.TokenAmount +} + +func (lf LockedFunds) TotalLockedFunds() abi.TokenAmount { + return big.Add(lf.VestingFunds, big.Add(lf.InitialPledgeRequirement, lf.PreCommitDeposits)) +} diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 8fffcc8d6..ae49a6d06 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -18,12 +18,13 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" - builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" - builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) @@ -58,7 +59,7 @@ const MinSectorExpiration = miner0.MinSectorExpiration var DeclarationsMax = miner2.DeclarationsMax var AddressedSectorsMax = miner2.AddressedSectorsMax -func Load(store adt.Store, act *types.Actor) (st State, err error) { +func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { case builtin0.StorageMinerActorCodeID: return load0(store, act.Head) diff --git a/chain/actors/builtin/miner/state.go.template b/chain/actors/builtin/miner/state.go.template new file mode 100644 index 000000000..df94c942c --- /dev/null +++ b/chain/actors/builtin/miner/state.go.template @@ -0,0 +1,446 @@ +package miner + +import ( + "bytes" + "errors" +{{if (le .v 1)}} + "github.com/filecoin-project/go-state-types/big" +{{end}} + "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/dline" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p-core/peer" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + +{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} miner{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/miner" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + miner{{.v}}.State + store adt.Store +} + +type deadline{{.v}} struct { + miner{{.v}}.Deadline + store adt.Store +} + +type partition{{.v}} struct { + miner{{.v}}.Partition + store adt.Store +} + +func (s *state{{.v}}) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) { + defer func() { + if r := recover(); r != nil { + err = xerrors.Errorf("failed to get available balance: %w", r) + available = abi.NewTokenAmount(0) + } + }() + // this panics if the miner doesnt have enough funds to cover their locked pledge + available{{if (ge .v 2)}}, err{{end}} = s.GetAvailableBalance(bal) + return available, err +} + +func (s *state{{.v}}) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.CheckVestedFunds(s.store, epoch) +} + +func (s *state{{.v}}) LockedFunds() (LockedFunds, error) { + return LockedFunds{ + VestingFunds: s.State.LockedFunds, + InitialPledgeRequirement: s.State.InitialPledge{{if (le .v 1)}}Requirement{{end}}, + PreCommitDeposits: s.State.PreCommitDeposits, + }, nil +} + +func (s *state{{.v}}) FeeDebt() (abi.TokenAmount, error) { + return {{if (ge .v 2)}}s.State.FeeDebt{{else}}big.Zero(){{end}}, nil +} + +func (s *state{{.v}}) InitialPledge() (abi.TokenAmount, error) { + return s.State.InitialPledge{{if (le .v 1)}}Requirement{{end}}, nil +} + +func (s *state{{.v}}) PreCommitDeposits() (abi.TokenAmount, error) { + return s.State.PreCommitDeposits, nil +} + +func (s *state{{.v}}) GetSector(num abi.SectorNumber) (*SectorOnChainInfo, error) { + info, ok, err := s.State.GetSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV{{.v}}SectorOnChainInfo(*info) + return &ret, nil +} + +func (s *state{{.v}}) FindSector(num abi.SectorNumber) (*SectorLocation, error) { + dlIdx, partIdx, err := s.State.FindSector(s.store, num) + if err != nil { + return nil, err + } + return &SectorLocation{ + Deadline: dlIdx, + Partition: partIdx, + }, nil +} + +func (s *state{{.v}}) NumLiveSectors() (uint64, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return 0, err + } + var total uint64 + if err := dls.ForEach(s.store, func(dlIdx uint64, dl *miner{{.v}}.Deadline) error { + total += dl.LiveSectors + return nil + }); err != nil { + return 0, err + } + return total, nil +} + +// GetSectorExpiration returns the effective expiration of the given sector. +// +// If the sector does not expire early, the Early expiration field is 0. +func (s *state{{.v}}) GetSectorExpiration(num abi.SectorNumber) (*SectorExpiration, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + // NOTE: this can be optimized significantly. + // 1. If the sector is non-faulty, it will either expire on-time (can be + // learned from the sector info), or in the next quantized expiration + // epoch (i.e., the first element in the partition's expiration queue. + // 2. If it's faulty, it will expire early within the first 14 entries + // of the expiration queue. + stopErr := errors.New("stop") + out := SectorExpiration{} + err = dls.ForEach(s.store, func(dlIdx uint64, dl *miner{{.v}}.Deadline) error { + partitions, err := dl.PartitionsArray(s.store) + if err != nil { + return err + } + quant := s.State.QuantSpecForDeadline(dlIdx) + var part miner{{.v}}.Partition + return partitions.ForEach(&part, func(partIdx int64) error { + if found, err := part.Sectors.IsSet(uint64(num)); err != nil { + return err + } else if !found { + return nil + } + if found, err := part.Terminated.IsSet(uint64(num)); err != nil { + return err + } else if found { + // already terminated + return stopErr + } + + q, err := miner{{.v}}.LoadExpirationQueue(s.store, part.ExpirationsEpochs, quant{{if (ge .v 3)}}, miner{{.v}}.PartitionExpirationAmtBitwidth{{end}}) + if err != nil { + return err + } + var exp miner{{.v}}.ExpirationSet + return q.ForEach(&exp, func(epoch int64) error { + if early, err := exp.EarlySectors.IsSet(uint64(num)); err != nil { + return err + } else if early { + out.Early = abi.ChainEpoch(epoch) + return nil + } + if onTime, err := exp.OnTimeSectors.IsSet(uint64(num)); err != nil { + return err + } else if onTime { + out.OnTime = abi.ChainEpoch(epoch) + return stopErr + } + return nil + }) + }) + }) + if err == stopErr { + err = nil + } + if err != nil { + return nil, err + } + if out.Early == 0 && out.OnTime == 0 { + return nil, xerrors.Errorf("failed to find sector %d", num) + } + return &out, nil +} + +func (s *state{{.v}}) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOnChainInfo, error) { + info, ok, err := s.State.GetPrecommittedSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV{{.v}}SectorPreCommitOnChainInfo(*info) + + return &ret, nil +} + +func (s *state{{.v}}) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) { + sectors, err := miner{{.v}}.LoadSectors(s.store, s.State.Sectors) + if err != nil { + return nil, err + } + + // If no sector numbers are specified, load all. + if snos == nil { + infos := make([]*SectorOnChainInfo, 0, sectors.Length()) + var info{{.v}} miner{{.v}}.SectorOnChainInfo + if err := sectors.ForEach(&info{{.v}}, func(_ int64) error { + info := fromV{{.v}}SectorOnChainInfo(info{{.v}}) + infos = append(infos, &info) + return nil + }); err != nil { + return nil, err + } + return infos, nil + } + + // Otherwise, load selected. + infos{{.v}}, err := sectors.Load(*snos) + if err != nil { + return nil, err + } + infos := make([]*SectorOnChainInfo, len(infos{{.v}})) + for i, info{{.v}} := range infos{{.v}} { + info := fromV{{.v}}SectorOnChainInfo(*info{{.v}}) + infos[i] = &info + } + return infos, nil +} + +func (s *state{{.v}}) IsAllocated(num abi.SectorNumber) (bool, error) { + var allocatedSectors bitfield.BitField + if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil { + return false, err + } + + return allocatedSectors.IsSet(uint64(num)) +} + +func (s *state{{.v}}) LoadDeadline(idx uint64) (Deadline, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + dl, err := dls.LoadDeadline(s.store, idx) + if err != nil { + return nil, err + } + return &deadline{{.v}}{*dl, s.store}, nil +} + +func (s *state{{.v}}) ForEachDeadline(cb func(uint64, Deadline) error) error { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return err + } + return dls.ForEach(s.store, func(i uint64, dl *miner{{.v}}.Deadline) error { + return cb(i, &deadline{{.v}}{*dl, s.store}) + }) +} + +func (s *state{{.v}}) NumDeadlines() (uint64, error) { + return miner{{.v}}.WPoStPeriodDeadlines, nil +} + +func (s *state{{.v}}) DeadlinesChanged(other State) (bool, error) { + other{{.v}}, ok := other.(*state{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !s.State.Deadlines.Equals(other{{.v}}.Deadlines), nil +} + +func (s *state{{.v}}) MinerInfoChanged(other State) (bool, error) { + other0, ok := other.(*state{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Info.Equals(other0.State.Info), nil +} + +func (s *state{{.v}}) Info() (MinerInfo, error) { + info, err := s.State.GetInfo(s.store) + if err != nil { + return MinerInfo{}, err + } + + var pid *peer.ID + if peerID, err := peer.IDFromBytes(info.PeerId); err == nil { + pid = &peerID + } +{{if (le .v 2)}} + wpp, err := info.SealProofType.RegisteredWindowPoStProof() + if err != nil { + return MinerInfo{}, err + } +{{end}} + mi := MinerInfo{ + Owner: info.Owner, + Worker: info.Worker, + ControlAddresses: info.ControlAddresses, + + NewWorker: address.Undef, + WorkerChangeEpoch: -1, + + PeerId: pid, + Multiaddrs: info.Multiaddrs, + WindowPoStProofType: {{if (ge .v 3)}}info.WindowPoStProofType{{else}}wpp{{end}}, + SectorSize: info.SectorSize, + WindowPoStPartitionSectors: info.WindowPoStPartitionSectors, + ConsensusFaultElapsed: {{if (ge .v 2)}}info.ConsensusFaultElapsed{{else}}-1{{end}}, + } + + if info.PendingWorkerKey != nil { + mi.NewWorker = info.PendingWorkerKey.NewWorker + mi.WorkerChangeEpoch = info.PendingWorkerKey.EffectiveAt + } + + return mi, nil +} + +func (s *state{{.v}}) DeadlineInfo(epoch abi.ChainEpoch) (*dline.Info, error) { + return s.State.{{if (ge .v 4)}}Recorded{{end}}DeadlineInfo(epoch), nil +} + +func (s *state{{.v}}) DeadlineCronActive() (bool, error) { + return {{if (ge .v 4)}}s.State.DeadlineCronActive{{else}}true{{end}}, nil{{if (lt .v 4)}} // always active in this version{{end}} +} + +func (s *state{{.v}}) sectors() (adt.Array, error) { + return adt{{.v}}.AsArray(s.store, s.Sectors{{if (ge .v 3)}}, miner{{.v}}.SectorsAmtBitwidth{{end}}) +} + +func (s *state{{.v}}) decodeSectorOnChainInfo(val *cbg.Deferred) (SectorOnChainInfo, error) { + var si miner{{.v}}.SectorOnChainInfo + err := si.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorOnChainInfo{}, err + } + + return fromV{{.v}}SectorOnChainInfo(si), nil +} + +func (s *state{{.v}}) precommits() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.PreCommittedSectors{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} + +func (s *state{{.v}}) decodeSectorPreCommitOnChainInfo(val *cbg.Deferred) (SectorPreCommitOnChainInfo, error) { + var sp miner{{.v}}.SectorPreCommitOnChainInfo + err := sp.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorPreCommitOnChainInfo{}, err + } + + return fromV{{.v}}SectorPreCommitOnChainInfo(sp), nil +} + +func (d *deadline{{.v}}) LoadPartition(idx uint64) (Partition, error) { + p, err := d.Deadline.LoadPartition(d.store, idx) + if err != nil { + return nil, err + } + return &partition{{.v}}{*p, d.store}, nil +} + +func (d *deadline{{.v}}) ForEachPartition(cb func(uint64, Partition) error) error { + ps, err := d.Deadline.PartitionsArray(d.store) + if err != nil { + return err + } + var part miner{{.v}}.Partition + return ps.ForEach(&part, func(i int64) error { + return cb(uint64(i), &partition{{.v}}{part, d.store}) + }) +} + +func (d *deadline{{.v}}) PartitionsChanged(other Deadline) (bool, error) { + other{{.v}}, ok := other.(*deadline{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !d.Deadline.Partitions.Equals(other{{.v}}.Deadline.Partitions), nil +} + +func (d *deadline{{.v}}) PartitionsPoSted() (bitfield.BitField, error) { + return d.Deadline.{{if (ge .v 3)}}PartitionsPoSted{{else}}PostSubmissions{{end}}, nil +} + +func (d *deadline{{.v}}) DisputableProofCount() (uint64, error) { +{{if (ge .v 3)}} ops, err := d.OptimisticProofsSnapshotArray(d.store) + if err != nil { + return 0, err + } + + return ops.Length(), nil{{else}} // field doesn't exist until v3 + return 0, nil{{end}} +} + +func (p *partition{{.v}}) AllSectors() (bitfield.BitField, error) { + return p.Partition.Sectors, nil +} + +func (p *partition{{.v}}) FaultySectors() (bitfield.BitField, error) { + return p.Partition.Faults, nil +} + +func (p *partition{{.v}}) RecoveringSectors() (bitfield.BitField, error) { + return p.Partition.Recoveries, nil +} + +func fromV{{.v}}SectorOnChainInfo(v{{.v}} miner{{.v}}.SectorOnChainInfo) SectorOnChainInfo { + {{if (ge .v 2)}}return SectorOnChainInfo{ + SectorNumber: v{{.v}}.SectorNumber, + SealProof: v{{.v}}.SealProof, + SealedCID: v{{.v}}.SealedCID, + DealIDs: v{{.v}}.DealIDs, + Activation: v{{.v}}.Activation, + Expiration: v{{.v}}.Expiration, + DealWeight: v{{.v}}.DealWeight, + VerifiedDealWeight: v{{.v}}.VerifiedDealWeight, + InitialPledge: v{{.v}}.InitialPledge, + ExpectedDayReward: v{{.v}}.ExpectedDayReward, + ExpectedStoragePledge: v{{.v}}.ExpectedStoragePledge, + }{{else}}return (SectorOnChainInfo)(v0){{end}} +} + +func fromV{{.v}}SectorPreCommitOnChainInfo(v{{.v}} miner{{.v}}.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + {{if (ge .v 2)}}return SectorPreCommitOnChainInfo{ + Info: (SectorPreCommitInfo)(v{{.v}}.Info), + PreCommitDeposit: v{{.v}}.PreCommitDeposit, + PreCommitEpoch: v{{.v}}.PreCommitEpoch, + DealWeight: v{{.v}}.DealWeight, + VerifiedDealWeight: v{{.v}}.VerifiedDealWeight, + }{{else}}return (SectorPreCommitOnChainInfo)(v0){{end}} +} diff --git a/chain/actors/builtin/miner/v0.go b/chain/actors/builtin/miner/v0.go index 4f02e313f..262c4870d 100644 --- a/chain/actors/builtin/miner/v0.go +++ b/chain/actors/builtin/miner/v0.go @@ -196,6 +196,7 @@ func (s *state0) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOn } ret := fromV0SectorPreCommitOnChainInfo(*info) + return &ret, nil } diff --git a/chain/actors/builtin/miner/v3.go b/chain/actors/builtin/miner/v3.go index 5e058ed1f..4f03fdcc9 100644 --- a/chain/actors/builtin/miner/v3.go +++ b/chain/actors/builtin/miner/v3.go @@ -208,9 +208,9 @@ func (s *state3) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err // If no sector numbers are specified, load all. if snos == nil { infos := make([]*SectorOnChainInfo, 0, sectors.Length()) - var info2 miner3.SectorOnChainInfo - if err := sectors.ForEach(&info2, func(_ int64) error { - info := fromV3SectorOnChainInfo(info2) + var info3 miner3.SectorOnChainInfo + if err := sectors.ForEach(&info3, func(_ int64) error { + info := fromV3SectorOnChainInfo(info3) infos = append(infos, &info) return nil }); err != nil { @@ -220,13 +220,13 @@ func (s *state3) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err } // Otherwise, load selected. - infos2, err := sectors.Load(*snos) + infos3, err := sectors.Load(*snos) if err != nil { return nil, err } - infos := make([]*SectorOnChainInfo, len(infos2)) - for i, info2 := range infos2 { - info := fromV3SectorOnChainInfo(*info2) + infos := make([]*SectorOnChainInfo, len(infos3)) + for i, info3 := range infos3 { + info := fromV3SectorOnChainInfo(*info3) infos[i] = &info } return infos, nil @@ -268,13 +268,13 @@ func (s *state3) NumDeadlines() (uint64, error) { } func (s *state3) DeadlinesChanged(other State) (bool, error) { - other2, ok := other.(*state3) + other3, ok := other.(*state3) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.Deadlines.Equals(other2.Deadlines), nil + return !s.State.Deadlines.Equals(other3.Deadlines), nil } func (s *state3) MinerInfoChanged(other State) (bool, error) { @@ -377,13 +377,13 @@ func (d *deadline3) ForEachPartition(cb func(uint64, Partition) error) error { } func (d *deadline3) PartitionsChanged(other Deadline) (bool, error) { - other2, ok := other.(*deadline3) + other3, ok := other.(*deadline3) if !ok { // treat an upgrade as a change, always return true, nil } - return !d.Deadline.Partitions.Equals(other2.Deadline.Partitions), nil + return !d.Deadline.Partitions.Equals(other3.Deadline.Partitions), nil } func (d *deadline3) PartitionsPoSted() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v4.go b/chain/actors/builtin/miner/v4.go index b354dbc33..e277c0298 100644 --- a/chain/actors/builtin/miner/v4.go +++ b/chain/actors/builtin/miner/v4.go @@ -208,9 +208,9 @@ func (s *state4) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err // If no sector numbers are specified, load all. if snos == nil { infos := make([]*SectorOnChainInfo, 0, sectors.Length()) - var info2 miner4.SectorOnChainInfo - if err := sectors.ForEach(&info2, func(_ int64) error { - info := fromV4SectorOnChainInfo(info2) + var info4 miner4.SectorOnChainInfo + if err := sectors.ForEach(&info4, func(_ int64) error { + info := fromV4SectorOnChainInfo(info4) infos = append(infos, &info) return nil }); err != nil { @@ -220,13 +220,13 @@ func (s *state4) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err } // Otherwise, load selected. - infos2, err := sectors.Load(*snos) + infos4, err := sectors.Load(*snos) if err != nil { return nil, err } - infos := make([]*SectorOnChainInfo, len(infos2)) - for i, info2 := range infos2 { - info := fromV4SectorOnChainInfo(*info2) + infos := make([]*SectorOnChainInfo, len(infos4)) + for i, info4 := range infos4 { + info := fromV4SectorOnChainInfo(*info4) infos[i] = &info } return infos, nil @@ -268,13 +268,13 @@ func (s *state4) NumDeadlines() (uint64, error) { } func (s *state4) DeadlinesChanged(other State) (bool, error) { - other2, ok := other.(*state4) + other4, ok := other.(*state4) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.Deadlines.Equals(other2.Deadlines), nil + return !s.State.Deadlines.Equals(other4.Deadlines), nil } func (s *state4) MinerInfoChanged(other State) (bool, error) { @@ -377,13 +377,13 @@ func (d *deadline4) ForEachPartition(cb func(uint64, Partition) error) error { } func (d *deadline4) PartitionsChanged(other Deadline) (bool, error) { - other2, ok := other.(*deadline4) + other4, ok := other.(*deadline4) if !ok { // treat an upgrade as a change, always return true, nil } - return !d.Deadline.Partitions.Equals(other2.Deadline.Partitions), nil + return !d.Deadline.Partitions.Equals(other4.Deadline.Partitions), nil } func (d *deadline4) PartitionsPoSted() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/multisig/actor.go.template b/chain/actors/builtin/multisig/actor.go.template new file mode 100644 index 000000000..76aff2581 --- /dev/null +++ b/chain/actors/builtin/multisig/actor.go.template @@ -0,0 +1,113 @@ +package multisig + +import ( + "fmt" + + "github.com/minio/blake2b-simd" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + + msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.MultisigActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + LockedBalance(epoch abi.ChainEpoch) (abi.TokenAmount, error) + StartEpoch() (abi.ChainEpoch, error) + UnlockDuration() (abi.ChainEpoch, error) + InitialBalance() (abi.TokenAmount, error) + Threshold() (uint64, error) + Signers() ([]address.Address, error) + + ForEachPendingTxn(func(id int64, txn Transaction) error) error + PendingTxnChanged(State) (bool, error) + + transactions() (adt.Map, error) + decodeTransaction(val *cbg.Deferred) (Transaction, error) +} + +type Transaction = msig0.Transaction + +var Methods = builtin{{.latestVersion}}.MethodsMultisig + +func Message(version actors.Version, from address.Address) MessageBuilder { + switch version { +{{range .versions}} case actors.Version{{.}}: + return message{{.}}{{"{"}}{{if (ge . 2)}}message0{from}{{else}}from{{end}}} +{{end}} default: + panic(fmt.Sprintf("unsupported actors version: %d", version)) + } +} + +type MessageBuilder interface { + // Create a new multisig with the specified parameters. + Create(signers []address.Address, threshold uint64, + vestingStart, vestingDuration abi.ChainEpoch, + initialAmount abi.TokenAmount) (*types.Message, error) + + // Propose a transaction to the given multisig. + Propose(msig, target address.Address, amt abi.TokenAmount, + method abi.MethodNum, params []byte) (*types.Message, error) + + // Approve a multisig transaction. The "hash" is optional. + Approve(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) + + // Cancel a multisig transaction. The "hash" is optional. + Cancel(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) +} + +// this type is the same between v0 and v2 +type ProposalHashData = msig{{.latestVersion}}.ProposalHashData +type ProposeReturn = msig{{.latestVersion}}.ProposeReturn +type ProposeParams = msig{{.latestVersion}}.ProposeParams + +func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { + params := msig{{.latestVersion}}.TxnIDParams{ID: msig4.TxnID(id)} + if data != nil { + if data.Requester.Protocol() != address.ID { + return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) + } + if data.Value.Sign() == -1 { + return nil, xerrors.Errorf("proposal value must be non-negative, was %s", data.Value) + } + if data.To == address.Undef { + return nil, xerrors.Errorf("proposed destination address must be set") + } + pser, err := data.Serialize() + if err != nil { + return nil, err + } + hash := blake2b.Sum256(pser) + params.ProposalHash = hash[:] + } + + return actors.SerializeParams(¶ms) +} diff --git a/chain/actors/builtin/multisig/message.go b/chain/actors/builtin/multisig/message.go deleted file mode 100644 index 096049002..000000000 --- a/chain/actors/builtin/multisig/message.go +++ /dev/null @@ -1,79 +0,0 @@ -package multisig - -import ( - "fmt" - - "github.com/minio/blake2b-simd" - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - - builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" - multisig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" - - "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/types" -) - -var Methods = builtin4.MethodsMultisig - -func Message(version actors.Version, from address.Address) MessageBuilder { - switch version { - case actors.Version0: - return message0{from} - case actors.Version2: - return message2{message0{from}} - case actors.Version3: - return message3{message0{from}} - case actors.Version4: - return message4{message0{from}} - default: - panic(fmt.Sprintf("unsupported actors version: %d", version)) - } -} - -type MessageBuilder interface { - // Create a new multisig with the specified parameters. - Create(signers []address.Address, threshold uint64, - vestingStart, vestingDuration abi.ChainEpoch, - initialAmount abi.TokenAmount) (*types.Message, error) - - // Propose a transaction to the given multisig. - Propose(msig, target address.Address, amt abi.TokenAmount, - method abi.MethodNum, params []byte) (*types.Message, error) - - // Approve a multisig transaction. The "hash" is optional. - Approve(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) - - // Cancel a multisig transaction. The "hash" is optional. - Cancel(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) -} - -// this type is the same between v0 and v2 -type ProposalHashData = multisig4.ProposalHashData -type ProposeReturn = multisig4.ProposeReturn -type ProposeParams = multisig4.ProposeParams - -func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { - params := multisig4.TxnIDParams{ID: multisig4.TxnID(id)} - if data != nil { - if data.Requester.Protocol() != address.ID { - return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) - } - if data.Value.Sign() == -1 { - return nil, xerrors.Errorf("proposal value must be non-negative, was %s", data.Value) - } - if data.To == address.Undef { - return nil, xerrors.Errorf("proposed destination address must be set") - } - pser, err := data.Serialize() - if err != nil { - return nil, err - } - hash := blake2b.Sum256(pser) - params.ProposalHash = hash[:] - } - - return actors.SerializeParams(¶ms) -} diff --git a/chain/actors/builtin/multisig/message.go.template b/chain/actors/builtin/multisig/message.go.template new file mode 100644 index 000000000..dfd5c6fc0 --- /dev/null +++ b/chain/actors/builtin/multisig/message.go.template @@ -0,0 +1,143 @@ +package multisig + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" + init{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/init" + multisig{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/multisig" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message{{.v}} struct{ {{if (ge .v 2)}}message0{{else}}from address.Address{{end}} } + +func (m message{{.v}}) Create( + signers []address.Address, threshold uint64, + unlockStart, unlockDuration abi.ChainEpoch, + initialAmount abi.TokenAmount, +) (*types.Message, error) { + + lenAddrs := uint64(len(signers)) + + if lenAddrs < threshold { + return nil, xerrors.Errorf("cannot require signing of more addresses than provided for multisig") + } + + if threshold == 0 { + threshold = lenAddrs + } + + if m.from == address.Undef { + return nil, xerrors.Errorf("must provide source address") + } +{{if (le .v 1)}} + if unlockStart != 0 { + return nil, xerrors.Errorf("actors v0 does not support a non-zero vesting start time") + } +{{end}} + // Set up constructor parameters for multisig + msigParams := &multisig{{.v}}.ConstructorParams{ + Signers: signers, + NumApprovalsThreshold: threshold, + UnlockDuration: unlockDuration,{{if (ge .v 2)}} + StartEpoch: unlockStart,{{end}} + } + + enc, actErr := actors.SerializeParams(msigParams) + if actErr != nil { + return nil, actErr + } + + // new actors are created by invoking 'exec' on the init actor with the constructor params + execParams := &init{{.v}}.ExecParams{ + CodeCID: builtin{{.v}}.MultisigActorCodeID, + ConstructorParams: enc, + } + + enc, actErr = actors.SerializeParams(execParams) + if actErr != nil { + return nil, actErr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Method: builtin{{.v}}.MethodsInit.Exec, + Params: enc, + Value: initialAmount, + }, nil +}{{if (le .v 1)}} + +func (m message0) Propose(msig, to address.Address, amt abi.TokenAmount, + method abi.MethodNum, params []byte) (*types.Message, error) { + + if msig == address.Undef { + return nil, xerrors.Errorf("must provide a multisig address for proposal") + } + + if to == address.Undef { + return nil, xerrors.Errorf("must provide a target address for proposal") + } + + if amt.Sign() == -1 { + return nil, xerrors.Errorf("must provide a non-negative amount for proposed send") + } + + if m.from == address.Undef { + return nil, xerrors.Errorf("must provide source address") + } + + enc, actErr := actors.SerializeParams(&multisig0.ProposeParams{ + To: to, + Value: amt, + Method: method, + Params: params, + }) + if actErr != nil { + return nil, xerrors.Errorf("failed to serialize parameters: %w", actErr) + } + + return &types.Message{ + To: msig, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin0.MethodsMultisig.Propose, + Params: enc, + }, nil +} + +func (m message0) Approve(msig address.Address, txID uint64, hashData *ProposalHashData) (*types.Message, error) { + enc, err := txnParams(txID, hashData) + if err != nil { + return nil, err + } + + return &types.Message{ + To: msig, + From: m.from, + Value: types.NewInt(0), + Method: builtin0.MethodsMultisig.Approve, + Params: enc, + }, nil +} + +func (m message0) Cancel(msig address.Address, txID uint64, hashData *ProposalHashData) (*types.Message, error) { + enc, err := txnParams(txID, hashData) + if err != nil { + return nil, err + } + + return &types.Message{ + To: msig, + From: m.from, + Value: types.NewInt(0), + Method: builtin0.MethodsMultisig.Cancel, + Params: enc, + }, nil +}{{end}} diff --git a/chain/actors/builtin/multisig/state.go b/chain/actors/builtin/multisig/multisig.go similarity index 51% rename from chain/actors/builtin/multisig/state.go rename to chain/actors/builtin/multisig/multisig.go index 0ce10d290..0cee644c5 100644 --- a/chain/actors/builtin/multisig/state.go +++ b/chain/actors/builtin/multisig/multisig.go @@ -1,6 +1,9 @@ package multisig import ( + "fmt" + + "github.com/minio/blake2b-simd" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -9,12 +12,15 @@ import ( "github.com/filecoin-project/go-state-types/cbor" "github.com/ipfs/go-cid" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" @@ -67,3 +73,65 @@ type State interface { } type Transaction = msig0.Transaction + +var Methods = builtin4.MethodsMultisig + +func Message(version actors.Version, from address.Address) MessageBuilder { + switch version { + case actors.Version0: + return message0{from} + case actors.Version2: + return message2{message0{from}} + case actors.Version3: + return message3{message0{from}} + case actors.Version4: + return message4{message0{from}} + default: + panic(fmt.Sprintf("unsupported actors version: %d", version)) + } +} + +type MessageBuilder interface { + // Create a new multisig with the specified parameters. + Create(signers []address.Address, threshold uint64, + vestingStart, vestingDuration abi.ChainEpoch, + initialAmount abi.TokenAmount) (*types.Message, error) + + // Propose a transaction to the given multisig. + Propose(msig, target address.Address, amt abi.TokenAmount, + method abi.MethodNum, params []byte) (*types.Message, error) + + // Approve a multisig transaction. The "hash" is optional. + Approve(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) + + // Cancel a multisig transaction. The "hash" is optional. + Cancel(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) +} + +// this type is the same between v0 and v2 +type ProposalHashData = msig4.ProposalHashData +type ProposeReturn = msig4.ProposeReturn +type ProposeParams = msig4.ProposeParams + +func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { + params := msig4.TxnIDParams{ID: msig4.TxnID(id)} + if data != nil { + if data.Requester.Protocol() != address.ID { + return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) + } + if data.Value.Sign() == -1 { + return nil, xerrors.Errorf("proposal value must be non-negative, was %s", data.Value) + } + if data.To == address.Undef { + return nil, xerrors.Errorf("proposed destination address must be set") + } + pser, err := data.Serialize() + if err != nil { + return nil, err + } + hash := blake2b.Sum256(pser) + params.ProposalHash = hash[:] + } + + return actors.SerializeParams(¶ms) +} diff --git a/chain/actors/builtin/multisig/state.go.template b/chain/actors/builtin/multisig/state.go.template new file mode 100644 index 000000000..c62655646 --- /dev/null +++ b/chain/actors/builtin/multisig/state.go.template @@ -0,0 +1,95 @@ +package multisig + +import ( + "bytes" + "encoding/binary" + + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + +{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} msig{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/multisig" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + msig{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) LockedBalance(currEpoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.State.AmountLocked(currEpoch - s.State.StartEpoch), nil +} + +func (s *state{{.v}}) StartEpoch() (abi.ChainEpoch, error) { + return s.State.StartEpoch, nil +} + +func (s *state{{.v}}) UnlockDuration() (abi.ChainEpoch, error) { + return s.State.UnlockDuration, nil +} + +func (s *state{{.v}}) InitialBalance() (abi.TokenAmount, error) { + return s.State.InitialBalance, nil +} + +func (s *state{{.v}}) Threshold() (uint64, error) { + return s.State.NumApprovalsThreshold, nil +} + +func (s *state{{.v}}) Signers() ([]address.Address, error) { + return s.State.Signers, nil +} + +func (s *state{{.v}}) ForEachPendingTxn(cb func(id int64, txn Transaction) error) error { + arr, err := adt{{.v}}.AsMap(s.store, s.State.PendingTxns{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) + if err != nil { + return err + } + var out msig{{.v}}.Transaction + return arr.ForEach(&out, func(key string) error { + txid, n := binary.Varint([]byte(key)) + if n <= 0 { + return xerrors.Errorf("invalid pending transaction key: %v", key) + } + return cb(txid, (Transaction)(out)) //nolint:unconvert + }) +} + +func (s *state{{.v}}) PendingTxnChanged(other State) (bool, error) { + other{{.v}}, ok := other.(*state{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.PendingTxns.Equals(other{{.v}}.PendingTxns), nil +} + +func (s *state{{.v}}) transactions() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.PendingTxns{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} + +func (s *state{{.v}}) decodeTransaction(val *cbg.Deferred) (Transaction, error) { + var tx msig{{.v}}.Transaction + if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Transaction{}, err + } + return tx, nil +} diff --git a/chain/actors/builtin/multisig/state0.go b/chain/actors/builtin/multisig/v0.go similarity index 95% rename from chain/actors/builtin/multisig/state0.go rename to chain/actors/builtin/multisig/v0.go index 27dd5c413..20c1557b0 100644 --- a/chain/actors/builtin/multisig/state0.go +++ b/chain/actors/builtin/multisig/v0.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" + adt0 "github.com/filecoin-project/specs-actors/actors/util/adt" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" @@ -13,8 +15,6 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - multisig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - adt0 "github.com/filecoin-project/specs-actors/actors/util/adt" ) var _ State = (*state0)(nil) @@ -86,7 +86,7 @@ func (s *state0) transactions() (adt.Map, error) { } func (s *state0) decodeTransaction(val *cbg.Deferred) (Transaction, error) { - var tx multisig0.Transaction + var tx msig0.Transaction if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return Transaction{}, err } diff --git a/chain/actors/builtin/multisig/state2.go b/chain/actors/builtin/multisig/v2.go similarity index 99% rename from chain/actors/builtin/multisig/state2.go rename to chain/actors/builtin/multisig/v2.go index d637abb91..ef317f903 100644 --- a/chain/actors/builtin/multisig/state2.go +++ b/chain/actors/builtin/multisig/v2.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" + adt2 "github.com/filecoin-project/specs-actors/v2/actors/util/adt" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" @@ -13,7 +15,6 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" msig2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig" - adt2 "github.com/filecoin-project/specs-actors/v2/actors/util/adt" ) var _ State = (*state2)(nil) diff --git a/chain/actors/builtin/multisig/state3.go b/chain/actors/builtin/multisig/v3.go similarity index 96% rename from chain/actors/builtin/multisig/state3.go rename to chain/actors/builtin/multisig/v3.go index a2eb1d909..1a7611440 100644 --- a/chain/actors/builtin/multisig/state3.go +++ b/chain/actors/builtin/multisig/v3.go @@ -74,12 +74,12 @@ func (s *state3) ForEachPendingTxn(cb func(id int64, txn Transaction) error) err } func (s *state3) PendingTxnChanged(other State) (bool, error) { - other2, ok := other.(*state3) + other3, ok := other.(*state3) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.PendingTxns.Equals(other2.PendingTxns), nil + return !s.State.PendingTxns.Equals(other3.PendingTxns), nil } func (s *state3) transactions() (adt.Map, error) { diff --git a/chain/actors/builtin/multisig/state4.go b/chain/actors/builtin/multisig/v4.go similarity index 93% rename from chain/actors/builtin/multisig/state4.go rename to chain/actors/builtin/multisig/v4.go index 3475ad361..50c1e9620 100644 --- a/chain/actors/builtin/multisig/state4.go +++ b/chain/actors/builtin/multisig/v4.go @@ -69,17 +69,17 @@ func (s *state4) ForEachPendingTxn(cb func(id int64, txn Transaction) error) err if n <= 0 { return xerrors.Errorf("invalid pending transaction key: %v", key) } - return cb(txid, (Transaction)(out)) + return cb(txid, (Transaction)(out)) //nolint:unconvert }) } func (s *state4) PendingTxnChanged(other State) (bool, error) { - other2, ok := other.(*state4) + other4, ok := other.(*state4) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.PendingTxns.Equals(other2.PendingTxns), nil + return !s.State.PendingTxns.Equals(other4.PendingTxns), nil } func (s *state4) transactions() (adt.Map, error) { diff --git a/chain/actors/builtin/paych/actor.go.template b/chain/actors/builtin/paych/actor.go.template new file mode 100644 index 000000000..bd0d6c03c --- /dev/null +++ b/chain/actors/builtin/paych/actor.go.template @@ -0,0 +1,103 @@ +package paych + +import ( + "encoding/base64" + "fmt" + + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + big "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + ipldcbor "github.com/ipfs/go-ipld-cbor" + + paych0 "github.com/filecoin-project/specs-actors/actors/builtin/paych" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +// Load returns an abstract copy of payment channel state, irregardless of actor version +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.PaymentChannelActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +// State is an abstract version of payment channel state that works across +// versions +type State interface { + cbor.Marshaler + // Channel owner, who has funded the actor + From() (address.Address, error) + // Recipient of payouts from channel + To() (address.Address, error) + + // Height at which the channel can be `Collected` + SettlingAt() (abi.ChainEpoch, error) + + // Amount successfully redeemed through the payment channel, paid out on `Collect()` + ToSend() (abi.TokenAmount, error) + + // Get total number of lanes + LaneCount() (uint64, error) + + // Iterate lane states + ForEachLaneState(cb func(idx uint64, dl LaneState) error) error +} + +// LaneState is an abstract copy of the state of a single lane +type LaneState interface { + Redeemed() (big.Int, error) + Nonce() (uint64, error) +} + +type SignedVoucher = paych0.SignedVoucher +type ModVerifyParams = paych0.ModVerifyParams + +// DecodeSignedVoucher decodes base64 encoded signed voucher. +func DecodeSignedVoucher(s string) (*SignedVoucher, error) { + data, err := base64.RawURLEncoding.DecodeString(s) + if err != nil { + return nil, err + } + + var sv SignedVoucher + if err := ipldcbor.DecodeInto(data, &sv); err != nil { + return nil, err + } + + return &sv, nil +} + +var Methods = builtin{{.latestVersion}}.MethodsPaych + +func Message(version actors.Version, from address.Address) MessageBuilder { + switch version { +{{range .versions}} case actors.Version{{.}}: + return message{{.}}{from} +{{end}} default: + panic(fmt.Sprintf("unsupported actors version: %d", version)) + } +} + +type MessageBuilder interface { + Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) + Update(paych address.Address, voucher *SignedVoucher, secret []byte) (*types.Message, error) + Settle(paych address.Address) (*types.Message, error) + Collect(paych address.Address) (*types.Message, error) +} diff --git a/chain/actors/builtin/paych/message.go b/chain/actors/builtin/paych/message.go deleted file mode 100644 index 6669cd227..000000000 --- a/chain/actors/builtin/paych/message.go +++ /dev/null @@ -1,36 +0,0 @@ -package paych - -import ( - "fmt" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/types" - - builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" -) - -var Methods = builtin4.MethodsPaych - -func Message(version actors.Version, from address.Address) MessageBuilder { - switch version { - case actors.Version0: - return message0{from} - case actors.Version2: - return message2{from} - case actors.Version3: - return message3{from} - case actors.Version4: - return message4{from} - default: - panic(fmt.Sprintf("unsupported actors version: %d", version)) - } -} - -type MessageBuilder interface { - Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) - Update(paych address.Address, voucher *SignedVoucher, secret []byte) (*types.Message, error) - Settle(paych address.Address) (*types.Message, error) - Collect(paych address.Address) (*types.Message, error) -} diff --git a/chain/actors/builtin/paych/message.go.template b/chain/actors/builtin/paych/message.go.template new file mode 100644 index 000000000..4a5ea2331 --- /dev/null +++ b/chain/actors/builtin/paych/message.go.template @@ -0,0 +1,74 @@ +package paych + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" + init{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/init" + paych{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/paych" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message{{.v}} struct{ from address.Address } + +func (m message{{.v}}) Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) { + params, aerr := actors.SerializeParams(&paych{{.v}}.ConstructorParams{From: m.from, To: to}) + if aerr != nil { + return nil, aerr + } + enc, aerr := actors.SerializeParams(&init{{.v}}.ExecParams{ + CodeCID: builtin{{.v}}.PaymentChannelActorCodeID, + ConstructorParams: params, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Value: initialAmount, + Method: builtin{{.v}}.MethodsInit.Exec, + Params: enc, + }, nil +} + +func (m message{{.v}}) Update(paych address.Address, sv *SignedVoucher, secret []byte) (*types.Message, error) { + params, aerr := actors.SerializeParams(&paych{{.v}}.UpdateChannelStateParams{ + Sv: *sv, + Secret: secret, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin{{.v}}.MethodsPaych.UpdateChannelState, + Params: params, + }, nil +} + +func (m message{{.v}}) Settle(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin{{.v}}.MethodsPaych.Settle, + }, nil +} + +func (m message{{.v}}) Collect(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin{{.v}}.MethodsPaych.Collect, + }, nil +} diff --git a/chain/actors/builtin/paych/state.go b/chain/actors/builtin/paych/paych.go similarity index 80% rename from chain/actors/builtin/paych/state.go rename to chain/actors/builtin/paych/paych.go index f28dc2f83..a62b2691f 100644 --- a/chain/actors/builtin/paych/state.go +++ b/chain/actors/builtin/paych/paych.go @@ -2,6 +2,7 @@ package paych import ( "encoding/base64" + "fmt" "golang.org/x/xerrors" @@ -12,12 +13,14 @@ import ( "github.com/ipfs/go-cid" ipldcbor "github.com/ipfs/go-ipld-cbor" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" paych0 "github.com/filecoin-project/specs-actors/actors/builtin/paych" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" @@ -98,3 +101,27 @@ func DecodeSignedVoucher(s string) (*SignedVoucher, error) { return &sv, nil } + +var Methods = builtin4.MethodsPaych + +func Message(version actors.Version, from address.Address) MessageBuilder { + switch version { + case actors.Version0: + return message0{from} + case actors.Version2: + return message2{from} + case actors.Version3: + return message3{from} + case actors.Version4: + return message4{from} + default: + panic(fmt.Sprintf("unsupported actors version: %d", version)) + } +} + +type MessageBuilder interface { + Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) + Update(paych address.Address, voucher *SignedVoucher, secret []byte) (*types.Message, error) + Settle(paych address.Address) (*types.Message, error) + Collect(paych address.Address) (*types.Message, error) +} diff --git a/chain/actors/builtin/paych/state.go.template b/chain/actors/builtin/paych/state.go.template new file mode 100644 index 000000000..b4b575a3e --- /dev/null +++ b/chain/actors/builtin/paych/state.go.template @@ -0,0 +1,104 @@ +package paych + +import ( + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/chain/actors/adt" + + paych{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/paych" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + paych{{.v}}.State + store adt.Store + lsAmt *adt{{.v}}.Array +} + +// Channel owner, who has funded the actor +func (s *state{{.v}}) From() (address.Address, error) { + return s.State.From, nil +} + +// Recipient of payouts from channel +func (s *state{{.v}}) To() (address.Address, error) { + return s.State.To, nil +} + +// Height at which the channel can be `Collected` +func (s *state{{.v}}) SettlingAt() (abi.ChainEpoch, error) { + return s.State.SettlingAt, nil +} + +// Amount successfully redeemed through the payment channel, paid out on `Collect()` +func (s *state{{.v}}) ToSend() (abi.TokenAmount, error) { + return s.State.ToSend, nil +} + +func (s *state{{.v}}) getOrLoadLsAmt() (*adt{{.v}}.Array, error) { + if s.lsAmt != nil { + return s.lsAmt, nil + } + + // Get the lane state from the chain + lsamt, err := adt{{.v}}.AsArray(s.store, s.State.LaneStates{{if (ge .v 3)}}, paych{{.v}}.LaneStatesAmtBitwidth{{end}}) + if err != nil { + return nil, err + } + + s.lsAmt = lsamt + return lsamt, nil +} + +// Get total number of lanes +func (s *state{{.v}}) LaneCount() (uint64, error) { + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return 0, err + } + return lsamt.Length(), nil +} + +// Iterate lane states +func (s *state{{.v}}) ForEachLaneState(cb func(idx uint64, dl LaneState) error) error { + // Get the lane state from the chain + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return err + } + + // Note: we use a map instead of an array to store laneStates because the + // client sets the lane ID (the index) and potentially they could use a + // very large index. + var ls paych{{.v}}.LaneState + return lsamt.ForEach(&ls, func(i int64) error { + return cb(uint64(i), &laneState{{.v}}{ls}) + }) +} + +type laneState{{.v}} struct { + paych{{.v}}.LaneState +} + +func (ls *laneState{{.v}}) Redeemed() (big.Int, error) { + return ls.LaneState.Redeemed, nil +} + +func (ls *laneState{{.v}}) Nonce() (uint64, error) { + return ls.LaneState.Nonce, nil +} diff --git a/chain/actors/builtin/paych/state0.go b/chain/actors/builtin/paych/v0.go similarity index 100% rename from chain/actors/builtin/paych/state0.go rename to chain/actors/builtin/paych/v0.go diff --git a/chain/actors/builtin/paych/state2.go b/chain/actors/builtin/paych/v2.go similarity index 100% rename from chain/actors/builtin/paych/state2.go rename to chain/actors/builtin/paych/v2.go diff --git a/chain/actors/builtin/paych/state3.go b/chain/actors/builtin/paych/v3.go similarity index 100% rename from chain/actors/builtin/paych/state3.go rename to chain/actors/builtin/paych/v3.go diff --git a/chain/actors/builtin/paych/state4.go b/chain/actors/builtin/paych/v4.go similarity index 100% rename from chain/actors/builtin/paych/state4.go rename to chain/actors/builtin/paych/v4.go diff --git a/chain/actors/builtin/power/actor.go.template b/chain/actors/builtin/power/actor.go.template new file mode 100644 index 000000000..56b591f7c --- /dev/null +++ b/chain/actors/builtin/power/actor.go.template @@ -0,0 +1,74 @@ +package power + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/big" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.StoragePowerActorAddr + Methods = builtin{{.latestVersion}}.MethodsPower +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.StoragePowerActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + TotalLocked() (abi.TokenAmount, error) + TotalPower() (Claim, error) + TotalCommitted() (Claim, error) + TotalPowerSmoothed() (builtin.FilterEstimate, error) + + // MinerCounts returns the number of miners. Participating is the number + // with power above the minimum miner threshold. + MinerCounts() (participating, total uint64, err error) + MinerPower(address.Address) (Claim, bool, error) + MinerNominalPowerMeetsConsensusMinimum(address.Address) (bool, error) + ListAllMiners() ([]address.Address, error) + ForEachClaim(func(miner address.Address, claim Claim) error) error + ClaimsChanged(State) (bool, error) + + // Diff helpers. Used by Diff* functions internally. + claims() (adt.Map, error) + decodeClaim(*cbg.Deferred) (Claim, error) +} + +type Claim struct { + // Sum of raw byte power for a miner's sectors. + RawBytePower abi.StoragePower + + // Sum of quality adjusted power for a miner's sectors. + QualityAdjPower abi.StoragePower +} + +func AddClaims(a Claim, b Claim) Claim { + return Claim{ + RawBytePower: big.Add(a.RawBytePower, b.RawBytePower), + QualityAdjPower: big.Add(a.QualityAdjPower, b.QualityAdjPower), + } +} diff --git a/chain/actors/builtin/power/power.go b/chain/actors/builtin/power/power.go index 7e15275a5..56b6f7c95 100644 --- a/chain/actors/builtin/power/power.go +++ b/chain/actors/builtin/power/power.go @@ -40,7 +40,7 @@ var ( Methods = builtin4.MethodsPower ) -func Load(store adt.Store, act *types.Actor) (st State, err error) { +func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { case builtin0.StoragePowerActorCodeID: return load0(store, act.Head) diff --git a/chain/actors/builtin/power/state.go.template b/chain/actors/builtin/power/state.go.template new file mode 100644 index 000000000..b71ac1c07 --- /dev/null +++ b/chain/actors/builtin/power/state.go.template @@ -0,0 +1,149 @@ +package power + +import ( + "bytes" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + +{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} power{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/power" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + power{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) TotalLocked() (abi.TokenAmount, error) { + return s.TotalPledgeCollateral, nil +} + +func (s *state{{.v}}) TotalPower() (Claim, error) { + return Claim{ + RawBytePower: s.TotalRawBytePower, + QualityAdjPower: s.TotalQualityAdjPower, + }, nil +} + +// Committed power to the network. Includes miners below the minimum threshold. +func (s *state{{.v}}) TotalCommitted() (Claim, error) { + return Claim{ + RawBytePower: s.TotalBytesCommitted, + QualityAdjPower: s.TotalQABytesCommitted, + }, nil +} + +func (s *state{{.v}}) MinerPower(addr address.Address) (Claim, bool, error) { + claims, err := s.claims() + if err != nil { + return Claim{}, false, err + } + var claim power{{.v}}.Claim + ok, err := claims.Get(abi.AddrKey(addr), &claim) + if err != nil { + return Claim{}, false, err + } + return Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }, ok, nil +} + +func (s *state{{.v}}) MinerNominalPowerMeetsConsensusMinimum(a address.Address) (bool, error) { + return s.State.MinerNominalPowerMeetsConsensusMinimum(s.store, a) +} + +func (s *state{{.v}}) TotalPowerSmoothed() (builtin.FilterEstimate, error) { + return builtin.FromV{{.v}}FilterEstimate({{if (le .v 1)}}*{{end}}s.State.ThisEpochQAPowerSmoothed), nil +} + +func (s *state{{.v}}) MinerCounts() (uint64, uint64, error) { + return uint64(s.State.MinerAboveMinPowerCount), uint64(s.State.MinerCount), nil +} + +func (s *state{{.v}}) ListAllMiners() ([]address.Address, error) { + claims, err := s.claims() + if err != nil { + return nil, err + } + + var miners []address.Address + err = claims.ForEach(nil, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + miners = append(miners, a) + return nil + }) + if err != nil { + return nil, err + } + + return miners, nil +} + +func (s *state{{.v}}) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { + claims, err := s.claims() + if err != nil { + return err + } + + var claim power{{.v}}.Claim + return claims.ForEach(&claim, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + return cb(a, Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }) + }) +} + +func (s *state{{.v}}) ClaimsChanged(other State) (bool, error) { + other{{.v}}, ok := other.(*state{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Claims.Equals(other{{.v}}.State.Claims), nil +} + +func (s *state{{.v}}) claims() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.Claims{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} + +func (s *state{{.v}}) decodeClaim(val *cbg.Deferred) (Claim, error) { + var ci power{{.v}}.Claim + if err := ci.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Claim{}, err + } + return fromV{{.v}}Claim(ci), nil +} + +func fromV{{.v}}Claim(v{{.v}} power{{.v}}.Claim) Claim { + return Claim{ + RawBytePower: v{{.v}}.RawBytePower, + QualityAdjPower: v{{.v}}.QualityAdjPower, + } +} diff --git a/chain/actors/builtin/power/v0.go b/chain/actors/builtin/power/v0.go index 7636b612b..91fad8c57 100644 --- a/chain/actors/builtin/power/v0.go +++ b/chain/actors/builtin/power/v0.go @@ -51,7 +51,7 @@ func (s *state0) TotalCommitted() (Claim, error) { } func (s *state0) MinerPower(addr address.Address) (Claim, bool, error) { - claims, err := adt0.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return Claim{}, false, err } @@ -79,7 +79,7 @@ func (s *state0) MinerCounts() (uint64, uint64, error) { } func (s *state0) ListAllMiners() ([]address.Address, error) { - claims, err := adt0.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return nil, err } @@ -101,7 +101,7 @@ func (s *state0) ListAllMiners() ([]address.Address, error) { } func (s *state0) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { - claims, err := adt0.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return err } @@ -141,5 +141,8 @@ func (s *state0) decodeClaim(val *cbg.Deferred) (Claim, error) { } func fromV0Claim(v0 power0.Claim) Claim { - return (Claim)(v0) + return Claim{ + RawBytePower: v0.RawBytePower, + QualityAdjPower: v0.QualityAdjPower, + } } diff --git a/chain/actors/builtin/power/v2.go b/chain/actors/builtin/power/v2.go index 012dc2a4f..313160a78 100644 --- a/chain/actors/builtin/power/v2.go +++ b/chain/actors/builtin/power/v2.go @@ -51,7 +51,7 @@ func (s *state2) TotalCommitted() (Claim, error) { } func (s *state2) MinerPower(addr address.Address) (Claim, bool, error) { - claims, err := adt2.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return Claim{}, false, err } @@ -79,7 +79,7 @@ func (s *state2) MinerCounts() (uint64, uint64, error) { } func (s *state2) ListAllMiners() ([]address.Address, error) { - claims, err := adt2.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return nil, err } @@ -101,7 +101,7 @@ func (s *state2) ListAllMiners() ([]address.Address, error) { } func (s *state2) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { - claims, err := adt2.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return err } diff --git a/chain/actors/builtin/power/v3.go b/chain/actors/builtin/power/v3.go index fd161dda5..174a091cd 100644 --- a/chain/actors/builtin/power/v3.go +++ b/chain/actors/builtin/power/v3.go @@ -121,12 +121,12 @@ func (s *state3) ForEachClaim(cb func(miner address.Address, claim Claim) error) } func (s *state3) ClaimsChanged(other State) (bool, error) { - other2, ok := other.(*state3) + other3, ok := other.(*state3) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.Claims.Equals(other2.State.Claims), nil + return !s.State.Claims.Equals(other3.State.Claims), nil } func (s *state3) claims() (adt.Map, error) { diff --git a/chain/actors/builtin/power/v4.go b/chain/actors/builtin/power/v4.go index bae5d044e..95cb47a65 100644 --- a/chain/actors/builtin/power/v4.go +++ b/chain/actors/builtin/power/v4.go @@ -121,12 +121,12 @@ func (s *state4) ForEachClaim(cb func(miner address.Address, claim Claim) error) } func (s *state4) ClaimsChanged(other State) (bool, error) { - other2, ok := other.(*state4) + other4, ok := other.(*state4) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.Claims.Equals(other2.State.Claims), nil + return !s.State.Claims.Equals(other4.State.Claims), nil } func (s *state4) claims() (adt.Map, error) { diff --git a/chain/actors/builtin/reward/actor.go.template b/chain/actors/builtin/reward/actor.go.template new file mode 100644 index 000000000..476ec6190 --- /dev/null +++ b/chain/actors/builtin/reward/actor.go.template @@ -0,0 +1,56 @@ +package reward + +import ( + "github.com/filecoin-project/go-state-types/abi" + reward0 "github.com/filecoin-project/specs-actors/actors/builtin/reward" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/cbor" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.RewardActorAddr + Methods = builtin{{.latestVersion}}.MethodsReward +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.RewardActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + ThisEpochBaselinePower() (abi.StoragePower, error) + ThisEpochReward() (abi.StoragePower, error) + ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) + + EffectiveBaselinePower() (abi.StoragePower, error) + EffectiveNetworkTime() (abi.ChainEpoch, error) + + TotalStoragePowerReward() (abi.TokenAmount, error) + + CumsumBaseline() (abi.StoragePower, error) + CumsumRealized() (abi.StoragePower, error) + + InitialPledgeForPower(abi.StoragePower, abi.TokenAmount, *builtin.FilterEstimate, abi.TokenAmount) (abi.TokenAmount, error) + PreCommitDepositForPower(builtin.FilterEstimate, abi.StoragePower) (abi.TokenAmount, error) +} + +type AwardBlockRewardParams = reward0.AwardBlockRewardParams diff --git a/chain/actors/builtin/reward/reward.go b/chain/actors/builtin/reward/reward.go index cfcd68dd5..ac836f854 100644 --- a/chain/actors/builtin/reward/reward.go +++ b/chain/actors/builtin/reward/reward.go @@ -7,6 +7,7 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/cbor" + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" @@ -37,7 +38,7 @@ var ( Methods = builtin4.MethodsReward ) -func Load(store adt.Store, act *types.Actor) (st State, err error) { +func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { case builtin0.RewardActorCodeID: return load0(store, act.Head) diff --git a/chain/actors/builtin/reward/state.go.template b/chain/actors/builtin/reward/state.go.template new file mode 100644 index 000000000..906b97837 --- /dev/null +++ b/chain/actors/builtin/reward/state.go.template @@ -0,0 +1,99 @@ +package reward + +import ( + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + + miner{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/miner" + reward{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/reward" + smoothing{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/smoothing" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + reward{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) ThisEpochReward() (abi.TokenAmount, error) { + return s.State.ThisEpochReward, nil +} + +func (s *state{{.v}}) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + {{if (ge .v 2)}}return builtin.FilterEstimate{ + PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, + VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, + }, nil{{else}}return builtin.FromV0FilterEstimate(*s.State.ThisEpochRewardSmoothed), nil{{end}} +} + +func (s *state{{.v}}) ThisEpochBaselinePower() (abi.StoragePower, error) { + return s.State.ThisEpochBaselinePower, nil +} + +func (s *state{{.v}}) TotalStoragePowerReward() (abi.TokenAmount, error) { + return s.State.{{if (ge .v 2)}}TotalStoragePowerReward{{else}}TotalMined{{end}}, nil +} + +func (s *state{{.v}}) EffectiveBaselinePower() (abi.StoragePower, error) { + return s.State.EffectiveBaselinePower, nil +} + +func (s *state{{.v}}) EffectiveNetworkTime() (abi.ChainEpoch, error) { + return s.State.EffectiveNetworkTime, nil +} + +func (s *state{{.v}}) CumsumBaseline() (reward{{.v}}.Spacetime, error) { + return s.State.CumsumBaseline, nil +} + +func (s *state{{.v}}) CumsumRealized() (reward{{.v}}.Spacetime, error) { + return s.State.CumsumRealized, nil +} +{{if (ge .v 2)}} +func (s *state{{.v}}) InitialPledgeForPower(qaPower abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { + return miner{{.v}}.InitialPledgeForPower( + qaPower, + s.State.ThisEpochBaselinePower, + s.State.ThisEpochRewardSmoothed, + smoothing{{.v}}.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + circSupply, + ), nil +} +{{else}} +func (s *state0) InitialPledgeForPower(sectorWeight abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { + return miner0.InitialPledgeForPower( + sectorWeight, + s.State.ThisEpochBaselinePower, + networkTotalPledge, + s.State.ThisEpochRewardSmoothed, + &smoothing0.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + circSupply), nil +} +{{end}} +func (s *state{{.v}}) PreCommitDepositForPower(networkQAPower builtin.FilterEstimate, sectorWeight abi.StoragePower) (abi.TokenAmount, error) { + return miner{{.v}}.PreCommitDepositForPower(s.State.ThisEpochRewardSmoothed, + {{if (le .v 0)}}&{{end}}smoothing{{.v}}.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + sectorWeight), nil +} diff --git a/chain/actors/builtin/reward/v0.go b/chain/actors/builtin/reward/v0.go index 6a6e6d12e..eba6f65d7 100644 --- a/chain/actors/builtin/reward/v0.go +++ b/chain/actors/builtin/reward/v0.go @@ -28,7 +28,7 @@ type state0 struct { store adt.Store } -func (s *state0) ThisEpochReward() (abi.StoragePower, error) { +func (s *state0) ThisEpochReward() (abi.TokenAmount, error) { return s.State.ThisEpochReward, nil } @@ -52,11 +52,11 @@ func (s *state0) EffectiveNetworkTime() (abi.ChainEpoch, error) { return s.State.EffectiveNetworkTime, nil } -func (s *state0) CumsumBaseline() (abi.StoragePower, error) { +func (s *state0) CumsumBaseline() (reward0.Spacetime, error) { return s.State.CumsumBaseline, nil } -func (s *state0) CumsumRealized() (abi.StoragePower, error) { +func (s *state0) CumsumRealized() (reward0.Spacetime, error) { return s.State.CumsumRealized, nil } diff --git a/chain/actors/builtin/verifreg/actor.go.template b/chain/actors/builtin/verifreg/actor.go.template new file mode 100644 index 000000000..d194a2c89 --- /dev/null +++ b/chain/actors/builtin/verifreg/actor.go.template @@ -0,0 +1,46 @@ +package verifreg + +import ( + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/go-state-types/cbor" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} builtin.RegisterActorState(builtin{{.}}.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.VerifiedRegistryActorAddr + Methods = builtin{{.latestVersion}}.MethodsVerifiedRegistry +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} case builtin{{.}}.VerifiedRegistryActorCodeID: + return load{{.}}(store, act.Head) +{{end}} } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + RootKey() (address.Address, error) + VerifiedClientDataCap(address.Address) (bool, abi.StoragePower, error) + VerifierDataCap(address.Address) (bool, abi.StoragePower, error) + ForEachVerifier(func(addr address.Address, dcap abi.StoragePower) error) error + ForEachClient(func(addr address.Address, dcap abi.StoragePower) error) error +} diff --git a/chain/actors/builtin/verifreg/state.go.template b/chain/actors/builtin/verifreg/state.go.template new file mode 100644 index 000000000..244d20932 --- /dev/null +++ b/chain/actors/builtin/verifreg/state.go.template @@ -0,0 +1,58 @@ +package verifreg + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + +{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} verifreg{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/verifreg" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + verifreg{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) RootKey() (address.Address, error) { + return s.State.RootKey, nil +} + +func (s *state{{.v}}) VerifiedClientDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version{{.v}}, s.verifiedClients, addr) +} + +func (s *state{{.v}}) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version{{.v}}, s.verifiers, addr) +} + +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) +} + +func (s *state{{.v}}) ForEachClient(cb func(addr address.Address, dcap abi.StoragePower) error) error { + return forEachCap(s.store, actors.Version{{.v}}, s.verifiedClients, cb) +} + +func (s *state{{.v}}) verifiedClients() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.VerifiedClients{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} + +func (s *state{{.v}}) verifiers() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.Verifiers{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} diff --git a/chain/actors/builtin/verifreg/verifreg.go b/chain/actors/builtin/verifreg/verifreg.go index 83ba0c6c5..88e8adfc1 100644 --- a/chain/actors/builtin/verifreg/verifreg.go +++ b/chain/actors/builtin/verifreg/verifreg.go @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/cbor" + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" From 715b3dccecf22997891952561951f6efabcab028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 28 Apr 2021 16:20:53 +0200 Subject: [PATCH 79/97] Fix error message --- chain/actors/agen/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go index 0d73a064e..637361f17 100644 --- a/chain/actors/agen/main.go +++ b/chain/actors/agen/main.go @@ -120,7 +120,7 @@ func generateMessages(actDir string) error { return nil // skip } - return xerrors.Errorf("loading state adapter template: %w", err) + return xerrors.Errorf("loading message adapter template: %w", err) } for _, version := range versions { From c6cebb448fd3be4db082d400c26649eed69d9f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Apr 2021 17:51:24 +0200 Subject: [PATCH 80/97] Cleanup actor adapter templates --- Makefile | 10 ++++-- chain/actors/builtin/account/account.go | 12 +++++++ .../actors/builtin/account/actor.go.template | 12 ++++--- chain/actors/builtin/init/actor.go.template | 12 ++++--- chain/actors/builtin/init/init.go | 12 +++++++ chain/actors/builtin/init/state.go.template | 7 ++-- chain/actors/builtin/init/v3.go | 3 +- chain/actors/builtin/init/v4.go | 3 +- chain/actors/builtin/market/actor.go.template | 29 ++++++++++++++--- chain/actors/builtin/market/market.go | 14 +++++++- chain/actors/builtin/miner/actor.go.template | 15 ++++++--- chain/actors/builtin/miner/miner.go | 13 ++++++++ chain/actors/builtin/miner/state.go.template | 32 +++++++++++++------ chain/actors/builtin/miner/v0.go | 6 ++++ chain/actors/builtin/miner/v2.go | 6 ++++ chain/actors/builtin/miner/v3.go | 7 ++++ chain/actors/builtin/miner/v4.go | 7 ++++ .../actors/builtin/multisig/actor.go.template | 15 ++++++--- .../builtin/multisig/message.go.template | 7 ++-- chain/actors/builtin/multisig/multisig.go | 16 ++++++++++ .../actors/builtin/multisig/state.go.template | 6 ++-- chain/actors/builtin/multisig/v3.go | 1 + chain/actors/builtin/multisig/v4.go | 1 + chain/actors/builtin/paych/actor.go.template | 18 +++++++---- chain/actors/builtin/paych/paych.go | 17 ++++++++++ chain/actors/builtin/power/actor.go.template | 12 ++++--- chain/actors/builtin/power/power.go | 12 +++++++ chain/actors/builtin/power/state.go.template | 6 ++-- chain/actors/builtin/power/v3.go | 1 + chain/actors/builtin/power/v4.go | 1 + chain/actors/builtin/reward/actor.go.template | 12 ++++--- chain/actors/builtin/reward/reward.go | 12 +++++++ chain/actors/builtin/reward/state.go.template | 8 +++-- chain/actors/builtin/reward/v0.go | 2 ++ chain/actors/builtin/reward/v2.go | 2 ++ chain/actors/builtin/reward/v3.go | 2 ++ chain/actors/builtin/reward/v4.go | 2 ++ .../actors/builtin/verifreg/actor.go.template | 15 ++++++--- chain/actors/builtin/verifreg/verifreg.go | 13 ++++++++ 39 files changed, 315 insertions(+), 66 deletions(-) diff --git a/Makefile b/Makefile index 1ccce11ed..67d26a71b 100644 --- a/Makefile +++ b/Makefile @@ -333,6 +333,10 @@ type-gen: api-gen method-gen: api-gen (cd ./lotuspond/front/src/chain && go run ./methodgen.go) +actors-gen: + go run ./chain/actors/agen + go fmt ./... + api-gen: go run ./gen/api goimports -w api @@ -341,9 +345,9 @@ api-gen: docsgen: docsgen-md docsgen-openrpc -docsgen-md-bin: api-gen +docsgen-md-bin: api-gen actors-gen go build $(GOFLAGS) -o docgen-md ./api/docgen/cmd -docsgen-openrpc-bin: api-gen +docsgen-openrpc-bin: api-gen actors-gen go build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker @@ -367,7 +371,7 @@ docsgen-openrpc-worker: docsgen-openrpc-bin .PHONY: docsgen docsgen-md-bin docsgen-openrpc-bin -gen: type-gen method-gen docsgen api-gen +gen: actors-gen type-gen method-gen docsgen api-gen @echo ">>> IF YOU'VE MODIFIED THE CLI, REMEMBER TO ALSO MAKE docsgen-cli" .PHONY: gen diff --git a/chain/actors/builtin/account/account.go b/chain/actors/builtin/account/account.go index 4d94b245a..8242e300d 100644 --- a/chain/actors/builtin/account/account.go +++ b/chain/actors/builtin/account/account.go @@ -12,21 +12,28 @@ import ( "github.com/filecoin-project/lotus/chain/types" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) func init() { + builtin.RegisterActorState(builtin0.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -36,14 +43,19 @@ var Methods = builtin4.MethodsAccount func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.AccountActorCodeID: return load0(store, act.Head) + case builtin2.AccountActorCodeID: return load2(store, act.Head) + case builtin3.AccountActorCodeID: return load3(store, act.Head) + case builtin4.AccountActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/account/actor.go.template b/chain/actors/builtin/account/actor.go.template index 860dadc1c..f75af3dfb 100644 --- a/chain/actors/builtin/account/actor.go.template +++ b/chain/actors/builtin/account/actor.go.template @@ -11,11 +11,13 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) {{end}}} @@ -24,9 +26,11 @@ var Methods = builtin4.MethodsAccount func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.AccountActorCodeID: +{{range .versions}} + case builtin{{.}}.AccountActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/init/actor.go.template b/chain/actors/builtin/init/actor.go.template index 0c9a6c702..5b700cec8 100644 --- a/chain/actors/builtin/init/actor.go.template +++ b/chain/actors/builtin/init/actor.go.template @@ -13,11 +13,13 @@ import ( "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/node/modules/dtypes" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) {{end}}} @@ -29,9 +31,11 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.InitActorCodeID: +{{range .versions}} + case builtin{{.}}.InitActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/init/init.go b/chain/actors/builtin/init/init.go index 697148641..730d21fd8 100644 --- a/chain/actors/builtin/init/init.go +++ b/chain/actors/builtin/init/init.go @@ -14,21 +14,28 @@ import ( "github.com/filecoin-project/lotus/node/modules/dtypes" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) func init() { + builtin.RegisterActorState(builtin0.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -41,14 +48,19 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.InitActorCodeID: return load0(store, act.Head) + case builtin2.InitActorCodeID: return load2(store, act.Head) + case builtin3.InitActorCodeID: return load3(store, act.Head) + case builtin4.InitActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/init/state.go.template b/chain/actors/builtin/init/state.go.template index 3fd7aaaa7..95f052bda 100644 --- a/chain/actors/builtin/init/state.go.template +++ b/chain/actors/builtin/init/state.go.template @@ -3,14 +3,17 @@ package init import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" -{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" -{{end}} "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/node/modules/dtypes" +{{if (ge .v 3)}} + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} + init{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/init" adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" ) diff --git a/chain/actors/builtin/init/v3.go b/chain/actors/builtin/init/v3.go index e586b3b11..eaa54dfd4 100644 --- a/chain/actors/builtin/init/v3.go +++ b/chain/actors/builtin/init/v3.go @@ -3,7 +3,6 @@ package init import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -11,6 +10,8 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/node/modules/dtypes" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + init3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/init" adt3 "github.com/filecoin-project/specs-actors/v3/actors/util/adt" ) diff --git a/chain/actors/builtin/init/v4.go b/chain/actors/builtin/init/v4.go index a2be603a3..38749eed5 100644 --- a/chain/actors/builtin/init/v4.go +++ b/chain/actors/builtin/init/v4.go @@ -3,7 +3,6 @@ package init import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -11,6 +10,8 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/node/modules/dtypes" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + init4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/init" adt4 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" ) diff --git a/chain/actors/builtin/market/actor.go.template b/chain/actors/builtin/market/actor.go.template index 8c16ffb91..39cfe1be7 100644 --- a/chain/actors/builtin/market/actor.go.template +++ b/chain/actors/builtin/market/actor.go.template @@ -5,13 +5,15 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/cbor" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" @@ -19,7 +21,8 @@ import ( ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) {{end}}} @@ -31,9 +34,11 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.StorageMarketActorCodeID: +{{range .versions}} + case builtin{{.}}.StorageMarketActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -136,3 +141,19 @@ func EmptyDealState() *DealState { LastUpdatedEpoch: -1, } } + +// returns the earned fees and pending fees for a given deal +func (deal DealProposal) GetDealFees(height abi.ChainEpoch) (abi.TokenAmount, abi.TokenAmount) { + tf := big.Mul(deal.StoragePricePerEpoch, big.NewInt(int64(deal.EndEpoch-deal.StartEpoch))) + + ef := big.Mul(deal.StoragePricePerEpoch, big.NewInt(int64(height-deal.StartEpoch))) + if ef.LessThan(big.Zero()) { + ef = big.Zero() + } + + if ef.GreaterThan(tf) { + ef = tf + } + + return ef, big.Sub(tf, ef) +} diff --git a/chain/actors/builtin/market/market.go b/chain/actors/builtin/market/market.go index 16c44339b..adf7ce33d 100644 --- a/chain/actors/builtin/market/market.go +++ b/chain/actors/builtin/market/market.go @@ -1,11 +1,11 @@ package market import ( - "github.com/filecoin-project/go-state-types/big" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/cbor" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" @@ -13,8 +13,11 @@ import ( market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -23,15 +26,19 @@ import ( ) func init() { + builtin.RegisterActorState(builtin0.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -44,14 +51,19 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.StorageMarketActorCodeID: return load0(store, act.Head) + case builtin2.StorageMarketActorCodeID: return load2(store, act.Head) + case builtin3.StorageMarketActorCodeID: return load3(store, act.Head) + case builtin4.StorageMarketActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/miner/actor.go.template b/chain/actors/builtin/miner/actor.go.template index 4265af7dc..4b3d8db5e 100644 --- a/chain/actors/builtin/miner/actor.go.template +++ b/chain/actors/builtin/miner/actor.go.template @@ -22,14 +22,17 @@ import ( miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) -{{end}}} +{{end}} +} var Methods = builtin{{.latestVersion}}.MethodsMiner @@ -49,9 +52,11 @@ var AddressedSectorsMax = miner2.AddressedSectorsMax func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.StorageMinerActorCodeID: +{{range .versions}} + case builtin{{.}}.StorageMinerActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} +} return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index ae49a6d06..a426e063b 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -23,24 +23,32 @@ import ( miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) func init() { + builtin.RegisterActorState(builtin0.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + } var Methods = builtin4.MethodsMiner @@ -61,14 +69,19 @@ var AddressedSectorsMax = miner2.AddressedSectorsMax func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.StorageMinerActorCodeID: return load0(store, act.Head) + case builtin2.StorageMinerActorCodeID: return load2(store, act.Head) + case builtin3.StorageMinerActorCodeID: return load3(store, act.Head) + case builtin4.StorageMinerActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/miner/state.go.template b/chain/actors/builtin/miner/state.go.template index df94c942c..0769eea10 100644 --- a/chain/actors/builtin/miner/state.go.template +++ b/chain/actors/builtin/miner/state.go.template @@ -17,8 +17,10 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" -{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" -{{end}} miner{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/miner" +{{if (ge .v 3)}} + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} + miner{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/miner" adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" ) @@ -398,13 +400,17 @@ func (d *deadline{{.v}}) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline{{.v}}) DisputableProofCount() (uint64, error) { -{{if (ge .v 3)}} ops, err := d.OptimisticProofsSnapshotArray(d.store) +{{if (ge .v 3)}} + ops, err := d.OptimisticProofsSnapshotArray(d.store) if err != nil { return 0, err } - return ops.Length(), nil{{else}} // field doesn't exist until v3 - return 0, nil{{end}} + return ops.Length(), nil +{{else}} + // field doesn't exist until v3 + return 0, nil +{{end}} } func (p *partition{{.v}}) AllSectors() (bitfield.BitField, error) { @@ -420,7 +426,8 @@ func (p *partition{{.v}}) RecoveringSectors() (bitfield.BitField, error) { } func fromV{{.v}}SectorOnChainInfo(v{{.v}} miner{{.v}}.SectorOnChainInfo) SectorOnChainInfo { - {{if (ge .v 2)}}return SectorOnChainInfo{ +{{if (ge .v 2)}} + return SectorOnChainInfo{ SectorNumber: v{{.v}}.SectorNumber, SealProof: v{{.v}}.SealProof, SealedCID: v{{.v}}.SealedCID, @@ -432,15 +439,22 @@ func fromV{{.v}}SectorOnChainInfo(v{{.v}} miner{{.v}}.SectorOnChainInfo) SectorO InitialPledge: v{{.v}}.InitialPledge, ExpectedDayReward: v{{.v}}.ExpectedDayReward, ExpectedStoragePledge: v{{.v}}.ExpectedStoragePledge, - }{{else}}return (SectorOnChainInfo)(v0){{end}} + } +{{else}} + return (SectorOnChainInfo)(v0) +{{end}} } func fromV{{.v}}SectorPreCommitOnChainInfo(v{{.v}} miner{{.v}}.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { - {{if (ge .v 2)}}return SectorPreCommitOnChainInfo{ +{{if (ge .v 2)}} + return SectorPreCommitOnChainInfo{ Info: (SectorPreCommitInfo)(v{{.v}}.Info), PreCommitDeposit: v{{.v}}.PreCommitDeposit, PreCommitEpoch: v{{.v}}.PreCommitEpoch, DealWeight: v{{.v}}.DealWeight, VerifiedDealWeight: v{{.v}}.VerifiedDealWeight, - }{{else}}return (SectorPreCommitOnChainInfo)(v0){{end}} + } +{{else}} + return (SectorPreCommitOnChainInfo)(v0) +{{end}} } diff --git a/chain/actors/builtin/miner/v0.go b/chain/actors/builtin/miner/v0.go index 262c4870d..2dc8ae23e 100644 --- a/chain/actors/builtin/miner/v0.go +++ b/chain/actors/builtin/miner/v0.go @@ -397,8 +397,10 @@ func (d *deadline0) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline0) DisputableProofCount() (uint64, error) { + // field doesn't exist until v3 return 0, nil + } func (p *partition0) AllSectors() (bitfield.BitField, error) { @@ -414,9 +416,13 @@ func (p *partition0) RecoveringSectors() (bitfield.BitField, error) { } func fromV0SectorOnChainInfo(v0 miner0.SectorOnChainInfo) SectorOnChainInfo { + return (SectorOnChainInfo)(v0) + } func fromV0SectorPreCommitOnChainInfo(v0 miner0.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + return (SectorPreCommitOnChainInfo)(v0) + } diff --git a/chain/actors/builtin/miner/v2.go b/chain/actors/builtin/miner/v2.go index 1720b619f..7564dd8b8 100644 --- a/chain/actors/builtin/miner/v2.go +++ b/chain/actors/builtin/miner/v2.go @@ -395,8 +395,10 @@ func (d *deadline2) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline2) DisputableProofCount() (uint64, error) { + // field doesn't exist until v3 return 0, nil + } func (p *partition2) AllSectors() (bitfield.BitField, error) { @@ -412,6 +414,7 @@ func (p *partition2) RecoveringSectors() (bitfield.BitField, error) { } func fromV2SectorOnChainInfo(v2 miner2.SectorOnChainInfo) SectorOnChainInfo { + return SectorOnChainInfo{ SectorNumber: v2.SectorNumber, SealProof: v2.SealProof, @@ -425,9 +428,11 @@ func fromV2SectorOnChainInfo(v2 miner2.SectorOnChainInfo) SectorOnChainInfo { ExpectedDayReward: v2.ExpectedDayReward, ExpectedStoragePledge: v2.ExpectedStoragePledge, } + } func fromV2SectorPreCommitOnChainInfo(v2 miner2.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + return SectorPreCommitOnChainInfo{ Info: (SectorPreCommitInfo)(v2.Info), PreCommitDeposit: v2.PreCommitDeposit, @@ -435,4 +440,5 @@ func fromV2SectorPreCommitOnChainInfo(v2 miner2.SectorPreCommitOnChainInfo) Sect DealWeight: v2.DealWeight, VerifiedDealWeight: v2.VerifiedDealWeight, } + } diff --git a/chain/actors/builtin/miner/v3.go b/chain/actors/builtin/miner/v3.go index 4f03fdcc9..72a080f73 100644 --- a/chain/actors/builtin/miner/v3.go +++ b/chain/actors/builtin/miner/v3.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" adt3 "github.com/filecoin-project/specs-actors/v3/actors/util/adt" ) @@ -391,12 +392,14 @@ func (d *deadline3) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline3) DisputableProofCount() (uint64, error) { + ops, err := d.OptimisticProofsSnapshotArray(d.store) if err != nil { return 0, err } return ops.Length(), nil + } func (p *partition3) AllSectors() (bitfield.BitField, error) { @@ -412,6 +415,7 @@ func (p *partition3) RecoveringSectors() (bitfield.BitField, error) { } func fromV3SectorOnChainInfo(v3 miner3.SectorOnChainInfo) SectorOnChainInfo { + return SectorOnChainInfo{ SectorNumber: v3.SectorNumber, SealProof: v3.SealProof, @@ -425,9 +429,11 @@ func fromV3SectorOnChainInfo(v3 miner3.SectorOnChainInfo) SectorOnChainInfo { ExpectedDayReward: v3.ExpectedDayReward, ExpectedStoragePledge: v3.ExpectedStoragePledge, } + } func fromV3SectorPreCommitOnChainInfo(v3 miner3.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + return SectorPreCommitOnChainInfo{ Info: (SectorPreCommitInfo)(v3.Info), PreCommitDeposit: v3.PreCommitDeposit, @@ -435,4 +441,5 @@ func fromV3SectorPreCommitOnChainInfo(v3 miner3.SectorPreCommitOnChainInfo) Sect DealWeight: v3.DealWeight, VerifiedDealWeight: v3.VerifiedDealWeight, } + } diff --git a/chain/actors/builtin/miner/v4.go b/chain/actors/builtin/miner/v4.go index e277c0298..698bdf2f5 100644 --- a/chain/actors/builtin/miner/v4.go +++ b/chain/actors/builtin/miner/v4.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" adt4 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" ) @@ -391,12 +392,14 @@ func (d *deadline4) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline4) DisputableProofCount() (uint64, error) { + ops, err := d.OptimisticProofsSnapshotArray(d.store) if err != nil { return 0, err } return ops.Length(), nil + } func (p *partition4) AllSectors() (bitfield.BitField, error) { @@ -412,6 +415,7 @@ func (p *partition4) RecoveringSectors() (bitfield.BitField, error) { } func fromV4SectorOnChainInfo(v4 miner4.SectorOnChainInfo) SectorOnChainInfo { + return SectorOnChainInfo{ SectorNumber: v4.SectorNumber, SealProof: v4.SealProof, @@ -425,9 +429,11 @@ func fromV4SectorOnChainInfo(v4 miner4.SectorOnChainInfo) SectorOnChainInfo { ExpectedDayReward: v4.ExpectedDayReward, ExpectedStoragePledge: v4.ExpectedStoragePledge, } + } func fromV4SectorPreCommitOnChainInfo(v4 miner4.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + return SectorPreCommitOnChainInfo{ Info: (SectorPreCommitInfo)(v4.Info), PreCommitDeposit: v4.PreCommitDeposit, @@ -435,4 +441,5 @@ func fromV4SectorPreCommitOnChainInfo(v4 miner4.SectorPreCommitOnChainInfo) Sect DealWeight: v4.DealWeight, VerifiedDealWeight: v4.VerifiedDealWeight, } + } diff --git a/chain/actors/builtin/multisig/actor.go.template b/chain/actors/builtin/multisig/actor.go.template index 76aff2581..304c0610c 100644 --- a/chain/actors/builtin/multisig/actor.go.template +++ b/chain/actors/builtin/multisig/actor.go.template @@ -15,7 +15,8 @@ import ( msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -24,16 +25,19 @@ import ( ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) {{end}}} func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.MultisigActorCodeID: +{{range .versions}} + case builtin{{.}}.MultisigActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -60,7 +64,8 @@ var Methods = builtin{{.latestVersion}}.MethodsMultisig func Message(version actors.Version, from address.Address) MessageBuilder { switch version { -{{range .versions}} case actors.Version{{.}}: +{{range .versions}} + case actors.Version{{.}}: return message{{.}}{{"{"}}{{if (ge . 2)}}message0{from}{{else}}from{{end}}} {{end}} default: panic(fmt.Sprintf("unsupported actors version: %d", version)) diff --git a/chain/actors/builtin/multisig/message.go.template b/chain/actors/builtin/multisig/message.go.template index dfd5c6fc0..6bff8983a 100644 --- a/chain/actors/builtin/multisig/message.go.template +++ b/chain/actors/builtin/multisig/message.go.template @@ -72,7 +72,9 @@ func (m message{{.v}}) Create( Params: enc, Value: initialAmount, }, nil -}{{if (le .v 1)}} +} + +{{if (le .v 1)}} func (m message0) Propose(msig, to address.Address, amt abi.TokenAmount, method abi.MethodNum, params []byte) (*types.Message, error) { @@ -140,4 +142,5 @@ func (m message0) Cancel(msig address.Address, txID uint64, hashData *ProposalHa Method: builtin0.MethodsMultisig.Cancel, Params: enc, }, nil -}{{end}} +} +{{end}} diff --git a/chain/actors/builtin/multisig/multisig.go b/chain/actors/builtin/multisig/multisig.go index 0cee644c5..79b1a57d7 100644 --- a/chain/actors/builtin/multisig/multisig.go +++ b/chain/actors/builtin/multisig/multisig.go @@ -16,8 +16,11 @@ import ( msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/lotus/chain/actors" @@ -27,15 +30,19 @@ import ( ) func init() { + builtin.RegisterActorState(builtin0.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -43,14 +50,19 @@ func init() { func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.MultisigActorCodeID: return load0(store, act.Head) + case builtin2.MultisigActorCodeID: return load2(store, act.Head) + case builtin3.MultisigActorCodeID: return load3(store, act.Head) + case builtin4.MultisigActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -78,12 +90,16 @@ var Methods = builtin4.MethodsMultisig func Message(version actors.Version, from address.Address) MessageBuilder { switch version { + case actors.Version0: return message0{from} + case actors.Version2: return message2{message0{from}} + case actors.Version3: return message3{message0{from}} + case actors.Version4: return message4{message0{from}} default: diff --git a/chain/actors/builtin/multisig/state.go.template b/chain/actors/builtin/multisig/state.go.template index c62655646..2316aadba 100644 --- a/chain/actors/builtin/multisig/state.go.template +++ b/chain/actors/builtin/multisig/state.go.template @@ -14,8 +14,10 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" -{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" -{{end}} msig{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/multisig" +{{if (ge .v 3)}} + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} + msig{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/multisig" ) var _ State = (*state{{.v}})(nil) diff --git a/chain/actors/builtin/multisig/v3.go b/chain/actors/builtin/multisig/v3.go index 1a7611440..8834e4553 100644 --- a/chain/actors/builtin/multisig/v3.go +++ b/chain/actors/builtin/multisig/v3.go @@ -15,6 +15,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + msig3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/multisig" ) diff --git a/chain/actors/builtin/multisig/v4.go b/chain/actors/builtin/multisig/v4.go index 50c1e9620..9f9dc7573 100644 --- a/chain/actors/builtin/multisig/v4.go +++ b/chain/actors/builtin/multisig/v4.go @@ -15,6 +15,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" ) diff --git a/chain/actors/builtin/paych/actor.go.template b/chain/actors/builtin/paych/actor.go.template index bd0d6c03c..3f68a5cfa 100644 --- a/chain/actors/builtin/paych/actor.go.template +++ b/chain/actors/builtin/paych/actor.go.template @@ -15,7 +15,8 @@ import ( paych0 "github.com/filecoin-project/specs-actors/actors/builtin/paych" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -24,7 +25,8 @@ import ( ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) {{end}}} @@ -32,9 +34,11 @@ func init() { // Load returns an abstract copy of payment channel state, irregardless of actor version func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.PaymentChannelActorCodeID: +{{range .versions}} + case builtin{{.}}.PaymentChannelActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -88,9 +92,11 @@ var Methods = builtin{{.latestVersion}}.MethodsPaych func Message(version actors.Version, from address.Address) MessageBuilder { switch version { -{{range .versions}} case actors.Version{{.}}: +{{range .versions}} + case actors.Version{{.}}: return message{{.}}{from} -{{end}} default: +{{end}} + default: panic(fmt.Sprintf("unsupported actors version: %d", version)) } } diff --git a/chain/actors/builtin/paych/paych.go b/chain/actors/builtin/paych/paych.go index a62b2691f..30e4408d8 100644 --- a/chain/actors/builtin/paych/paych.go +++ b/chain/actors/builtin/paych/paych.go @@ -16,8 +16,11 @@ import ( paych0 "github.com/filecoin-project/specs-actors/actors/builtin/paych" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/lotus/chain/actors" @@ -27,15 +30,19 @@ import ( ) func init() { + builtin.RegisterActorState(builtin0.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -44,14 +51,19 @@ func init() { // Load returns an abstract copy of payment channel state, irregardless of actor version func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.PaymentChannelActorCodeID: return load0(store, act.Head) + case builtin2.PaymentChannelActorCodeID: return load2(store, act.Head) + case builtin3.PaymentChannelActorCodeID: return load3(store, act.Head) + case builtin4.PaymentChannelActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -106,14 +118,19 @@ var Methods = builtin4.MethodsPaych func Message(version actors.Version, from address.Address) MessageBuilder { switch version { + case actors.Version0: return message0{from} + case actors.Version2: return message2{from} + case actors.Version3: return message3{from} + case actors.Version4: return message4{from} + default: panic(fmt.Sprintf("unsupported actors version: %d", version)) } diff --git a/chain/actors/builtin/power/actor.go.template b/chain/actors/builtin/power/actor.go.template index 56b591f7c..82f791e58 100644 --- a/chain/actors/builtin/power/actor.go.template +++ b/chain/actors/builtin/power/actor.go.template @@ -14,11 +14,13 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) {{end}}} @@ -30,9 +32,11 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.StoragePowerActorCodeID: +{{range .versions}} + case builtin{{.}}.StoragePowerActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/power/power.go b/chain/actors/builtin/power/power.go index 56b6f7c95..bf530a21a 100644 --- a/chain/actors/builtin/power/power.go +++ b/chain/actors/builtin/power/power.go @@ -15,21 +15,28 @@ import ( "github.com/filecoin-project/lotus/chain/types" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) func init() { + builtin.RegisterActorState(builtin0.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -42,14 +49,19 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.StoragePowerActorCodeID: return load0(store, act.Head) + case builtin2.StoragePowerActorCodeID: return load2(store, act.Head) + case builtin3.StoragePowerActorCodeID: return load3(store, act.Head) + case builtin4.StoragePowerActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/power/state.go.template b/chain/actors/builtin/power/state.go.template index b71ac1c07..4cb904a1d 100644 --- a/chain/actors/builtin/power/state.go.template +++ b/chain/actors/builtin/power/state.go.template @@ -11,8 +11,10 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" -{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" -{{end}} power{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/power" +{{if (ge .v 3)}} + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} + power{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/power" adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" ) diff --git a/chain/actors/builtin/power/v3.go b/chain/actors/builtin/power/v3.go index 174a091cd..2ef1e2808 100644 --- a/chain/actors/builtin/power/v3.go +++ b/chain/actors/builtin/power/v3.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + power3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/power" adt3 "github.com/filecoin-project/specs-actors/v3/actors/util/adt" ) diff --git a/chain/actors/builtin/power/v4.go b/chain/actors/builtin/power/v4.go index 95cb47a65..686550456 100644 --- a/chain/actors/builtin/power/v4.go +++ b/chain/actors/builtin/power/v4.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + power4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/power" adt4 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" ) diff --git a/chain/actors/builtin/reward/actor.go.template b/chain/actors/builtin/reward/actor.go.template index 476ec6190..81437d26f 100644 --- a/chain/actors/builtin/reward/actor.go.template +++ b/chain/actors/builtin/reward/actor.go.template @@ -8,7 +8,8 @@ import ( "github.com/filecoin-project/go-state-types/cbor" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" @@ -16,7 +17,8 @@ import ( ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) {{end}}} @@ -28,9 +30,11 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.RewardActorCodeID: +{{range .versions}} + case builtin{{.}}.RewardActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/reward/reward.go b/chain/actors/builtin/reward/reward.go index ac836f854..1037cf741 100644 --- a/chain/actors/builtin/reward/reward.go +++ b/chain/actors/builtin/reward/reward.go @@ -9,8 +9,11 @@ import ( "github.com/filecoin-project/go-state-types/cbor" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -19,15 +22,19 @@ import ( ) func init() { + builtin.RegisterActorState(builtin0.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -40,14 +47,19 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.RewardActorCodeID: return load0(store, act.Head) + case builtin2.RewardActorCodeID: return load2(store, act.Head) + case builtin3.RewardActorCodeID: return load3(store, act.Head) + case builtin4.RewardActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/reward/state.go.template b/chain/actors/builtin/reward/state.go.template index 906b97837..1758d1413 100644 --- a/chain/actors/builtin/reward/state.go.template +++ b/chain/actors/builtin/reward/state.go.template @@ -33,10 +33,14 @@ func (s *state{{.v}}) ThisEpochReward() (abi.TokenAmount, error) { } func (s *state{{.v}}) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { - {{if (ge .v 2)}}return builtin.FilterEstimate{ +{{if (ge .v 2)}} + return builtin.FilterEstimate{ PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, - }, nil{{else}}return builtin.FromV0FilterEstimate(*s.State.ThisEpochRewardSmoothed), nil{{end}} + }, nil +{{else}} + return builtin.FromV0FilterEstimate(*s.State.ThisEpochRewardSmoothed), nil +{{end}} } func (s *state{{.v}}) ThisEpochBaselinePower() (abi.StoragePower, error) { diff --git a/chain/actors/builtin/reward/v0.go b/chain/actors/builtin/reward/v0.go index eba6f65d7..fe053cc16 100644 --- a/chain/actors/builtin/reward/v0.go +++ b/chain/actors/builtin/reward/v0.go @@ -33,7 +33,9 @@ func (s *state0) ThisEpochReward() (abi.TokenAmount, error) { } func (s *state0) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + return builtin.FromV0FilterEstimate(*s.State.ThisEpochRewardSmoothed), nil + } func (s *state0) ThisEpochBaselinePower() (abi.StoragePower, error) { diff --git a/chain/actors/builtin/reward/v2.go b/chain/actors/builtin/reward/v2.go index c9a591532..90621e467 100644 --- a/chain/actors/builtin/reward/v2.go +++ b/chain/actors/builtin/reward/v2.go @@ -33,10 +33,12 @@ func (s *state2) ThisEpochReward() (abi.TokenAmount, error) { } func (s *state2) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + return builtin.FilterEstimate{ PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, }, nil + } func (s *state2) ThisEpochBaselinePower() (abi.StoragePower, error) { diff --git a/chain/actors/builtin/reward/v3.go b/chain/actors/builtin/reward/v3.go index 18bd58f8e..926cc085b 100644 --- a/chain/actors/builtin/reward/v3.go +++ b/chain/actors/builtin/reward/v3.go @@ -33,10 +33,12 @@ func (s *state3) ThisEpochReward() (abi.TokenAmount, error) { } func (s *state3) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + return builtin.FilterEstimate{ PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, }, nil + } func (s *state3) ThisEpochBaselinePower() (abi.StoragePower, error) { diff --git a/chain/actors/builtin/reward/v4.go b/chain/actors/builtin/reward/v4.go index 7320d1701..f034b0018 100644 --- a/chain/actors/builtin/reward/v4.go +++ b/chain/actors/builtin/reward/v4.go @@ -33,10 +33,12 @@ func (s *state4) ThisEpochReward() (abi.TokenAmount, error) { } func (s *state4) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + return builtin.FilterEstimate{ PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, }, nil + } func (s *state4) ThisEpochBaselinePower() (abi.StoragePower, error) { diff --git a/chain/actors/builtin/verifreg/actor.go.template b/chain/actors/builtin/verifreg/actor.go.template index d194a2c89..22e809ccf 100644 --- a/chain/actors/builtin/verifreg/actor.go.template +++ b/chain/actors/builtin/verifreg/actor.go.template @@ -9,7 +9,8 @@ import ( "github.com/filecoin-project/go-state-types/cbor" {{range .versions}} - builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin"{{end}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" @@ -17,10 +18,12 @@ import ( ) func init() { -{{range .versions}} builtin.RegisterActorState(builtin{{.}}.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load{{.}}(store, root) }) -{{end}}} +{{end}} +} var ( Address = builtin{{.latestVersion}}.VerifiedRegistryActorAddr @@ -29,9 +32,11 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { -{{range .versions}} case builtin{{.}}.VerifiedRegistryActorCodeID: +{{range .versions}} + case builtin{{.}}.VerifiedRegistryActorCodeID: return load{{.}}(store, act.Head) -{{end}} } +{{end}} + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/verifreg/verifreg.go b/chain/actors/builtin/verifreg/verifreg.go index 88e8adfc1..32f50a4ae 100644 --- a/chain/actors/builtin/verifreg/verifreg.go +++ b/chain/actors/builtin/verifreg/verifreg.go @@ -10,8 +10,11 @@ import ( "github.com/filecoin-project/go-state-types/cbor" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -20,18 +23,23 @@ import ( ) func init() { + builtin.RegisterActorState(builtin0.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + } var ( @@ -41,14 +49,19 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.VerifiedRegistryActorCodeID: return load0(store, act.Head) + case builtin2.VerifiedRegistryActorCodeID: return load2(store, act.Head) + case builtin3.VerifiedRegistryActorCodeID: return load3(store, act.Head) + case builtin4.VerifiedRegistryActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } From 94d3c8bed7885a51f1a1280de56e912fe3df9063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Apr 2021 18:02:57 +0200 Subject: [PATCH 81/97] fix lint --- chain/actors/agen/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go index 637361f17..82a92aa00 100644 --- a/chain/actors/agen/main.go +++ b/chain/actors/agen/main.go @@ -3,11 +3,12 @@ package main import ( "bytes" "fmt" - "golang.org/x/xerrors" "io/ioutil" "os" "path/filepath" "text/template" + + "golang.org/x/xerrors" ) var latestVersion = 4 From a80259d986facfa66b08ef2264572ecfd0267a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Apr 2021 18:43:06 +0200 Subject: [PATCH 82/97] Actor upgrade checklist --- api/test/ccupgrade.go | 2 +- api/test/deadlines.go | 2 +- api/test/mining.go | 2 +- api/test/test.go | 2 +- api/test/window_post.go | 8 ++++---- chain/actors/agen/main.go | 4 ++-- cmd/lotus-storage-miner/actor_test.go | 2 +- documentation/misc/actors_version_checklist.md | 18 ++++++++++++++++++ 8 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 documentation/misc/actors_version_checklist.md diff --git a/api/test/ccupgrade.go b/api/test/ccupgrade.go index 8c2bdc9b4..283c8f610 100644 --- a/api/test/ccupgrade.go +++ b/api/test/ccupgrade.go @@ -31,7 +31,7 @@ func TestCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) { func testCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, upgradeHeight abi.ChainEpoch) { ctx := context.Background() - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(upgradeHeight)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(upgradeHeight)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] diff --git a/api/test/deadlines.go b/api/test/deadlines.go index 182b6d302..43fa731be 100644 --- a/api/test/deadlines.go +++ b/api/test/deadlines.go @@ -63,7 +63,7 @@ func TestDeadlineToggling(t *testing.T, b APIBuilder, blocktime time.Duration) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(upgradeH)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(upgradeH)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) minerA := sn[0] diff --git a/api/test/mining.go b/api/test/mining.go index 8e300a9c9..4a4f1e1a4 100644 --- a/api/test/mining.go +++ b/api/test/mining.go @@ -206,7 +206,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo func (ts *testSuite) testNonGenesisMiner(t *testing.T) { ctx := context.Background() n, sn := ts.makeNodes(t, []FullNodeOpts{ - FullNodeWithActorsV4At(-1), + FullNodeWithLatestActorsAt(-1), }, []StorageMiner{ {Full: 0, Preseal: PresealGenesis}, }) diff --git a/api/test/test.go b/api/test/test.go index 21ebc94b5..d09827f5e 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -121,7 +121,7 @@ var OneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}} var OneFull = DefaultFullOpts(1) var TwoFull = DefaultFullOpts(2) -var FullNodeWithActorsV4At = func(upgradeHeight abi.ChainEpoch) FullNodeOpts { +var FullNodeWithLatestActorsAt = func(upgradeHeight abi.ChainEpoch) FullNodeOpts { if upgradeHeight == -1 { upgradeHeight = 3 } diff --git a/api/test/window_post.go b/api/test/window_post.go index 1ff9f79b3..bb5010b25 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -223,7 +223,7 @@ func testWindowPostUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(upgradeHeight)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(upgradeHeight)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] @@ -442,7 +442,7 @@ func TestTerminate(t *testing.T, b APIBuilder, blocktime time.Duration) { nSectors := uint64(2) - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(-1)}, []StorageMiner{{Full: 0, Preseal: int(nSectors)}}) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, []StorageMiner{{Full: 0, Preseal: int(nSectors)}}) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] @@ -617,7 +617,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) /// // Then we're going to manually submit bad proofs. n, sn := b(t, []FullNodeOpts{ - FullNodeWithActorsV4At(-1), + FullNodeWithLatestActorsAt(-1), }, []StorageMiner{ {Full: 0, Preseal: PresealGenesis}, {Full: 0}, @@ -900,7 +900,7 @@ func TestWindowPostDisputeFails(t *testing.T, b APIBuilder, blocktime time.Durat ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(-1)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go index 82a92aa00..7554c3938 100644 --- a/chain/actors/agen/main.go +++ b/chain/actors/agen/main.go @@ -36,13 +36,13 @@ var actors = map[string][]int{ } func main() { - if err := run(); err != nil { + if err := generateAdapters(); err != nil { fmt.Println(err) return } } -func run() error { +func generateAdapters() error { for act, versions := range actors { actDir := filepath.Join("chain/actors/builtin", act) diff --git a/cmd/lotus-storage-miner/actor_test.go b/cmd/lotus-storage-miner/actor_test.go index bbe9362d0..5bc82d842 100644 --- a/cmd/lotus-storage-miner/actor_test.go +++ b/cmd/lotus-storage-miner/actor_test.go @@ -51,7 +51,7 @@ func TestWorkerKeyChange(t *testing.T) { blocktime := 1 * time.Millisecond - n, sn := builder.MockSbBuilder(t, []test.FullNodeOpts{test.FullNodeWithActorsV4At(-1), test.FullNodeWithActorsV4At(-1)}, test.OneMiner) + n, sn := builder.MockSbBuilder(t, []test.FullNodeOpts{test.FullNodeWithLatestActorsAt(-1), test.FullNodeWithLatestActorsAt(-1)}, test.OneMiner) client1 := n[0] client2 := n[1] diff --git a/documentation/misc/actors_version_checklist.md b/documentation/misc/actors_version_checklist.md new file mode 100644 index 000000000..764b52e55 --- /dev/null +++ b/documentation/misc/actors_version_checklist.md @@ -0,0 +1,18 @@ +### Actor version integration checklist + +- [ ] Import new actors +- [ ] Generate adapters + - [ ] Add the new version in `chain/actors/agen/main.go` + - [ ] Update adapter code in `chain/actors/builtin` if needed +- [ ] Update `chain/actors/policy/policy.go` +- [ ] Update `chain/actors/version.go` +- [ ] Register in `chain/vm/invoker.go` +- [ ] Register in `chain/vm/mkactor.go` +- [ ] Update `chain/types/state.go` +- [ ] Update `chain/state/statetree.go` +- [ ] Update `chain/stmgr/forks.go` + - [ ] Schedule + - [ ] Migration +- [ ] Define upgrade heights in `build/params_` +- [ ] Update upgrade schedule in `api/test/test.go` +- [ ] Register in init in `chain/stmgr/utils.go` From cd2b959a88fc947b0a0e640949a547f9b546c8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 30 Apr 2021 20:35:01 +0200 Subject: [PATCH 83/97] wip partial codegen --- chain/actors/policy/policy.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 07f489b11..be756ea6d 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -7,6 +7,14 @@ import ( "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/chain/actors" + /* TEMPLATE START + {{range .versions}} + market{{.v}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/market" + miner{{.v}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/miner" + power{{.v}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/power" + verifreg{{.v}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/verifreg" + {{end}} + * GENERATED WITH make gen */ market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" power0 "github.com/filecoin-project/specs-actors/actors/builtin/power" @@ -27,7 +35,7 @@ import ( miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" paych4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/paych" verifreg4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg" -) + /* GENERATED END */) const ( ChainFinality = miner4.ChainFinality From 60446b46c8e0aaabfc09e018e8594ba59ad0cc6a Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 4 May 2021 13:53:08 -0400 Subject: [PATCH 84/97] Generate policy.go --- chain/actors/agen/main.go | 36 ++++ chain/actors/policy/policy.go | 78 +++++++-- chain/actors/policy/policy.go.template | 232 +++++++++++++++++++++++++ 3 files changed, 331 insertions(+), 15 deletions(-) create mode 100644 chain/actors/policy/policy.go.template diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go index 7554c3938..2182b02ac 100644 --- a/chain/actors/agen/main.go +++ b/chain/actors/agen/main.go @@ -40,6 +40,11 @@ func main() { fmt.Println(err) return } + + if err := generatePolicy("chain/actors/policy/policy.go"); err != nil { + fmt.Println(err) + return + } } func generateAdapters() error { @@ -144,3 +149,34 @@ func generateMessages(actDir string) error { return nil } + +func generatePolicy(policyPath string) error { + + pf, err := ioutil.ReadFile(policyPath + ".template") + if err != nil { + if os.IsNotExist(err) { + return nil // skip + } + + return xerrors.Errorf("loading policy file: %w", err) + } + + tpl := template.Must(template.New("").Funcs(template.FuncMap{ + "import": func(v int) string { return versionImports[v] }, + }).Parse(string(pf))) + var b bytes.Buffer + + err = tpl.Execute(&b, map[string]interface{}{ + "versions": versions, + "latestVersion": latestVersion, + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(policyPath, b.Bytes(), 0666); err != nil { + return err + } + + return nil +} diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index be756ea6d..164f19a76 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -7,14 +7,6 @@ import ( "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/chain/actors" - /* TEMPLATE START - {{range .versions}} - market{{.v}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/market" - miner{{.v}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/miner" - power{{.v}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/power" - verifreg{{.v}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/verifreg" - {{end}} - * GENERATED WITH make gen */ market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" power0 "github.com/filecoin-project/specs-actors/actors/builtin/power" @@ -33,9 +25,10 @@ import ( builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" market4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/market" miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" - paych4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/paych" verifreg4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg" - /* GENERATED END */) + + paych4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/paych" +) const ( ChainFinality = miner4.ChainFinality @@ -47,7 +40,9 @@ const ( // SetSupportedProofTypes sets supported proof types, across all actor versions. // This should only be used for testing. func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { + miner0.SupportedProofTypes = make(map[abi.RegisteredSealProof]struct{}, len(types)) + miner2.PreCommitSealProofTypesV0 = make(map[abi.RegisteredSealProof]struct{}, len(types)) miner2.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) miner2.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) @@ -71,6 +66,7 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { panic("must specify v1 proof types only") } // Set for all miner versions. + miner0.SupportedProofTypes[t] = struct{}{} miner2.PreCommitSealProofTypesV0[t] = struct{}{} @@ -87,6 +83,7 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { miner4.PreCommitSealProofTypesV7[t] = struct{}{} miner4.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} miner4.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + } } @@ -94,22 +91,29 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { // actors versions. Use for testing. func SetPreCommitChallengeDelay(delay abi.ChainEpoch) { // Set for all miner versions. + miner0.PreCommitChallengeDelay = delay + miner2.PreCommitChallengeDelay = delay + miner3.PreCommitChallengeDelay = delay + miner4.PreCommitChallengeDelay = delay + } // TODO: this function shouldn't really exist. Instead, the API should expose the precommit delay. func GetPreCommitChallengeDelay() abi.ChainEpoch { - return miner0.PreCommitChallengeDelay + return miner4.PreCommitChallengeDelay } // SetConsensusMinerMinPower sets the minimum power of an individual miner must // meet for leader election, across all actor versions. This should only be used // for testing. func SetConsensusMinerMinPower(p abi.StoragePower) { + power0.ConsensusMinerMinPower = p + for _, policy := range builtin2.SealProofPolicies { policy.ConsensusMinerMinPower = p } @@ -121,27 +125,42 @@ func SetConsensusMinerMinPower(p abi.StoragePower) { for _, policy := range builtin4.PoStProofPolicies { policy.ConsensusMinerMinPower = p } + } // SetMinVerifiedDealSize sets the minimum size of a verified deal. This should // only be used for testing. func SetMinVerifiedDealSize(size abi.StoragePower) { + verifreg0.MinVerifiedDealSize = size + verifreg2.MinVerifiedDealSize = size + verifreg3.MinVerifiedDealSize = size + verifreg4.MinVerifiedDealSize = size + } func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) abi.ChainEpoch { switch ver { + case actors.Version0: + return miner0.MaxSealDuration[t] + case actors.Version2: + return miner2.MaxProveCommitDuration[t] + case actors.Version3: + return miner3.MaxProveCommitDuration[t] + case actors.Version4: + return miner4.MaxProveCommitDuration[t] + default: panic("unsupported actors version") } @@ -153,26 +172,36 @@ func DealProviderCollateralBounds( circulatingFil abi.TokenAmount, nwVer network.Version, ) (min, max abi.TokenAmount) { switch actors.VersionForNetwork(nwVer) { + case actors.Version0: + return market0.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil, nwVer) + case actors.Version2: + return market2.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + case actors.Version3: + return market3.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + case actors.Version4: + return market4.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + default: panic("unsupported actors version") } } func DealDurationBounds(pieceSize abi.PaddedPieceSize) (min, max abi.ChainEpoch) { - return market2.DealDurationBounds(pieceSize) + return market4.DealDurationBounds(pieceSize) } // Sets the challenge window and scales the proving period to match (such that // there are always 48 challenge windows in a proving period). func SetWPoStChallengeWindow(period abi.ChainEpoch) { + miner0.WPoStChallengeWindow = period miner0.WPoStProvingPeriod = period * abi.ChainEpoch(miner0.WPoStPeriodDeadlines) @@ -181,13 +210,18 @@ func SetWPoStChallengeWindow(period abi.ChainEpoch) { miner3.WPoStChallengeWindow = period miner3.WPoStProvingPeriod = period * abi.ChainEpoch(miner3.WPoStPeriodDeadlines) + // by default, this is 2x finality which is 30 periods. // scale it if we're scaling the challenge period. miner3.WPoStDisputeWindow = period * 30 miner4.WPoStChallengeWindow = period miner4.WPoStProvingPeriod = period * abi.ChainEpoch(miner4.WPoStPeriodDeadlines) - miner4.WPoStDisputeWindow = period * 30 // see the miner3 comment + + // by default, this is 2x finality which is 30 periods. + // scale it if we're scaling the challenge period. + miner4.WPoStDisputeWindow = period * 30 + } func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { @@ -200,12 +234,12 @@ func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { } func GetMaxSectorExpirationExtension() abi.ChainEpoch { - return miner0.MaxSectorExpirationExtension + return miner4.MaxSectorExpirationExtension } // TODO: we'll probably need to abstract over this better in the future. func GetMaxPoStPartitions(p abi.RegisteredPoStProof) (int, error) { - sectorsPerPart, err := builtin3.PoStProofWindowPoStPartitionSectors(p) + sectorsPerPart, err := builtin4.PoStProofWindowPoStPartitionSectors(p) if err != nil { return 0, err } @@ -241,14 +275,19 @@ func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) func GetAddressedSectorsMax(nwVer network.Version) int { switch actors.VersionForNetwork(nwVer) { + case actors.Version0: return miner0.AddressedSectorsMax + case actors.Version2: return miner2.AddressedSectorsMax + case actors.Version3: return miner3.AddressedSectorsMax + case actors.Version4: return miner4.AddressedSectorsMax + default: panic("unsupported network version") } @@ -256,15 +295,24 @@ func GetAddressedSectorsMax(nwVer network.Version) int { func GetDeclarationsMax(nwVer network.Version) int { switch actors.VersionForNetwork(nwVer) { + case actors.Version0: + // TODO: Should we instead panic here since the concept doesn't exist yet? return miner0.AddressedPartitionsMax + case actors.Version2: + return miner2.DeclarationsMax + case actors.Version3: + return miner3.DeclarationsMax + case actors.Version4: + return miner4.DeclarationsMax + default: panic("unsupported network version") } diff --git a/chain/actors/policy/policy.go.template b/chain/actors/policy/policy.go.template new file mode 100644 index 000000000..b9e26171e --- /dev/null +++ b/chain/actors/policy/policy.go.template @@ -0,0 +1,232 @@ +package policy + +import ( + "sort" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/network" + "github.com/filecoin-project/lotus/chain/actors" + + {{range .versions}} + {{if (ge . 2)}} builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" {{end}} + market{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/market" + miner{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/miner" + verifreg{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/verifreg" + {{if (eq . 0)}} power{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/power" {{end}} + {{end}} + + paych{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/builtin/paych" +) + +const ( + ChainFinality = miner{{.latestVersion}}.ChainFinality + SealRandomnessLookback = ChainFinality + PaychSettleDelay = paych{{.latestVersion}}.SettleDelay + MaxPreCommitRandomnessLookback = builtin{{.latestVersion}}.EpochsInDay + SealRandomnessLookback +) + +// SetSupportedProofTypes sets supported proof types, across all actor versions. +// This should only be used for testing. +func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { + {{range .versions}} + {{if (eq . 0)}} + miner{{.}}.SupportedProofTypes = make(map[abi.RegisteredSealProof]struct{}, len(types)) + {{else}} + miner{{.}}.PreCommitSealProofTypesV0 = make(map[abi.RegisteredSealProof]struct{}, len(types)) + miner{{.}}.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) + miner{{.}}.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) + {{end}} + {{end}} + + AddSupportedProofTypes(types...) +} + +// AddSupportedProofTypes sets supported proof types, across all actor versions. +// This should only be used for testing. +func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { + for _, t := range types { + if t >= abi.RegisteredSealProof_StackedDrg2KiBV1_1 { + panic("must specify v1 proof types only") + } + // Set for all miner versions. + + {{range .versions}} + {{if (eq . 0)}} + miner{{.}}.SupportedProofTypes[t] = struct{}{} + {{else}} + miner{{.}}.PreCommitSealProofTypesV0[t] = struct{}{} + miner{{.}}.PreCommitSealProofTypesV7[t] = struct{}{} + miner{{.}}.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + miner{{.}}.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + {{end}} + {{end}} + } +} + +// SetPreCommitChallengeDelay sets the pre-commit challenge delay across all +// actors versions. Use for testing. +func SetPreCommitChallengeDelay(delay abi.ChainEpoch) { + // Set for all miner versions. + {{range .versions}} + miner{{.}}.PreCommitChallengeDelay = delay + {{end}} +} + +// TODO: this function shouldn't really exist. Instead, the API should expose the precommit delay. +func GetPreCommitChallengeDelay() abi.ChainEpoch { + return miner{{.latestVersion}}.PreCommitChallengeDelay +} + +// SetConsensusMinerMinPower sets the minimum power of an individual miner must +// meet for leader election, across all actor versions. This should only be used +// for testing. +func SetConsensusMinerMinPower(p abi.StoragePower) { + {{range .versions}} + {{if (eq . 0)}} + power{{.}}.ConsensusMinerMinPower = p + {{else if (eq . 2)}} + for _, policy := range builtin{{.}}.SealProofPolicies { + policy.ConsensusMinerMinPower = p + } + {{else}} + for _, policy := range builtin{{.}}.PoStProofPolicies { + policy.ConsensusMinerMinPower = p + } + {{end}} + {{end}} +} + +// SetMinVerifiedDealSize sets the minimum size of a verified deal. This should +// only be used for testing. +func SetMinVerifiedDealSize(size abi.StoragePower) { + {{range .versions}} + verifreg{{.}}.MinVerifiedDealSize = size + {{end}} +} + +func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) abi.ChainEpoch { + switch ver { + {{range .versions}} + case actors.Version{{.}}: + {{if (eq . 0)}} + return miner{{.}}.MaxSealDuration[t] + {{else}} + return miner{{.}}.MaxProveCommitDuration[t] + {{end}} + {{end}} + default: + panic("unsupported actors version") + } +} + +func DealProviderCollateralBounds( + size abi.PaddedPieceSize, verified bool, + rawBytePower, qaPower, baselinePower abi.StoragePower, + circulatingFil abi.TokenAmount, nwVer network.Version, +) (min, max abi.TokenAmount) { + switch actors.VersionForNetwork(nwVer) { + {{range .versions}} + case actors.Version{{.}}: + {{if (eq . 0)}} + return market{{.}}.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil, nwVer) + {{else}} + return market{{.}}.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + {{end}} + {{end}} + default: + panic("unsupported actors version") + } +} + +func DealDurationBounds(pieceSize abi.PaddedPieceSize) (min, max abi.ChainEpoch) { + return market{{.latestVersion}}.DealDurationBounds(pieceSize) +} + +// Sets the challenge window and scales the proving period to match (such that +// there are always 48 challenge windows in a proving period). +func SetWPoStChallengeWindow(period abi.ChainEpoch) { + {{range .versions}} + miner{{.}}.WPoStChallengeWindow = period + miner{{.}}.WPoStProvingPeriod = period * abi.ChainEpoch(miner{{.}}.WPoStPeriodDeadlines) + {{if (ge . 3)}} + // by default, this is 2x finality which is 30 periods. + // scale it if we're scaling the challenge period. + miner{{.}}.WPoStDisputeWindow = period * 30 + {{end}} + {{end}} +} + +func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { + if nwVer <= network.Version3 { + return 10 + } + + return ChainFinality +} + +func GetMaxSectorExpirationExtension() abi.ChainEpoch { + return miner{{.latestVersion}}.MaxSectorExpirationExtension +} + +// TODO: we'll probably need to abstract over this better in the future. +func GetMaxPoStPartitions(p abi.RegisteredPoStProof) (int, error) { + sectorsPerPart, err := builtin{{.latestVersion}}.PoStProofWindowPoStPartitionSectors(p) + if err != nil { + return 0, err + } + return int(miner{{.latestVersion}}.AddressedSectorsMax / sectorsPerPart), nil +} + +func GetDefaultSectorSize() abi.SectorSize { + // supported sector sizes are the same across versions. + szs := make([]abi.SectorSize, 0, len(miner{{.latestVersion}}.PreCommitSealProofTypesV8)) + for spt := range miner{{.latestVersion}}.PreCommitSealProofTypesV8 { + ss, err := spt.SectorSize() + if err != nil { + panic(err) + } + + szs = append(szs, ss) + } + + sort.Slice(szs, func(i, j int) bool { + return szs[i] < szs[j] + }) + + return szs[0] +} + +func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) abi.ChainEpoch { + if nwVer <= network.Version10 { + return builtin{{.latestVersion}}.SealProofPoliciesV0[proof].SectorMaxLifetime + } + + return builtin{{.latestVersion}}.SealProofPoliciesV11[proof].SectorMaxLifetime +} + +func GetAddressedSectorsMax(nwVer network.Version) int { + switch actors.VersionForNetwork(nwVer) { + {{range .versions}} + case actors.Version{{.}}: + return miner{{.}}.AddressedSectorsMax + {{end}} + default: + panic("unsupported network version") + } +} + +func GetDeclarationsMax(nwVer network.Version) int { + switch actors.VersionForNetwork(nwVer) { + {{range .versions}} + case actors.Version{{.}}: + {{if (eq . 0)}} + // TODO: Should we instead panic here since the concept doesn't exist yet? + return miner{{.}}.AddressedPartitionsMax + {{else}} + return miner{{.}}.DeclarationsMax + {{end}} + {{end}} + default: + panic("unsupported network version") + } +} From c95206cde1068f9638f0e532686d6a135fbaec1b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 5 May 2021 22:38:24 -0400 Subject: [PATCH 85/97] Add reminder for NewestNetworkVersion to actors version checklist --- documentation/misc/actors_version_checklist.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/misc/actors_version_checklist.md b/documentation/misc/actors_version_checklist.md index 764b52e55..308358948 100644 --- a/documentation/misc/actors_version_checklist.md +++ b/documentation/misc/actors_version_checklist.md @@ -1,6 +1,7 @@ ### Actor version integration checklist - [ ] Import new actors +- [ ] Define upgrade heights in `build/params_` - [ ] Generate adapters - [ ] Add the new version in `chain/actors/agen/main.go` - [ ] Update adapter code in `chain/actors/builtin` if needed @@ -13,6 +14,6 @@ - [ ] Update `chain/stmgr/forks.go` - [ ] Schedule - [ ] Migration -- [ ] Define upgrade heights in `build/params_` - [ ] Update upgrade schedule in `api/test/test.go` +- [ ] Update `NewestNetworkVersion` in `build/params_shared_vals.go` - [ ] Register in init in `chain/stmgr/utils.go` From 5f821cc733f32bd9f52872113a0ac3e412732667 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 6 May 2021 00:17:35 -0400 Subject: [PATCH 86/97] Generate builtin.go --- chain/actors/agen/main.go | 38 +++- chain/actors/builtin/builtin.go | 168 +++++++++++++----- chain/actors/builtin/builtin.go.template | 144 +++++++++++++++ .../actors/builtin/multisig/actor.go.template | 7 +- chain/actors/builtin/multisig/multisig.go | 3 +- chain/actors/policy/policy.go.template | 1 + 6 files changed, 312 insertions(+), 49 deletions(-) create mode 100644 chain/actors/builtin/builtin.go.template diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go index 2182b02ac..7269d9ae5 100644 --- a/chain/actors/agen/main.go +++ b/chain/actors/agen/main.go @@ -45,6 +45,11 @@ func main() { fmt.Println(err) return } + + if err := generateBuiltin("chain/actors/builtin/builtin.go"); err != nil { + fmt.Println(err) + return + } } func generateAdapters() error { @@ -158,7 +163,7 @@ func generatePolicy(policyPath string) error { return nil // skip } - return xerrors.Errorf("loading policy file: %w", err) + return xerrors.Errorf("loading policy template file: %w", err) } tpl := template.Must(template.New("").Funcs(template.FuncMap{ @@ -180,3 +185,34 @@ func generatePolicy(policyPath string) error { return nil } + +func generateBuiltin(builtinPath string) error { + + bf, err := ioutil.ReadFile(builtinPath + ".template") + if err != nil { + if os.IsNotExist(err) { + return nil // skip + } + + return xerrors.Errorf("loading builtin template file: %w", err) + } + + tpl := template.Must(template.New("").Funcs(template.FuncMap{ + "import": func(v int) string { return versionImports[v] }, + }).Parse(string(bf))) + var b bytes.Buffer + + err = tpl.Execute(&b, map[string]interface{}{ + "versions": versions, + "latestVersion": latestVersion, + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(builtinPath, b.Bytes(), 0666); err != nil { + return err + } + + return nil +} diff --git a/chain/actors/builtin/builtin.go b/chain/actors/builtin/builtin.go index 4ff524797..5e34c015a 100644 --- a/chain/actors/builtin/builtin.go +++ b/chain/actors/builtin/builtin.go @@ -6,9 +6,16 @@ import ( "golang.org/x/xerrors" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + smoothing0 "github.com/filecoin-project/specs-actors/actors/util/smoothing" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + smoothing2 "github.com/filecoin-project/specs-actors/v2/actors/util/smoothing" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + smoothing3 "github.com/filecoin-project/specs-actors/v3/actors/util/smoothing" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + smoothing4 "github.com/filecoin-project/specs-actors/v4/actors/util/smoothing" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/cbor" @@ -16,30 +23,25 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/types" - smoothing0 "github.com/filecoin-project/specs-actors/actors/util/smoothing" - smoothing2 "github.com/filecoin-project/specs-actors/v2/actors/util/smoothing" - smoothing3 "github.com/filecoin-project/specs-actors/v3/actors/util/smoothing" - smoothing4 "github.com/filecoin-project/specs-actors/v4/actors/util/smoothing" - - miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" - proof0 "github.com/filecoin-project/specs-actors/actors/runtime/proof" + miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" + proof4 "github.com/filecoin-project/specs-actors/v4/actors/runtime/proof" ) -var SystemActorAddr = builtin0.SystemActorAddr -var BurntFundsActorAddr = builtin0.BurntFundsActorAddr -var CronActorAddr = builtin0.CronActorAddr +var SystemActorAddr = builtin4.SystemActorAddr +var BurntFundsActorAddr = builtin4.BurntFundsActorAddr +var CronActorAddr = builtin4.CronActorAddr var SaftAddress = makeAddress("t0122") var ReserveAddress = makeAddress("t090") var RootVerifierAddress = makeAddress("t080") var ( - ExpectedLeadersPerEpoch = builtin0.ExpectedLeadersPerEpoch + ExpectedLeadersPerEpoch = builtin4.ExpectedLeadersPerEpoch ) const ( - EpochDurationSeconds = builtin0.EpochDurationSeconds - EpochsInDay = builtin0.EpochsInDay - SecondsInDay = builtin0.SecondsInDay + EpochDurationSeconds = builtin4.EpochDurationSeconds + EpochsInDay = builtin4.EpochsInDay + SecondsInDay = builtin4.SecondsInDay ) const ( @@ -47,31 +49,38 @@ const ( MethodConstructor = builtin4.MethodConstructor ) -// These are all just type aliases across actor versions 0, 2, & 3. In the future, that might change +// These are all just type aliases across actor versions. In the future, that might change // and we might need to do something fancier. -type SectorInfo = proof0.SectorInfo -type PoStProof = proof0.PoStProof +type SectorInfo = proof4.SectorInfo +type PoStProof = proof4.PoStProof type FilterEstimate = smoothing0.FilterEstimate -func FromV0FilterEstimate(v0 smoothing0.FilterEstimate) FilterEstimate { - return (FilterEstimate)(v0) //nolint:unconvert +func QAPowerForWeight(size abi.SectorSize, duration abi.ChainEpoch, dealWeight, verifiedWeight abi.DealWeight) abi.StoragePower { + return miner4.QAPowerForWeight(size, duration, dealWeight, verifiedWeight) } -// Doesn't change between actors v0, v2, and v3. -func QAPowerForWeight(size abi.SectorSize, duration abi.ChainEpoch, dealWeight, verifiedWeight abi.DealWeight) abi.StoragePower { - return miner0.QAPowerForWeight(size, duration, dealWeight, verifiedWeight) +func FromV0FilterEstimate(v0 smoothing0.FilterEstimate) FilterEstimate { + + return (FilterEstimate)(v0) //nolint:unconvert + } func FromV2FilterEstimate(v2 smoothing2.FilterEstimate) FilterEstimate { + return (FilterEstimate)(v2) + } func FromV3FilterEstimate(v3 smoothing3.FilterEstimate) FilterEstimate { + return (FilterEstimate)(v3) + } func FromV4FilterEstimate(v4 smoothing4.FilterEstimate) FilterEstimate { + return (FilterEstimate)(v4) + } type ActorStateLoader func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) @@ -92,52 +101,127 @@ func Load(store adt.Store, act *types.Actor) (cbor.Marshaler, error) { func ActorNameByCode(c cid.Cid) string { switch { + case builtin0.IsBuiltinActor(c): return builtin0.ActorNameByCode(c) + case builtin2.IsBuiltinActor(c): return builtin2.ActorNameByCode(c) + case builtin3.IsBuiltinActor(c): return builtin3.ActorNameByCode(c) + case builtin4.IsBuiltinActor(c): return builtin4.ActorNameByCode(c) + default: return "" } } func IsBuiltinActor(c cid.Cid) bool { - return builtin0.IsBuiltinActor(c) || - builtin2.IsBuiltinActor(c) || - builtin3.IsBuiltinActor(c) || - builtin4.IsBuiltinActor(c) + + if builtin0.IsBuiltinActor(c) { + return true + } + + if builtin2.IsBuiltinActor(c) { + return true + } + + if builtin3.IsBuiltinActor(c) { + return true + } + + if builtin4.IsBuiltinActor(c) { + return true + } + + return false } func IsAccountActor(c cid.Cid) bool { - return c == builtin0.AccountActorCodeID || - c == builtin2.AccountActorCodeID || - c == builtin3.AccountActorCodeID || - c == builtin4.AccountActorCodeID + + if c == builtin0.AccountActorCodeID { + return true + } + + if c == builtin2.AccountActorCodeID { + return true + } + + if c == builtin3.AccountActorCodeID { + return true + } + + if c == builtin4.AccountActorCodeID { + return true + } + + return false } func IsStorageMinerActor(c cid.Cid) bool { - return c == builtin0.StorageMinerActorCodeID || - c == builtin2.StorageMinerActorCodeID || - c == builtin3.StorageMinerActorCodeID || - c == builtin4.StorageMinerActorCodeID + + if c == builtin0.StorageMinerActorCodeID { + return true + } + + if c == builtin2.StorageMinerActorCodeID { + return true + } + + if c == builtin3.StorageMinerActorCodeID { + return true + } + + if c == builtin4.StorageMinerActorCodeID { + return true + } + + return false } func IsMultisigActor(c cid.Cid) bool { - return c == builtin0.MultisigActorCodeID || - c == builtin2.MultisigActorCodeID || - c == builtin3.MultisigActorCodeID || - c == builtin4.MultisigActorCodeID + + if c == builtin0.MultisigActorCodeID { + return true + } + + if c == builtin2.MultisigActorCodeID { + return true + } + + if c == builtin3.MultisigActorCodeID { + return true + } + + if c == builtin4.MultisigActorCodeID { + return true + } + + return false } func IsPaymentChannelActor(c cid.Cid) bool { - return c == builtin0.PaymentChannelActorCodeID || - c == builtin2.PaymentChannelActorCodeID || - c == builtin3.PaymentChannelActorCodeID || - c == builtin4.PaymentChannelActorCodeID + + if c == builtin0.PaymentChannelActorCodeID { + return true + } + + if c == builtin2.PaymentChannelActorCodeID { + return true + } + + if c == builtin3.PaymentChannelActorCodeID { + return true + } + + if c == builtin4.PaymentChannelActorCodeID { + return true + } + + return false } func makeAddress(addr string) address.Address { diff --git a/chain/actors/builtin/builtin.go.template b/chain/actors/builtin/builtin.go.template new file mode 100644 index 000000000..9b89b13f5 --- /dev/null +++ b/chain/actors/builtin/builtin.go.template @@ -0,0 +1,144 @@ +package builtin + +import ( + "github.com/filecoin-project/go-address" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + {{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" + smoothing{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/util/smoothing" + {{end}} + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/types" + + miner{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/builtin/miner" + proof{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/runtime/proof" +) + +var SystemActorAddr = builtin{{.latestVersion}}.SystemActorAddr +var BurntFundsActorAddr = builtin{{.latestVersion}}.BurntFundsActorAddr +var CronActorAddr = builtin{{.latestVersion}}.CronActorAddr +var SaftAddress = makeAddress("t0122") +var ReserveAddress = makeAddress("t090") +var RootVerifierAddress = makeAddress("t080") + +var ( + ExpectedLeadersPerEpoch = builtin{{.latestVersion}}.ExpectedLeadersPerEpoch +) + +const ( + EpochDurationSeconds = builtin{{.latestVersion}}.EpochDurationSeconds + EpochsInDay = builtin{{.latestVersion}}.EpochsInDay + SecondsInDay = builtin{{.latestVersion}}.SecondsInDay +) + +const ( + MethodSend = builtin{{.latestVersion}}.MethodSend + MethodConstructor = builtin{{.latestVersion}}.MethodConstructor +) + +// These are all just type aliases across actor versions. In the future, that might change +// and we might need to do something fancier. +type SectorInfo = proof{{.latestVersion}}.SectorInfo +type PoStProof = proof{{.latestVersion}}.PoStProof +type FilterEstimate = smoothing0.FilterEstimate + +func QAPowerForWeight(size abi.SectorSize, duration abi.ChainEpoch, dealWeight, verifiedWeight abi.DealWeight) abi.StoragePower { + return miner{{.latestVersion}}.QAPowerForWeight(size, duration, dealWeight, verifiedWeight) +} + +{{range .versions}} + func FromV{{.}}FilterEstimate(v{{.}} smoothing{{.}}.FilterEstimate) FilterEstimate { + {{if (eq . 0)}} + return (FilterEstimate)(v{{.}}) //nolint:unconvert + {{else}} + return (FilterEstimate)(v{{.}}) + {{end}} + } +{{end}} + +type ActorStateLoader func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) + +var ActorStateLoaders = make(map[cid.Cid]ActorStateLoader) + +func RegisterActorState(code cid.Cid, loader ActorStateLoader) { + ActorStateLoaders[code] = loader +} + +func Load(store adt.Store, act *types.Actor) (cbor.Marshaler, error) { + loader, found := ActorStateLoaders[act.Code] + if !found { + return nil, xerrors.Errorf("unknown actor code %s", act.Code) + } + return loader(store, act.Head) +} + +func ActorNameByCode(c cid.Cid) string { + switch { + {{range .versions}} + case builtin{{.}}.IsBuiltinActor(c): + return builtin{{.}}.ActorNameByCode(c) + {{end}} + default: + return "" + } +} + +func IsBuiltinActor(c cid.Cid) bool { + {{range .versions}} + if builtin{{.}}.IsBuiltinActor(c) { + return true + } + {{end}} + return false +} + +func IsAccountActor(c cid.Cid) bool { + {{range .versions}} + if c == builtin{{.}}.AccountActorCodeID { + return true + } + {{end}} + return false +} + +func IsStorageMinerActor(c cid.Cid) bool { + {{range .versions}} + if c == builtin{{.}}.StorageMinerActorCodeID { + return true + } + {{end}} + return false +} + +func IsMultisigActor(c cid.Cid) bool { + {{range .versions}} + if c == builtin{{.}}.MultisigActorCodeID { + return true + } + {{end}} + return false +} + +func IsPaymentChannelActor(c cid.Cid) bool { + {{range .versions}} + if c == builtin{{.}}.PaymentChannelActorCodeID { + return true + } + {{end}} + return false +} + +func makeAddress(addr string) address.Address { + ret, err := address.NewFromString(addr) + if err != nil { + panic(err) + } + + return ret +} diff --git a/chain/actors/builtin/multisig/actor.go.template b/chain/actors/builtin/multisig/actor.go.template index 304c0610c..19d99dcb7 100644 --- a/chain/actors/builtin/multisig/actor.go.template +++ b/chain/actors/builtin/multisig/actor.go.template @@ -12,8 +12,7 @@ import ( "github.com/filecoin-project/go-state-types/cbor" "github.com/ipfs/go-cid" - msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" + msig{{.latestVersion}} "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" {{range .versions}} builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" {{end}} @@ -58,7 +57,7 @@ type State interface { decodeTransaction(val *cbg.Deferred) (Transaction, error) } -type Transaction = msig0.Transaction +type Transaction = msig{{.latestVersion}}.Transaction var Methods = builtin{{.latestVersion}}.MethodsMultisig @@ -95,7 +94,7 @@ type ProposeReturn = msig{{.latestVersion}}.ProposeReturn type ProposeParams = msig{{.latestVersion}}.ProposeParams func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { - params := msig{{.latestVersion}}.TxnIDParams{ID: msig4.TxnID(id)} + params := msig{{.latestVersion}}.TxnIDParams{ID: msig{{.latestVersion}}.TxnID(id)} if data != nil { if data.Requester.Protocol() != address.ID { return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) diff --git a/chain/actors/builtin/multisig/multisig.go b/chain/actors/builtin/multisig/multisig.go index 79b1a57d7..d8f6fabae 100644 --- a/chain/actors/builtin/multisig/multisig.go +++ b/chain/actors/builtin/multisig/multisig.go @@ -12,7 +12,6 @@ import ( "github.com/filecoin-project/go-state-types/cbor" "github.com/ipfs/go-cid" - msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -84,7 +83,7 @@ type State interface { decodeTransaction(val *cbg.Deferred) (Transaction, error) } -type Transaction = msig0.Transaction +type Transaction = msig4.Transaction var Methods = builtin4.MethodsMultisig diff --git a/chain/actors/policy/policy.go.template b/chain/actors/policy/policy.go.template index b9e26171e..d395d7132 100644 --- a/chain/actors/policy/policy.go.template +++ b/chain/actors/policy/policy.go.template @@ -161,6 +161,7 @@ func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { return 10 } + // NOTE: if this ever changes, adjust it in a (*Miner).mineOne() logline as well return ChainFinality } From 6f71eab8cee0b32832a3643e7918b6c7d66573fd Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Tue, 11 May 2021 04:26:04 +0200 Subject: [PATCH 87/97] Adjust lp2p retry params --- node/impl/client/client.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/node/impl/client/client.go b/node/impl/client/client.go index 5423f7f79..fa87446c9 100644 --- a/node/impl/client/client.go +++ b/node/impl/client/client.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "time" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" @@ -254,7 +255,9 @@ func (a *API) dealStarter(ctx context.Context, params *api.StartDealParams, isSt ClientSignature: *dealProposalSig, } dStream, err := network.NewFromLibp2pHost(a.Host, - network.RetryParameters(0, 0, 0, 0), + // params duplicated from .../node/modules/client.go + // https://github.com/filecoin-project/lotus/pull/5961#discussion_r629768011 + network.RetryParameters(time.Second, 5*time.Minute, 15, 5), ).NewDealStream(ctx, *mi.PeerId) if err != nil { return nil, xerrors.Errorf("opening dealstream to %s/%s failed: %w", params.Miner, *mi.PeerId, err) From f7fbaef361bf59aa3baaa70d3e84bd04cfa47000 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Tue, 4 May 2021 21:16:18 -0700 Subject: [PATCH 88/97] chore: update go-libp2p From 0.12.0 to 0.14.0. Headline for lotus: faster yamux. --- documentation/en/api-v0-methods-miner.md | 12 +- documentation/en/api-v0-methods.md | 12 +- documentation/en/api-v1-unstable-methods.md | 12 +- go.mod | 32 ++-- go.sum | 153 +++++++++++++------- node/impl/common/common.go | 2 +- 6 files changed, 138 insertions(+), 85 deletions(-) diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index ea5ca75f8..1f6cc98e9 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -885,8 +885,8 @@ Inputs: `null` Response: ```json { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ``` @@ -1035,8 +1035,8 @@ Inputs: ```json [ { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ] ``` @@ -1086,8 +1086,8 @@ Inputs: Response: ```json { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ``` diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 3c5356a56..2ce222878 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -2747,8 +2747,8 @@ Inputs: `null` Response: ```json { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ``` @@ -2897,8 +2897,8 @@ Inputs: ```json [ { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ] ``` @@ -2948,8 +2948,8 @@ Inputs: Response: ```json { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ``` diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 83c2d6d41..60c369948 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2974,8 +2974,8 @@ Inputs: `null` Response: ```json { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ``` @@ -3124,8 +3124,8 @@ Inputs: ```json [ { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ] ``` @@ -3175,8 +3175,8 @@ Inputs: Response: ```json { - "Addrs": null, - "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf" + "ID": "12D3KooWGzxzKZYveHXtpG6AsrUJBcWxHBFS2HsEoGTxrMLvKXtf", + "Addrs": [] } ``` diff --git a/go.mod b/go.mod index 384cac442..52d5cfbb4 100644 --- a/go.mod +++ b/go.mod @@ -88,7 +88,7 @@ require ( github.com/ipfs/go-ipfs-util v0.0.2 github.com/ipfs/go-ipld-cbor v0.0.5 github.com/ipfs/go-ipld-format v0.2.0 - github.com/ipfs/go-log/v2 v2.1.2 + github.com/ipfs/go-log/v2 v2.1.3 github.com/ipfs/go-merkledag v0.3.2 github.com/ipfs/go-metrics-interface v0.0.1 github.com/ipfs/go-metrics-prometheus v0.0.2 @@ -101,21 +101,21 @@ require ( github.com/lib/pq v1.7.0 github.com/libp2p/go-buffer-pool v0.0.2 github.com/libp2p/go-eventbus v0.2.1 - github.com/libp2p/go-libp2p v0.12.0 + github.com/libp2p/go-libp2p v0.14.0 github.com/libp2p/go-libp2p-connmgr v0.2.4 - github.com/libp2p/go-libp2p-core v0.7.0 + github.com/libp2p/go-libp2p-core v0.8.5 github.com/libp2p/go-libp2p-discovery v0.5.0 github.com/libp2p/go-libp2p-kad-dht v0.11.0 - github.com/libp2p/go-libp2p-mplex v0.3.0 - github.com/libp2p/go-libp2p-noise v0.1.2 - github.com/libp2p/go-libp2p-peerstore v0.2.6 + github.com/libp2p/go-libp2p-mplex v0.4.1 + github.com/libp2p/go-libp2p-noise v0.2.0 + github.com/libp2p/go-libp2p-peerstore v0.2.7 github.com/libp2p/go-libp2p-pubsub v0.4.2-0.20210212194758-6c1addf493eb - github.com/libp2p/go-libp2p-quic-transport v0.9.0 + github.com/libp2p/go-libp2p-quic-transport v0.10.0 github.com/libp2p/go-libp2p-record v0.1.3 github.com/libp2p/go-libp2p-routing-helpers v0.2.3 - github.com/libp2p/go-libp2p-swarm v0.3.1 + github.com/libp2p/go-libp2p-swarm v0.5.0 github.com/libp2p/go-libp2p-tls v0.1.3 - github.com/libp2p/go-libp2p-yamux v0.4.1 + github.com/libp2p/go-libp2p-yamux v0.5.3 github.com/libp2p/go-maddr-filter v0.1.0 github.com/mattn/go-colorable v0.1.6 // indirect github.com/mattn/go-isatty v0.0.12 @@ -123,10 +123,9 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-base32 v0.0.3 github.com/multiformats/go-multiaddr v0.3.1 - github.com/multiformats/go-multiaddr-dns v0.2.0 + github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.0.3 github.com/multiformats/go-multihash v0.0.14 - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 github.com/opentracing/opentracing-go v1.2.0 github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a @@ -144,18 +143,17 @@ require ( github.com/whyrusleeping/pubsub v0.0.0-20190708150250-92bcb0691325 github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 go.etcd.io/bbolt v1.3.4 - go.opencensus.io v0.22.5 + go.opencensus.io v0.23.0 go.uber.org/dig v1.10.0 // indirect go.uber.org/fx v1.9.0 go.uber.org/multierr v1.6.0 go.uber.org/zap v1.16.0 - golang.org/x/net v0.0.0-20201022231255-08b38378de70 - golang.org/x/sync v0.0.0-20201207232520-09787c993a3a - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 + golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20210426080607-c94f62235c83 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 - golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696 + golang.org/x/tools v0.0.0-20210106214847-113979e3529a golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 gotest.tools v2.2.0+incompatible honnef.co/go/tools v0.0.1-2020.1.3 // indirect diff --git a/go.sum b/go.sum index ef3ac9678..29e62646e 100644 --- a/go.sum +++ b/go.sum @@ -105,14 +105,18 @@ github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dm github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= +github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/goterm v0.0.0-20200322175922-2f3e71b85129 h1:gfAMKE626QEuKG3si0pdTRcr/YEbBoxY+3GOH3gWvl4= @@ -185,8 +189,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= -github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f h1:BOaYiTvg8p9vBUXpklC22XSK/mifLF7lG9jtmYYi3Tc= github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e h1:lj77EKYUpYXTd8CD/+QMIf8b6OIOTsfEBSXiAzuEHTU= github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e/go.mod h1:3ZQK6DMPSz/QZ73jlWxBtUhNA8xZx7LzUFSq/OfP8vk= github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= @@ -321,8 +327,9 @@ github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/g github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= +github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= +github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -387,8 +394,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRsugc= github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= @@ -418,8 +426,9 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf h1:gFVkHXmVAhEbxZVDln5V9GKrLaluNoFHDbrZwAWZgws= @@ -431,14 +440,16 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= -github.com/google/gopacket v1.1.18 h1:lum7VRA9kdlvBi7/v2p7/zcbkduHaCH/SVVyurs7OpY= github.com/google/gopacket v1.1.18/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -679,8 +690,9 @@ github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscw github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= -github.com/ipfs/go-log/v2 v2.1.2 h1:a0dRiL098zY23vay1h3dimx6y94XchEUyt5h0l4VvQU= github.com/ipfs/go-log/v2 v2.1.2/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= +github.com/ipfs/go-log/v2 v2.1.3 h1:1iS3IU7aXRlbgUpN8yTTpJ53NXYjAe37vcI5+5nYrzk= +github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA= github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= @@ -782,6 +794,7 @@ github.com/kilic/bls12-381 v0.0.0-20200820230200-6b2c19996391 h1:51kHw7l/dUDdOdW github.com/kilic/bls12-381 v0.0.0-20200820230200-6b2c19996391/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -812,8 +825,9 @@ github.com/libp2p/go-conn-security v0.0.1/go.mod h1:bGmu51N0KU9IEjX7kl2PQjgZa40J github.com/libp2p/go-conn-security-multistream v0.0.1/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE= github.com/libp2p/go-conn-security-multistream v0.0.2/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE= github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= -github.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M= github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= +github.com/libp2p/go-conn-security-multistream v0.2.1 h1:ft6/POSK7F+vl/2qzegnHDaXFU0iWB4yVTYrioC6Zy0= +github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= github.com/libp2p/go-eventbus v0.0.2/go.mod h1:Hr/yGlwxA/stuLnpMiu82lpNKpvRy3EaJxPu40XYOwk= github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= github.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc= @@ -836,8 +850,9 @@ github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qD github.com/libp2p/go-libp2p v0.8.3/go.mod h1:EsH1A+8yoWK+L4iKcbPYu6MPluZ+CHWI9El8cTaefiM= github.com/libp2p/go-libp2p v0.9.2/go.mod h1:cunHNLDVus66Ct9iXXcjKRLdmHdFdHVe1TAnbubJQqQ= github.com/libp2p/go-libp2p v0.10.0/go.mod h1:yBJNpb+mGJdgrwbKAKrhPU0u3ogyNFTfjJ6bdM+Q/G8= -github.com/libp2p/go-libp2p v0.12.0 h1:+xai9RQnQ9l5elFOKvp5wRyjyWisSwEx+6nU2+onpUA= github.com/libp2p/go-libp2p v0.12.0/go.mod h1:FpHZrfC1q7nA8jitvdjKBDF31hguaC676g/nT9PgQM0= +github.com/libp2p/go-libp2p v0.14.0 h1:mYab0qShfAojYN/QTOtxPyQoK9knUHbUncwst4+wBcA= +github.com/libp2p/go-libp2p v0.14.0/go.mod h1:dsQrWLAoIn+GkHPN/U+yypizkHiB9tnv79Os+kSgQ4Q= github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052 h1:BM7aaOF7RpmNn9+9g6uTjGJ0cTzWr5j9i9IKeun2M8U= github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo= github.com/libp2p/go-libp2p-autonat v0.0.2/go.mod h1:fs71q5Xk+pdnKU014o2iq1RhMs9/PMaG5zXRFNnIIT4= @@ -848,8 +863,9 @@ github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQ github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= github.com/libp2p/go-libp2p-autonat v0.2.3/go.mod h1:2U6bNWCNsAG9LEbwccBDQbjzQ8Krdjge1jLTE9rdoMM= -github.com/libp2p/go-libp2p-autonat v0.4.0 h1:3y8XQbpr+ssX8QfZUHekjHCYK64sj6/4hnf/awD4+Ug= github.com/libp2p/go-libp2p-autonat v0.4.0/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= +github.com/libp2p/go-libp2p-autonat v0.4.2 h1:YMp7StMi2dof+baaxkbxaizXjY1RPvU71CXfxExzcUU= +github.com/libp2p/go-libp2p-autonat v0.4.2/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= github.com/libp2p/go-libp2p-autonat-svc v0.1.0/go.mod h1:fqi8Obl/z3R4PFVLm8xFtZ6PBL9MlV/xumymRFkKq5A= github.com/libp2p/go-libp2p-blankhost v0.0.1/go.mod h1:Ibpbw/7cPPYwFb7PACIWdvxxv0t0XCCI10t7czjAjTc= github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= @@ -896,8 +912,12 @@ github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.7.0 h1:4a0TMjrWNTZlNvcqxZmrMRDi/NQWrhwO2pkTuLSQ/IQ= github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= +github.com/libp2p/go-libp2p-core v0.8.5 h1:aEgbIcPGsKy6zYcC+5AJivYFedhYa4sW7mIpWpUaLKw= +github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= github.com/libp2p/go-libp2p-crypto v0.1.0 h1:k9MFy+o2zGDNGsaoZl0MA3iZ75qXxr9OOoAZF+sD5OQ= @@ -932,8 +952,10 @@ github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3 github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek= -github.com/libp2p/go-libp2p-mplex v0.3.0 h1:CZyqqKP0BSGQyPLvpRQougbfXaaaJZdGgzhCpJNuNSk= github.com/libp2p/go-libp2p-mplex v0.3.0/go.mod h1:l9QWxRbbb5/hQMECEb908GbS9Sm2UAR2KFZKUJEynEs= +github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= +github.com/libp2p/go-libp2p-mplex v0.4.1 h1:/pyhkP1nLwjG3OM+VuaNJkQT/Pqq73WzB3aDN3Fx1sc= +github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= github.com/libp2p/go-libp2p-nat v0.0.2/go.mod h1:QrjXQSD5Dj4IJOdEcjHRkWTSomyxRo6HnUkf/TfQpLQ= github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= @@ -945,8 +967,8 @@ github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFx github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ= github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= github.com/libp2p/go-libp2p-noise v0.1.1/go.mod h1:QDFLdKX7nluB7DEnlVPbz7xlLHdwHFA9HiohJRr3vwM= -github.com/libp2p/go-libp2p-noise v0.1.2 h1:IH9GRihQJTx56obm+GnpdPX4KeVIlvpXrP6xnJ0wxWk= -github.com/libp2p/go-libp2p-noise v0.1.2/go.mod h1:9B10b7ueo7TIxZHHcjcDCo5Hd6kfKT2m77by82SFRfE= +github.com/libp2p/go-libp2p-noise v0.2.0 h1:wmk5nhB9a2w2RxMOyvsoKjizgJOEaJdfAakr0jN8gds= +github.com/libp2p/go-libp2p-noise v0.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo= github.com/libp2p/go-libp2p-peer v0.1.1/go.mod h1:jkF12jGB4Gk/IOo+yomm+7oLWxF278F7UnrYUQ1Q8es= github.com/libp2p/go-libp2p-peer v0.2.0 h1:EQ8kMjaCUwt/Y5uLgjT8iY2qg0mGUT0N1zUjer50DsY= @@ -961,8 +983,9 @@ github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRj github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= github.com/libp2p/go-libp2p-peerstore v0.2.3/go.mod h1:K8ljLdFn590GMttg/luh4caB/3g0vKuY01psze0upRw= github.com/libp2p/go-libp2p-peerstore v0.2.4/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.6 h1:2ACefBX23iMdJU9Ke+dcXt3w86MIryes9v7In4+Qq3U= github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= +github.com/libp2p/go-libp2p-peerstore v0.2.7 h1:83JoLxyR9OYTnNfB5vvFqvMUv/xDNa6NoPHnENhBsGw= +github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k= github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s= @@ -973,8 +996,8 @@ github.com/libp2p/go-libp2p-pubsub v0.4.2-0.20210212194758-6c1addf493eb h1:HExLc github.com/libp2p/go-libp2p-pubsub v0.4.2-0.20210212194758-6c1addf493eb/go.mod h1:izkeMLvz6Ht8yAISXjx60XUQZMq9ZMe5h2ih4dLIBIQ= github.com/libp2p/go-libp2p-quic-transport v0.1.1/go.mod h1:wqG/jzhF3Pu2NrhJEvE+IE0NTHNXslOPn9JQzyCAxzU= github.com/libp2p/go-libp2p-quic-transport v0.5.0/go.mod h1:IEcuC5MLxvZ5KuHKjRu+dr3LjCT1Be3rcD/4d8JrX8M= -github.com/libp2p/go-libp2p-quic-transport v0.9.0 h1:WPuq5nV/chmIZIzvrkC2ulSdAQ0P0BDvgvAhZFOZ59E= -github.com/libp2p/go-libp2p-quic-transport v0.9.0/go.mod h1:xyY+IgxL0qsW7Kiutab0+NlxM0/p9yRtrGTYsuMWf70= +github.com/libp2p/go-libp2p-quic-transport v0.10.0 h1:koDCbWD9CCHwcHZL3/WEvP2A+e/o5/W5L3QS/2SPMA0= +github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= github.com/libp2p/go-libp2p-record v0.0.1/go.mod h1:grzqg263Rug/sRex85QrDOLntdFAymLDLm7lxMgU79Q= github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= github.com/libp2p/go-libp2p-record v0.1.1/go.mod h1:VRgKajOyMVgP/F0L5g3kH7SVskp17vFi2xheb5uMJtg= @@ -1001,8 +1024,9 @@ github.com/libp2p/go-libp2p-swarm v0.2.4/go.mod h1:/xIpHFPPh3wmSthtxdGbkHZ0OET1h github.com/libp2p/go-libp2p-swarm v0.2.7/go.mod h1:ZSJ0Q+oq/B1JgfPHJAT2HTall+xYRNYp1xs4S2FBWKA= github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= -github.com/libp2p/go-libp2p-swarm v0.3.1 h1:UTobu+oQHGdXTOGpZ4RefuVqYoJXcT0EBtSR74m2LkI= github.com/libp2p/go-libp2p-swarm v0.3.1/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= +github.com/libp2p/go-libp2p-swarm v0.5.0 h1:HIK0z3Eqoo8ugmN8YqWAhD2RORgR+3iNXYG4U2PFd1E= +github.com/libp2p/go-libp2p-swarm v0.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= @@ -1010,8 +1034,9 @@ github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MB github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= -github.com/libp2p/go-libp2p-testing v0.3.0 h1:ZiBYstPamsi7y6NJZebRudUzsYmVkt998hltyLqf8+g= github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= +github.com/libp2p/go-libp2p-testing v0.4.0 h1:PrwHRi0IGqOwVQWR3xzgigSlhlLfxgfXgkHxr77EghQ= +github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= github.com/libp2p/go-libp2p-tls v0.1.3 h1:twKMhMu44jQO+HgQK9X8NHO5HkeJu2QbhLzLJpa8oNM= github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= github.com/libp2p/go-libp2p-transport v0.0.1/go.mod h1:UzbUs9X+PHOSw7S3ZmeOxfnwaQY5vGDzZmKPod3N3tk= @@ -1021,8 +1046,9 @@ github.com/libp2p/go-libp2p-transport-upgrader v0.0.1/go.mod h1:NJpUAgQab/8K6K0m github.com/libp2p/go-libp2p-transport-upgrader v0.0.4/go.mod h1:RGq+tupk+oj7PzL2kn/m1w6YXxcIAYJYeI90h6BGgUc= github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= -github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 h1:q3ULhsknEQ34eVDhv4YwKS8iet69ffs9+Fir6a7weN4= github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= +github.com/libp2p/go-libp2p-transport-upgrader v0.4.2 h1:4JsnbfJzgZeRS9AWN7B9dPqn/LY/HoQTlO9gtdJTIYM= +github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= github.com/libp2p/go-libp2p-yamux v0.1.2/go.mod h1:xUoV/RmYkg6BW/qGxA9XJyg+HzXFYkeXbnhjmnYzKp8= github.com/libp2p/go-libp2p-yamux v0.1.3/go.mod h1:VGSQVrqkh6y4nm0189qqxMtvyBft44MOYYPpYKXiVt4= github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= @@ -1032,8 +1058,9 @@ github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30= -github.com/libp2p/go-libp2p-yamux v0.4.1 h1:TJxRVPY9SjH7TNrNC80l1OJMBiWhs1qpKmeB+1Ug3xU= -github.com/libp2p/go-libp2p-yamux v0.4.1/go.mod h1:FA/NjRYRVNjqOzpGuGqcruH7jAU2mYIjtKBicVOL3dc= +github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po= +github.com/libp2p/go-libp2p-yamux v0.5.3 h1:x2bK2BWktdMdTrciiDmgTMIxYNBdkxewQFEjHDl7VgU= +github.com/libp2p/go-libp2p-yamux v0.5.3/go.mod h1:Vy3TMonBAfTMXHWopsMc8iX/XGRYrRlpUaMzaeuHV/s= github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= @@ -1045,8 +1072,9 @@ github.com/libp2p/go-mplex v0.0.4/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTW github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.2.0 h1:Ov/D+8oBlbRkjBs1R1Iua8hJ8cUfbdiW8EOdZuxcgaI= github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= +github.com/libp2p/go-mplex v0.3.0 h1:U1T+vmCYJaEoDJPV1aq31N56hS+lJgb397GsylNSgrU= +github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= github.com/libp2p/go-msgio v0.0.1/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.0.3/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= @@ -1058,8 +1086,9 @@ github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/ github.com/libp2p/go-nat v0.0.5 h1:qxnwkco8RLKqVh1NmjQ+tJ8p8khNLFxuElYG/TwqW4Q= github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.3 h1:1ngWRx61us/EpaKkdqkMjKk/ufr/JlIFYQAxV2XX8Ig= github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= +github.com/libp2p/go-netroute v0.1.6 h1:ruPJStbYyXVYGQ81uzEDzuvbYRLKRrLvTYd33yomC38= +github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= @@ -1075,8 +1104,9 @@ github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2 github.com/libp2p/go-reuseport-transport v0.0.4 h1:OZGz0RB620QDGpv300n1zaOcKGGAoGVf8h9txtt/1uM= github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.0 h1:Y4s3/jNoryVRKEBrkJ576F17CPOaMIzUeCsg7dlTDj0= github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= +github.com/libp2p/go-sockaddr v0.1.1 h1:yD80l2ZOdGksnOyHrhxDdTDFrf7Oy+v3FMVArIRgZxQ= +github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= github.com/libp2p/go-stream-muxer v0.1.0/go.mod h1:8JAVsjeRBCWwPoZeH0W1imLOcriqXJyFvB0mR4A04sQ= github.com/libp2p/go-stream-muxer-multistream v0.1.1/go.mod h1:zmGdfkQ1AzOECIAcccoL8L//laqawOsO03zX8Sa+eGw= @@ -1098,8 +1128,9 @@ github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw github.com/libp2p/go-ws-transport v0.1.2/go.mod h1:dsh2Ld8F+XNmzpkaAijmg5Is+e9l6/1tK/6VFOdN69Y= github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= -github.com/libp2p/go-ws-transport v0.3.1 h1:ZX5rWB8nhRRJVaPO6tmkGI/Xx8XNboYX20PW5hXIscw= github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= +github.com/libp2p/go-ws-transport v0.4.0 h1:9tvtQ9xbws6cA5LvqdE6Ne3vcmGB4f1z9SByggk4s0k= +github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= github.com/libp2p/go-yamux v1.2.1/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= @@ -1111,12 +1142,14 @@ github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/h github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI= github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/libp2p/go-yamux/v2 v2.1.1 h1:3RkXAnDmaXJPckF/QbDnNbA6lZXMgycNTVMMTQ2YlAI= +github.com/libp2p/go-yamux/v2 v2.1.1/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= github.com/lucas-clemente/quic-go v0.16.0/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE= -github.com/lucas-clemente/quic-go v0.18.1 h1:DMR7guC0NtVS8zNZR3IO7NARZvZygkSC56GGtC6cyys= -github.com/lucas-clemente/quic-go v0.18.1/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg= +github.com/lucas-clemente/quic-go v0.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4= +github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/iostat v1.1.0/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg= @@ -1131,13 +1164,13 @@ github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7 github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= -github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= +github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/marten-seemann/qtls v0.9.1/go.mod h1:T1MmAdDPyISzxlK6kjRr0pcZFBVd1OZbBb/j3cvzHhk= github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc= github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.0 h1:i/YPXVxz8q9umso/5y474CNcHmTpA+5DH+mFPjx6PZg= -github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ= +github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -1171,6 +1204,8 @@ github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nr github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= @@ -1217,8 +1252,9 @@ github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/94 github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.1.0/go.mod h1:01k2RAqtoXIuPa3DCavAE9/6jc6nM0H3EgZyfUhN2oY= -github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= +github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= +github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= @@ -1248,8 +1284,10 @@ github.com/multiformats/go-multistream v0.0.1/go.mod h1:fJTiDfXJVmItycydCnNx4+wS github.com/multiformats/go-multistream v0.0.4/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= -github.com/multiformats/go-multistream v0.2.0 h1:6AuNmQVKUkRnddw2YiDjt5Elit40SFxMJkVnhmETXtU= github.com/multiformats/go-multistream v0.2.0/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= +github.com/multiformats/go-multistream v0.2.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= +github.com/multiformats/go-multistream v0.2.2 h1:TCYu1BHTDr1F/Qm75qwYISQdzGcRdC21nFgQW7l7GBo= +github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= @@ -1266,8 +1304,6 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c h1:5bFTChQxSKNwy8ALwOebjekYExl9HTT9urdawqC95tA= github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c/go.mod h1:7qN3Y0BvzRUf4LofcoJplQL10lsFDb4PYlePTVwrP28= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg= @@ -1285,6 +1321,7 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= @@ -1569,6 +1606,7 @@ github.com/xorcare/golden v0.6.0/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/ github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 h1:oWgZJmC1DorFZDpfMfWg7xk29yEOZiXmo/wZl+utTI8= github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= @@ -1597,8 +1635,8 @@ go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -1651,16 +1689,19 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1683,8 +1724,9 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1695,6 +1737,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1730,6 +1773,7 @@ golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1737,8 +1781,11 @@ golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY= golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 h1:0PC75Fz/kyMGhL0e1QnypqK2kQMqKt9csD1GnMJR+Zk= +golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1756,8 +1803,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180202135801-37707fdb30a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1824,16 +1871,21 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83 h1:kHSDPqCtsHZOg0nVylfTo20DDhE9gG4Y0jn7hKQ0QAM= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1877,10 +1929,12 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200827010519-17fd2f27a9e3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696 h1:Bfazo+enXJET5SbHeh95NtxabJF6fJ9r/jpfRJgd3j4= golang.org/x/tools v0.0.0-20201112185108-eeaa07dd7696/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1948,8 +2002,9 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1965,8 +2020,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= diff --git a/node/impl/common/common.go b/node/impl/common/common.go index 7d99fb42a..f1c57665c 100644 --- a/node/impl/common/common.go +++ b/node/impl/common/common.go @@ -156,7 +156,7 @@ func (a *CommonAPI) NetFindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, } func (a *CommonAPI) NetAutoNatStatus(ctx context.Context) (i api.NatInfo, err error) { - autonat := a.RawHost.(*basichost.BasicHost).AutoNat + autonat := a.RawHost.(*basichost.BasicHost).GetAutoNat() if autonat == nil { return api.NatInfo{ From 0019187a4f67dec7694350329cdb972eafda7ddf Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Tue, 11 May 2021 04:44:07 +0200 Subject: [PATCH 89/97] Forgotten pre-API zero-price check --- cli/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/client.go b/cli/client.go index 2bea31cd6..84e077943 100644 --- a/cli/client.go +++ b/cli/client.go @@ -479,7 +479,7 @@ The minimum value is 518400 (6 months).`, var proposal *cid.Cid if cctx.Bool("manual-stateless-deal") { - if ref.TransferType != storagemarket.TTManual { + if ref.TransferType != storagemarket.TTManual || price.Int64() != 0 { return xerrors.New("when manual-stateless-deal is enabled, you must also provide a 'price' of 0 and specify 'manual-piece-cid' and 'manual-piece-size'") } proposal, err = api.ClientStatelessDeal(ctx, sdParams) From 9fb4bea9ab058ebfc2b895861b19393f9697f8b6 Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Wed, 12 May 2021 14:31:34 -0400 Subject: [PATCH 90/97] chore(ci): Enable build on RC tags --- .circleci/config.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c3deab6ad..e02fb58a0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -751,7 +751,7 @@ workflows: filters: tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - test-conformance: test-suite-name: conformance packages: "./conformance" @@ -772,28 +772,28 @@ workflows: filters: tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - build-ntwk-calibration: requires: - test-short filters: tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - build-ntwk-butterfly: requires: - test-short filters: tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - build-ntwk-nerpa: requires: - test-short filters: tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - build-lotus-soup - build-macos: requires: @@ -804,7 +804,7 @@ workflows: - /.*/ tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - publish: requires: - build-all @@ -815,7 +815,7 @@ workflows: - /.*/ tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - build-and-push-image: dockerfile: Dockerfile.lotus path: . @@ -830,7 +830,7 @@ workflows: - /.*/ tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - publish-packer-calibrationnet: requires: - build-ntwk-calibration @@ -840,7 +840,7 @@ workflows: - /.*/ tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - publish-packer-butterflynet: requires: - build-ntwk-butterfly @@ -850,7 +850,7 @@ workflows: - /.*/ tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - publish-packer-nerpanet: requires: - build-ntwk-nerpa @@ -860,4 +860,4 @@ workflows: - /.*/ tags: only: - - /^v\d+\.\d+\.\d+$/ + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ From 624f5969b30e298d9b369121e227c3727be46568 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 13 May 2021 19:58:15 +0200 Subject: [PATCH 91/97] fix: wait-api should use GetAPI to acquire binary specific API Fixes #6244 Signed-off-by: Jakub Sztandera --- cli/wait.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/wait.go b/cli/wait.go index 98fc9c0d8..ea897d5ad 100644 --- a/cli/wait.go +++ b/cli/wait.go @@ -12,7 +12,7 @@ var WaitApiCmd = &cli.Command{ Usage: "Wait for lotus api to come online", Action: func(cctx *cli.Context) error { for i := 0; i < 30; i++ { - api, closer, err := GetFullNodeAPI(cctx) + api, closer, err := GetAPI(cctx) if err != nil { fmt.Printf("Not online yet... (%s)\n", err) time.Sleep(time.Second) From b4b38788efcddceb87ee1677a5aa53a8380f2e6e Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 13 May 2021 11:00:42 -0700 Subject: [PATCH 92/97] add flags to control gateway lookback parameters --- cmd/lotus-gateway/main.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-gateway/main.go b/cmd/lotus-gateway/main.go index e2ef27dd9..60f45f283 100644 --- a/cmd/lotus-gateway/main.go +++ b/cmd/lotus-gateway/main.go @@ -8,6 +8,7 @@ import ( "contrib.go.opencensus.io/exporter/prometheus" "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/go-state-types/abi" promclient "github.com/prometheus/client_golang/prometheus" "go.opencensus.io/tag" @@ -70,6 +71,16 @@ var runCmd = &cli.Command{ Name: "api-max-req-size", Usage: "maximum API request size accepted by the JSON RPC server", }, + &cli.DurationFlag{ + Name: "api-max-lookback", + Usage: "maximum duration allowable for tipset lookbacks", + Value: LookbackCap, + }, + &cli.Int64Flag{ + Name: "api-wait-lookback-limit", + Usage: "maximum number of blocks to search back through for message inclusion", + Value: int64(StateWaitLookbackLimit), + }, }, Action: func(cctx *cli.Context) error { log.Info("Starting lotus gateway") @@ -107,7 +118,11 @@ var runCmd = &cli.Command{ mux.Handle(path, rpcServer) } - ma := metrics.MetricedGatewayAPI(NewGatewayAPI(api)) + lookbackCap := cctx.Duration("api-max-lookback") + + waitLookback := abi.ChainEpoch(cctx.Int64("api-wait-lookback-limit")) + + ma := metrics.MetricedGatewayAPI(newGatewayAPI(api, lookbackCap, waitLookback)) serveRpc("/rpc/v1", ma) serveRpc("/rpc/v0", lapi.Wrap(new(v1api.FullNodeStruct), new(v0api.WrapperV1Full), ma)) From 3910d7cb4a5654faf494f716da7c3da9a4afa0a9 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Thu, 13 May 2021 15:50:10 -0400 Subject: [PATCH 93/97] Upgrade nerpa to actor v4 around May 27th 1600 ET --- build/params_nerpanet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/params_nerpanet.go b/build/params_nerpanet.go index fb6cfc47a..4587e81f8 100644 --- a/build/params_nerpanet.go +++ b/build/params_nerpanet.go @@ -40,8 +40,8 @@ const UpgradeClausHeight = 250 const UpgradeOrangeHeight = 300 const UpgradeActorsV3Height = 600 -const UpgradeNorwegianHeight = 999999 -const UpgradeActorsV4Height = 99999999 +const UpgradeNorwegianHeight = 201000 +const UpgradeActorsV4Height = 203000 func init() { // Minimum block production power is set to 4 TiB From 314be51e1b910c3e3cc012539500f963b656127f Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Fri, 14 May 2021 00:22:00 -0400 Subject: [PATCH 94/97] fix(ci): Use recent ubuntu LTS release; Update release params --- .circleci/config.yml | 2 +- scripts/publish-release.sh | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e02fb58a0..8d08ef8a8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,7 +11,7 @@ executors: resource_class: 2xlarge ubuntu: docker: - - image: ubuntu:19.10 + - image: ubuntu:20.04 commands: install-deps: diff --git a/scripts/publish-release.sh b/scripts/publish-release.sh index 103de1f0f..e77a6a949 100755 --- a/scripts/publish-release.sh +++ b/scripts/publish-release.sh @@ -32,6 +32,7 @@ if [ "${RELEASE_ID}" = "null" ]; then RELEASE_DATA="{ \"tag_name\": \"${CIRCLE_TAG}\", \"target_commitish\": \"${CIRCLE_SHA1}\", + \"discussion_category_name\": \"announcement\", \"name\": \"${CIRCLE_TAG}\", \"body\": \"\", \"prerelease\": false @@ -51,8 +52,9 @@ else fi RELEASE_UPLOAD_URL=`echo "${RELEASE_RESPONSE}" | jq -r '.upload_url' | cut -d'{' -f1` +echo "Preparing to send artifacts to ${RELEASE_UPLOAD_URL}" -bundles=( +artifacts=( "lotus_${CIRCLE_TAG}_linux-amd64.tar.gz" "lotus_${CIRCLE_TAG}_linux-amd64.tar.gz.cid" "lotus_${CIRCLE_TAG}_linux-amd64.tar.gz.sha512" @@ -60,9 +62,10 @@ bundles=( "lotus_${CIRCLE_TAG}_darwin-amd64.tar.gz.cid" "lotus_${CIRCLE_TAG}_darwin-amd64.tar.gz.sha512" ) -for RELEASE_FILE in "${bundles[@]}" + +for RELEASE_FILE in "${artifacts[@]}" do - echo "Uploading release bundle: ${RELEASE_FILE}" + echo "Uploading ${RELEASE_FILE}..." curl \ --request POST \ --header "Authorization: token ${GITHUB_TOKEN}" \ @@ -70,7 +73,7 @@ do --data-binary "@${RELEASE_FILE}" \ "$RELEASE_UPLOAD_URL?name=$(basename "${RELEASE_FILE}")" - echo "Release bundle uploaded: ${RELEASE_FILE}" + echo "Uploaded ${RELEASE_FILE}" done popd From eccdff459eea2e52f7c200e54d4863aa75083aaf Mon Sep 17 00:00:00 2001 From: Mike Greenberg Date: Fri, 14 May 2021 18:14:39 -0400 Subject: [PATCH 95/97] fix(ci): Conditional announcement and prerelease on RC tag --- scripts/publish-release.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/publish-release.sh b/scripts/publish-release.sh index e77a6a949..ad2a52dcf 100755 --- a/scripts/publish-release.sh +++ b/scripts/publish-release.sh @@ -29,13 +29,20 @@ RELEASE_ID=`echo "${RELEASE_RESPONSE}" | jq '.id'` if [ "${RELEASE_ID}" = "null" ]; then echo "creating release" + COND_CREATE_DISCUSSION="" + PRERELEASE=true + if [[ ${CIRCLE_TAG} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + COND_CREATE_DISCUSSION="\"discussion_category_name\": \"announcement\"," + PRERELEASE=false + fi + RELEASE_DATA="{ \"tag_name\": \"${CIRCLE_TAG}\", \"target_commitish\": \"${CIRCLE_SHA1}\", - \"discussion_category_name\": \"announcement\", + ${COND_CREATE_DISCUSSION} \"name\": \"${CIRCLE_TAG}\", \"body\": \"\", - \"prerelease\": false + \"prerelease\": ${PRERELEASE} }" # create it if it doesn't exist yet From 72fc16e8a794f6d552c6002e4159820362fe62a9 Mon Sep 17 00:00:00 2001 From: Jennifer <42981373+jennijuju@users.noreply.github.com> Date: Fri, 14 May 2021 23:18:04 -0400 Subject: [PATCH 96/97] Update RELEASE_ISSUE_TEMPLATE.md --- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index df5e8d1c5..859da43e0 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -81,7 +81,7 @@ Testing an RC: - [ ] Ensure that [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) is up to date - [ ] Check if any [config](https://docs.filecoin.io/get-started/lotus/configuration-and-advanced-usage/#configuration) updates are needed - [ ] Invite the wider community through (link to the release issue): - - [ ] Create a lotus disucssion, example [here](https://github.com/filecoin-project/lotus/discussions/5595) + - [ ] Check `Create a discussion for this release` when tagging for the major rcs(new features, hot-fixes) release - [ ] Link the disucssion in #fil-lotus on Filecoin slack - [ ] **Stage 4 - Release** @@ -90,11 +90,11 @@ Testing an RC: - [ ] Ensure that [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) is up to date - [ ] Ensure that [README.md](https://github.com/filecoin-project/lotus/blob/master/README.md) is up to date - [ ] Merge `release-vX.Y.Z` into the `releases` branch. - - [ ] Tag this merge commit (on the `releases` branch) with `vX.Y.Z`. + - [ ] Tag this merge commit (on the `releases` branch) with `vX.Y.Z` - [ ] Cut the release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=true&target=releases). + - [ ] Check `Create a discussion for this release` when tagging the release - [ ] Final announcements - [ ] Update network.filecoin.io for mainnet, calib and nerpa. - - [ ] Add a comment when the final release is tagged, example [here](https://github.com/filecoin-project/lotus/discussions/5905#discussioncomment-571752) - [ ] repost in #fil-lotus in filecoin slack - [ ] Inform node provides (Protofire, Digital Ocean..) From 71bd00559467afb3dfc767001d336dacf227a80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 13 Apr 2021 12:27:46 +0200 Subject: [PATCH 97/97] wip --- CHANGELOG.md | 287 ++++++++++++++++++++++++++++++++------------ extern/filecoin-ffi | 2 +- 2 files changed, 213 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55534ff05..811b691c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,147 @@ # Lotus changelog -# 1.8.0 / 2021-04-27 +# 1.9.0-rc4 / 2021-05-13 + +This is an optional Lotus release that introduces various improvements to the sealing, mining, and deal-making processes. + +## Highlights + +- OpenRPC Support (https://github.com/filecoin-project/lotus/pull/5843) +- Take latency into account when making interactive deals (https://github.com/filecoin-project/lotus/pull/5876) +- Update go-commp-utils for >10x faster client commp calculation (https://github.com/filecoin-project/lotus/pull/5892) +- add `lotus client cancel-retrieval` cmd to lotus CLI (https://github.com/filecoin-project/lotus/pull/5871) +- add `inspect-deal` command to `lotus client` (https://github.com/filecoin-project/lotus/pull/5833) +- Local retrieval support (https://github.com/filecoin-project/lotus/pull/5917) +- go-fil-markets v1.1.9 -> v1.2.5 + - For a detailed changelog see https://github.com/filecoin-project/go-fil-markets/blob/master/CHANGELOG.md +- rust-fil-proofs v5.4.1 -> v7.0.1 + - For a detailed changelog see https://github.com/filecoin-project/rust-fil-proofs/blob/master/CHANGELOG.md + +## Changes +- storagefsm: Apply global events even in broken states (https://github.com/filecoin-project/lotus/pull/5962) +- Default the AlwaysKeepUnsealedCopy flag to true (https://github.com/filecoin-project/lotus/pull/5743) +- splitstore: compact hotstore prior to garbage collection (https://github.com/filecoin-project/lotus/pull/5778) +- ipfs-force bootstrapper update (https://github.com/filecoin-project/lotus/pull/5799) +- better logging when unsealing fails (https://github.com/filecoin-project/lotus/pull/5851) +- perf: add cache for gas permium estimation (https://github.com/filecoin-project/lotus/pull/5709) +- backupds: Compact log on restart (https://github.com/filecoin-project/lotus/pull/5875) +- backupds: Improve truncated log handling (https://github.com/filecoin-project/lotus/pull/5891) +- State CLI improvements (State CLI improvements) +- API proxy struct codegen (https://github.com/filecoin-project/lotus/pull/5854) +- move DI stuff for paychmgr into modules (https://github.com/filecoin-project/lotus/pull/5791) +- Implement Event observer and Settings for 3rd party dep injection (https://github.com/filecoin-project/lotus/pull/5693) +- Export developer and network commands for consumption by derivatives of Lotus (https://github.com/filecoin-project/lotus/pull/5864) +- mock sealer: Simulate randomness sideeffects (https://github.com/filecoin-project/lotus/pull/5805) +- localstorage: Demote reservation stat error to debug (https://github.com/filecoin-project/lotus/pull/5976) +- shed command to unpack miner info dumps (https://github.com/filecoin-project/lotus/pull/5800) +- Add two utils to Lotus-shed (https://github.com/filecoin-project/lotus/pull/5867) +- add shed election estimate command (https://github.com/filecoin-project/lotus/pull/5092) +- Add --actor flag in lotus-shed sectors terminate (https://github.com/filecoin-project/lotus/pull/5819) +- Move lotus mpool clear to lotus-shed (https://github.com/filecoin-project/lotus/pull/5900) +- Centralize everything on ipfs/go-log/v2 (https://github.com/filecoin-project/lotus/pull/5974) +- expose NextID from nice market actor interface (https://github.com/filecoin-project/lotus/pull/5850) +- add available options for perm on error (https://github.com/filecoin-project/lotus/pull/5814) +- API docs clarification: Document StateSearchMsg replaced message behavior (https://github.com/filecoin-project/lotus/pull/5838) +- api: Document StateReplay replaced message behavior (https://github.com/filecoin-project/lotus/pull/5840) +- add godocs to miner objects (https://github.com/filecoin-project/lotus/pull/2184) +- Add description to the client deal CLI command (https://github.com/filecoin-project/lotus/pull/5999) +- lint: don't skip builtin (https://github.com/filecoin-project/lotus/pull/5881) +- use deal duration from actors (https://github.com/filecoin-project/lotus/pull/5270) +- remote calc winningpost proof (https://github.com/filecoin-project/lotus/pull/5884) +- packer: other network images (https://github.com/filecoin-project/lotus/pull/5930) +- Convert the chainstore lock to RW (https://github.com/filecoin-project/lotus/pull/5971) +- Remove CachedBlockstore (https://github.com/filecoin-project/lotus/pull/5972) +- remove messagepool CapGasFee duplicate code (https://github.com/filecoin-project/lotus/pull/5992) +- Add a mining-heartbeat INFO line at every epoch (https://github.com/filecoin-project/lotus/pull/6183) +- chore(ci): Enable build on RC tags (https://github.com/filecoin-project/lotus/pull/6245) +- Upgrade nerpa to actor v4 and bump the version to rc4 (https://github.com/filecoin-project/lotus/pull/6249) +## Fixes +- return buffers after canceling badger operation (https://github.com/filecoin-project/lotus/pull/5796) +- avoid holding a lock while calling the View callback (https://github.com/filecoin-project/lotus/pull/5792) +- storagefsm: Trigger input processing when below limits (https://github.com/filecoin-project/lotus/pull/5801) +- After importing a previously deleted key, be able to delete it again (https://github.com/filecoin-project/lotus/pull/4653) +- fix StateManager.Replay on reward actor (https://github.com/filecoin-project/lotus/pull/5804) +- make sure atomic 64bit fields are 64bit aligned (https://github.com/filecoin-project/lotus/pull/5794) +- Import secp sigs in paych tests (https://github.com/filecoin-project/lotus/pull/5879) +- fix ci build-macos (https://github.com/filecoin-project/lotus/pull/5934) +- Fix creation of remainder account when it's not a multisig (https://github.com/filecoin-project/lotus/pull/5807) +- Fix fallback chainstore (https://github.com/filecoin-project/lotus/pull/6003) +- fix 4857: show help for set-addrs (https://github.com/filecoin-project/lotus/pull/5943) +- fix health report (https://github.com/filecoin-project/lotus/pull/6011) + + +# 1.9.0-rc2 / 2021-04-30 + +This is an optional Lotus release that introduces various improvements to the sealing, mining, and deal-making processes. + +## Highlights + +- OpenRPC Support (https://github.com/filecoin-project/lotus/pull/5843) +- Take latency into account when making interactive deals (https://github.com/filecoin-project/lotus/pull/5876) +- Update go-commp-utils for >10x faster client commp calculation (https://github.com/filecoin-project/lotus/pull/5892) +- add `lotus client cancel-retrieval` cmd to lotus CLI (https://github.com/filecoin-project/lotus/pull/5871) +- add `inspect-deal` command to `lotus client` (https://github.com/filecoin-project/lotus/pull/5833) +- Local retrieval support (https://github.com/filecoin-project/lotus/pull/5917) +- go-fil-markets v1.1.9 -> v1.2.5 + - For a detailed changelog see https://github.com/filecoin-project/go-fil-markets/blob/master/CHANGELOG.md +- rust-fil-proofs v5.4.1 -> v7 + - For a detailed changelog see https://github.com/filecoin-project/rust-fil-proofs/blob/master/CHANGELOG.md + +## Changes +- storagefsm: Apply global events even in broken states (https://github.com/filecoin-project/lotus/pull/5962) +- Default the AlwaysKeepUnsealedCopy flag to true (https://github.com/filecoin-project/lotus/pull/5743) +- splitstore: compact hotstore prior to garbage collection (https://github.com/filecoin-project/lotus/pull/5778) +- ipfs-force bootstrapper update (https://github.com/filecoin-project/lotus/pull/5799) +- better logging when unsealing fails (https://github.com/filecoin-project/lotus/pull/5851) +- perf: add cache for gas permium estimation (https://github.com/filecoin-project/lotus/pull/5709) +- backupds: Compact log on restart (https://github.com/filecoin-project/lotus/pull/5875) +- backupds: Improve truncated log handling (https://github.com/filecoin-project/lotus/pull/5891) +- State CLI improvements (State CLI improvements) +- API proxy struct codegen (https://github.com/filecoin-project/lotus/pull/5854) +- move DI stuff for paychmgr into modules (https://github.com/filecoin-project/lotus/pull/5791) +- Implement Event observer and Settings for 3rd party dep injection (https://github.com/filecoin-project/lotus/pull/5693) +- Export developer and network commands for consumption by derivatives of Lotus (https://github.com/filecoin-project/lotus/pull/5864) +- mock sealer: Simulate randomness sideeffects (https://github.com/filecoin-project/lotus/pull/5805) +- localstorage: Demote reservation stat error to debug (https://github.com/filecoin-project/lotus/pull/5976) +- shed command to unpack miner info dumps (https://github.com/filecoin-project/lotus/pull/5800) +- Add two utils to Lotus-shed (https://github.com/filecoin-project/lotus/pull/5867) +- add shed election estimate command (https://github.com/filecoin-project/lotus/pull/5092) +- Add --actor flag in lotus-shed sectors terminate (https://github.com/filecoin-project/lotus/pull/5819) +- Move lotus mpool clear to lotus-shed (https://github.com/filecoin-project/lotus/pull/5900) +- Centralize everything on ipfs/go-log/v2 (https://github.com/filecoin-project/lotus/pull/5974) +- expose NextID from nice market actor interface (https://github.com/filecoin-project/lotus/pull/5850) +- add available options for perm on error (https://github.com/filecoin-project/lotus/pull/5814) +- API docs clarification: Document StateSearchMsg replaced message behavior (https://github.com/filecoin-project/lotus/pull/5838) +- api: Document StateReplay replaced message behavior (https://github.com/filecoin-project/lotus/pull/5840) +- add godocs to miner objects (https://github.com/filecoin-project/lotus/pull/2184) +- Add description to the client deal CLI command (https://github.com/filecoin-project/lotus/pull/5999) +- lint: don't skip builtin (https://github.com/filecoin-project/lotus/pull/5881) +- use deal duration from actors (https://github.com/filecoin-project/lotus/pull/5270) +- remote calc winningpost proof (https://github.com/filecoin-project/lotus/pull/5884) +- packer: other network images (https://github.com/filecoin-project/lotus/pull/5930) +- Convert the chainstore lock to RW (https://github.com/filecoin-project/lotus/pull/5971) +- Remove CachedBlockstore (https://github.com/filecoin-project/lotus/pull/5972) +- remove messagepool CapGasFee duplicate code (https://github.com/filecoin-project/lotus/pull/5992) + +## Fixes +- return buffers after canceling badger operation (https://github.com/filecoin-project/lotus/pull/5796) +- avoid holding a lock while calling the View callback (https://github.com/filecoin-project/lotus/pull/5792) +- storagefsm: Trigger input processing when below limits (https://github.com/filecoin-project/lotus/pull/5801) +- After importing a previously deleted key, be able to delete it again (https://github.com/filecoin-project/lotus/pull/4653) +- fix StateManager.Replay on reward actor (https://github.com/filecoin-project/lotus/pull/5804) +- make sure atomic 64bit fields are 64bit aligned (https://github.com/filecoin-project/lotus/pull/5794) +- Import secp sigs in paych tests (https://github.com/filecoin-project/lotus/pull/5879) +- fix ci build-macos (https://github.com/filecoin-project/lotus/pull/5934) +- Fix creation of remainder account when it's not a multisig (https://github.com/filecoin-project/lotus/pull/5807) +- Fix fallback chainstore (https://github.com/filecoin-project/lotus/pull/6003) +- fix 4857: show help for set-addrs (https://github.com/filecoin-project/lotus/pull/5943) +- fix health report (https://github.com/filecoin-project/lotus/pull/6011) + +# 1.8.0 / 2021-04-05 This is a mandatory release of Lotus that upgrades the network to version 12, which introduces various performance improvements to the cron processing of the power actor. The network will upgrade at height 712320, which is 2021-04-29T06:00:00Z. -## Changes +## Changes - v4 specs-actors integration, nv12 migration (https://github.com/filecoin-project/lotus/pull/6116) @@ -20,7 +157,7 @@ This release also expands the `lotus-miner sectors extend` CLI, with a new optio - The `expiration-cutoff` flag can be passed to skip sectors whose expiration is past a certain point from the current head. It defaults to infinity (no cutoff), but if, say, 28800 was specified, then only sectors expiring in the next 10 days would be extended (2880 epochs in 1 day). -## Changes +## Changes - Util for miners to extend all v1 sectors (https://github.com/filecoin-project/lotus/pull/5924) - Upgrade the butterfly network (https://github.com/filecoin-project/lotus/pull/5929) @@ -31,7 +168,7 @@ This release also expands the `lotus-miner sectors extend` CLI, with a new optio This is a patch release of Lotus that introduces small fixes to the Storage FSM. -## Changes +## Changes - storagefsm: Fix double unlock with ready WaitDeals sectors (https://github.com/filecoin-project/lotus/pull/5783) - backupds: Allow larger values in write log (https://github.com/filecoin-project/lotus/pull/5776) @@ -47,7 +184,7 @@ This is an hotfix release of Lotus that fixes a critical bug introduced in v1.5. # 1.5.1 / 2021-03-10 -This is an optional release of Lotus that introduces an important fix to the WindowPoSt computation process. The change is to wait for some confidence before drawing beacon randomness for the proof. Without this, invalid proofs might be generated as the result of a null tipset. +This is an optional release of Lotus that introduces an important fix to the WindowPoSt computation process. The change is to wait for some confidence before drawing beacon randomness for the proof. Without this, invalid proofs might be generated as the result of a null tipset. ## Splitstore @@ -140,7 +277,7 @@ FIP-0010 introduces the ability to dispute bad Window PoSts. Node operators are ## Changes - [#5341](https://github.com/filecoin-project/lotus/pull/5341) Add a `LOTUS_DISABLE_V3_ACTOR_MIGRATION` envvar - - Setting this envvar to 1 disables the v3 actor migration, should only be used in the event of a failed migration + - Setting this envvar to 1 disables the v3 actor migration, should only be used in the event of a failed migration # 1.4.2 / 2021-02-17 @@ -148,14 +285,14 @@ This is a large, and highly recommended, optional release with new features and - [FIP-0007 h/amt-v3](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0007.md) which improves the performance of the Filecoin HAMT and AMT. - [FIP-0010 off-chain Window PoSt Verification](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0010.md) which reduces the gas consumption of `SubmitWindowedPoSt` messages significantly by optimistically accepting Window PoSt proofs without verification, and allowing them to be disputed later by off-chain verifiers. - + Note that this release does NOT set an upgrade epoch for v3 actors to take effect. That will be done in the upcoming 1.5.0 release. - - ## New Features - - - [#5341](https://github.com/filecoin-project/lotus/pull/5341) Added sector termination API and CLI - - Run `lotus-miner sectors terminate` -- [#5342](https://github.com/filecoin-project/lotus/pull/5342) Added CLI for using a multisig wallet as miner's owner address + +## New Features + +- [#5341](https://github.com/filecoin-project/lotus/pull/5341) Added sector termination API and CLI + - Run `lotus-miner sectors terminate` +- [#5342](https://github.com/filecoin-project/lotus/pull/5342) Added CLI for using a multisig wallet as miner's owner address - See how to set it up [here](https://github.com/filecoin-project/lotus/pull/5342#issue-554009129) - [#5363](https://github.com/filecoin-project/lotus/pull/5363), [#5418](https://github.com/filecoin-project/lotus/pull/), [#5476](https://github.com/filecoin-project/lotus/pull/5476), [#5459](https://github.com/filecoin-project/lotus/pull/5459) Integrated [spec-actor v3](https://github.com/filecoin-pro5418ject/specs-actors/releases/tag/v3.0.0) - [#5472](https://github.com/filecoin-project/lotus/pull/5472) Generate actor v3 methods for pond @@ -166,7 +303,7 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5411](https://github.com/filecoin-project/lotus/pull/5411) Handle batch `PublishStorageDeals` message in sealing recovery - [#5505](https://github.com/filecoin-project/lotus/pull/5505) Exclude expired deals from batching in `PublishStorageDeals` messages - Added `PublishMsgPeriod` and `MaxDealsPerPublishMsg` to miner `Dealmaking` [configuration](https://docs.filecoin.io/mine/lotus/miner-configuration/#dealmaking-section). See how they work [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#publishing-several-deals-in-one-message). - - [#5538](https://github.com/filecoin-project/lotus/pull/5538), [#5549](https://github.com/filecoin-project/lotus/pull/5549) Added a command to list pending deals and force publish messages. + - [#5538](https://github.com/filecoin-project/lotus/pull/5538), [#5549](https://github.com/filecoin-project/lotus/pull/5549) Added a command to list pending deals and force publish messages. - Run `lotus-miner market pending-publish` - [#5428](https://github.com/filecoin-project/lotus/pull/5428) Moved waiting for `PublishStorageDeals` messages' receipt from markets to lotus - [#5510](https://github.com/filecoin-project/lotus/pull/5510) Added `nerpanet` build option @@ -176,32 +313,32 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5219](https://github.com/filecoin-project/lotus/pull/5219) Added interactive mode for lotus-wallet - [5529](https://github.com/filecoin-project/lotus/pull/5529) Added support for minder nodes in `lotus-shed rpc` util - ## Bug Fixes - - - [#5210](https://github.com/filecoin-project/lotus/pull/5210) Miner should not dial client on restart - - [#5403](https://github.com/filecoin-project/lotus/pull/5403) When estimating GasLimit only apply prior messages up to the nonce - - [#5410](https://github.com/filecoin-project/lotus/pull/510) Fix the calibnet build option - - [#5492](https://github.com/filecoin-project/lotus/pull/5492) Fixed `has` for ipfsbstore for non-existing blocks - - [#5361](https://github.com/filecoin-project/lotus/pull/5361) Fixed retrieval hangs when using `IpfsOnlineMode=true` - - [#5493](https://github.com/filecoin-project/lotus/pull/5493) Fixed retrieval failure when price-per-byte is zero - - [#5506](https://github.com/filecoin-project/lotus/pull/5506) Fixed contexts in the storage adpater - - [#5515](https://github.com/filecoin-project/lotus/pull/5515) Properly wire up `StateReadState` on gateway API - - [#5582](https://github.com/filecoin-project/lotus/pull/5582) Fixed error logging format strings - - [#5614](https://github.com/filecoin-project/lotus/pull/5614) Fixed websocket reconnecting handling +## Bug Fixes + +- [#5210](https://github.com/filecoin-project/lotus/pull/5210) Miner should not dial client on restart +- [#5403](https://github.com/filecoin-project/lotus/pull/5403) When estimating GasLimit only apply prior messages up to the nonce +- [#5410](https://github.com/filecoin-project/lotus/pull/510) Fix the calibnet build option +- [#5492](https://github.com/filecoin-project/lotus/pull/5492) Fixed `has` for ipfsbstore for non-existing blocks +- [#5361](https://github.com/filecoin-project/lotus/pull/5361) Fixed retrieval hangs when using `IpfsOnlineMode=true` +- [#5493](https://github.com/filecoin-project/lotus/pull/5493) Fixed retrieval failure when price-per-byte is zero +- [#5506](https://github.com/filecoin-project/lotus/pull/5506) Fixed contexts in the storage adpater +- [#5515](https://github.com/filecoin-project/lotus/pull/5515) Properly wire up `StateReadState` on gateway API +- [#5582](https://github.com/filecoin-project/lotus/pull/5582) Fixed error logging format strings +- [#5614](https://github.com/filecoin-project/lotus/pull/5614) Fixed websocket reconnecting handling - ## Improvements - - - [#5389](https://github.com/filecoin-project/lotus/pull/5389) Show verified indicator for `./lotus-miner storage-deals list` +## Improvements + +- [#5389](https://github.com/filecoin-project/lotus/pull/5389) Show verified indicator for `./lotus-miner storage-deals list` - [#5229](https://github.com/filecoin-project/lotus/pull/5220) Show power for verified deals in `./lotus-miner setocr list` - [#5407](https://github.com/filecoin-project/lotus/pull/5407) Added explicit check of the miner address protocol - - [#5399](https://github.com/filecoin-project/lotus/pull/5399) watchdog: increase heapprof capture threshold to 90% - - [#5398](https://github.com/filecoin-project/lotus/pull/5398) storageadapter: Look at precommits on-chain since deal publish msg - - [#5470](https://github.com/filecoin-project/lotus/pull/5470) Added `--no-timing` option for `./lotus state compute-state --html` +- [#5399](https://github.com/filecoin-project/lotus/pull/5399) watchdog: increase heapprof capture threshold to 90% +- [#5398](https://github.com/filecoin-project/lotus/pull/5398) storageadapter: Look at precommits on-chain since deal publish msg +- [#5470](https://github.com/filecoin-project/lotus/pull/5470) Added `--no-timing` option for `./lotus state compute-state --html` - [#5417](https://github.com/filecoin-project/lotus/pull/5417) Storage Manager: Always unseal full sectors - [#5393](https://github.com/filecoin-project/lotus/pull/5393) Switched to [filecoin-ffi bls api ](https://github.com/filecoin-project/filecoin-ffi/pull/159)for bls signatures -- [#5380](https://github.com/filecoin-project/lotus/pull/5210) Refactor deals API tests -- [#5397](https://github.com/filecoin-project/lotus/pull/5397) Fixed a flake in the sync manager edge case test +- [#5380](https://github.com/filecoin-project/lotus/pull/5210) Refactor deals API tests +- [#5397](https://github.com/filecoin-project/lotus/pull/5397) Fixed a flake in the sync manager edge case test - [#5406](https://github.com/filecoin-project/lotus/pull/5406) Added a test to ensure a correct window post cannot be disputed - [#5294](https://github.com/filecoin-project/lotus/pull/5394) Added jobs to build Lotus docker image and push it to AWS ECR - [#5387](https://github.com/filecoin-project/lotus/pull/5387) Added network info(mainnet|calibnet) in version @@ -210,7 +347,7 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5047](https://github.com/filecoin-project/lotus/pull/5047) Improved the UX for `./lotus-shed bitfield enc` - [#5282](https://github.com/filecoin-project/lotus/pull/5282) Snake a context through the chian blockstore creation - [#5350](https://github.com/filecoin-project/lotus/pull/5350) Avoid using `mp.cfg` directrly to prevent race condition -- [#5449](https://github.com/filecoin-project/lotus/pull/5449) Documented the block-header better +- [#5449](https://github.com/filecoin-project/lotus/pull/5449) Documented the block-header better - [#5404](https://github.com/filecoin-project/lotus/pull/5404) Added retrying proofs if an incorrect one is generated - [#4545](https://github.com/filecoin-project/lotus/pull/4545) Made state tipset usage consistent in the API - [#5540](https://github.com/filecoin-project/lotus/pull/5540) Removed unnecessary database reads in validation check @@ -227,14 +364,14 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5592](https://github.com/filecoin-project/lotus/pull/5592) Verify FFI version before building ## Dependency Updates - - [#5296](https://github.com/filecoin-project/lotus/pull/5396) Upgraded to [raulk/go-watchdog@v1.0.1](https://github.com/raulk/go-watchdog/releases/tag/v1.0.1) - - [#5450](https://github.com/filecoin-project/lotus/pull/5450) Dependency updates - - [#5425](https://github.com/filecoin-project/lotus/pull/5425) Fixed stale imports in testplans/lotus-soup - - [#5535](https://github.com/filecoin-project/lotus/pull/5535) Updated to [go-fil-markets@v1.1.7](https://github.com/filecoin-project/go-fil-markets/releases/tag/v1.1.7) - - [#5616](https://github.com/filecoin-project/lotus/pull/5600) Updated to [filecoin-ffi@b6e0b35fb49ed0fe](https://github.com/filecoin-project/filecoin-ffi/releases/tag/b6e0b35fb49ed0fe) - - [#5599](https://github.com/filecoin-project/lotus/pull/5599) Updated to [go-bitfield@v0.2.4](https://github.com/filecoin-project/go-bitfield/releases/tag/v0.2.4) - - [#5614](https://github.com/filecoin-project/lotus/pull/5614), , [#5621](https://github.com/filecoin-project/lotus/pull/5621) Updated to [go-jsonrpc@v0.1.3](https://github.com/filecoin-project/go-jsonrpc/releases/tag/v0.1.3) - - [#5459](https://github.com/filecoin-project/lotus/pull/5459) Updated to [spec-actors@v3.0.1](https://github.com/filecoin-project/specs-actors/releases/tag/v3.0.1) +- [#5296](https://github.com/filecoin-project/lotus/pull/5396) Upgraded to [raulk/go-watchdog@v1.0.1](https://github.com/raulk/go-watchdog/releases/tag/v1.0.1) +- [#5450](https://github.com/filecoin-project/lotus/pull/5450) Dependency updates +- [#5425](https://github.com/filecoin-project/lotus/pull/5425) Fixed stale imports in testplans/lotus-soup +- [#5535](https://github.com/filecoin-project/lotus/pull/5535) Updated to [go-fil-markets@v1.1.7](https://github.com/filecoin-project/go-fil-markets/releases/tag/v1.1.7) +- [#5616](https://github.com/filecoin-project/lotus/pull/5600) Updated to [filecoin-ffi@b6e0b35fb49ed0fe](https://github.com/filecoin-project/filecoin-ffi/releases/tag/b6e0b35fb49ed0fe) +- [#5599](https://github.com/filecoin-project/lotus/pull/5599) Updated to [go-bitfield@v0.2.4](https://github.com/filecoin-project/go-bitfield/releases/tag/v0.2.4) +- [#5614](https://github.com/filecoin-project/lotus/pull/5614), , [#5621](https://github.com/filecoin-project/lotus/pull/5621) Updated to [go-jsonrpc@v0.1.3](https://github.com/filecoin-project/go-jsonrpc/releases/tag/v0.1.3) +- [#5459](https://github.com/filecoin-project/lotus/pull/5459) Updated to [spec-actors@v3.0.1](https://github.com/filecoin-project/specs-actors/releases/tag/v3.0.1) ## Network Version v10 Upgrade @@ -242,7 +379,7 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5603](https://github.com/filecoin-project/lotus/pull/5603) Set nerpanet's upgrade epochs up to v3 actors - [#5471](https://github.com/filecoin-project/lotus/pull/5471), [#5456](https://github.com/filecoin-project/lotus/pull/5456) Set calibration net actor v3 migration epochs for testing - [#5434](https://github.com/filecoin-project/lotus/pull/5434) Implemented pre-migration framework -- [#5476](https://github.com/filecoin-project/lotus/pull/5477) Tune migration +- [#5476](https://github.com/filecoin-project/lotus/pull/5477) Tune migration # 1.4.1 / 2021-01-20 @@ -441,9 +578,9 @@ This is an optional Lotus release that introduces various improvements to the mi - Error out deals that are not activated by proposed deal start epoch (https://github.com/filecoin-project/lotus/pull/5061) # 1.2.1 / 2020-11-20 - + This is a very small release of Lotus that fixes an issue users are experiencing when importing snapshots. There is no need to upgrade unless you experience an issue with creating a new datastore directory in the Lotus repo. - + ## Changes - fix blockstore directory not created automatically (https://github.com/filecoin-project/lotus/pull/4922) @@ -463,7 +600,7 @@ The changes that break consensus are: - Correction of the VM circulating supply calculation (https://github.com/filecoin-project/lotus/pull/4862) - Retuning gas costs (https://github.com/filecoin-project/lotus/pull/4830) - Avoid sending messages to the zero BLS address (https://github.com/filecoin-project/lotus/pull/4888) - + ## Other Changes - delayed pubsub subscribe for messages topic (https://github.com/filecoin-project/lotus/pull/3646) @@ -620,7 +757,7 @@ This is an optional release of Lotus that upgrades Lotus dependencies, and inclu This is a patch release of Lotus that builds on the fixes involving worker keys that was introduced in v1.1.1. Miners and node operators should update to this release as soon as possible in order to ensure their blocks are propagated and validated. -## Changes +## Changes - Handle worker key changes correctly in runtime (https://github.com/filecoin-project/lotus/pull/4579) @@ -863,7 +1000,7 @@ This consensus-breaking release of Lotus upgrades the actors version to v2.0.0. - Fix pond (https://github.com/filecoin-project/lotus/pull/4203) - allow manual setting of noncefix fee cap (https://github.com/filecoin-project/lotus/pull/4205) - implement command to get execution traces of any message (https://github.com/filecoin-project/lotus/pull/4200) -- conformance: minor driver refactors (https://github.com/filecoin-project/lotus/pull/4211) +- conformance: minor driver refactors (https://github.com/filecoin-project/lotus/pull/4211) - lotus-pcr: ignore all other messages (https://github.com/filecoin-project/lotus/pull/4218) - lotus-pcr: zero refund (https://github.com/filecoin-project/lotus/pull/4229) @@ -890,7 +1027,7 @@ We are grateful for every contribution! This optional release of Lotus introduces a new version of markets which switches to CBOR-map encodings, and allows datastore migrations. The release also introduces several improvements to the mining process, a few performance optimizations, and a battery of UX additions and enhancements. -## Changes +## Changes #### Dependencies @@ -961,7 +1098,7 @@ This consensus-breaking release of Lotus introduces an upgrade to the network. T This release also updates go-fil-markets to fix an incompatibility issue between v0.7.2 and earlier versions. -## Changes +## Changes #### Dependencies @@ -1050,7 +1187,7 @@ This optional release of Lotus introduces some critical fixes to the window PoSt ## Changes -#### Some notable improvements: +#### Some notable improvements: - Correctly construct params for `SubmitWindowedPoSt` messages (https://github.com/filecoin-project/lotus/pull/3909) - Skip sectors correctly for Window PoSt (https://github.com/filecoin-project/lotus/pull/3839) @@ -1086,7 +1223,7 @@ This consensus-breaking release of Lotus is designed to test a network upgrade o - Drand upgrade (https://github.com/filecoin-project/lotus/pull/3670) - Multisig API additions (https://github.com/filecoin-project/lotus/pull/3590) -#### Storage Miner +#### Storage Miner - Increase the number of times precommit2 is attempted before moving back to precommit1 (https://github.com/filecoin-project/lotus/pull/3720) @@ -1129,7 +1266,7 @@ This release introduces some critical fixes to message selection and gas estimat ## Changes -#### Messagepool +#### Messagepool - Warn when optimal selection fails to pack a block and we fall back to random selection (https://github.com/filecoin-project/lotus/pull/3708) - Add basic command for printing gas performance of messages in the mpool (https://github.com/filecoin-project/lotus/pull/3701) @@ -1199,7 +1336,7 @@ This release also introduces many improvements to Lotus! Among them are a new ve - Add additional info about gas premium (https://github.com/filecoin-project/lotus/pull/3578) - Fix GasPremium capping logic (https://github.com/filecoin-project/lotus/pull/3552) -#### Payment channels +#### Payment channels - Get available funds by address or by from/to (https://github.com/filecoin-project/lotus/pull/3547) - Create `lotus paych status` command (https://github.com/filecoin-project/lotus/pull/3523) @@ -1249,7 +1386,7 @@ This patch includes a crucial fix to the message pool selection logic, strongly This patch includes a hotfix to the `GasEstimateFeeCap` method, capping the estimated fee to a reasonable level by default. -## Changes +## Changes - Added target height to sync wait (https://github.com/filecoin-project/lotus/pull/3502) - Disable codecov annotations (https://github.com/filecoin-project/lotus/pull/3514) @@ -1279,7 +1416,7 @@ This patch includes some bugfixes to the sector sealing process, and updates go- # 0.5.7 / 2020-08-31 -This patch release includes some bugfixes and enhancements to the sector lifecycle and message pool logic. +This patch release includes some bugfixes and enhancements to the sector lifecycle and message pool logic. ## Changes @@ -1299,7 +1436,7 @@ Hotfix release that fixes a panic in the sealing scheduler (https://github.com/f # 0.5.5 This patch release introduces a large number of improvements to the sealing process. -It also updates go-fil-markets to +It also updates go-fil-markets to [version 0.5.8](https://github.com/filecoin-project/go-fil-markets/releases/tag/v0.5.8), and go-libp2p-pubsub to [v0.3.5](https://github.com/libp2p/go-libp2p-pubsub/releases/tag/v0.3.5). @@ -1312,16 +1449,16 @@ and go-libp2p-pubsub to [v0.3.5](https://github.com/libp2p/go-libp2p-pubsub/rele - The following improvements were introduced in https://github.com/filecoin-project/lotus/pull/3350. - - Allow `lotus-miner sectors remove` to remove a sector in any state. - - Create a separate state in the storage FSM dedicated to submitting the Commit message. - - Recovery for when the Deal IDs of deals in a sector get changed in a reorg. - - Auto-retry sending Precommit and Commit messages if they run out of gas - - Auto-retry sector remove tasks when they fail - - Compact worker windows, and allow their tasks to be executed in any order + - Allow `lotus-miner sectors remove` to remove a sector in any state. + - Create a separate state in the storage FSM dedicated to submitting the Commit message. + - Recovery for when the Deal IDs of deals in a sector get changed in a reorg. + - Auto-retry sending Precommit and Commit messages if they run out of gas + - Auto-retry sector remove tasks when they fail + - Compact worker windows, and allow their tasks to be executed in any order - Don't simply skip PoSt for bad sectors (https://github.com/filecoin-project/lotus/pull/3323) -#### Message Pool +#### Message Pool - Spam Protection: Track required funds for pending messages (https://github.com/filecoin-project/lotus/pull/3313) @@ -1346,7 +1483,7 @@ A patch release, containing a few nice bugfixes and improvements: # 0.5.3 -Yet another hotfix release. +Yet another hotfix release. A lesson for readers, having people who have been awake for 12+ hours review your hotfix PR is not a good idea. Find someone who has enough slept recently enough to give you good code review, otherwise you'll end up quickly bumping @@ -1365,9 +1502,9 @@ This is a hotfix release. # 0.5.1 / 2020-08-24 -The Space Race release! +The Space Race release! This release contains the genesis car file and bootstrap peers for the space -race network. +race network. Additionally, we included two small fixes to genesis creation: - Randomize ticket value in genesis generation @@ -1385,9 +1522,9 @@ Among the highlights included in this release are: - Gas changes: We implemented EIP-1559 and introduced real gas values. - Deal-making: We now support "Committed Capacity" sectors, "fast-retrieval" deals, -and the packing of multiple deals into a single sector. + and the packing of multiple deals into a single sector. - Renamed features: We renamed some of the binaries, environment variables, and default -paths associated with a Lotus node. + paths associated with a Lotus node. ### Gas changes @@ -1395,19 +1532,19 @@ We made some significant changes to the mechanics of gas in this release. #### Network fee -We implemented something similar to +We implemented something similar to [Ethereum's EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md). The `Message` structure had three changes: - The `GasPrice` field has been removed - A new `GasFeeCap` field has been added, which controls the maximum cost -the sender incurs for the message + the sender incurs for the message - A new `GasPremium` field has been added, which controls the reward a miner -earns for including the message + earns for including the message -A sender will never be charged more than `GasFeeCap * GasLimit`. +A sender will never be charged more than `GasFeeCap * GasLimit`. A miner will typically earn `GasPremium * GasLimit` as a reward. -The `Blockheader` structure has one new field, called `ParentBaseFee`. +The `Blockheader` structure has one new field, called `ParentBaseFee`. Informally speaking,the `ParentBaseFee` is increased when blocks are densely packed with messages, and decreased otherwise. diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 3db17a0a0..dc4e4e8dc 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 3db17a0a0f24ce6a04e946f86bf18b0e1d8c0007 +Subproject commit dc4e4e8dc9554dedb6f48304f7f0c6328331f9ec