Merge branch 'master' into chore/snake_context_through_blockstore_init
This commit is contained in:
+24
-2
@@ -27,6 +27,16 @@ func main() {
|
||||
Hidden: true,
|
||||
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "limit",
|
||||
Usage: "spam transaction count limit, <= 0 is no limit",
|
||||
Value: 0,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "rate",
|
||||
Usage: "spam transaction rate, count per second",
|
||||
Value: 5,
|
||||
},
|
||||
},
|
||||
Commands: []*cli.Command{runCmd},
|
||||
}
|
||||
@@ -52,11 +62,17 @@ var runCmd = &cli.Command{
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
return sendSmallFundsTxs(ctx, api, addr, 5)
|
||||
rate := cctx.Int("rate")
|
||||
if rate <= 0 {
|
||||
rate = 5
|
||||
}
|
||||
limit := cctx.Int("limit")
|
||||
|
||||
return sendSmallFundsTxs(ctx, api, addr, rate, limit)
|
||||
},
|
||||
}
|
||||
|
||||
func sendSmallFundsTxs(ctx context.Context, api api.FullNode, from address.Address, rate int) error {
|
||||
func sendSmallFundsTxs(ctx context.Context, api api.FullNode, from address.Address, rate, limit int) error {
|
||||
var sendSet []address.Address
|
||||
for i := 0; i < 20; i++ {
|
||||
naddr, err := api.WalletNew(ctx, types.KTSecp256k1)
|
||||
@@ -66,9 +82,14 @@ func sendSmallFundsTxs(ctx context.Context, api api.FullNode, from address.Addre
|
||||
|
||||
sendSet = append(sendSet, naddr)
|
||||
}
|
||||
count := limit
|
||||
|
||||
tick := build.Clock.Ticker(time.Second / time.Duration(rate))
|
||||
for {
|
||||
if count <= 0 && limit > 0 {
|
||||
fmt.Printf("%d messages sent.\n", limit)
|
||||
return nil
|
||||
}
|
||||
select {
|
||||
case <-tick.C:
|
||||
msg := &types.Message{
|
||||
@@ -81,6 +102,7 @@ func sendSmallFundsTxs(ctx context.Context, api api.FullNode, from address.Addre
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count--
|
||||
fmt.Println("Message sent: ", smsg.Cid())
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
|
||||
@@ -40,6 +40,8 @@ import (
|
||||
var log = logging.Logger("lotus-bench")
|
||||
|
||||
type BenchResults struct {
|
||||
EnvVar map[string]string
|
||||
|
||||
SectorSize abi.SectorSize
|
||||
SectorNumber int
|
||||
|
||||
@@ -446,6 +448,15 @@ var sealBenchCmd = &cli.Command{
|
||||
bo.VerifyWindowPostHot = verifyWindowpost2.Sub(verifyWindowpost1)
|
||||
}
|
||||
|
||||
bo.EnvVar = make(map[string]string)
|
||||
for _, envKey := range []string{"BELLMAN_NO_GPU", "FIL_PROOFS_MAXIMIZE_CACHING", "FIL_PROOFS_USE_GPU_COLUMN_BUILDER",
|
||||
"FIL_PROOFS_USE_GPU_TREE_BUILDER", "FIL_PROOFS_USE_MULTICORE_SDR", "BELLMAN_CUSTOM_GPU"} {
|
||||
envValue, found := os.LookupEnv(envKey)
|
||||
if found {
|
||||
bo.EnvVar[envKey] = envValue
|
||||
}
|
||||
}
|
||||
|
||||
if c.Bool("json-out") {
|
||||
data, err := json.MarshalIndent(bo, "", " ")
|
||||
if err != nil {
|
||||
@@ -454,6 +465,10 @@ var sealBenchCmd = &cli.Command{
|
||||
|
||||
fmt.Println(string(data))
|
||||
} else {
|
||||
fmt.Println("environment variable list:")
|
||||
for envKey, envValue := range bo.EnvVar {
|
||||
fmt.Printf("%s=%s\n", envKey, envValue)
|
||||
}
|
||||
fmt.Printf("----\nresults (v28) SectorSize:(%d), SectorNumber:(%d)\n", sectorSize, sectorNumber)
|
||||
if robench == "" {
|
||||
fmt.Printf("seal: addPiece: %s (%s)\n", bo.SealingSum.AddPiece, bps(bo.SectorSize, bo.SectorNumber, bo.SealingSum.AddPiece))
|
||||
|
||||
@@ -57,6 +57,7 @@ type gatewayDepsAPI interface {
|
||||
StateMarketBalance(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MarketBalance, error)
|
||||
StateMarketStorageDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*api.MarketDeal, error)
|
||||
StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error)
|
||||
StateSearchMsgLimited(ctx context.Context, msg cid.Cid, lookbackLimit abi.ChainEpoch) (*api.MsgLookup, error)
|
||||
StateWaitMsgLimited(ctx context.Context, msg cid.Cid, confidence uint64, h abi.ChainEpoch) (*api.MsgLookup, error)
|
||||
StateReadState(ctx context.Context, actor address.Address, tsk types.TipSetKey) (*api.ActorState, error)
|
||||
StateMinerPower(context.Context, address.Address, types.TipSetKey) (*api.MinerPower, error)
|
||||
@@ -299,6 +300,10 @@ func (a *GatewayAPI) StateNetworkVersion(ctx context.Context, tsk types.TipSetKe
|
||||
return a.api.StateNetworkVersion(ctx, tsk)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateSearchMsg(ctx context.Context, msg cid.Cid) (*api.MsgLookup, error) {
|
||||
return a.api.StateSearchMsgLimited(ctx, msg, a.stateWaitLookbackLimit)
|
||||
}
|
||||
|
||||
func (a *GatewayAPI) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) {
|
||||
return a.api.StateWaitMsgLimited(ctx, msg, confidence, a.stateWaitLookbackLimit)
|
||||
}
|
||||
|
||||
@@ -622,8 +622,8 @@ var actorControlSet = &cli.Command{
|
||||
|
||||
var actorSetOwnerCmd = &cli.Command{
|
||||
Name: "set-owner",
|
||||
Usage: "Set owner address",
|
||||
ArgsUsage: "[address]",
|
||||
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.BoolFlag{
|
||||
Name: "really-do-it",
|
||||
@@ -637,8 +637,8 @@ var actorSetOwnerCmd = &cli.Command{
|
||||
return nil
|
||||
}
|
||||
|
||||
if !cctx.Args().Present() {
|
||||
return fmt.Errorf("must pass address of new owner address")
|
||||
if cctx.NArg() != 2 {
|
||||
return fmt.Errorf("must pass new owner address and sender address")
|
||||
}
|
||||
|
||||
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
@@ -660,7 +660,17 @@ var actorSetOwnerCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK)
|
||||
newAddrId, err := api.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 := api.StateLookupID(ctx, fa, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -675,13 +685,17 @@ var actorSetOwnerCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
sp, err := actors.SerializeParams(&newAddr)
|
||||
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 := api.MpoolPushMessage(ctx, &types.Message{
|
||||
From: mi.Owner,
|
||||
From: fromAddrId,
|
||||
To: maddr,
|
||||
Method: miner.Methods.ChangeOwnerAddress,
|
||||
Value: big.Zero(),
|
||||
@@ -691,7 +705,7 @@ var actorSetOwnerCmd = &cli.Command{
|
||||
return xerrors.Errorf("mpool push: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Propose Message CID:", smsg.Cid())
|
||||
fmt.Println("Message CID:", smsg.Cid())
|
||||
|
||||
// wait for it to get mined into a block
|
||||
wait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence)
|
||||
@@ -701,34 +715,11 @@ var actorSetOwnerCmd = &cli.Command{
|
||||
|
||||
// check it executed successfully
|
||||
if wait.Receipt.ExitCode != 0 {
|
||||
fmt.Println("Propose owner change failed!")
|
||||
fmt.Println("owner change failed!")
|
||||
return err
|
||||
}
|
||||
|
||||
smsg, err = api.MpoolPushMessage(ctx, &types.Message{
|
||||
From: newAddr,
|
||||
To: maddr,
|
||||
Method: miner.Methods.ChangeOwnerAddress,
|
||||
Value: big.Zero(),
|
||||
Params: sp,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("mpool push: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Approve Message CID:", smsg.Cid())
|
||||
|
||||
// wait for it to get mined into a block
|
||||
wait, err = api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check it executed successfully
|
||||
if wait.Receipt.ExitCode != 0 {
|
||||
fmt.Println("Approve owner change failed!")
|
||||
return err
|
||||
}
|
||||
fmt.Println("message succeeded!")
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -222,7 +222,7 @@ func infoCmdAct(cctx *cli.Context) error {
|
||||
fmt.Printf(" PreCommit: %s\n", types.FIL(lockedFunds.PreCommitDeposits).Short())
|
||||
fmt.Printf(" Pledge: %s\n", types.FIL(lockedFunds.InitialPledgeRequirement).Short())
|
||||
fmt.Printf(" Vesting: %s\n", types.FIL(lockedFunds.VestingFunds).Short())
|
||||
color.Green(" Available: %s", types.FIL(availBalance).Short())
|
||||
colorTokenAmount(" Available: %s\n", availBalance)
|
||||
|
||||
mb, err := api.StateMarketBalance(ctx, maddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
@@ -232,7 +232,7 @@ func infoCmdAct(cctx *cli.Context) error {
|
||||
|
||||
fmt.Printf("Market Balance: %s\n", types.FIL(mb.Escrow).Short())
|
||||
fmt.Printf(" Locked: %s\n", types.FIL(mb.Locked).Short())
|
||||
color.Green(" Available: %s\n", types.FIL(big.Sub(mb.Escrow, mb.Locked)).Short())
|
||||
colorTokenAmount(" Available: %s\n", big.Sub(mb.Escrow, mb.Locked))
|
||||
|
||||
wb, err := api.WalletBalance(ctx, mi.Worker)
|
||||
if err != nil {
|
||||
@@ -253,7 +253,7 @@ func infoCmdAct(cctx *cli.Context) error {
|
||||
|
||||
fmt.Printf(" Control: %s\n", types.FIL(cbsum).Short())
|
||||
}
|
||||
fmt.Printf("Total Spendable: %s\n", color.YellowString(types.FIL(spendable).Short()))
|
||||
colorTokenAmount("Total Spendable: %s\n", spendable)
|
||||
|
||||
fmt.Println()
|
||||
|
||||
@@ -298,6 +298,10 @@ var stateList = []stateMeta{
|
||||
{col: color.FgYellow, state: sealing.CommitWait},
|
||||
{col: color.FgYellow, state: sealing.FinalizeSector},
|
||||
|
||||
{col: color.FgCyan, state: sealing.Terminating},
|
||||
{col: color.FgCyan, state: sealing.TerminateWait},
|
||||
{col: color.FgCyan, state: sealing.TerminateFinality},
|
||||
{col: color.FgCyan, state: sealing.TerminateFailed},
|
||||
{col: color.FgCyan, state: sealing.Removing},
|
||||
{col: color.FgCyan, state: sealing.Removed},
|
||||
|
||||
@@ -355,3 +359,13 @@ func sectorsInfo(ctx context.Context, napi api.StorageMiner) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func colorTokenAmount(format string, amount abi.TokenAmount) {
|
||||
if amount.GreaterThan(big.Zero()) {
|
||||
color.Green(format, types.FIL(amount).Short())
|
||||
} else if amount.Equals(big.Zero()) {
|
||||
color.Yellow(format, types.FIL(amount).Short())
|
||||
} else {
|
||||
color.Red(format, types.FIL(amount).Short())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +451,7 @@ func outputStorageDeals(out io.Writer, deals []storagemarket.MinerDeal, verbose
|
||||
w := tabwriter.NewWriter(out, 2, 4, 2, ' ', 0)
|
||||
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "Creation\tProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\tTransferChannelID\tMessage\n")
|
||||
_, _ = fmt.Fprintf(w, "Creation\tVerified\tProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\tTransferChannelID\tMessage\n")
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(w, "ProposalCid\tDealId\tState\tClient\tSize\tPrice\tDuration\n")
|
||||
}
|
||||
@@ -465,7 +465,7 @@ func outputStorageDeals(out io.Writer, deals []storagemarket.MinerDeal, verbose
|
||||
fil := types.FIL(types.BigMul(deal.Proposal.StoragePricePerEpoch, types.NewInt(uint64(deal.Proposal.Duration()))))
|
||||
|
||||
if verbose {
|
||||
_, _ = fmt.Fprintf(w, "%s\t", deal.CreationTime.Time().Format(time.Stamp))
|
||||
_, _ = fmt.Fprintf(w, "%s\t%t\t", deal.CreationTime.Time().Format(time.Stamp), deal.Proposal.VerifiedDeal)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s\t%s", propcid, deal.DealID, storagemarket.DealStates[deal.State], deal.Proposal.Client, units.BytesSize(float64(deal.Proposal.PieceSize)), fil, deal.Proposal.Duration())
|
||||
@@ -744,6 +744,11 @@ var transfersListCmd = &cli.Command{
|
||||
Name: "list",
|
||||
Usage: "List ongoing data transfers for this miner",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "print verbose transfer details",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "color",
|
||||
Usage: "use color in display output",
|
||||
@@ -775,6 +780,7 @@ var transfersListCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
verbose := cctx.Bool("verbose")
|
||||
completed := cctx.Bool("completed")
|
||||
color := cctx.Bool("color")
|
||||
watch := cctx.Bool("watch")
|
||||
@@ -790,7 +796,7 @@ var transfersListCmd = &cli.Command{
|
||||
|
||||
tm.MoveCursor(1, 1)
|
||||
|
||||
lcli.OutputDataTransferChannels(tm.Screen, channels, completed, color, showFailed)
|
||||
lcli.OutputDataTransferChannels(tm.Screen, channels, verbose, completed, color, showFailed)
|
||||
|
||||
tm.Flush()
|
||||
|
||||
@@ -815,7 +821,7 @@ var transfersListCmd = &cli.Command{
|
||||
}
|
||||
}
|
||||
}
|
||||
lcli.OutputDataTransferChannels(os.Stdout, channels, completed, color, showFailed)
|
||||
lcli.OutputDataTransferChannels(os.Stdout, channels, verbose, completed, color, showFailed)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ var provingCheckProvableCmd = &cli.Command{
|
||||
for parIdx, par := range partitions {
|
||||
sectors := make(map[abi.SectorNumber]struct{})
|
||||
|
||||
sectorInfos, err := api.StateMinerSectors(ctx, addr, &par.AllSectors, types.EmptyTSK)
|
||||
sectorInfos, err := api.StateMinerSectors(ctx, addr, &par.LiveSectors, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ var sectorsCmd = &cli.Command{
|
||||
sectorsRefsCmd,
|
||||
sectorsUpdateCmd,
|
||||
sectorsPledgeCmd,
|
||||
sectorsTerminateCmd,
|
||||
sectorsRemoveCmd,
|
||||
sectorsMarkForUpgradeCmd,
|
||||
sectorsStartSealCmd,
|
||||
@@ -396,9 +397,123 @@ var sectorsRefsCmd = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var sectorsTerminateCmd = &cli.Command{
|
||||
Name: "terminate",
|
||||
Usage: "Terminate sector on-chain then remove (WARNING: This means losing power and collateral for the removed sector)",
|
||||
ArgsUsage: "<sectorNum>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "really-do-it",
|
||||
Usage: "pass this flag if you know what you are doing",
|
||||
},
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
sectorsTerminateFlushCmd,
|
||||
sectorsTerminatePendingCmd,
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
if !cctx.Bool("really-do-it") {
|
||||
return xerrors.Errorf("pass --really-do-it to confirm this action")
|
||||
}
|
||||
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
if cctx.Args().Len() != 1 {
|
||||
return xerrors.Errorf("must pass sector number")
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(cctx.Args().Get(0), 10, 64)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("could not parse sector number: %w", err)
|
||||
}
|
||||
|
||||
return nodeApi.SectorTerminate(ctx, abi.SectorNumber(id))
|
||||
},
|
||||
}
|
||||
|
||||
var sectorsTerminateFlushCmd = &cli.Command{
|
||||
Name: "flush",
|
||||
Usage: "Send a terminate message if there are sectors queued for termination",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
mcid, err := nodeApi.SectorTerminateFlush(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mcid == nil {
|
||||
return xerrors.New("no sectors were queued for termination")
|
||||
}
|
||||
|
||||
fmt.Println(mcid)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var sectorsTerminatePendingCmd = &cli.Command{
|
||||
Name: "pending",
|
||||
Usage: "List sector numbers of sectors pending termination",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closer()
|
||||
api, nCloser, err := lcli.GetFullNodeAPI(cctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer nCloser()
|
||||
ctx := lcli.ReqContext(cctx)
|
||||
|
||||
pending, err := nodeApi.SectorTerminatePending(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
maddr, err := nodeApi.ActorAddress(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dl, err := api.StateMinerProvingDeadline(ctx, maddr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("getting proving deadline info failed: %w", err)
|
||||
}
|
||||
|
||||
for _, id := range pending {
|
||||
loc, err := api.StateSectorPartition(ctx, maddr, id.Number, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("finding sector partition: %w", err)
|
||||
}
|
||||
|
||||
fmt.Print(id.Number)
|
||||
|
||||
if loc.Deadline == (dl.Index+1)%miner.WPoStPeriodDeadlines || // not in next (in case the terminate message takes a while to get on chain)
|
||||
loc.Deadline == dl.Index || // not in current
|
||||
(loc.Deadline+1)%miner.WPoStPeriodDeadlines == dl.Index { // not in previous
|
||||
fmt.Print(" (in proving window)")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var sectorsRemoveCmd = &cli.Command{
|
||||
Name: "remove",
|
||||
Usage: "Forcefully remove a sector (WARNING: This means losing power and collateral for the removed sector)",
|
||||
Usage: "Forcefully remove a sector (WARNING: This means losing power and collateral for the removed sector (use 'terminate' for lower penalty))",
|
||||
ArgsUsage: "<sectorNum>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
|
||||
@@ -1,14 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
dstore "github.com/ipfs/go-datastore"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/cheggaaa/pb.v1"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/lib/backupds"
|
||||
"github.com/filecoin-project/lotus/node/config"
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
|
||||
var backupCmd = lcli.BackupCmd("repo", repo.FullNode, func(cctx *cli.Context) (lcli.BackupAPI, jsonrpc.ClientCloser, error) {
|
||||
return lcli.GetFullNodeAPI(cctx)
|
||||
})
|
||||
|
||||
func restore(cctx *cli.Context, r repo.Repo) error {
|
||||
bf, err := homedir.Expand(cctx.Path("restore"))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("expand backup file path: %w", err)
|
||||
}
|
||||
|
||||
st, err := os.Stat(bf)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("stat backup file (%s): %w", bf, err)
|
||||
}
|
||||
|
||||
f, err := os.Open(bf)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("opening backup file: %w", err)
|
||||
}
|
||||
defer f.Close() // nolint:errcheck
|
||||
|
||||
lr, err := r.Lock(repo.FullNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer lr.Close() // nolint:errcheck
|
||||
|
||||
if cctx.IsSet("restore-config") {
|
||||
log.Info("Restoring config")
|
||||
|
||||
cf, err := homedir.Expand(cctx.String("restore-config"))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("expanding config path: %w", err)
|
||||
}
|
||||
|
||||
_, err = os.Stat(cf)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("stat config file (%s): %w", cf, err)
|
||||
}
|
||||
|
||||
var cerr error
|
||||
err = lr.SetConfig(func(raw interface{}) {
|
||||
rcfg, ok := raw.(*config.FullNode)
|
||||
if !ok {
|
||||
cerr = xerrors.New("expected miner config")
|
||||
return
|
||||
}
|
||||
|
||||
ff, err := config.FromFile(cf, rcfg)
|
||||
if err != nil {
|
||||
cerr = xerrors.Errorf("loading config: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
*rcfg = *ff.(*config.FullNode)
|
||||
})
|
||||
if cerr != nil {
|
||||
return cerr
|
||||
}
|
||||
if err != nil {
|
||||
return xerrors.Errorf("setting config: %w", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Warn("--restore-config NOT SET, WILL USE DEFAULT VALUES")
|
||||
}
|
||||
|
||||
log.Info("Restoring metadata backup")
|
||||
|
||||
mds, err := lr.Datastore("/metadata")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bar := pb.New64(st.Size())
|
||||
br := bar.NewProxyReader(f)
|
||||
bar.ShowTimeLeft = true
|
||||
bar.ShowPercent = true
|
||||
bar.ShowSpeed = true
|
||||
bar.Units = pb.U_BYTES
|
||||
|
||||
bar.Start()
|
||||
err = backupds.RestoreInto(br, mds)
|
||||
bar.Finish()
|
||||
|
||||
if err != nil {
|
||||
return xerrors.Errorf("restoring metadata: %w", err)
|
||||
}
|
||||
|
||||
log.Info("Resetting chainstore metadata")
|
||||
|
||||
chainHead := dstore.NewKey("head")
|
||||
if err := mds.Delete(chainHead); err != nil {
|
||||
return xerrors.Errorf("clearing chain head: %w", err)
|
||||
}
|
||||
if err := store.FlushValidationCache(mds); err != nil {
|
||||
return xerrors.Errorf("clearing chain validation cache: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+20
-1
@@ -144,6 +144,14 @@ var DaemonCmd = &cli.Command{
|
||||
Name: "api-max-req-size",
|
||||
Usage: "maximum API request size accepted by the JSON RPC server",
|
||||
},
|
||||
&cli.PathFlag{
|
||||
Name: "restore",
|
||||
Usage: "restore from backup file",
|
||||
},
|
||||
&cli.PathFlag{
|
||||
Name: "restore-config",
|
||||
Usage: "config file to use when restoring from backup",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
isLite := cctx.Bool("lite")
|
||||
@@ -203,9 +211,11 @@ var DaemonCmd = &cli.Command{
|
||||
r.SetConfigPath(cctx.String("config"))
|
||||
}
|
||||
|
||||
if err := r.Init(repo.FullNode); err != nil && err != repo.ErrRepoExists {
|
||||
err = r.Init(repo.FullNode)
|
||||
if err != nil && err != repo.ErrRepoExists {
|
||||
return xerrors.Errorf("repo init error: %w", err)
|
||||
}
|
||||
freshRepo := err != repo.ErrRepoExists
|
||||
|
||||
if !isLite {
|
||||
if err := paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), 0); err != nil {
|
||||
@@ -223,6 +233,15 @@ var DaemonCmd = &cli.Command{
|
||||
genBytes = build.MaybeGenesis()
|
||||
}
|
||||
|
||||
if cctx.IsSet("restore") {
|
||||
if !freshRepo {
|
||||
return xerrors.Errorf("restoring from backup is only possible with a fresh repo!")
|
||||
}
|
||||
if err := restore(cctx, r); err != nil {
|
||||
return xerrors.Errorf("restoring from backup: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
chainfile := cctx.String("import-chain")
|
||||
snapshot := cctx.String("import-snapshot")
|
||||
if chainfile != "" || snapshot != "" {
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestProtocolCodenames(t *testing.T) {
|
||||
t.Fatal("expected breeze codename")
|
||||
}
|
||||
|
||||
if height := build.UpgradeActorsV2Height + 1; GetProtocolCodename(height) != "actorsv2" {
|
||||
if height := build.UpgradeActorsV2Height + 1; GetProtocolCodename(abi.ChainEpoch(height)) != "actorsv2" {
|
||||
t.Fatal("expected actorsv2 codename")
|
||||
}
|
||||
|
||||
|
||||
+134
-31
@@ -1,33 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/filecoin-project/go-address"
|
||||
cbornode "github.com/ipfs/go-ipld-cbor"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/filecoin-project/lotus/conformance"
|
||||
|
||||
"github.com/filecoin-project/test-vectors/schema"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/state"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/conformance"
|
||||
"github.com/filecoin-project/lotus/lib/blockstore"
|
||||
)
|
||||
|
||||
var execFlags struct {
|
||||
file string
|
||||
out string
|
||||
driverOpts cli.StringSlice
|
||||
fallbackBlockstore bool
|
||||
}
|
||||
|
||||
const (
|
||||
optSaveBalances = "save-balances"
|
||||
)
|
||||
|
||||
var execCmd = &cli.Command{
|
||||
Name: "exec",
|
||||
Description: "execute one or many test vectors against Lotus; supplied as a single JSON file, or a ndjson stdin stream",
|
||||
Action: runExecLotus,
|
||||
Description: "execute one or many test vectors against Lotus; supplied as a single JSON file, a directory, or a ndjson stdin stream",
|
||||
Action: runExec,
|
||||
Flags: []cli.Flag{
|
||||
&repoFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "file",
|
||||
Usage: "input file; if not supplied, the vector will be read from stdin",
|
||||
Usage: "input file or directory; if not supplied, the vector will be read from stdin",
|
||||
TakesFile: true,
|
||||
Destination: &execFlags.file,
|
||||
},
|
||||
@@ -36,10 +51,20 @@ var execCmd = &cli.Command{
|
||||
Usage: "sets the full node API as a fallback blockstore; use this if you're transplanting vectors and get block not found errors",
|
||||
Destination: &execFlags.fallbackBlockstore,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "out",
|
||||
Usage: "output directory where to save the results, only used when the input is a directory",
|
||||
Destination: &execFlags.out,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "driver-opt",
|
||||
Usage: "comma-separated list of driver options (EXPERIMENTAL; will change), supported: 'save-balances=<dst>', 'pipeline-basefee' (unimplemented); only available in single-file mode",
|
||||
Destination: &execFlags.driverOpts,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runExecLotus(c *cli.Context) error {
|
||||
func runExec(c *cli.Context) error {
|
||||
if execFlags.fallbackBlockstore {
|
||||
if err := initialize(c); err != nil {
|
||||
return fmt.Errorf("fallback blockstore was enabled, but could not resolve lotus API endpoint: %w", err)
|
||||
@@ -48,30 +73,97 @@ func runExecLotus(c *cli.Context) error {
|
||||
conformance.FallbackBlockstoreGetter = FullAPI
|
||||
}
|
||||
|
||||
if file := execFlags.file; file != "" {
|
||||
// we have a single test vector supplied as a file.
|
||||
file, err := os.Open(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open test vector: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
dec = json.NewDecoder(file)
|
||||
tv schema.TestVector
|
||||
)
|
||||
|
||||
if err = dec.Decode(&tv); err != nil {
|
||||
return fmt.Errorf("failed to decode test vector: %w", err)
|
||||
}
|
||||
|
||||
return executeTestVector(tv)
|
||||
path := execFlags.file
|
||||
if path == "" {
|
||||
return execVectorsStdin()
|
||||
}
|
||||
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
// we're in directory mode; ensure the out directory exists.
|
||||
outdir := execFlags.out
|
||||
if outdir == "" {
|
||||
return fmt.Errorf("no output directory provided")
|
||||
}
|
||||
if err := ensureDir(outdir); err != nil {
|
||||
return err
|
||||
}
|
||||
return execVectorDir(path, outdir)
|
||||
}
|
||||
|
||||
// process tipset vector options.
|
||||
if err := processTipsetOpts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = execVectorFile(new(conformance.LogReporter), path)
|
||||
return err
|
||||
}
|
||||
|
||||
func processTipsetOpts() error {
|
||||
for _, opt := range execFlags.driverOpts.Value() {
|
||||
switch ss := strings.Split(opt, "="); {
|
||||
case ss[0] == optSaveBalances:
|
||||
filename := ss[1]
|
||||
log.Printf("saving balances after each tipset in: %s", filename)
|
||||
balancesFile, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w := bufio.NewWriter(balancesFile)
|
||||
cb := func(bs blockstore.Blockstore, params *conformance.ExecuteTipsetParams, res *conformance.ExecuteTipsetResult) {
|
||||
cst := cbornode.NewCborStore(bs)
|
||||
st, err := state.LoadStateTree(cst, res.PostStateRoot)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = st.ForEach(func(addr address.Address, actor *types.Actor) error {
|
||||
_, err := fmt.Fprintln(w, params.ExecEpoch, addr, actor.Balance)
|
||||
return err
|
||||
})
|
||||
_ = w.Flush()
|
||||
}
|
||||
conformance.TipsetVectorOpts.OnTipsetApplied = append(conformance.TipsetVectorOpts.OnTipsetApplied, cb)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func execVectorDir(path string, outdir string) error {
|
||||
files, err := filepath.Glob(filepath.Join(path, "*"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to glob input directory %s: %w", path, err)
|
||||
}
|
||||
for _, f := range files {
|
||||
outfile := strings.TrimSuffix(filepath.Base(f), filepath.Ext(f)) + ".out"
|
||||
outpath := filepath.Join(outdir, outfile)
|
||||
outw, err := os.Create(outpath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file %s: %w", outpath, err)
|
||||
}
|
||||
|
||||
log.Printf("processing vector %s; sending output to %s", f, outpath)
|
||||
log.SetOutput(io.MultiWriter(os.Stderr, outw)) // tee the output.
|
||||
_, _ = execVectorFile(new(conformance.LogReporter), f)
|
||||
log.SetOutput(os.Stderr)
|
||||
_ = outw.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func execVectorsStdin() error {
|
||||
r := new(conformance.LogReporter)
|
||||
for dec := json.NewDecoder(os.Stdin); ; {
|
||||
var tv schema.TestVector
|
||||
switch err := dec.Decode(&tv); err {
|
||||
case nil:
|
||||
if err = executeTestVector(tv); err != nil {
|
||||
if _, err = executeTestVector(r, tv); err != nil {
|
||||
return err
|
||||
}
|
||||
case io.EOF:
|
||||
@@ -84,19 +176,30 @@ func runExecLotus(c *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func executeTestVector(tv schema.TestVector) error {
|
||||
func execVectorFile(r conformance.Reporter, path string) (diffs []string, error error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open test vector: %w", err)
|
||||
}
|
||||
|
||||
var tv schema.TestVector
|
||||
if err = json.NewDecoder(file).Decode(&tv); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode test vector: %w", err)
|
||||
}
|
||||
return executeTestVector(r, tv)
|
||||
}
|
||||
|
||||
func executeTestVector(r conformance.Reporter, tv schema.TestVector) (diffs []string, err error) {
|
||||
log.Println("executing test vector:", tv.Meta.ID)
|
||||
|
||||
for _, v := range tv.Pre.Variants {
|
||||
r := new(conformance.LogReporter)
|
||||
|
||||
switch class, v := tv.Class, v; class {
|
||||
case "message":
|
||||
conformance.ExecuteMessageVector(r, &tv, &v)
|
||||
diffs, err = conformance.ExecuteMessageVector(r, &tv, &v)
|
||||
case "tipset":
|
||||
conformance.ExecuteTipsetVector(r, &tv, &v)
|
||||
diffs, err = conformance.ExecuteTipsetVector(r, &tv, &v)
|
||||
default:
|
||||
return fmt.Errorf("test vector class %s not supported", class)
|
||||
return nil, fmt.Errorf("test vector class %s not supported", class)
|
||||
}
|
||||
|
||||
if r.Failed() {
|
||||
@@ -106,5 +209,5 @@ func executeTestVector(tv schema.TestVector) error {
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return diffs, err
|
||||
}
|
||||
|
||||
+55
-2
@@ -1,8 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/filecoin-project/test-vectors/schema"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -21,6 +27,7 @@ type extractOpts struct {
|
||||
retain string
|
||||
precursor string
|
||||
ignoreSanityChecks bool
|
||||
squash bool
|
||||
}
|
||||
|
||||
var extractFlags extractOpts
|
||||
@@ -62,13 +69,13 @@ var extractCmd = &cli.Command{
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tsk",
|
||||
Usage: "tipset key to extract into a vector",
|
||||
Usage: "tipset key to extract into a vector, or range of tipsets in tsk1..tsk2 form",
|
||||
Destination: &extractFlags.tsk,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "out",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "file to write test vector to",
|
||||
Usage: "file to write test vector to, or directory to write the batch to",
|
||||
Destination: &extractFlags.file,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
@@ -93,6 +100,12 @@ var extractCmd = &cli.Command{
|
||||
Value: false,
|
||||
Destination: &extractFlags.ignoreSanityChecks,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "squash",
|
||||
Usage: "when extracting a tipset range, squash all tipsets into a single vector",
|
||||
Value: false,
|
||||
Destination: &extractFlags.squash,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -106,3 +119,43 @@ func runExtract(_ *cli.Context) error {
|
||||
return fmt.Errorf("unsupported vector class")
|
||||
}
|
||||
}
|
||||
|
||||
// writeVector writes the vector into the specified file, or to stdout if
|
||||
// file is empty.
|
||||
func writeVector(vector *schema.TestVector, file string) (err error) {
|
||||
output := io.WriteCloser(os.Stdout)
|
||||
if file := file; file != "" {
|
||||
dir := filepath.Dir(file)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("unable to create directory %s: %w", dir, err)
|
||||
}
|
||||
output, err = os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer output.Close() //nolint:errcheck
|
||||
defer log.Printf("wrote test vector to file: %s", file)
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(output)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(&vector)
|
||||
}
|
||||
|
||||
// writeVectors writes each vector to a different file under the specified
|
||||
// directory.
|
||||
func writeVectors(dir string, vectors ...*schema.TestVector) error {
|
||||
// verify the output directory exists.
|
||||
if err := ensureDir(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
// write each vector to its file.
|
||||
for _, v := range vectors {
|
||||
id := v.Meta.ID
|
||||
path := filepath.Join(dir, fmt.Sprintf("%s.json", id))
|
||||
if err := writeVector(v, path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,12 +4,9 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/filecoin-project/go-address"
|
||||
@@ -316,28 +313,7 @@ func doExtractMessage(opts extractOpts) error {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return writeVector(vector, opts.file)
|
||||
}
|
||||
|
||||
func writeVector(vector schema.TestVector, file string) (err error) {
|
||||
output := io.WriteCloser(os.Stdout)
|
||||
if file := file; file != "" {
|
||||
dir := filepath.Dir(file)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("unable to create directory %s: %w", dir, err)
|
||||
}
|
||||
output, err = os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer output.Close() //nolint:errcheck
|
||||
defer log.Printf("wrote test vector to file: %s", file)
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(output)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(&vector)
|
||||
return writeVector(&vector, opts.file)
|
||||
}
|
||||
|
||||
// resolveFromChain queries the chain for the provided message, using the block CID to
|
||||
|
||||
+204
-113
@@ -6,10 +6,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/filecoin-project/test-vectors/schema"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/conformance"
|
||||
)
|
||||
@@ -17,170 +19,259 @@ import (
|
||||
func doExtractTipset(opts extractOpts) error {
|
||||
ctx := context.Background()
|
||||
|
||||
if opts.tsk == "" {
|
||||
return fmt.Errorf("tipset key cannot be empty")
|
||||
}
|
||||
|
||||
if opts.retain != "accessed-cids" {
|
||||
return fmt.Errorf("tipset extraction only supports 'accessed-cids' state retention")
|
||||
}
|
||||
|
||||
ts, err := lcli.ParseTipSetRef(ctx, FullAPI, opts.tsk)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch tipset: %w", err)
|
||||
if opts.tsk == "" {
|
||||
return fmt.Errorf("tipset key cannot be empty")
|
||||
}
|
||||
|
||||
log.Printf("tipset block count: %d", len(ts.Blocks()))
|
||||
|
||||
var blocks []schema.Block
|
||||
for _, b := range ts.Blocks() {
|
||||
msgs, err := FullAPI.ChainGetBlockMessages(ctx, b.Cid())
|
||||
ss := strings.Split(opts.tsk, "..")
|
||||
switch len(ss) {
|
||||
case 1: // extracting a single tipset.
|
||||
ts, err := lcli.ParseTipSetRef(ctx, FullAPI, opts.tsk)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get block messages (cid: %s): %w", b.Cid(), err)
|
||||
return fmt.Errorf("failed to fetch tipset: %w", err)
|
||||
}
|
||||
v, err := extractTipsets(ctx, ts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeVector(v, opts.file)
|
||||
|
||||
case 2: // extracting a range of tipsets.
|
||||
left, err := lcli.ParseTipSetRef(ctx, FullAPI, ss[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch tipset %s: %w", ss[0], err)
|
||||
}
|
||||
right, err := lcli.ParseTipSetRef(ctx, FullAPI, ss[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch tipset %s: %w", ss[1], err)
|
||||
}
|
||||
|
||||
log.Printf("block %s has %d messages", b.Cid(), len(msgs.Cids))
|
||||
// resolve the tipset range.
|
||||
tss, err := resolveTipsetRange(ctx, left, right)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
packed := make([]schema.Base64EncodedBytes, 0, len(msgs.Cids))
|
||||
for _, m := range msgs.BlsMessages {
|
||||
b, err := m.Serialize()
|
||||
// are are squashing all tipsets into a single multi-tipset vector?
|
||||
if opts.squash {
|
||||
vector, err := extractTipsets(ctx, tss...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize message: %w", err)
|
||||
return err
|
||||
}
|
||||
packed = append(packed, b)
|
||||
return writeVector(vector, opts.file)
|
||||
}
|
||||
for _, m := range msgs.SecpkMessages {
|
||||
b, err := m.Message.Serialize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to serialize message: %w", err)
|
||||
}
|
||||
packed = append(packed, b)
|
||||
|
||||
// we are generating a single-tipset vector per tipset.
|
||||
vectors, err := extractIndividualTipsets(ctx, tss...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blocks = append(blocks, schema.Block{
|
||||
MinerAddr: b.Miner,
|
||||
WinCount: b.ElectionProof.WinCount,
|
||||
Messages: packed,
|
||||
})
|
||||
return writeVectors(opts.file, vectors...)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unrecognized tipset format")
|
||||
}
|
||||
}
|
||||
|
||||
func resolveTipsetRange(ctx context.Context, left *types.TipSet, right *types.TipSet) (tss []*types.TipSet, err error) {
|
||||
// start from the right tipset and walk back the chain until the left tipset, inclusive.
|
||||
for curr := right; curr.Key() != left.Parents(); {
|
||||
tss = append(tss, curr)
|
||||
curr, err = FullAPI.ChainGetTipSet(ctx, curr.Parents())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get tipset %s (height: %d): %w", curr.Parents(), curr.Height()-1, err)
|
||||
}
|
||||
}
|
||||
// reverse the slice.
|
||||
for i, j := 0, len(tss)-1; i < j; i, j = i+1, j-1 {
|
||||
tss[i], tss[j] = tss[j], tss[i]
|
||||
}
|
||||
return tss, nil
|
||||
}
|
||||
|
||||
func extractIndividualTipsets(ctx context.Context, tss ...*types.TipSet) (vectors []*schema.TestVector, err error) {
|
||||
for _, ts := range tss {
|
||||
v, err := extractTipsets(ctx, ts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vectors = append(vectors, v)
|
||||
}
|
||||
return vectors, nil
|
||||
}
|
||||
|
||||
func extractTipsets(ctx context.Context, tss ...*types.TipSet) (*schema.TestVector, error) {
|
||||
var (
|
||||
// create a read-through store that uses ChainGetObject to fetch unknown CIDs.
|
||||
pst = NewProxyingStores(ctx, FullAPI)
|
||||
g = NewSurgeon(ctx, FullAPI, pst)
|
||||
|
||||
// recordingRand will record randomness so we can embed it in the test vector.
|
||||
recordingRand = conformance.NewRecordingRand(new(conformance.LogReporter), FullAPI)
|
||||
)
|
||||
|
||||
tbs, ok := pst.Blockstore.(TracingBlockstore)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("requested 'accessed-cids' state retention, but no tracing blockstore was present")
|
||||
}
|
||||
|
||||
driver := conformance.NewDriver(ctx, schema.Selector{}, conformance.DriverOpts{
|
||||
DisableVMFlush: true,
|
||||
})
|
||||
|
||||
base := tss[0]
|
||||
last := tss[len(tss)-1]
|
||||
|
||||
// this is the root of the state tree we start with.
|
||||
root := ts.ParentState()
|
||||
root := base.ParentState()
|
||||
log.Printf("base state tree root CID: %s", root)
|
||||
|
||||
basefee := ts.Blocks()[0].ParentBaseFee
|
||||
log.Printf("basefee: %s", basefee)
|
||||
|
||||
tipset := schema.Tipset{
|
||||
BaseFee: *basefee.Int,
|
||||
Blocks: blocks,
|
||||
codename := GetProtocolCodename(base.Height())
|
||||
nv, err := FullAPI.StateNetworkVersion(ctx, base.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// recordingRand will record randomness so we can embed it in the test vector.
|
||||
recordingRand := conformance.NewRecordingRand(new(conformance.LogReporter), FullAPI)
|
||||
version, err := FullAPI.Version(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("using state retention strategy: %s", extractFlags.retain)
|
||||
ntwkName, err := FullAPI.StateNetworkName(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tbs, ok := pst.Blockstore.(TracingBlockstore)
|
||||
if !ok {
|
||||
return fmt.Errorf("requested 'accessed-cids' state retention, but no tracing blockstore was present")
|
||||
vector := schema.TestVector{
|
||||
Class: schema.ClassTipset,
|
||||
Meta: &schema.Metadata{
|
||||
ID: fmt.Sprintf("@%d..@%d", base.Height(), last.Height()),
|
||||
Gen: []schema.GenerationData{
|
||||
{Source: fmt.Sprintf("network:%s", ntwkName)},
|
||||
{Source: "github.com/filecoin-project/lotus", Version: version.String()}},
|
||||
// will be completed by extra tipset stamps.
|
||||
},
|
||||
Selector: schema.Selector{
|
||||
schema.SelectorMinProtocolVersion: codename,
|
||||
},
|
||||
Pre: &schema.Preconditions{
|
||||
Variants: []schema.Variant{
|
||||
{ID: codename, Epoch: int64(base.Height()), NetworkVersion: uint(nv)},
|
||||
},
|
||||
StateTree: &schema.StateTree{
|
||||
RootCID: base.ParentState(),
|
||||
},
|
||||
},
|
||||
Post: &schema.Postconditions{
|
||||
StateTree: new(schema.StateTree),
|
||||
},
|
||||
}
|
||||
|
||||
tbs.StartTracing()
|
||||
|
||||
params := conformance.ExecuteTipsetParams{
|
||||
Preroot: ts.ParentState(),
|
||||
ParentEpoch: ts.Height() - 1,
|
||||
Tipset: &tipset,
|
||||
ExecEpoch: ts.Height(),
|
||||
Rand: recordingRand,
|
||||
}
|
||||
result, err := driver.ExecuteTipset(pst.Blockstore, pst.Datastore, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute tipset: %w", err)
|
||||
roots := []cid.Cid{base.ParentState()}
|
||||
for i, ts := range tss {
|
||||
log.Printf("tipset %s block count: %d", ts.Key(), len(ts.Blocks()))
|
||||
|
||||
var blocks []schema.Block
|
||||
for _, b := range ts.Blocks() {
|
||||
msgs, err := FullAPI.ChainGetBlockMessages(ctx, b.Cid())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get block messages (cid: %s): %w", b.Cid(), err)
|
||||
}
|
||||
|
||||
log.Printf("block %s has %d messages", b.Cid(), len(msgs.Cids))
|
||||
|
||||
packed := make([]schema.Base64EncodedBytes, 0, len(msgs.Cids))
|
||||
for _, m := range msgs.BlsMessages {
|
||||
b, err := m.Serialize()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize message: %w", err)
|
||||
}
|
||||
packed = append(packed, b)
|
||||
}
|
||||
for _, m := range msgs.SecpkMessages {
|
||||
b, err := m.Message.Serialize()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize message: %w", err)
|
||||
}
|
||||
packed = append(packed, b)
|
||||
}
|
||||
blocks = append(blocks, schema.Block{
|
||||
MinerAddr: b.Miner,
|
||||
WinCount: b.ElectionProof.WinCount,
|
||||
Messages: packed,
|
||||
})
|
||||
}
|
||||
|
||||
basefee := base.Blocks()[0].ParentBaseFee
|
||||
log.Printf("tipset basefee: %s", basefee)
|
||||
|
||||
tipset := schema.Tipset{
|
||||
BaseFee: *basefee.Int,
|
||||
Blocks: blocks,
|
||||
EpochOffset: int64(i),
|
||||
}
|
||||
|
||||
params := conformance.ExecuteTipsetParams{
|
||||
Preroot: roots[len(roots)-1],
|
||||
ParentEpoch: ts.Height() - 1,
|
||||
Tipset: &tipset,
|
||||
ExecEpoch: ts.Height(),
|
||||
Rand: recordingRand,
|
||||
}
|
||||
|
||||
result, err := driver.ExecuteTipset(pst.Blockstore, pst.Datastore, params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute tipset: %w", err)
|
||||
}
|
||||
|
||||
roots = append(roots, result.PostStateRoot)
|
||||
|
||||
// update the vector.
|
||||
vector.ApplyTipsets = append(vector.ApplyTipsets, tipset)
|
||||
vector.Post.ReceiptsRoots = append(vector.Post.ReceiptsRoots, result.ReceiptsRoot)
|
||||
|
||||
for _, res := range result.AppliedResults {
|
||||
vector.Post.Receipts = append(vector.Post.Receipts, &schema.Receipt{
|
||||
ExitCode: int64(res.ExitCode),
|
||||
ReturnValue: res.Return,
|
||||
GasUsed: res.GasUsed,
|
||||
})
|
||||
}
|
||||
|
||||
vector.Meta.Gen = append(vector.Meta.Gen, schema.GenerationData{
|
||||
Source: "tipset:" + ts.Key().String(),
|
||||
})
|
||||
}
|
||||
|
||||
accessed := tbs.FinishTracing()
|
||||
|
||||
//
|
||||
// ComputeBaseFee(ctx, baseTs)
|
||||
|
||||
// write a CAR with the accessed state into a buffer.
|
||||
var (
|
||||
out = new(bytes.Buffer)
|
||||
gw = gzip.NewWriter(out)
|
||||
)
|
||||
if err := g.WriteCARIncluding(gw, accessed, ts.ParentState(), result.PostStateRoot); err != nil {
|
||||
return err
|
||||
if err := g.WriteCARIncluding(gw, accessed, roots...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = gw.Flush(); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if err = gw.Close(); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
codename := GetProtocolCodename(ts.Height())
|
||||
nv, err := FullAPI.StateNetworkVersion(ctx, ts.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vector.Randomness = recordingRand.Recorded()
|
||||
vector.Post.StateTree.RootCID = roots[len(roots)-1]
|
||||
vector.CAR = out.Bytes()
|
||||
|
||||
version, err := FullAPI.Version(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ntwkName, err := FullAPI.StateNetworkName(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vector := schema.TestVector{
|
||||
Class: schema.ClassTipset,
|
||||
Meta: &schema.Metadata{
|
||||
ID: opts.id,
|
||||
Gen: []schema.GenerationData{
|
||||
{Source: fmt.Sprintf("network:%s", ntwkName)},
|
||||
{Source: fmt.Sprintf("tipset:%s", ts.Key())},
|
||||
{Source: "github.com/filecoin-project/lotus", Version: version.String()}},
|
||||
},
|
||||
Selector: schema.Selector{
|
||||
schema.SelectorMinProtocolVersion: codename,
|
||||
},
|
||||
Randomness: recordingRand.Recorded(),
|
||||
CAR: out.Bytes(),
|
||||
Pre: &schema.Preconditions{
|
||||
Variants: []schema.Variant{
|
||||
{ID: codename, Epoch: int64(ts.Height()), NetworkVersion: uint(nv)},
|
||||
},
|
||||
BaseFee: basefee.Int,
|
||||
StateTree: &schema.StateTree{
|
||||
RootCID: ts.ParentState(),
|
||||
},
|
||||
},
|
||||
ApplyTipsets: []schema.Tipset{tipset},
|
||||
Post: &schema.Postconditions{
|
||||
StateTree: &schema.StateTree{
|
||||
RootCID: result.PostStateRoot,
|
||||
},
|
||||
ReceiptsRoots: []cid.Cid{result.ReceiptsRoot},
|
||||
},
|
||||
}
|
||||
|
||||
for _, res := range result.AppliedResults {
|
||||
vector.Post.Receipts = append(vector.Post.Receipts, &schema.Receipt{
|
||||
ExitCode: int64(res.ExitCode),
|
||||
ReturnValue: res.Return,
|
||||
GasUsed: res.GasUsed,
|
||||
})
|
||||
}
|
||||
|
||||
return writeVector(vector, opts.file)
|
||||
return &vector, nil
|
||||
}
|
||||
|
||||
@@ -113,3 +113,19 @@ func destroy(_ *cli.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureDir(path string) error {
|
||||
switch fi, err := os.Stat(path); {
|
||||
case os.IsNotExist(err):
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory %s: %w", path, err)
|
||||
}
|
||||
case err == nil:
|
||||
if !fi.IsDir() {
|
||||
return fmt.Errorf("path %s is not a directory: %w", path, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("failed to stat directory %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -202,7 +202,7 @@ func runSimulateCmd(_ *cli.Context) error {
|
||||
},
|
||||
}
|
||||
|
||||
if err := writeVector(vector, simulateFlags.out); err != nil {
|
||||
if err := writeVector(&vector, simulateFlags.out); err != nil {
|
||||
return fmt.Errorf("failed to write vector: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -149,3 +149,14 @@ func (pb *proxyingBlockstore) Put(block blocks.Block) error {
|
||||
pb.lk.Unlock()
|
||||
return pb.Blockstore.Put(block)
|
||||
}
|
||||
|
||||
func (pb *proxyingBlockstore) PutMany(blocks []blocks.Block) error {
|
||||
pb.lk.Lock()
|
||||
if pb.tracing {
|
||||
for _, b := range blocks {
|
||||
pb.traced[b.Cid()] = struct{}{}
|
||||
}
|
||||
}
|
||||
pb.lk.Unlock()
|
||||
return pb.Blockstore.PutMany(blocks)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user