package main import ( "context" "database/sql" "encoding/json" "errors" "fmt" "os" "time" "github.com/urfave/cli/v2" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/dline" "github.com/filecoin-project/lotus/cmd/lotus-provider/deps" "github.com/filecoin-project/lotus/lib/harmony/harmonydb" "github.com/filecoin-project/lotus/provider" ) var testCmd = &cli.Command{ Name: "test", Usage: "Utility functions for testing", Subcommands: []*cli.Command{ //provingInfoCmd, wdPostCmd, }, } var wdPostCmd = &cli.Command{ Name: "window-post", Aliases: []string{"wd", "windowpost", "wdpost"}, Usage: "Compute a proof-of-spacetime for a sector (requires the sector to be pre-sealed). These will not send to the chain.", Subcommands: []*cli.Command{ wdPostHereCmd, wdPostTaskCmd, }, } // wdPostTaskCmd writes to harmony_task and wdpost_partition_tasks, then waits for the result. // It is intended to be used to test the windowpost scheduler. // The end of the compute task puts the task_id onto wdpost_proofs, which is read by the submit task. // The submit task will not send test tasks to the chain, and instead will write the result to harmony_test. // The result is read by this command, and printed to stdout. var wdPostTaskCmd = &cli.Command{ Name: "task", Aliases: []string{"scheduled", "schedule", "async", "asynchronous"}, Usage: "Test the windowpost scheduler by running it on the next available lotus-provider. ", Flags: []cli.Flag{ &cli.Uint64Flag{ Name: "deadline", Usage: "deadline to compute WindowPoSt for ", Value: 0, }, &cli.StringSliceFlag{ Name: "layers", Usage: "list of layers to be interpreted (atop defaults). Default: base", Value: cli.NewStringSlice("base"), }, }, Action: func(cctx *cli.Context) error { ctx := context.Background() deps, err := deps.GetDeps(ctx, cctx) if err != nil { return err } ts, err := deps.Full.ChainHead(ctx) if err != nil { return xerrors.Errorf("cannot get chainhead %w", err) } ht := ts.Height() addr, err := address.NewFromString(deps.Cfg.Addresses.MinerAddresses[0]) if err != nil { return xerrors.Errorf("cannot get miner address %w", err) } maddr, err := address.IDFromAddress(addr) if err != nil { return xerrors.Errorf("cannot get miner id %w", err) } var id int64 retryDelay := time.Millisecond * 10 retryAddTask: _, err = deps.DB.BeginTransaction(ctx, func(tx *harmonydb.Tx) (commit bool, err error) { err = tx.QueryRow(`INSERT INTO harmony_task (name, posted_time, added_by) VALUES ('WdPost', CURRENT_TIMESTAMP, 123) RETURNING id`).Scan(&id) if err != nil { log.Error("inserting harmony_task: ", err) return false, xerrors.Errorf("inserting harmony_task: %w", err) } _, err = tx.Exec(`INSERT INTO wdpost_partition_tasks (task_id, sp_id, proving_period_start, deadline_index, partition_index) VALUES ($1, $2, $3, $4, $5)`, id, maddr, ht, cctx.Uint64("deadline"), 0) if err != nil { log.Error("inserting wdpost_partition_tasks: ", err) return false, xerrors.Errorf("inserting wdpost_partition_tasks: %w", err) } _, err = tx.Exec("INSERT INTO harmony_test (task_id) VALUES ($1)", id) if err != nil { return false, xerrors.Errorf("inserting into harmony_tests: %w", err) } return true, nil }) if err != nil { if harmonydb.IsErrSerialization(err) { time.Sleep(retryDelay) retryDelay *= 2 goto retryAddTask } return xerrors.Errorf("writing SQL transaction: %w", err) } fmt.Printf("Inserted task %v. Waiting for success ", id) var result sql.NullString for { time.Sleep(time.Second) err = deps.DB.QueryRow(ctx, `SELECT result FROM harmony_test WHERE task_id=$1`, id).Scan(&result) if err != nil { return xerrors.Errorf("reading result from harmony_test: %w", err) } if result.Valid { break } fmt.Print(".") } log.Infof("Result: %s", result.String) return nil }, } // This command is intended to be used to verify PoSt compute performance. // It will not send any messages to the chain. Since it can compute any deadline, output may be incorrectly timed for the chain. // The entire processing happens in this process while you wait. It does not use the scheduler. var wdPostHereCmd = &cli.Command{ Name: "here", Aliases: []string{"cli"}, Usage: "Compute WindowPoSt for performance and configuration testing.", Description: `Note: This command is intended to be used to verify PoSt compute performance. It will not send any messages to the chain. Since it can compute any deadline, output may be incorrectly timed for the chain.`, ArgsUsage: "[deadline index]", Flags: []cli.Flag{ &cli.Uint64Flag{ Name: "deadline", Usage: "deadline to compute WindowPoSt for ", Value: 0, }, &cli.StringSliceFlag{ Name: "layers", Usage: "list of layers to be interpreted (atop defaults). Default: base", Value: cli.NewStringSlice("base"), }, &cli.StringFlag{ Name: "storage-json", Usage: "path to json file containing storage config", Value: "~/.lotus-provider/storage.json", }, &cli.Uint64Flag{ Name: "partition", Usage: "partition to compute WindowPoSt for", Value: 0, }, }, Action: func(cctx *cli.Context) error { ctx := context.Background() deps, err := deps.GetDeps(ctx, cctx) if err != nil { return err } wdPostTask, wdPoStSubmitTask, derlareRecoverTask, err := provider.WindowPostScheduler(ctx, deps.Cfg.Fees, deps.Cfg.Proving, deps.Full, deps.Verif, deps.LW, nil, deps.As, deps.Maddrs, deps.DB, deps.Stor, deps.Si, deps.Cfg.Subsystems.WindowPostMaxTasks) if err != nil { return err } _, _ = wdPoStSubmitTask, derlareRecoverTask if len(deps.Maddrs) == 0 { return errors.New("no miners to compute WindowPoSt for") } head, err := deps.Full.ChainHead(ctx) if err != nil { return xerrors.Errorf("failed to get chain head: %w", err) } di := dline.NewInfo(head.Height(), cctx.Uint64("deadline"), 0, 0, 0, 10 /*challenge window*/, 0, 0) for _, maddr := range deps.Maddrs { out, err := wdPostTask.DoPartition(ctx, head, address.Address(maddr), di, cctx.Uint64("partition")) if err != nil { fmt.Println("Error computing WindowPoSt for miner", maddr, err) continue } fmt.Println("Computed WindowPoSt for miner", maddr, ":") err = json.NewEncoder(os.Stdout).Encode(out) if err != nil { fmt.Println("Could not encode WindowPoSt output for miner", maddr, err) continue } } return nil }, }