package main import ( "fmt" "io" "io/ioutil" "os" "os/exec" "path/filepath" "sync/atomic" "time" "golang.org/x/xerrors" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/cmd/lotus-seed/seed" ) func (api *api) Spawn() (nodeInfo, error) { dir, err := ioutil.TempDir(os.TempDir(), "lotus-") if err != nil { return nodeInfo{}, err } params := []string{"daemon", "--bootstrap=false"} genParam := "--genesis=" + api.genesis id := atomic.AddInt32(&api.cmds, 1) if id == 1 { // preseal genMiner, err := address.NewIDAddress(101) if err != nil { return nodeInfo{}, err } sbroot := filepath.Join(dir, "preseal") genm, err := seed.PreSeal(genMiner, build.SectorSizes[0], 1, sbroot, []byte("8")) if err != nil { return nodeInfo{}, xerrors.Errorf("preseal failed: %w", err) } if err := seed.WriteGenesisMiner(genMiner, sbroot, genm); err != nil { return nodeInfo{}, xerrors.Errorf("failed to write genminer info: %w", err) } params = append(params, "--genesis-presealed-sectors="+filepath.Join(dir, "preseal", "pre-seal-t0101.json")) // make genesis genf, err := ioutil.TempFile(os.TempDir(), "lotus-genesis-") if err != nil { return nodeInfo{}, err } api.genesis = genf.Name() genParam = "--lotus-make-random-genesis=" + api.genesis if err := genf.Close(); err != nil { return nodeInfo{}, err } } errlogfile, err := os.OpenFile(dir+".err.log", os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nodeInfo{}, err } logfile, err := os.OpenFile(dir+".out.log", os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nodeInfo{}, err } mux := newWsMux() confStr := fmt.Sprintf("[API]\nListenAddress = \"/ip4/127.0.0.1/tcp/%d/http\"\n", 2500+id) err = ioutil.WriteFile(filepath.Join(dir, "config.toml"), []byte(confStr), 0700) if err != nil { return nodeInfo{}, err } cmd := exec.Command("./lotus", append(params, genParam)...) cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile, mux.errpw) cmd.Stdout = io.MultiWriter(os.Stdout, logfile, mux.outpw) cmd.Env = append(os.Environ(), "LOTUS_PATH="+dir) if err := cmd.Start(); err != nil { return nodeInfo{}, err } info := nodeInfo{ Repo: dir, ID: id, ApiPort: 2500 + id, State: NodeRunning, } api.runningLk.Lock() api.running[id] = &runningNode{ cmd: cmd, meta: info, mux: mux, stop: func() { cmd.Process.Signal(os.Interrupt) cmd.Process.Wait() api.runningLk.Lock() api.running[id].meta.State = NodeStopped api.runningLk.Unlock() //logfile.Close() //errlogfile.Close() //close(mux.stop) }, } api.runningLk.Unlock() time.Sleep(time.Millisecond * 750) // TODO: Something less terrible return info, nil } func (api *api) SpawnStorage(fullNodeRepo string) (nodeInfo, error) { dir, err := ioutil.TempDir(os.TempDir(), "lotus-storage-") if err != nil { return nodeInfo{}, err } errlogfile, err := os.OpenFile(dir+".err.log", os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nodeInfo{}, err } logfile, err := os.OpenFile(dir+".out.log", os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return nodeInfo{}, err } initArgs := []string{"init"} if fullNodeRepo == api.running[1].meta.Repo { initArgs = []string{"init", "--actor=t0101", "--genesis-miner", "--pre-sealed-sectors=" + filepath.Join(fullNodeRepo, "preseal")} } id := atomic.AddInt32(&api.cmds, 1) cmd := exec.Command("./lotus-storage-miner", initArgs...) cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile) cmd.Stdout = io.MultiWriter(os.Stdout, logfile) cmd.Env = append(os.Environ(), "LOTUS_STORAGE_PATH="+dir, "LOTUS_PATH="+fullNodeRepo) if err := cmd.Run(); err != nil { return nodeInfo{}, err } time.Sleep(time.Millisecond * 300) mux := newWsMux() cmd = exec.Command("./lotus-storage-miner", "run", "--api", fmt.Sprintf("%d", 2500+id)) cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile, mux.errpw) cmd.Stdout = io.MultiWriter(os.Stdout, logfile, mux.outpw) cmd.Env = append(os.Environ(), "LOTUS_STORAGE_PATH="+dir, "LOTUS_PATH="+fullNodeRepo) if err := cmd.Start(); err != nil { return nodeInfo{}, err } info := nodeInfo{ Repo: dir, ID: id, ApiPort: 2500 + id, State: NodeRunning, FullNode: fullNodeRepo, Storage: true, } api.runningLk.Lock() api.running[id] = &runningNode{ cmd: cmd, meta: info, mux: mux, stop: func() { cmd.Process.Signal(os.Interrupt) cmd.Process.Wait() api.runningLk.Lock() api.running[id].meta.State = NodeStopped api.runningLk.Unlock() //logfile.Close() //errlogfile.Close() //close(mux.stop) }, } api.runningLk.Unlock() time.Sleep(time.Millisecond * 750) // TODO: Something less terrible return info, nil } func (api *api) RestartNode(id int32) (nodeInfo, error) { api.runningLk.Lock() defer api.runningLk.Unlock() nd, ok := api.running[id] if !ok { return nodeInfo{}, xerrors.New("node not found") } if nd.meta.State != NodeStopped { return nodeInfo{}, xerrors.New("node not stopped") } var cmd *exec.Cmd if nd.meta.Storage { cmd = exec.Command("./lotus-storage-miner", "run", "--api", fmt.Sprintf("%d", 2500+id)) } else { cmd = exec.Command("./lotus", "daemon", "--api", fmt.Sprintf("%d", 2500+id)) } cmd.Stderr = nd.cmd.Stderr // recycle old vars cmd.Stdout = nd.cmd.Stdout cmd.Env = nd.cmd.Env if err := cmd.Start(); err != nil { return nodeInfo{}, err } nd.cmd = cmd nd.stop = func() { cmd.Process.Signal(os.Interrupt) cmd.Process.Wait() api.runningLk.Lock() api.running[id].meta.State = NodeStopped api.runningLk.Unlock() //logfile.Close() //errlogfile.Close() //close(mux.stop) } nd.meta.State = NodeRunning time.Sleep(time.Millisecond * 750) // TODO: Something less terrible return nd.meta, nil }