Merge branch 'master' into sbansal/nonce-coordination-and-consensus-for-chain-nodes
This commit is contained in:
+3
-2
@@ -62,6 +62,7 @@ var ChainCmd = &cli.Command{
|
||||
ChainDecodeCmd,
|
||||
ChainEncodeCmd,
|
||||
ChainDisputeSetCmd,
|
||||
ChainPruneCmd,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -494,8 +495,8 @@ var ChainInspectUsage = &cli.Command{
|
||||
|
||||
mm := filcns.NewActorRegistry().Methods[code][m.Message.Method] // TODO: use remote map
|
||||
|
||||
byMethod[mm.Num] += m.Message.GasLimit
|
||||
byMethodC[mm.Num]++
|
||||
byMethod[mm.Name] += m.Message.GasLimit
|
||||
byMethodC[mm.Name]++
|
||||
}
|
||||
|
||||
type keyGasPair struct {
|
||||
|
||||
+1
-2
@@ -327,8 +327,7 @@ func TestInspectUsage(t *testing.T) {
|
||||
// check for gas by sender
|
||||
assert.Contains(t, out, "By Sender")
|
||||
// check for gas by method
|
||||
methodStr := fmt.Sprintf("By Method:\n%d", builtin.MethodSend)
|
||||
assert.Contains(t, out, methodStr)
|
||||
assert.Contains(t, out, "By Method:\nSend")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+4
-63
@@ -3,14 +3,9 @@ package cli
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -29,8 +24,6 @@ import (
|
||||
"github.com/ipld/go-ipld-prime/traversal/selector/builder"
|
||||
selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse"
|
||||
textselector "github.com/ipld/go-ipld-selector-text-lite"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
@@ -40,6 +33,7 @@ import (
|
||||
|
||||
lapi "github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cliutil "github.com/filecoin-project/lotus/cli/util"
|
||||
"github.com/filecoin-project/lotus/markets/utils"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
@@ -337,60 +331,6 @@ Examples:
|
||||
},
|
||||
}
|
||||
|
||||
func ClientExportStream(apiAddr string, apiAuth http.Header, eref lapi.ExportRef, car bool) (io.ReadCloser, error) {
|
||||
rj, err := json.Marshal(eref)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("marshaling export ref: %w", err)
|
||||
}
|
||||
|
||||
ma, err := multiaddr.NewMultiaddr(apiAddr)
|
||||
if err == nil {
|
||||
_, addr, err := manet.DialArgs(ma)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// todo: make cliutil helpers for this
|
||||
apiAddr = "http://" + addr
|
||||
}
|
||||
|
||||
aa, err := url.Parse(apiAddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parsing api address: %w", err)
|
||||
}
|
||||
switch aa.Scheme {
|
||||
case "ws":
|
||||
aa.Scheme = "http"
|
||||
case "wss":
|
||||
aa.Scheme = "https"
|
||||
}
|
||||
|
||||
aa.Path = path.Join(aa.Path, "rest/v0/export")
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s?car=%t&export=%s", aa, car, url.QueryEscape(string(rj))), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header = apiAuth
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
em, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("reading error body: %w", err)
|
||||
}
|
||||
|
||||
resp.Body.Close() // nolint
|
||||
return nil, xerrors.Errorf("getting root car: http %d: %s", resp.StatusCode, string(em))
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
var clientRetrieveCatCmd = &cli.Command{
|
||||
Name: "cat",
|
||||
Usage: "Show data from network",
|
||||
@@ -440,7 +380,7 @@ var clientRetrieveCatCmd = &cli.Command{
|
||||
eref.DAGs = append(eref.DAGs, lapi.DagSpec{DataSelector: &sel})
|
||||
}
|
||||
|
||||
rc, err := ClientExportStream(ainfo.Addr, ainfo.AuthHeader(), *eref, false)
|
||||
rc, err := cliutil.ClientExportStream(ainfo.Addr, ainfo.AuthHeader(), *eref, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -528,7 +468,7 @@ var clientRetrieveLsCmd = &cli.Command{
|
||||
DataSelector: &dataSelector,
|
||||
})
|
||||
|
||||
rc, err := ClientExportStream(ainfo.Addr, ainfo.AuthHeader(), *eref, true)
|
||||
rc, err := cliutil.ClientExportStream(ainfo.Addr, ainfo.AuthHeader(), *eref, true)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("export: %w", err)
|
||||
}
|
||||
@@ -583,6 +523,7 @@ var clientRetrieveLsCmd = &cli.Command{
|
||||
dserv,
|
||||
roots[0],
|
||||
sel,
|
||||
nil,
|
||||
func(p traversal.Progress, n ipld.Node, r traversal.VisitReason) error {
|
||||
if r == traversal.VisitReason_SelectionMatch {
|
||||
fmt.Println(p.Path)
|
||||
|
||||
+234
-16
@@ -5,6 +5,8 @@ import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -12,6 +14,7 @@ import (
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
actorstypes "github.com/filecoin-project/go-state-types/actors"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
verifregtypes8 "github.com/filecoin-project/go-state-types/builtin/v8/verifreg"
|
||||
verifregtypes9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg"
|
||||
@@ -22,8 +25,10 @@ import (
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors"
|
||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/datacap"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/lib/tablewriter"
|
||||
)
|
||||
|
||||
var filplusCmd = &cli.Command{
|
||||
@@ -37,12 +42,15 @@ var filplusCmd = &cli.Command{
|
||||
filplusCheckClientCmd,
|
||||
filplusCheckNotaryCmd,
|
||||
filplusSignRemoveDataCapProposal,
|
||||
filplusListAllocationsCmd,
|
||||
filplusRemoveExpiredAllocationsCmd,
|
||||
},
|
||||
}
|
||||
|
||||
var filplusVerifyClientCmd = &cli.Command{
|
||||
Name: "grant-datacap",
|
||||
Usage: "give allowance to the specified verified client address",
|
||||
Name: "grant-datacap",
|
||||
Usage: "give allowance to the specified verified client address",
|
||||
ArgsUsage: "[clientAddress datacap]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
@@ -132,6 +140,10 @@ var filplusListNotariesCmd = &cli.Command{
|
||||
Name: "list-notaries",
|
||||
Usage: "list all notaries",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.NArg() != 0 {
|
||||
return IncorrectNumArgs(cctx)
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -162,6 +174,10 @@ var filplusListClientsCmd = &cli.Command{
|
||||
Name: "list-clients",
|
||||
Usage: "list all verified clients",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.NArg() != 0 {
|
||||
return IncorrectNumArgs(cctx)
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -169,15 +185,40 @@ var filplusListClientsCmd = &cli.Command{
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK)
|
||||
apibs := blockstore.NewAPIBlockstore(api)
|
||||
store := adt.WrapStore(ctx, cbor.NewCborStore(apibs))
|
||||
|
||||
nv, err := api.StateNetworkVersion(ctx, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apibs := blockstore.NewAPIBlockstore(api)
|
||||
store := adt.WrapStore(ctx, cbor.NewCborStore(apibs))
|
||||
av, err := actorstypes.VersionForNetwork(nv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
st, err := verifreg.Load(store, act)
|
||||
if av <= 8 {
|
||||
act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
st, err := verifreg.Load(store, act)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return st.ForEachClient(func(addr address.Address, dcap abi.StoragePower) error {
|
||||
_, err := fmt.Printf("%s: %s\n", addr, dcap)
|
||||
return err
|
||||
})
|
||||
}
|
||||
act, err := api.StateGetActor(ctx, datacap.Address, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
st, err := datacap.Load(store, act)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -188,11 +229,186 @@ var filplusListClientsCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var filplusCheckClientCmd = &cli.Command{
|
||||
Name: "check-client-datacap",
|
||||
Usage: "check verified client remaining bytes",
|
||||
var filplusListAllocationsCmd = &cli.Command{
|
||||
Name: "list-allocations",
|
||||
Usage: "List allocations made by client",
|
||||
ArgsUsage: "clientAddress",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "expired",
|
||||
Usage: "list only expired allocations",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
if cctx.NArg() != 1 {
|
||||
return IncorrectNumArgs(cctx)
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
clientAddr, err := address.NewFromString(cctx.Args().Get(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientIdAddr, err := api.StateLookupID(ctx, clientAddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store := adt.WrapStore(ctx, cbor.NewCborStore(blockstore.NewAPIBlockstore(api)))
|
||||
|
||||
verifregActor, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
verifregState, err := verifreg.Load(store, verifregActor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ts, err := api.ChainHead(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allocationsMap, err := verifregState.GetAllocations(clientIdAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tw := tablewriter.New(
|
||||
tablewriter.Col("ID"),
|
||||
tablewriter.Col("Provider"),
|
||||
tablewriter.Col("Data"),
|
||||
tablewriter.Col("Size"),
|
||||
tablewriter.Col("TermMin"),
|
||||
tablewriter.Col("TermMax"),
|
||||
tablewriter.Col("Expiration"),
|
||||
)
|
||||
|
||||
for allocationId, allocation := range allocationsMap {
|
||||
if ts.Height() > allocation.Expiration || !cctx.IsSet("expired") {
|
||||
tw.Write(map[string]interface{}{
|
||||
"ID": allocationId,
|
||||
"Provider": allocation.Provider,
|
||||
"Data": allocation.Data,
|
||||
"Size": allocation.Size,
|
||||
"TermMin": allocation.TermMin,
|
||||
"TermMax": allocation.TermMax,
|
||||
"Expiration": allocation.Expiration,
|
||||
})
|
||||
}
|
||||
}
|
||||
return tw.Flush(os.Stdout)
|
||||
},
|
||||
}
|
||||
|
||||
var filplusRemoveExpiredAllocationsCmd = &cli.Command{
|
||||
Name: "remove-expired-allocations",
|
||||
Usage: "remove expired allocations (if no allocations are specified all eligible allocations are removed)",
|
||||
ArgsUsage: "clientAddress Optional[...allocationId]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "from",
|
||||
Usage: "optionally specify the account to send the message from",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.NArg() < 1 {
|
||||
return IncorrectNumArgs(cctx)
|
||||
}
|
||||
|
||||
api, closer, err := GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := ReqContext(cctx)
|
||||
|
||||
args := cctx.Args().Slice()
|
||||
|
||||
clientAddr, err := address.NewFromString(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientIdAddr, err := api.StateLookupID(ctx, clientAddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientId, err := address.IDFromAddress(clientIdAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fromAddr := clientIdAddr
|
||||
if from := cctx.String("from"); from != "" {
|
||||
addr, err := address.NewFromString(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fromAddr = addr
|
||||
}
|
||||
|
||||
allocationIDs := make([]verifregtypes9.AllocationId, len(args)-1)
|
||||
for i, allocationString := range args[1:] {
|
||||
id, err := strconv.ParseUint(allocationString, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allocationIDs[i] = verifregtypes9.AllocationId(id)
|
||||
}
|
||||
|
||||
params, err := actors.SerializeParams(&verifregtypes9.RemoveExpiredAllocationsParams{
|
||||
Client: abi.ActorID(clientId),
|
||||
AllocationIds: allocationIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := &types.Message{
|
||||
To: verifreg.Address,
|
||||
From: fromAddr,
|
||||
Method: verifreg.Methods.RemoveExpiredAllocations,
|
||||
Params: params,
|
||||
}
|
||||
|
||||
smsg, err := api.MpoolPushMessage(ctx, msg, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("message sent, now waiting on cid: %s\n", smsg.Cid())
|
||||
|
||||
mwait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mwait.Receipt.ExitCode.IsError() {
|
||||
return fmt.Errorf("failed to remove expired allocations: %d", mwait.Receipt.ExitCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var filplusCheckClientCmd = &cli.Command{
|
||||
Name: "check-client-datacap",
|
||||
Usage: "check verified client remaining bytes",
|
||||
ArgsUsage: "clientAddress",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if cctx.NArg() != 1 {
|
||||
return fmt.Errorf("must specify client address to check")
|
||||
}
|
||||
|
||||
@@ -223,10 +439,11 @@ var filplusCheckClientCmd = &cli.Command{
|
||||
}
|
||||
|
||||
var filplusCheckNotaryCmd = &cli.Command{
|
||||
Name: "check-notary-datacap",
|
||||
Usage: "check a notary's remaining bytes",
|
||||
Name: "check-notary-datacap",
|
||||
Usage: "check a notary's remaining bytes",
|
||||
ArgsUsage: "notaryAddress",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Args().Present() {
|
||||
if cctx.NArg() != 1 {
|
||||
return fmt.Errorf("must specify notary address to check")
|
||||
}
|
||||
|
||||
@@ -279,8 +496,9 @@ func checkNotary(ctx context.Context, api v0api.FullNode, vaddr address.Address)
|
||||
}
|
||||
|
||||
var filplusSignRemoveDataCapProposal = &cli.Command{
|
||||
Name: "sign-remove-data-cap-proposal",
|
||||
Usage: "allows a notary to sign a Remove Data Cap Proposal",
|
||||
Name: "sign-remove-data-cap-proposal",
|
||||
Usage: "allows a notary to sign a Remove Data Cap Proposal",
|
||||
ArgsUsage: "[verifierAddress clientAddress allowanceToRemove]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.Int64Flag{
|
||||
Name: "id",
|
||||
@@ -336,7 +554,7 @@ var filplusSignRemoveDataCapProposal = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
_, dataCap, err := st.VerifiedClientDataCap(clientIdAddr)
|
||||
dataCap, err := api.StateVerifiedClientStatus(ctx, clientIdAddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to find verified client data cap: %w", err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -341,7 +341,7 @@ var msigInspectCmd = &cli.Command{
|
||||
paramStr = string(b)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%d\t%s\t%d\t%s\t%s\t%s(%d)\t%s\n", txid, "pending", len(tx.Approved), target, types.FIL(tx.Value), method.Num, tx.Method, paramStr)
|
||||
fmt.Fprintf(w, "%d\t%s\t%d\t%s\t%s\t%s(%d)\t%s\n", txid, "pending", len(tx.Approved), target, types.FIL(tx.Value), method.Name, tx.Method, paramStr)
|
||||
}
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
|
||||
+5
-4
@@ -46,6 +46,7 @@ import (
|
||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
cliutil "github.com/filecoin-project/lotus/cli/util"
|
||||
)
|
||||
|
||||
var StateCmd = &cli.Command{
|
||||
@@ -230,7 +231,7 @@ var StateMinerInfo = &cli.Command{
|
||||
return xerrors.Errorf("getting miner info: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Proving Period Start:\t%s\n", EpochTime(cd.CurrentEpoch, cd.PeriodStart))
|
||||
fmt.Printf("Proving Period Start:\t%s\n", cliutil.EpochTime(cd.CurrentEpoch, cd.PeriodStart))
|
||||
|
||||
return nil
|
||||
},
|
||||
@@ -1407,7 +1408,7 @@ func codeStr(c cid.Cid) string {
|
||||
}
|
||||
|
||||
func getMethod(code cid.Cid, method abi.MethodNum) string {
|
||||
return filcns.NewActorRegistry().Methods[code][method].Num // todo: use remote
|
||||
return filcns.NewActorRegistry().Methods[code][method].Name // todo: use remote
|
||||
}
|
||||
|
||||
func toFil(f types.BigInt) types.FIL {
|
||||
@@ -1816,8 +1817,8 @@ var StateSectorCmd = &cli.Command{
|
||||
}
|
||||
fmt.Println("DealIDs: ", si.DealIDs)
|
||||
fmt.Println()
|
||||
fmt.Println("Activation: ", EpochTimeTs(ts.Height(), si.Activation, ts))
|
||||
fmt.Println("Expiration: ", EpochTimeTs(ts.Height(), si.Expiration, ts))
|
||||
fmt.Println("Activation: ", cliutil.EpochTimeTs(ts.Height(), si.Activation, ts))
|
||||
fmt.Println("Expiration: ", cliutil.EpochTimeTs(ts.Height(), si.Expiration, ts))
|
||||
fmt.Println()
|
||||
fmt.Println("DealWeight: ", si.DealWeight)
|
||||
fmt.Println("VerifiedDealWeight: ", si.VerifiedDealWeight)
|
||||
|
||||
-39
@@ -2,19 +2,13 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/hako/durafmt"
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/mattn/go-isatty"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/api/v0api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
@@ -43,36 +37,3 @@ func parseTipSet(ctx context.Context, api v0api.FullNode, vals []string) (*types
|
||||
|
||||
return types.NewTipSet(headers)
|
||||
}
|
||||
|
||||
func EpochTime(curr, e abi.ChainEpoch) string {
|
||||
switch {
|
||||
case curr > e:
|
||||
return fmt.Sprintf("%d (%s ago)", e, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e))).LimitFirstN(2))
|
||||
case curr == e:
|
||||
return fmt.Sprintf("%d (now)", e)
|
||||
case curr < e:
|
||||
return fmt.Sprintf("%d (in %s)", e, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr))).LimitFirstN(2))
|
||||
}
|
||||
|
||||
panic("math broke")
|
||||
}
|
||||
|
||||
// EpochTimeTs is like EpochTime, but also outputs absolute time. `ts` is only
|
||||
// used to provide a timestamp at some epoch to calculate time from. It can be
|
||||
// a genesis tipset.
|
||||
//
|
||||
// Example output: `1944975 (01 Jul 22 08:07 CEST, 10 hours 29 minutes ago)`
|
||||
func EpochTimeTs(curr, e abi.ChainEpoch, ts *types.TipSet) string {
|
||||
timeStr := time.Unix(int64(ts.MinTimestamp()+(uint64(e-ts.Height())*build.BlockDelaySecs)), 0).Format(time.RFC822)
|
||||
|
||||
switch {
|
||||
case curr > e:
|
||||
return fmt.Sprintf("%d (%s, %s ago)", e, timeStr, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e))).LimitFirstN(2))
|
||||
case curr == e:
|
||||
return fmt.Sprintf("%d (%s, now)", e, timeStr)
|
||||
case curr < e:
|
||||
return fmt.Sprintf("%d (%s, in %s)", e, timeStr, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr))).LimitFirstN(2))
|
||||
}
|
||||
|
||||
panic("math broke")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package cliutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hako/durafmt"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
func EpochTime(curr, e abi.ChainEpoch) string {
|
||||
switch {
|
||||
case curr > e:
|
||||
return fmt.Sprintf("%d (%s ago)", e, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e))).LimitFirstN(2))
|
||||
case curr == e:
|
||||
return fmt.Sprintf("%d (now)", e)
|
||||
case curr < e:
|
||||
return fmt.Sprintf("%d (in %s)", e, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr))).LimitFirstN(2))
|
||||
}
|
||||
|
||||
panic("math broke")
|
||||
}
|
||||
|
||||
// EpochTimeTs is like EpochTime, but also outputs absolute time. `ts` is only
|
||||
// used to provide a timestamp at some epoch to calculate time from. It can be
|
||||
// a genesis tipset.
|
||||
//
|
||||
// Example output: `1944975 (01 Jul 22 08:07 CEST, 10 hours 29 minutes ago)`
|
||||
func EpochTimeTs(curr, e abi.ChainEpoch, ts *types.TipSet) string {
|
||||
timeStr := time.Unix(int64(ts.MinTimestamp()+(uint64(e-ts.Height())*build.BlockDelaySecs)), 0).Format(time.RFC822)
|
||||
|
||||
switch {
|
||||
case curr > e:
|
||||
return fmt.Sprintf("%d (%s, %s ago)", e, timeStr, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(curr-e))).LimitFirstN(2))
|
||||
case curr == e:
|
||||
return fmt.Sprintf("%d (%s, now)", e, timeStr)
|
||||
case curr < e:
|
||||
return fmt.Sprintf("%d (%s, in %s)", e, timeStr, durafmt.Parse(time.Second*time.Duration(int64(build.BlockDelaySecs)*int64(e-curr))).LimitFirstN(2))
|
||||
}
|
||||
|
||||
panic("math broke")
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package cliutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
)
|
||||
|
||||
func ApiAddrToUrl(apiAddr string) (*url.URL, error) {
|
||||
ma, err := multiaddr.NewMultiaddr(apiAddr)
|
||||
if err == nil {
|
||||
_, addr, err := manet.DialArgs(ma)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// todo: make cliutil helpers for this
|
||||
apiAddr = "http://" + addr
|
||||
}
|
||||
aa, err := url.Parse(apiAddr)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("parsing api address: %w", err)
|
||||
}
|
||||
switch aa.Scheme {
|
||||
case "ws":
|
||||
aa.Scheme = "http"
|
||||
case "wss":
|
||||
aa.Scheme = "https"
|
||||
}
|
||||
|
||||
return aa, nil
|
||||
}
|
||||
|
||||
func ClientExportStream(apiAddr string, apiAuth http.Header, eref api.ExportRef, car bool) (io.ReadCloser, error) {
|
||||
rj, err := json.Marshal(eref)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("marshaling export ref: %w", err)
|
||||
}
|
||||
|
||||
aa, err := ApiAddrToUrl(apiAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aa.Path = path.Join(aa.Path, "rest/v0/export")
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s?car=%t&export=%s", aa, car, url.QueryEscape(string(rj))), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header = apiAuth
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
em, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("reading error body: %w", err)
|
||||
}
|
||||
|
||||
resp.Body.Close() // nolint
|
||||
return nil, xerrors.Errorf("getting root car: http %d: %s", resp.StatusCode, string(em))
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
Reference in New Issue
Block a user