Delete lotus-pond (#9352)
This commit is contained in:
parent
94add978b0
commit
9e94dc5550
3
.gitignore
vendored
3
.gitignore
vendored
@ -9,7 +9,6 @@
|
||||
/lotus-chainwatch
|
||||
/lotus-shed
|
||||
/lotus-sim
|
||||
/lotus-pond
|
||||
/lotus-townhall
|
||||
/lotus-fountain
|
||||
/lotus-stats
|
||||
@ -21,8 +20,6 @@
|
||||
/docgen-md
|
||||
/docgen-openrpc
|
||||
/bench.json
|
||||
/lotuspond/front/node_modules
|
||||
/lotuspond/front/build
|
||||
/cmd/lotus-townhall/townhall/node_modules
|
||||
/cmd/lotus-townhall/townhall/build
|
||||
/cmd/lotus-townhall/townhall/package-lock.json
|
||||
|
@ -43,9 +43,6 @@ issues:
|
||||
|
||||
exclude-use-default: false
|
||||
exclude-rules:
|
||||
- path: lotuspond
|
||||
linters:
|
||||
- errcheck
|
||||
|
||||
- path: node/modules/lp2p
|
||||
linters:
|
||||
|
17
Makefile
17
Makefile
@ -160,18 +160,6 @@ benchmarks:
|
||||
@curl -X POST 'http://benchmark.kittyhawk.wtf/benchmark' -d '@bench.json' -u "${benchmark_http_cred}"
|
||||
.PHONY: benchmarks
|
||||
|
||||
lotus-pond: 2k
|
||||
$(GOCC) build -o lotus-pond ./lotuspond
|
||||
.PHONY: lotus-pond
|
||||
BINS+=lotus-pond
|
||||
|
||||
lotus-pond-front:
|
||||
(cd lotuspond/front && npm i && CI=false npm run build)
|
||||
.PHONY: lotus-pond-front
|
||||
|
||||
lotus-pond-app: lotus-pond-front lotus-pond
|
||||
.PHONY: lotus-pond-app
|
||||
|
||||
lotus-fountain:
|
||||
rm -f lotus-fountain
|
||||
$(GOCC) build $(GOFLAGS) -o lotus-fountain ./cmd/lotus-fountain
|
||||
@ -299,9 +287,6 @@ type-gen: api-gen
|
||||
$(GOCC) generate -x ./...
|
||||
goimports -w api/
|
||||
|
||||
method-gen: api-gen
|
||||
(cd ./lotuspond/front/src/chain && $(GOCC) run ./methodgen.go)
|
||||
|
||||
actors-code-gen:
|
||||
$(GOCC) run ./gen/inline-gen . gen/inlinegen-data.json
|
||||
$(GOCC) run ./chain/actors/agen
|
||||
@ -367,7 +352,7 @@ docsgen-openrpc-gateway: docsgen-openrpc-bin
|
||||
fiximports:
|
||||
./scripts/fiximports
|
||||
|
||||
gen: actors-code-gen type-gen method-gen cfgdoc-gen docsgen api-gen circleci bundle-gen fiximports
|
||||
gen: actors-code-gen type-gen cfgdoc-gen docsgen api-gen circleci bundle-gen fiximports
|
||||
@echo ">>> IF YOU'VE MODIFIED THE CLI OR CONFIG, REMEMBER TO ALSO MAKE docsgen-cli"
|
||||
.PHONY: gen
|
||||
|
||||
|
4
go.mod
4
go.mod
@ -63,7 +63,6 @@ require (
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026
|
||||
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
@ -126,7 +125,6 @@ require (
|
||||
github.com/multiformats/go-multihash v0.2.1
|
||||
github.com/multiformats/go-varint v0.0.6
|
||||
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-20201211092308-30ac6d18308e
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/raulk/clock v1.1.0
|
||||
@ -215,6 +213,7 @@ require (
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hannahhoward/cbor-gen-for v0.0.0-20200817222906-ea96cece81f1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/huin/goupnp v1.0.3 // indirect
|
||||
@ -288,6 +287,7 @@ require (
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
|
139
lotuspond/api.go
139
lotuspond/api.go
@ -1,139 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
|
||||
"github.com/filecoin-project/lotus/node/repo"
|
||||
)
|
||||
|
||||
type NodeState int
|
||||
|
||||
const (
|
||||
NodeUnknown = iota //nolint:deadcode
|
||||
NodeRunning
|
||||
NodeStopped
|
||||
)
|
||||
|
||||
type api struct {
|
||||
cmds int32
|
||||
running map[int32]*runningNode
|
||||
runningLk sync.Mutex
|
||||
genesis string
|
||||
}
|
||||
|
||||
type nodeInfo struct {
|
||||
Repo string
|
||||
ID int32
|
||||
APIPort int32
|
||||
State NodeState
|
||||
|
||||
FullNode string // only for storage nodes
|
||||
Storage bool
|
||||
}
|
||||
|
||||
func (api *api) Nodes() []nodeInfo {
|
||||
api.runningLk.Lock()
|
||||
out := make([]nodeInfo, 0, len(api.running))
|
||||
for _, node := range api.running {
|
||||
out = append(out, node.meta)
|
||||
}
|
||||
|
||||
api.runningLk.Unlock()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (api *api) TokenFor(id int32) (string, error) {
|
||||
api.runningLk.Lock()
|
||||
defer api.runningLk.Unlock()
|
||||
|
||||
rnd, ok := api.running[id]
|
||||
if !ok {
|
||||
return "", xerrors.New("no running node with this ID")
|
||||
}
|
||||
|
||||
r, err := repo.NewFS(rnd.meta.Repo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
t, err := r.APIToken()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(t), nil
|
||||
}
|
||||
|
||||
func (api *api) FullID(id int32) (int32, error) {
|
||||
api.runningLk.Lock()
|
||||
defer api.runningLk.Unlock()
|
||||
|
||||
stor, ok := api.running[id]
|
||||
if !ok {
|
||||
return 0, xerrors.New("storage node not found")
|
||||
}
|
||||
|
||||
if !stor.meta.Storage {
|
||||
return 0, xerrors.New("node is not a storage node")
|
||||
}
|
||||
|
||||
for id, n := range api.running {
|
||||
if n.meta.Repo == stor.meta.FullNode {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
return 0, xerrors.New("node not found")
|
||||
}
|
||||
|
||||
func (api *api) CreateRandomFile(size int64) (string, error) {
|
||||
tf, err := ioutil.TempFile(os.TempDir(), "pond-random-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = io.CopyN(tf, rand.Reader, size)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := tf.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tf.Name(), nil
|
||||
}
|
||||
|
||||
func (api *api) Stop(node int32) error {
|
||||
api.runningLk.Lock()
|
||||
nd, ok := api.running[node]
|
||||
api.runningLk.Unlock()
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
nd.stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
type client struct {
|
||||
Nodes func() []nodeInfo
|
||||
}
|
||||
|
||||
func apiClient(ctx context.Context) (*client, error) {
|
||||
c := &client{}
|
||||
if _, err := jsonrpc.NewClient(ctx, "ws://"+listenAddr+"/rpc/v0", "Pond", c, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
23
lotuspond/front/.gitignore
vendored
23
lotuspond/front/.gitignore
vendored
@ -1,23 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
13251
lotuspond/front/package-lock.json
generated
13251
lotuspond/front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "front",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"borc": "^2.1.1",
|
||||
"cids": "^0.7.1",
|
||||
"ipld-dag-cbor": "^0.15.0",
|
||||
"jsonrpc-websocket-client": "^0.5.0",
|
||||
"multihashes": "^0.4.15",
|
||||
"react": "^16.8.6",
|
||||
"react-cristal": "^0.0.12",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-tooltip": "^3.11.1",
|
||||
"rpc-websockets": "^4.5.1",
|
||||
"styled-components": "^3.3.3",
|
||||
"xterm": "^3.14.5",
|
||||
"xterm-addon-attach": "^0.1.0",
|
||||
"xterm-addon-fit": "^0.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#b7c4cd" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<title>Lotus Pond</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
import React from 'react'
|
||||
import CID from 'cids'
|
||||
import ReactTooltip from 'react-tooltip'
|
||||
import * as multihash from "multihashes"
|
||||
import State from "./State"
|
||||
import methods from "./chain/methods.json"
|
||||
import Fil from "./Fil";
|
||||
|
||||
function truncAddr(addr, len) {
|
||||
if (!addr) {
|
||||
return "<!nil>"
|
||||
}
|
||||
if (addr.length > len) {
|
||||
return <abbr title={addr}>{addr.substr(0, len - 3) + '..'}</abbr>
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
let sheet = document.createElement('style')
|
||||
document.body.appendChild(sheet);
|
||||
|
||||
class Address extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.openState = this.openState.bind(this)
|
||||
|
||||
this.state = {balance: -2}
|
||||
this.refresh = this.refresh.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.refresh()
|
||||
if(!this.props.ts) {
|
||||
this.updates = setInterval(this.refresh, 2050)
|
||||
this.props.client.on('close', () => clearInterval(this.updates))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.updates)
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
let balance = 0
|
||||
let actor = {}
|
||||
let actorInfo
|
||||
let minerInfo
|
||||
let nonce
|
||||
|
||||
try {
|
||||
balance = await this.props.client.call('Filecoin.WalletBalance', [this.props.addr])
|
||||
actor = await this.props.client.call('Filecoin.StateGetActor', [this.props.addr, (this.props.ts || {}).Cids])
|
||||
|
||||
actorInfo = await this.actorInfo(actor, this.props.addr)
|
||||
if(this.props.miner) {
|
||||
minerInfo = await this.props.client.call('Filecoin.StateMinerPower', [this.props.addr, (this.props.ts || {}).Cids])
|
||||
}
|
||||
if(this.props.nonce) {
|
||||
nonce = await this.props.client.call('Filecoin.MpoolGetNonce', [this.props.addr])
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
balance = -1
|
||||
}
|
||||
this.setState({balance, actor, actorInfo, minerInfo, nonce})
|
||||
}
|
||||
|
||||
openState() {
|
||||
this.props.mountWindow((onClose) => <State addr={this.props.addr} actor={this.state.actor} client={this.props.client} onClose={onClose} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
async actorInfo(actor, addr) {
|
||||
const c = new CID(actor.Code['/'])
|
||||
const mh = multihash.decode(c.multihash) // TODO: check identity
|
||||
|
||||
let method = <span></span>
|
||||
if(this.props.method !== undefined && mh.digest.toString()) {
|
||||
method = <span>.{methods[mh.digest.toString()][this.props.method]}</span>
|
||||
}
|
||||
|
||||
let info = <span>({mh.digest.toString()}{method})</span>
|
||||
switch(mh.digest.toString()) {
|
||||
case 'paych':
|
||||
const actstate = await this.props.client.call('Filecoin.StateReadState', [addr, (this.props.ts || {}).Cids])
|
||||
info = <span>({mh.digest.toString()}{method} to <Address nobalance={true} client={this.props.client} addr={actstate.State.To} mountWindow={this.props.mountWindow}/>)</span>
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
addColl = async () => {
|
||||
const coll = await this.props.client.call('Filecoin.StatePledgeCollateral', [null])
|
||||
this.props.addN(this.props.addr, coll)
|
||||
}
|
||||
|
||||
render() {
|
||||
let add20k = <span/>
|
||||
if(this.props.addN) {
|
||||
add20k = <span> <a href="#" onClick={() => this.props.addN(this.props.addr, 2e+18)}>[+2]</a></span>
|
||||
if (this.props.add10k) {
|
||||
add20k = <span>{add20k} <a href="#" onClick={() => this.props.addN(this.props.addr, 20e+18)}>[+20]</a></span>
|
||||
add20k = <span>{add20k} <a href="#" onClick={() => this.props.addN(this.props.addr, 200e+18)}>[+200]</a></span>
|
||||
add20k = <span>{add20k} <a href="#" onClick={() => this.addColl()}>[<abbr title="min collateral">+C</abbr>]</a></span>
|
||||
}
|
||||
}
|
||||
let addr = truncAddr(this.props.addr, this.props.short ? 12 : 17)
|
||||
|
||||
let actInfo = <span>(?)</span>
|
||||
if(this.state.balance >= 0) {
|
||||
actInfo = this.state.actorInfo
|
||||
addr = <a href="#" onClick={this.openState}>{addr}</a>
|
||||
}
|
||||
|
||||
addr = <span className={`pondaddr-${this.props.addr}`}
|
||||
onMouseEnter={() => sheet.sheet.insertRule(`.pondaddr-${this.props.addr}, .pondaddr-${this.props.addr} * { color: #11ee11 !important; }`, 0)}
|
||||
onMouseLeave={() => sheet.sheet.deleteRule(0)}
|
||||
>{addr}</span>
|
||||
|
||||
let nonce = <span/>
|
||||
if(this.props.nonce) {
|
||||
nonce = <span> <abbr title={"Next nonce"}>Nc:{this.state.nonce}</abbr>{nonce}</span>
|
||||
}
|
||||
|
||||
let balance = <span>: {<Fil>{this.state.balance}</Fil>} </span>
|
||||
if(this.props.nobalance) {
|
||||
balance = <span/>
|
||||
}
|
||||
if(this.props.short) {
|
||||
actInfo = <ReactTooltip id={this.props.addr} place="top" type="dark" effect="solid">{actInfo}: {<Fil>this.state.balance</Fil>}</ReactTooltip>
|
||||
balance = <span/>
|
||||
}
|
||||
|
||||
let transfer = <span/>
|
||||
if(this.props.transfer) {
|
||||
transfer = <span> <Fil>{this.props.transfer}</Fil>FIL</span>
|
||||
}
|
||||
|
||||
let minerInfo = <span/>
|
||||
if(this.state.minerInfo) {
|
||||
minerInfo = <span> Power: {this.state.minerInfo.MinerPower.QualityAdjPower} ({this.state.minerInfo.MinerPower.QualityAdjPower/this.state.minerInfo.TotalPower.QualityAdjPower*100}%)</span>
|
||||
}
|
||||
|
||||
return <span data-tip data-for={this.props.addr}>{addr}{balance}{actInfo}{nonce}{add20k}{transfer}{minerInfo}</span>
|
||||
}
|
||||
}
|
||||
|
||||
export default Address
|
@ -1,232 +0,0 @@
|
||||
.Index {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: #1a1a1a;
|
||||
color: #f0f0f0;
|
||||
font-family: monospace;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto 40vw auto;
|
||||
grid-template-rows: auto auto auto 3em;
|
||||
grid-template-areas:
|
||||
". . ."
|
||||
". main ."
|
||||
". . ."
|
||||
"footer footer footer";
|
||||
}
|
||||
|
||||
.Index-footer {
|
||||
background: #2a2a2a;
|
||||
grid-area: footer;
|
||||
}
|
||||
|
||||
.Index-footer > div {
|
||||
padding-left: 0.7em;
|
||||
padding-top: 0.7em;
|
||||
}
|
||||
|
||||
.Index-nodes {
|
||||
grid-area: main;
|
||||
background: #2a2a2a;
|
||||
}
|
||||
|
||||
.Index-node {
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
background: #1f1f1f;
|
||||
}
|
||||
|
||||
.Index-addwrap {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* SingleNode */
|
||||
|
||||
|
||||
.SingleNode-connecting {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background: #1a1a1a;
|
||||
color: #ffffff;
|
||||
font-family: monospace;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto min-content auto;
|
||||
grid-template-rows: auto min-content auto;
|
||||
grid-template-areas:
|
||||
". . ."
|
||||
". main ."
|
||||
". . ."
|
||||
}
|
||||
|
||||
.SingleNode-connecting > div {
|
||||
grid-area: main;
|
||||
padding: 15px;
|
||||
white-space: nowrap;
|
||||
background: #2a2a2a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****/
|
||||
|
||||
a:link {
|
||||
color: #50f020;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #50f020;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #30a00a;
|
||||
}
|
||||
|
||||
.Button {
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
background: #1f1f1f;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.Window {
|
||||
background: #2a2a2a !important;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.Window b {
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.Window > :first-child > :nth-child(2)::before {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.Window > :first-child > :nth-child(2)::after {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.Window a:link {
|
||||
color: #30a015;
|
||||
}
|
||||
|
||||
.Window a:visited {
|
||||
color: #30a015;
|
||||
}
|
||||
|
||||
/* POND */
|
||||
|
||||
.Pond-connecting {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background: #1a1a1a;
|
||||
color: #ffffff;
|
||||
font-family: monospace;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto min-content auto;
|
||||
grid-template-rows: auto min-content auto;
|
||||
grid-template-areas:
|
||||
". . ."
|
||||
". main ."
|
||||
". . ."
|
||||
}
|
||||
|
||||
.App {
|
||||
min-height: 100vh;
|
||||
background: #1a1a1a;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.NodeList {
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 40em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.FullNode {
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 50em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.FullNode-voucher {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.StorageNode {
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 40em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.Block {
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 60em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.State {
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
min-width: 40em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.Client {
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.CristalScroll {
|
||||
display: flex;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.Consensus {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.ChainExplorer {
|
||||
font-family: monospace;
|
||||
color: #d0d0d0;
|
||||
}
|
||||
|
||||
.ChainExplorer-at {
|
||||
min-width: 40em;
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
.ChainExplorer-after {
|
||||
background: #440000
|
||||
}
|
||||
|
||||
.ChainExplorer-after:hover {
|
||||
background: #770000
|
||||
}
|
||||
|
||||
.ChainExplorer-before {
|
||||
background: #444400
|
||||
}
|
||||
|
||||
.ChainExplorer-before:hover {
|
||||
background: #777700
|
||||
}
|
||||
|
||||
.Logs {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Logs-window :nth-child(2) {
|
||||
height: 100%;
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||
import Pond from "./Pond";
|
||||
import SingleNode from "./SingleNode";
|
||||
import Index from "./NodeIndex";
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Router>
|
||||
<Route path="/" exact component={Index} />
|
||||
<Route path="/app/pond/" component={Pond} />
|
||||
<Route path="/app/node/:node" component={SingleNode} />
|
||||
</Router>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Pond from './Pond';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Pond />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
@ -1,89 +0,0 @@
|
||||
import React from 'react';
|
||||
import {BlockLinks} from "./BlockLink";
|
||||
import Address from "./Address";
|
||||
import Window from "./Window";
|
||||
|
||||
class Block extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {}
|
||||
|
||||
this.loadHeader()
|
||||
}
|
||||
|
||||
async loadHeader() {
|
||||
const header = await this.props.conn.call('Filecoin.ChainGetBlock', [this.props.cid])
|
||||
let messages = await this.props.conn.call('Filecoin.ChainGetParentMessages', [this.props.cid])
|
||||
let receipts = await this.props.conn.call('Filecoin.ChainGetParentReceipts', [this.props.cid])
|
||||
|
||||
if (!messages) {
|
||||
messages = []
|
||||
}
|
||||
|
||||
|
||||
messages = messages.map((msg, k) => ({...msg.Message, cid: msg.Cid, receipt: receipts[k]}))
|
||||
|
||||
messages = await Promise.all(messages.map(async (msg, i) => {
|
||||
if (msg.receipt.ExitCode !== 0) {
|
||||
let reply = await this.props.conn.call('Filecoin.StateReplay', [{Cids: [this.props.cid], Blocks: [header], Height: header.Height}, msg.Cid])
|
||||
if(!reply.Error) {
|
||||
reply.Error = "reply: no error"
|
||||
}
|
||||
msg.Error = reply.Error
|
||||
}
|
||||
return msg
|
||||
}))
|
||||
|
||||
this.setState({header: header, messages: messages})
|
||||
}
|
||||
|
||||
render() {
|
||||
let content = <div>Loading Block Info</div>
|
||||
if (this.state.header) {
|
||||
let head = this.state.header
|
||||
|
||||
const messages = this.state.messages.map((m, k) => (
|
||||
<div key={k}>
|
||||
<div>
|
||||
<Address client={this.props.conn} addr={m.From} mountWindow={this.props.mountWindow}/><b> => </b>
|
||||
<Address client={this.props.conn} addr={m.To} mountWindow={this.props.mountWindow} transfer={m.Value} method={m.Method}/>
|
||||
<span> N{m.Nonce}</span>
|
||||
<span> {m.receipt.GasUsed}Gas</span>
|
||||
{m.receipt.ExitCode !== 0 ? <span> <b>EXIT:{m.receipt.ExitCode}</b></span> : <span/>}
|
||||
</div>
|
||||
{m.receipt.ExitCode !== 0 ? <div> Error: <b>{m.Error}</b></div> : <span/>}
|
||||
</div>
|
||||
))
|
||||
|
||||
content = (
|
||||
<div className="Block">
|
||||
<div>Height: {head.Height}</div>
|
||||
<div>Parents: <BlockLinks cids={head.Parents} conn={this.props.conn} mountWindow={this.props.mountWindow}/></div>
|
||||
<div>Weight: {head.ParentWeight}</div>
|
||||
<div>Miner: {<Address client={this.props.conn} addr={head.Miner} mountWindow={this.props.mountWindow}/>}</div>
|
||||
<div>Messages: {head.Messages['/']} {/*TODO: link to message explorer */}</div>
|
||||
<div>Parent Receipts: {head.ParentMessageReceipts['/']}</div>
|
||||
<div>
|
||||
<span>Parent State Root: {head.ParentStateRoot['/']}</span>
|
||||
<span> <Address client={this.props.conn} short={true} addr="t00" mountWindow={this.props.mountWindow}/></span>
|
||||
<span> <Address client={this.props.conn} short={true} addr="t01" mountWindow={this.props.mountWindow}/></span>
|
||||
<span> <Address client={this.props.conn} short={true} addr="t02" mountWindow={this.props.mountWindow}/></span>
|
||||
<span> <Address client={this.props.conn} short={true} addr="t03" mountWindow={this.props.mountWindow}/></span>
|
||||
<span> <Address client={this.props.conn} short={true} addr="t04" mountWindow={this.props.mountWindow}/></span>
|
||||
<span> <Address client={this.props.conn} short={true} addr="t05" mountWindow={this.props.mountWindow}/></span>
|
||||
<span> <Address client={this.props.conn} short={true} addr="t099" mountWindow={this.props.mountWindow}/></span>
|
||||
</div>
|
||||
<div>----</div>
|
||||
<div>{messages}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (<Window className="CristalScroll" initialSize={{width: 1050, height: 400}} onClose={this.props.onClose} title={`Block ${this.props.cid['/']}`}>
|
||||
{content}
|
||||
</Window>)
|
||||
}
|
||||
}
|
||||
|
||||
export default Block
|
@ -1,41 +0,0 @@
|
||||
import React from 'react';
|
||||
import Block from "./Block";
|
||||
import Address from "./Address";
|
||||
|
||||
|
||||
export class BlockLinks extends React.Component {
|
||||
render() {
|
||||
return this.props.cids.map((c, k) => {
|
||||
let block
|
||||
|
||||
if(this.props.blocks) {
|
||||
block = this.props.blocks[k]
|
||||
}
|
||||
|
||||
return <span key={c + '-' + k}><BlockLink block={block} conn={this.props.conn} cid={c} mountWindow={this.props.mountWindow}/> </span>
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class BlockLink extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.openBlockViewer = this.openBlockViewer.bind(this)
|
||||
}
|
||||
|
||||
openBlockViewer() {
|
||||
this.props.mountWindow((onClose) => <Block cid={this.props.cid} conn={this.props.conn} onClose={onClose} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
render() {
|
||||
let info = <span></span>
|
||||
if(this.props.block) {
|
||||
info = <span> (by <Address client={this.props.conn} addr={this.props.block.Miner} mountWindow={this.props.mountWindow} short={true}/>)</span>
|
||||
}
|
||||
|
||||
return <span><a href="#" onClick={this.openBlockViewer}><abbr title={this.props.cid['/']}>{this.props.cid['/'].substr(-8)}</abbr></a>{info}</span>
|
||||
}
|
||||
}
|
||||
|
||||
export default BlockLink
|
@ -1,178 +0,0 @@
|
||||
import React from 'react';
|
||||
import {BlockLinks} from "./BlockLink";
|
||||
import Window from "./Window";
|
||||
|
||||
const rows = 32
|
||||
|
||||
class ChainExplorer extends React.Component {
|
||||
fetching = []
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.update = this.update.bind(this)
|
||||
this.scroll = this.scroll.bind(this)
|
||||
|
||||
this.state = {
|
||||
follow: true,
|
||||
at: props.ts.Height,
|
||||
highest: props.ts,
|
||||
|
||||
cache: {[props.ts.Height]: props.ts},
|
||||
messages: {},
|
||||
}
|
||||
}
|
||||
|
||||
viewport() {
|
||||
const base = this.state.at - this.state.at % rows
|
||||
|
||||
return Array(rows).fill(0)
|
||||
.map((_, k) => k + base)
|
||||
.map(k => k > this.state.at ? k - rows : k)
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
let msgcache = {}
|
||||
await this.updateMessages(this.props.ts.Cids, msgcache)
|
||||
this.setState(prev => ({messages: {...prev.messages, ...msgcache}}))
|
||||
|
||||
setInterval(this.update, 1000)
|
||||
}
|
||||
|
||||
async updateMessages(cids, msgcache) {
|
||||
const msgs = await Promise.all(cids.map(async cid => [cid['/'], await this.props.client.call('Filecoin.ChainGetParentMessages', [cid])]))
|
||||
msgs.forEach(([cid, msg]) => msgcache[cid] = msg)
|
||||
}
|
||||
|
||||
async update() {
|
||||
const tipset = await this.props.client.call("Filecoin.ChainHead", [])
|
||||
if(tipset.Height > this.state.highest.Height) {
|
||||
let msgcache = {}
|
||||
await this.updateMessages(tipset.Cids, msgcache)
|
||||
|
||||
this.setState(prev => ({highest: tipset, messages: {...prev.messages, ...msgcache}, cache: {...prev.cache, [tipset.Height]: tipset}}))
|
||||
if(this.state.follow) {
|
||||
this.setState({at: tipset.Height})
|
||||
}
|
||||
}
|
||||
|
||||
await this.fetchVisible()
|
||||
}
|
||||
|
||||
async fetch(h, base, cache, msgcache) {
|
||||
//console.log(h, base, cache)
|
||||
|
||||
if (this.fetching[h]) {
|
||||
return cache[h]
|
||||
}
|
||||
this.fetching[h] = true
|
||||
|
||||
if (h < 0) {
|
||||
return
|
||||
}
|
||||
if(!base.Blocks) {
|
||||
console.log("base for H is nil blk", h, base)
|
||||
return
|
||||
}
|
||||
let cids = base.Blocks.map(b => (b.Parents || []))
|
||||
.reduce((acc, val) => {
|
||||
let out = {...acc}
|
||||
val.forEach(c => out[c['/']] = 8)
|
||||
return out
|
||||
}, {})
|
||||
cids = Object.keys(cids).map(k => ({'/': k}))
|
||||
console.log("parents", cids)
|
||||
|
||||
const blocks = await Promise.all(cids.map(cid => this.props.client.call('Filecoin.ChainGetBlock', [cid])))
|
||||
|
||||
if (!blocks[0]) {
|
||||
return
|
||||
}
|
||||
|
||||
cache[h] = {
|
||||
Height: blocks[0].Height,
|
||||
Cids: cids,
|
||||
Blocks: blocks,
|
||||
}
|
||||
|
||||
await this.updateMessages(cids, msgcache)
|
||||
|
||||
return cache[h]
|
||||
}
|
||||
|
||||
async fetchVisible() {
|
||||
await this.fetchN(this.state.at)
|
||||
}
|
||||
|
||||
async fetchN(top) {
|
||||
if(!this.state.cache[top]) {
|
||||
if(top === this.state.highest.Height) {
|
||||
throw "fetchN broke (tipset not fetched)"
|
||||
}
|
||||
let h = top + rows > this.state.highest.Height ? this.state.highest.Height : top + rows
|
||||
|
||||
await this.fetchN(h)
|
||||
}
|
||||
|
||||
let cache = {...this.state.cache}
|
||||
let msgcache = {...this.state.messages}
|
||||
|
||||
console.log("top", top)
|
||||
|
||||
let parent = cache[top]
|
||||
for(let i = 0; i < rows; i++) {
|
||||
let newts = await this.fetch(top - i, parent, cache, msgcache)
|
||||
parent = newts ? newts : parent
|
||||
}
|
||||
this.setState({cache: cache, messages: msgcache})
|
||||
}
|
||||
|
||||
scroll(event) {
|
||||
if(event.deltaY < 0 && this.state.at > 0) {
|
||||
this.setState(prev => ({at: prev.at - 1, follow: false}))
|
||||
}
|
||||
if(event.deltaY > 0 && this.state.at < this.state.highest.Height) {
|
||||
this.setState(prev => ({at: prev.at + 1, follow: prev.at + 1 === this.state.highest.Height}))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const view = this.viewport()
|
||||
|
||||
const content = <div className="ChainExplorer" onWheel={this.scroll}>{view.map(row => {
|
||||
const base = this.state.at - this.state.at % rows
|
||||
const className = row === this.state.at ? 'ChainExplorer-at' : (row < base ? 'ChainExplorer-after' : 'ChainExplorer-before')
|
||||
let info = <span>(fetching)</span>
|
||||
let h = <i>{row}</i>
|
||||
if(this.state.cache[row]) {
|
||||
const ts = this.state.cache[row]
|
||||
|
||||
h = ts.Height
|
||||
|
||||
let msgc = -1
|
||||
if(ts.Cids[0] && this.state.messages[ts.Cids[0]['/']]) { // TODO: get from all blks
|
||||
msgc = this.state.messages[ts.Cids[0]['/']].length
|
||||
}
|
||||
if(msgc > 0) {
|
||||
msgc = <b>{msgc}</b>
|
||||
}
|
||||
let time = '?'
|
||||
if(this.state.cache[row - 1]){
|
||||
time = <span>{ts.Blocks[0].Timestamp - this.state.cache[row - 1].Blocks[0].Timestamp}s</span>
|
||||
}
|
||||
|
||||
info = <span>
|
||||
<BlockLinks cids={ts.Cids} blocks={ts.Blocks} conn={this.props.client} mountWindow={this.props.mountWindow} /> Msgs: {msgc} ΔT:{time}
|
||||
</span>
|
||||
}
|
||||
|
||||
return <div key={row} className={className}>@{h} {info}</div>
|
||||
})}</div>
|
||||
|
||||
return (<Window initialSize={{width: 800}} onClose={this.props.onClose} title={`Chain Explorer ${this.state.follow ? '(Following)' : ''}`}>
|
||||
{content}
|
||||
</Window>)
|
||||
}
|
||||
}
|
||||
|
||||
export default ChainExplorer
|
@ -1,228 +0,0 @@
|
||||
import React from 'react'
|
||||
import Address from './Address'
|
||||
import Window from './Window'
|
||||
import Fil from './Fil'
|
||||
|
||||
const dealStates = [
|
||||
'Unknown',
|
||||
'ProposalNotFound',
|
||||
'ProposalRejected',
|
||||
'ProposalAccepted',
|
||||
'Staged',
|
||||
'Sealing',
|
||||
'ProposalSigned',
|
||||
'Published',
|
||||
'Committed',
|
||||
'Active',
|
||||
'Failing',
|
||||
'Recovering',
|
||||
'Expired',
|
||||
'NotFound',
|
||||
|
||||
'Validating',
|
||||
'Transferring',
|
||||
'VerifyData',
|
||||
'Publishing',
|
||||
'Error'
|
||||
]
|
||||
|
||||
class Client extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
miners: ['t0101'],
|
||||
ask: { Price: '1000000000' }, // 2x min default ask to account for bin packing (could also do the math correctly below, but..)
|
||||
|
||||
kbs: 1,
|
||||
blocks: 12,
|
||||
total: 36000,
|
||||
miner: 't0101',
|
||||
|
||||
deals: [],
|
||||
|
||||
blockDelay: 10
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
let ver = await this.props.client.call('Filecoin.Version', [])
|
||||
this.setState({ blockDelay: ver.BlockDelay })
|
||||
|
||||
this.getDeals()
|
||||
setInterval(this.getDeals, 1325)
|
||||
}
|
||||
|
||||
getDeals = async () => {
|
||||
let miners = await this.props.client.call('Filecoin.StateListMiners', [
|
||||
null
|
||||
])
|
||||
let deals = await this.props.client.call('Filecoin.ClientListDeals', [])
|
||||
miners.sort()
|
||||
this.setState({ deals, miners })
|
||||
}
|
||||
|
||||
update = name => e => this.setState({ [name]: e.target.value })
|
||||
|
||||
makeDeal = async () => {
|
||||
let perBlk =
|
||||
((this.state.ask.Price * this.state.kbs * 1000) / (1 << 30)) * 2
|
||||
|
||||
let file = await this.props.pondClient.call('Pond.CreateRandomFile', [
|
||||
this.state.kbs * 1000
|
||||
]) // 1024 won't fit in 1k blocks :(
|
||||
let cid = await this.props.client.call('Filecoin.ClientImport', [
|
||||
{
|
||||
Path: file,
|
||||
IsCar: false
|
||||
}
|
||||
])
|
||||
let dealcid = await this.props.client.call('Filecoin.ClientStartDeal', [
|
||||
cid,
|
||||
this.state.miner,
|
||||
`${Math.round(perBlk)}`,
|
||||
Number(this.state.blocks)
|
||||
])
|
||||
console.log('deal cid: ', dealcid)
|
||||
}
|
||||
|
||||
retrieve = deal => async () => {
|
||||
console.log(deal)
|
||||
let client = await this.props.client.call(
|
||||
'Filecoin.WalletDefaultAddress',
|
||||
[]
|
||||
)
|
||||
|
||||
let order = {
|
||||
Root: deal.PieceRef,
|
||||
Size: deal.Size,
|
||||
// TODO: support offset
|
||||
Total: String(deal.Size * 2),
|
||||
|
||||
Client: client,
|
||||
Miner: deal.Miner
|
||||
}
|
||||
|
||||
await this.props.client.call('Filecoin.ClientRetrieve', [
|
||||
order,
|
||||
{
|
||||
Path: '/dev/null',
|
||||
IsCAR: false
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
render() {
|
||||
let perBlk = this.state.ask.Price * this.state.kbs * 1000
|
||||
let total = perBlk * this.state.blocks
|
||||
let days = (this.state.blocks * this.state.blockDelay) / 60 / 60 / 24
|
||||
|
||||
let dealMaker = (
|
||||
<div hidden={!this.props.pondClient}>
|
||||
<div>
|
||||
<span>Make Deal: </span>
|
||||
<select>
|
||||
{this.state.miners.map(m => (
|
||||
<option key={m} value={m}>
|
||||
{m}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<span>
|
||||
{' '}
|
||||
Ask:{' '}
|
||||
<b>
|
||||
<Fil>{this.state.ask.Price}</Fil>
|
||||
</b>{' '}
|
||||
Fil/Byte/Block
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
Data Size:{' '}
|
||||
<input
|
||||
type="text"
|
||||
placeholder="KBs"
|
||||
defaultValue={1}
|
||||
onChange={this.update('kbs')}
|
||||
style={{ width: '5em' }}
|
||||
/>
|
||||
KB; Duration:
|
||||
<input
|
||||
type="text"
|
||||
placeholder="blocks"
|
||||
defaultValue={12}
|
||||
onChange={this.update('blocks')}
|
||||
style={{ width: '5em' }}
|
||||
/>
|
||||
Blocks
|
||||
</div>
|
||||
<div>
|
||||
Total: <Fil>{total}</Fil>; {days} Days
|
||||
</div>
|
||||
<button onClick={this.makeDeal}>Deal!</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
let deals = this.state.deals.map((deal, i) => (
|
||||
<div key={i}>
|
||||
<ul>
|
||||
<li>
|
||||
{i}. Proposal: {deal.ProposalCid['/'].substr(0, 18)}...{' '}
|
||||
<Address
|
||||
nobalance={true}
|
||||
client={this.props.client}
|
||||
addr={deal.Provider}
|
||||
mountWindow={this.props.mountWindow}
|
||||
/>
|
||||
: <b>{dealStates[deal.State]}</b>
|
||||
{dealStates[deal.State] === 'Complete' ? (
|
||||
<span>
|
||||
|
||||
<a href="#" onClick={this.retrieve(deal)}>
|
||||
[Retrieve]
|
||||
</a>
|
||||
</span>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
<ul>
|
||||
<li>
|
||||
Data: {deal.PieceRef['/']}, <b>{deal.Size}</b>B; Duration:{' '}
|
||||
<b>{deal.Duration}</b>Blocks
|
||||
</li>
|
||||
<li>
|
||||
Total: <b>{deal.TotalPrice}</b>FIL; Per Block:{' '}
|
||||
<b>
|
||||
{Math.round((deal.TotalPrice / deal.Duration) * 100) / 100}
|
||||
</b>
|
||||
FIL; PerMbyteByteBlock:{' '}
|
||||
<b>
|
||||
{Math.round(
|
||||
(deal.TotalPrice / deal.Duration / (deal.Size / 1000000)) *
|
||||
100
|
||||
) / 100}
|
||||
</b>
|
||||
FIL
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
))
|
||||
|
||||
return (
|
||||
<Window
|
||||
title={'Client - Node ' + this.props.node.ID}
|
||||
onClose={this.props.onClose}
|
||||
initialSize={{ width: 600, height: 400 }}
|
||||
>
|
||||
<div className="Client">
|
||||
<div>{dealMaker}</div>
|
||||
<div>{deals}</div>
|
||||
</div>
|
||||
</Window>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Client
|
@ -1,126 +0,0 @@
|
||||
import React from 'react';
|
||||
import Window from "./Window";
|
||||
|
||||
async function awaitReducer(prev, c) {
|
||||
return {...await prev, ...await c}
|
||||
}
|
||||
|
||||
class ConnMgr extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.connect = this.connect.bind(this)
|
||||
this.connectAll = this.connectAll.bind(this)
|
||||
this.connect1 = this.connect1.bind(this)
|
||||
this.connectChain = this.connectChain.bind(this)
|
||||
this.getActualState = this.getActualState.bind(this)
|
||||
|
||||
this.state = {conns: {}, lock: true}
|
||||
|
||||
this.getActualState()
|
||||
setInterval(this.getActualState, 500)
|
||||
}
|
||||
|
||||
async getActualState() {
|
||||
const nodes = this.props.nodes
|
||||
let keys = Object.keys(nodes)
|
||||
|
||||
const newConns = await keys.filter((_, i) => i > 0).filter(kfrom => this.props.nodes[kfrom].conn !== undefined).map(async (kfrom, i) => {
|
||||
return keys.filter((_, j) => i >= j).filter(kto => this.props.nodes[kto].conn !== undefined).map(async kto => {
|
||||
|
||||
const fromNd = this.props.nodes[kfrom]
|
||||
const toNd = this.props.nodes[kto]
|
||||
|
||||
const connectedness = await fromNd.conn.call('Filecoin.NetConnectedness', [toNd.peerid])
|
||||
|
||||
return {[`${kfrom},${kto}`]: connectedness === 1}
|
||||
}).reduce(awaitReducer, Promise.resolve({}))
|
||||
}).reduce(awaitReducer, Promise.resolve({}))
|
||||
|
||||
this.setState({conns: newConns, lock: false})
|
||||
}
|
||||
|
||||
async connect(action, from, to, noupdate) {
|
||||
const fromNd = this.props.nodes[from]
|
||||
const toNd = this.props.nodes[to]
|
||||
|
||||
if (action) {
|
||||
const toPeerInfo = await toNd.conn.call('Filecoin.NetAddrsListen', [])
|
||||
|
||||
await fromNd.conn.call('Filecoin.NetConnect', [toPeerInfo])
|
||||
} else {
|
||||
await fromNd.conn.call('Filecoin.NetDisconnect', [toNd.peerid])
|
||||
}
|
||||
|
||||
if (!noupdate)
|
||||
this.setState(prev => ({conns: {...prev.conns, [`${from},${to}`]: action}}))
|
||||
}
|
||||
|
||||
connectAll(discon) {
|
||||
return () => {
|
||||
const nodes = this.props.nodes
|
||||
let keys = Object.keys(nodes)
|
||||
|
||||
keys.filter((_, i) => i > 0).forEach((kfrom, i) => {
|
||||
keys.filter((_, j) => i >= j).forEach((kto, i) => {
|
||||
this.connect(!discon, kfrom, kto, true)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
connect1() {
|
||||
const nodes = this.props.nodes
|
||||
let keys = Object.keys(nodes)
|
||||
|
||||
keys.filter((_, i) => i > 0).forEach((k, i) => {
|
||||
this.connect(true, k, keys[0])
|
||||
})
|
||||
}
|
||||
|
||||
connectChain() {
|
||||
const nodes = this.props.nodes
|
||||
let keys = Object.keys(nodes)
|
||||
|
||||
keys.filter((_, i) => i > 0).forEach((k, i) => {
|
||||
this.connect(true, k, keys[i])
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const nodes = this.props.nodes
|
||||
let keys = Object.keys(nodes)
|
||||
|
||||
const rows = keys.filter((_, i) => i > 0).map((k, i) => {
|
||||
const cols = keys.filter((_, j) => i >= j).map((kt, i) => {
|
||||
const checked = this.state.conns[`${k},${kt}`] === true
|
||||
|
||||
return (
|
||||
<td key={k + "," + kt}>
|
||||
<input checked={checked} disabled={this.state.lock} type="checkbox" onChange={e => this.connect(e.target.checked, k, kt)}/>
|
||||
</td>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<tr key={k}><td>{k}</td>{cols}</tr>
|
||||
)
|
||||
})
|
||||
|
||||
return(
|
||||
<Window title={`Connection Manager${this.state.lock ? ' (syncing)' : ''}`}>
|
||||
<table>
|
||||
<thead><tr><td></td>{keys.slice(0, -1).map((i) => (<td key={i}>{i}</td>))}</tr></thead>
|
||||
<tbody>{rows}</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<button onClick={this.connectAll(true)}>DisonnAll</button>
|
||||
<button onClick={this.connectAll(false)}>ConnAll</button>
|
||||
<button onClick={this.connect1}>Conn1</button>
|
||||
<button onClick={this.connectChain}>ConnChain</button>
|
||||
</div>
|
||||
</Window>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ConnMgr
|
@ -1,69 +0,0 @@
|
||||
import React from 'react';
|
||||
import {BlockLinks} from "./BlockLink";
|
||||
import Window from "./Window";
|
||||
|
||||
function styleForHDiff(max, act) {
|
||||
switch (max - act) {
|
||||
case 0:
|
||||
return {background: '#004400'}
|
||||
case 1:
|
||||
return {background: '#aaaa00'}
|
||||
default:
|
||||
return {background: '#aa0000'}
|
||||
}
|
||||
}
|
||||
|
||||
class Consensus extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
maxH: -1,
|
||||
tipsets: []
|
||||
}
|
||||
|
||||
this.updateNodes = this.updateNodes.bind(this)
|
||||
|
||||
setInterval(this.updateNodes, 333)
|
||||
}
|
||||
|
||||
async updateNodes() {
|
||||
const nodes = this.props.nodes
|
||||
let keys = Object.keys(nodes).filter(k => !nodes[k].Storage)
|
||||
|
||||
const tipsets = await keys.map(async k => {
|
||||
const tipset = await nodes[k].conn.call("Filecoin.ChainHead", [])
|
||||
return [k, tipset]
|
||||
}).reduce(async(p, i) => ([...await p, await i]), Promise.resolve([]))
|
||||
|
||||
const maxH = tipsets.reduce((p, [_, i]) => Math.max(p, i.Height), -1)
|
||||
|
||||
this.setState({maxH, tipsets})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<Window title={`Consensus`}>
|
||||
<div className='Consensus'>
|
||||
<div>Max Height: {this.state.maxH}</div>
|
||||
<div>
|
||||
<table cellSpacing={0}>
|
||||
<thead>
|
||||
<tr><td>Node</td><td>Height</td><td>TipSet</td></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this.state.tipsets.map(([k, ts]) => {
|
||||
return (
|
||||
<tr style={styleForHDiff(this.state.maxH, ts.Height)}>
|
||||
<td>{k}</td><td>{ts.Height}</td><td><BlockLinks cids={ts.Cids} conn={this.props.nodes[k].conn} mountWindow={this.props.mountWindow}/></td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</Window>)
|
||||
}
|
||||
}
|
||||
|
||||
export default Consensus
|
@ -1,22 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
function filStr(raw) {
|
||||
if(typeof raw !== 'string') {
|
||||
raw = String(raw)
|
||||
}
|
||||
if(raw.length < 19) {
|
||||
raw = '0'.repeat(19 - raw.length).concat(raw)
|
||||
}
|
||||
|
||||
let out = raw.substring(0, raw.length - 18).concat('.', raw.substring(raw.length - 18, raw.length)).replace(/\.0+$|0+$/g, '');
|
||||
return out ? out : '0'
|
||||
}
|
||||
|
||||
|
||||
class Fil extends React.Component {
|
||||
render() {
|
||||
return filStr(this.props.children)
|
||||
}
|
||||
}
|
||||
|
||||
export default Fil
|
@ -1,173 +0,0 @@
|
||||
import React from 'react';
|
||||
import { BlockLinks } from "./BlockLink";
|
||||
import Address from "./Address";
|
||||
import ChainExplorer from "./ChainExplorer";
|
||||
import Client from "./Client";
|
||||
import Window from "./Window";
|
||||
|
||||
class FullNode extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {}
|
||||
|
||||
this.loadInfo = this.loadInfo.bind(this)
|
||||
this.newSecpAddr = this.newSecpAddr.bind(this)
|
||||
this.newBLSAddr = this.newBLSAddr.bind(this)
|
||||
this.startStorageMiner = this.startStorageMiner.bind(this)
|
||||
this.explorer = this.explorer.bind(this)
|
||||
this.client = this.client.bind(this)
|
||||
this.stop = this.stop.bind(this)
|
||||
|
||||
this.loadInfo()
|
||||
let updates = setInterval(this.loadInfo, 2050)
|
||||
this.props.client.on('close', () => clearInterval(updates))
|
||||
}
|
||||
|
||||
async loadInfo() {
|
||||
const id = await this.props.client.call("Filecoin.ID", [])
|
||||
|
||||
const version = await this.props.client.call("Filecoin.Version", [])
|
||||
|
||||
const peers = await this.props.client.call("Filecoin.NetPeers", [])
|
||||
|
||||
const tipset = await this.props.client.call("Filecoin.ChainHead", [])
|
||||
|
||||
let addrs = await this.props.client.call('Filecoin.WalletList', [])
|
||||
let defaultAddr = ""
|
||||
if (addrs.length > 0) {
|
||||
defaultAddr = await this.props.client.call('Filecoin.WalletDefaultAddress', [])
|
||||
}
|
||||
let paychs = await this.props.client.call('Filecoin.PaychList', [])
|
||||
if(!paychs)
|
||||
paychs = []
|
||||
const vouchers = await Promise.all(paychs.map(paych => {
|
||||
return this.props.client.call('Filecoin.PaychVoucherList', [paych])
|
||||
}))
|
||||
|
||||
let mpoolPending = (await this.props.client.call('Filecoin.MpoolPending', [tipset.Cids])).length
|
||||
|
||||
this.setState(() => ({
|
||||
id: id,
|
||||
version: version,
|
||||
peers: peers.length,
|
||||
tipset: tipset,
|
||||
|
||||
mpoolPending: mpoolPending,
|
||||
|
||||
addrs: addrs,
|
||||
paychs: paychs,
|
||||
vouchers: vouchers,
|
||||
|
||||
defaultAddr: defaultAddr,
|
||||
}))
|
||||
}
|
||||
|
||||
async newSecpAddr() {
|
||||
const t = 1
|
||||
await this.props.client.call("Filecoin.WalletNew", [t])
|
||||
this.loadInfo()
|
||||
}
|
||||
|
||||
async newBLSAddr() {
|
||||
const t = 2
|
||||
await this.props.client.call("Filecoin.WalletNew", [t])
|
||||
this.loadInfo()
|
||||
}
|
||||
|
||||
startStorageMiner() {
|
||||
this.props.spawnStorageNode(this.props.node.Repo, this.props.client)
|
||||
}
|
||||
|
||||
explorer() {
|
||||
this.props.mountWindow((onClose) => <ChainExplorer onClose={onClose} ts={this.state.tipset} client={this.props.client} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
client() {
|
||||
this.props.mountWindow((onClose) => <Client onClose={onClose} node={this.props.node} client={this.props.client} pondClient={this.props.pondClient} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
async stop() {
|
||||
await this.props.stop()
|
||||
}
|
||||
|
||||
render() {
|
||||
let runtime = <div></div>
|
||||
|
||||
if (this.state.id) {
|
||||
let chainInfo = <div></div>
|
||||
if (this.state.tipset !== undefined) {
|
||||
chainInfo = (
|
||||
<div>
|
||||
Head: {
|
||||
<BlockLinks cids={this.state.tipset.Cids} conn={this.props.client} mountWindow={this.props.mountWindow} />
|
||||
} H:{this.state.tipset.Height} Mp:{this.state.mpoolPending} <a href="#" onClick={this.explorer}>[Explore]</a> <a href="#" onClick={this.client}>[Client]</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
let storageMine = <a href="#" onClick={this.startStorageMiner} hidden={!this.props.spawnStorageNode}>[Spawn Miner]</a>
|
||||
|
||||
let addresses = this.state.addrs.map((addr) => {
|
||||
let line = <Address client={this.props.client} addN={this.props.giveN} add10k={true} nonce={true} addr={addr} mountWindow={this.props.mountWindow}/>
|
||||
if (this.state.defaultAddr === addr) {
|
||||
line = <b>{line}</b>
|
||||
}
|
||||
return <div key={addr}>{line}</div>
|
||||
})
|
||||
let paychannels = this.state.paychs.map((addr, ak) => {
|
||||
const line = <Address client={this.props.client} addN={this.addN} add10k={true} addr={addr} mountWindow={this.props.mountWindow}/>
|
||||
const vouchers = this.state.vouchers[ak].map(voucher => {
|
||||
let extra = <span></span>
|
||||
if(voucher.Extra) {
|
||||
extra = <span>Verif: <<b><Address nobalance={true} client={this.props.client} addr={voucher.Extra.Actor} method={voucher.Extra.Method} mountWindow={this.props.mountWindow}/></b>></span>
|
||||
}
|
||||
|
||||
return <div key={`${addr} ${voucher.Lane} ${voucher.Nonce}`} className="FullNode-voucher">
|
||||
Voucher Nonce:<b>{voucher.Nonce}</b> Lane:<b>{voucher.Lane}</b> Amt:<b>{voucher.Amount}</b> TL:<b>{voucher.TimeLock}</b> MinCl:<b>{voucher.MinCloseHeight}</b> {extra}
|
||||
</div>
|
||||
})
|
||||
return <div key={addr}>
|
||||
{line}
|
||||
{vouchers}
|
||||
</div>
|
||||
})
|
||||
|
||||
runtime = (
|
||||
<div>
|
||||
<div>{this.props.node.ID} - v{this.state.version.Version}, <abbr title={this.state.id}>{this.state.id.substr(-8)}</abbr>, {this.state.peers} peers</div>
|
||||
<div>Repo: LOTUS_PATH={this.props.node.Repo}</div>
|
||||
{chainInfo}
|
||||
<div>
|
||||
{storageMine}
|
||||
</div>
|
||||
<div>
|
||||
<div>Balances: [New <a href="#" onClick={this.newSecpAddr}>[Secp256k1]</a> <a href="#" onClick={this.newBLSAddr}>[BLS]</a>]</div>
|
||||
<div>{addresses}</div>
|
||||
<div>{paychannels}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
let nodeID = this.props.node.ID ? this.props.node.ID : ''
|
||||
let nodePos = this.props.node.ID ? {x: this.props.node.ID*30, y: this.props.node.ID * 30} : 'center'
|
||||
|
||||
return (
|
||||
<Window
|
||||
title={"Node " + nodeID}
|
||||
initialPosition={nodePos}
|
||||
initialSize={{width: 690, height: 300}}
|
||||
onClose={this.stop} >
|
||||
<div className="CristalScroll">
|
||||
<div className="FullNode">
|
||||
{runtime}
|
||||
</div>
|
||||
</div>
|
||||
</Window>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default FullNode
|
@ -1,31 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Terminal } from 'xterm';
|
||||
import { AttachAddon } from "xterm-addon-attach";
|
||||
import 'xterm/dist/xterm.css';
|
||||
import * as fit from 'xterm/lib/addons/fit/fit';
|
||||
import Window from "./Window";
|
||||
|
||||
class Logs extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.termRef = React.createRef()
|
||||
this.winRef = React.createRef()
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
Terminal.applyAddon(fit);
|
||||
|
||||
this.terminal = new Terminal({convertEol: true, fontSize: 11});
|
||||
this.terminal.loadAddon(new AttachAddon(new WebSocket(`ws://127.0.0.1:2222/logs/${this.props.node}`), {bidirectional: false, inputUtf8: true}))
|
||||
this.terminal.open(this.termRef.current)
|
||||
setInterval(() => this.terminal.fit(), 200)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Window className="Logs-window" onClose={this.props.onClose} initialSize={{width: 1000, height: 480}} title={`Node ${this.props.node} Logs`}>
|
||||
<div ref={this.termRef} className="Logs"/>
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
export default Logs
|
@ -1,110 +0,0 @@
|
||||
import React from 'react';
|
||||
import {Link} from "react-router-dom";
|
||||
import {Client} from "rpc-websockets";
|
||||
|
||||
class Index extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {rpcUrl: "ws://127.0.0.1:1234/rpc/v0", rpcToken: '', conns: {}, info: {}}
|
||||
|
||||
const initialState = JSON.parse(window.localStorage.getItem('saved-nodes'))
|
||||
if (initialState) {
|
||||
this.state.nodes = initialState
|
||||
} else {
|
||||
this.state.nodes = []
|
||||
}
|
||||
this.state.nodes.forEach((n, i) => this.connTo(i, n))
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
window.localStorage.setItem('saved-nodes', JSON.stringify(this.state.nodes))
|
||||
//this.state.nodes.filter(i => [i, this.state.conns[i]]).forEach(([i, n]) => this.connTo(i, n))
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
Object.keys(this.state.conns).forEach(c => this.state.conns[c].close())
|
||||
}
|
||||
|
||||
async updateInfo(n) {
|
||||
const conn = this.state.conns[n]
|
||||
const head = await conn.call('Filecoin.ChainHead', [])
|
||||
const peers = await conn.call('Filecoin.NetPeers', [])
|
||||
|
||||
this.setState(p => ({info: {...p.info, [n]: {head, peers}}}))
|
||||
}
|
||||
|
||||
connTo = async (n, node) => {
|
||||
const client = new Client(`${node.addr}?token=${node.token}`)
|
||||
client.on('open', async () => {
|
||||
this.setState(p => ({conns: {...p.conns, [n]: client}}))
|
||||
setInterval(() => this.updateInfo(n), 1333)
|
||||
})
|
||||
}
|
||||
|
||||
onAdd = () => {
|
||||
this.setState({addingNode: true})
|
||||
}
|
||||
|
||||
update = (name) => (e) => this.setState({ [name]: e.target.value })
|
||||
|
||||
tokenOk = () => {
|
||||
let m = this.state.rpcToken.match(/\.(.+)\./)
|
||||
// TODO: eww
|
||||
if(m && atob(m[1]) === '{"Allow":["read","write","sign","admin"]}') {
|
||||
return (
|
||||
<span>-Token OK-
|
||||
<div>
|
||||
<button onClick={this.addNode}>Add Node</button>
|
||||
</div>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return <span>-Expecting valid admin token-</span>
|
||||
}
|
||||
|
||||
addNode = async () => {
|
||||
this.setState(p => ({nodes: [...p.nodes, {addr: this.state.rpcUrl, token: this.state.rpcToken}], addingNode: true}))
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="Index">
|
||||
<div className="Index-nodes">
|
||||
<div>
|
||||
{
|
||||
this.state.nodes.map((node, i) => {
|
||||
let info = <span>[no conn]</span>
|
||||
if (this.state.info[i]) {
|
||||
const ni = this.state.info[i]
|
||||
info = <span>H:{ni.head.Height}; Peers:{ni.peers.length}</span>
|
||||
}
|
||||
|
||||
return <div className="Index-node">
|
||||
<span>{i}. {node.addr} <Link to={`/app/node/${i}`}>[OPEN UI]</Link> {info}</span>
|
||||
</div>}
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<a hidden={this.state.addingNode} href='#' onClick={this.onAdd} className="Button">[Add Node]</a>
|
||||
<div hidden={!this.state.addingNode}>
|
||||
<div>---------------</div>
|
||||
<div>
|
||||
+ RPC:<input defaultValue={"ws://127.0.0.1:1234/rpc/v0"} onChange={this.update("rpcUrl")}/>
|
||||
</div>
|
||||
<div>
|
||||
Token (<code>lotus auth create-token --perm admin</code>): <input onChange={this.update("rpcToken")}/>{this.tokenOk()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="Index-footer">
|
||||
<div>
|
||||
<Link to={"/app/pond"}>Open Pond</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Index
|
@ -1,198 +0,0 @@
|
||||
import React from 'react';
|
||||
import FullNode from "./FullNode";
|
||||
import ConnMgr from "./ConnMgr";
|
||||
import Consensus from "./Consensus";
|
||||
import StorageNode from "./StorageNode";
|
||||
import {Client} from "rpc-websockets";
|
||||
import pushMessage from "./chain/send";
|
||||
import Logs from "./Logs";
|
||||
import StorageNodeInit from "./StorageNodeInit";
|
||||
import Window from "./Window";
|
||||
|
||||
const [NodeUnknown, NodeRunning, NodeStopped] = [0, 1, 2]
|
||||
|
||||
class NodeList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
existingLoaded: false,
|
||||
nodes: {},
|
||||
|
||||
showConnMgr: false,
|
||||
showConsensus: false,
|
||||
}
|
||||
|
||||
// This binding is necessary to make `this` work in the callback
|
||||
this.spawnNode = this.spawnNode.bind(this)
|
||||
this.spawnStorageNode = this.spawnStorageNode.bind(this)
|
||||
this.connMgr = this.connMgr.bind(this)
|
||||
this.consensus = this.consensus.bind(this)
|
||||
this.transferNFrom1 = this.transferNFrom1.bind(this)
|
||||
|
||||
this.getNodes()
|
||||
}
|
||||
|
||||
async mountNode(node) {
|
||||
const token = await this.props.client.call('Pond.TokenFor', [node.ID])
|
||||
|
||||
const client = new Client(`ws://127.0.0.1:${node.APIPort}/rpc/v0?token=${token}`)
|
||||
client.on('open', async () => {
|
||||
const id = await client.call("Filecoin.ID", [])
|
||||
|
||||
this.setState(prev => ({
|
||||
nodes: {
|
||||
...prev.nodes,
|
||||
[node.ID]: {...node, conn: client, peerid: id}
|
||||
}
|
||||
}))
|
||||
|
||||
if (!node.Storage) {
|
||||
this.props.mountWindow((onClose) =>
|
||||
<FullNode key={node.ID}
|
||||
node={{...node}}
|
||||
client={client}
|
||||
pondClient={this.props.client}
|
||||
giveN={this.transferNFrom1}
|
||||
mountWindow={this.props.mountWindow}
|
||||
spawnStorageNode={this.spawnStorageNode}
|
||||
stop={this.stopNode(node.ID, onClose)}
|
||||
/>)
|
||||
} else {
|
||||
const fullId = await this.props.client.call('Pond.FullID', [node.ID])
|
||||
|
||||
this.props.mountWindow((onClose) =>
|
||||
<StorageNode node={{...node}}
|
||||
pondClient={this.props.client}
|
||||
fullConn={this.state.nodes[fullId].conn}
|
||||
mountWindow={this.props.mountWindow}
|
||||
stop={this.stopNode(node.ID, onClose)}
|
||||
/>)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async getNodes() {
|
||||
const nds = await this.props.client.call('Pond.Nodes')
|
||||
const nodes = nds.reduce((o, i) => {o[i.ID] = i; return o}, {})
|
||||
console.log('nds', nodes)
|
||||
|
||||
Object.keys(nodes).map(n => nodes[n]).filter(n => n.State === NodeRunning).forEach(n => this.mountNode(n))
|
||||
|
||||
this.setState({existingLoaded: true, nodes: nodes})
|
||||
}
|
||||
|
||||
async transferNFrom1(to, n) {
|
||||
const addrss = await this.state.nodes[1].conn.call('Filecoin.WalletList', [])
|
||||
const [bestaddr, bal] = await addrss.map(async addr => {
|
||||
let balance = 0
|
||||
try {
|
||||
balance = await this.state.nodes[1].conn.call('Filecoin.WalletBalance', [addr])
|
||||
} catch {
|
||||
balance = -1
|
||||
}
|
||||
return [addr, balance]
|
||||
}).reduce(async (c, n) => (await c)[1] > (await n)[1] ? await c : await n, Promise.resolve(['', -2]))
|
||||
|
||||
await pushMessage(this.state.nodes[1].conn, bestaddr, {
|
||||
To: to,
|
||||
From: bestaddr,
|
||||
Value: String(n),
|
||||
})
|
||||
}
|
||||
|
||||
async spawnNode() {
|
||||
const node = await this.props.client.call('Pond.Spawn')
|
||||
console.log(node)
|
||||
await this.mountNode(node)
|
||||
|
||||
this.setState(state => ({nodes: {...state.nodes, [node.ID]: node}}))
|
||||
}
|
||||
|
||||
async spawnStorageNode(fullRepo, fullConn) {
|
||||
let nodePromise = this.props.client.call('Pond.SpawnStorage', [fullRepo])
|
||||
|
||||
this.props.mountWindow((onClose) => <StorageNodeInit node={nodePromise} fullRepo={fullRepo} fullConn={fullConn} pondClient={this.props.client} onClose={onClose} mountWindow={this.props.mountWindow}/>)
|
||||
let node = await nodePromise
|
||||
await this.mountNode(node)
|
||||
|
||||
//this.setState(state => ({nodes: {...state.nodes, [node.ID]: node}}))
|
||||
}
|
||||
|
||||
stopNode = (id, closeWindow) => async () => {
|
||||
this.state.nodes[id].conn.close()
|
||||
await this.props.client.call('Pond.Stop', [id])
|
||||
closeWindow()
|
||||
this.setState(prev => ({
|
||||
nodes: {
|
||||
...prev.nodes,
|
||||
[id]: {...(prev.nodes[id]), State: NodeStopped, conn: undefined}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
startNode = (id) => async () => {
|
||||
let node = await this.props.client.call('Pond.RestartNode', [Number(id)])
|
||||
await this.mountNode(node)
|
||||
}
|
||||
|
||||
connMgr() {
|
||||
this.setState({showConnMgr: true})
|
||||
}
|
||||
|
||||
consensus() {
|
||||
this.setState({showConsensus: true})
|
||||
}
|
||||
|
||||
render() {
|
||||
let connMgr
|
||||
if (this.state.showConnMgr) {
|
||||
connMgr = (<ConnMgr nodes={this.state.nodes}/>)
|
||||
}
|
||||
|
||||
let consensus
|
||||
if (this.state.showConsensus) {
|
||||
consensus = (<Consensus nodes={this.state.nodes} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
return (
|
||||
<Window title={"Node List"} initialPosition="bottom-left">
|
||||
<div className={'NodeList'}>
|
||||
<div>
|
||||
<button onClick={this.spawnNode} disabled={!this.state.existingLoaded}>Spawn Node</button>
|
||||
<button onClick={this.connMgr} disabled={!this.state.existingLoaded && !this.state.showConnMgr}>Connections</button>
|
||||
<button onClick={this.consensus} disabled={!this.state.existingLoaded && !this.state.showConsensus}>Consensus</button>
|
||||
</div>
|
||||
<div>
|
||||
{Object.keys(this.state.nodes).map(n => {
|
||||
const nd = this.state.nodes[n]
|
||||
let type = "FULL"
|
||||
if (nd.Storage) {
|
||||
type = "STOR"
|
||||
}
|
||||
|
||||
let logs = "[logs]"
|
||||
let info = "[CONNECTING..]"
|
||||
if (nd.conn) {
|
||||
info = <span>{nd.peerid}</span>
|
||||
logs = <a href='#' onClick={() => this.props.mountWindow(cl => <Logs node={nd.ID} onClose={cl}/>)}>[logs]</a>
|
||||
}
|
||||
if (nd.State === NodeStopped) {
|
||||
info = <span>[stopped] <a href="#" onClick={this.startNode(n)}>[START]</a></span>
|
||||
}
|
||||
|
||||
return <div key={n}>
|
||||
{n} {type} {logs} {info}
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{connMgr}
|
||||
{consensus}
|
||||
</div>
|
||||
</Window>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NodeList
|
@ -1,57 +0,0 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import { Client } from 'rpc-websockets'
|
||||
import NodeList from "./NodeList";
|
||||
|
||||
|
||||
class Pond extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const client = new Client('ws://127.0.0.1:2222/rpc/v0')
|
||||
client.on('open', () => {
|
||||
this.setState(() => ({client: client}))
|
||||
})
|
||||
|
||||
this.state = {
|
||||
windows: {},
|
||||
nextWindow: 0,
|
||||
}
|
||||
|
||||
this.mountWindow = this.mountWindow.bind(this)
|
||||
}
|
||||
|
||||
mountWindow(cb) {
|
||||
const id = this.state.nextWindow
|
||||
this.setState({nextWindow: id + 1})
|
||||
|
||||
const window = cb(() => {
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: undefined}}))
|
||||
})
|
||||
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: window}}))
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.client === undefined) {
|
||||
return (
|
||||
<div className="Pond-connecting">
|
||||
<div>
|
||||
<div>Connecting to Pond RPC</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<NodeList client={this.state.client} mountWindow={this.mountWindow}/>
|
||||
<div>
|
||||
{Object.keys(this.state.windows).map((w, i) => <div key={i}>{this.state.windows[w]}</div>)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Pond
|
@ -1,67 +0,0 @@
|
||||
import React from 'react';
|
||||
import './App.css';
|
||||
import {Client} from "rpc-websockets";
|
||||
import FullNode from "./FullNode";
|
||||
|
||||
class SingleNode extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const nodes = JSON.parse(window.localStorage.getItem('saved-nodes'))
|
||||
const node = nodes[this.props.match.params.node]
|
||||
|
||||
const client = new Client(`${node.addr}?token=${node.token}`)
|
||||
client.on('open', async () => {
|
||||
this.setState(() => ({client: client}))
|
||||
})
|
||||
|
||||
this.state = {
|
||||
windows: {},
|
||||
nextWindow: 0,
|
||||
|
||||
addr: node.addr
|
||||
}
|
||||
}
|
||||
|
||||
mountWindow = (cb) => {
|
||||
const id = this.state.nextWindow
|
||||
this.setState({nextWindow: id + 1})
|
||||
|
||||
const window = cb(() => {
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: undefined}}))
|
||||
})
|
||||
|
||||
this.setState(prev => ({windows: {...prev.windows, [id]: window}}))
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.client === undefined) {
|
||||
return (
|
||||
<div className="SingleNode-connecting">
|
||||
<div>
|
||||
<div>Connecting to Node RPC:</div>
|
||||
<div>{`${this.state.addr}?token=****`}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
let node = <FullNode
|
||||
node={{Repo: '/i/dont/exist/fixme', ID: ''}}
|
||||
client={this.state.client}
|
||||
give1k={null}
|
||||
mountWindow={this.mountWindow}
|
||||
/>
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
{node}
|
||||
<div>
|
||||
{Object.keys(this.state.windows).map((w, i) => <div key={i}>{this.state.windows[w]}</div>)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default SingleNode;
|
@ -1,209 +0,0 @@
|
||||
import React from 'react'
|
||||
import Window from "./Window";
|
||||
import CID from "cids";
|
||||
import * as multihash from "multihashes";
|
||||
import code from "./chain/code";
|
||||
import Address from "./Address";
|
||||
import Fil from "./Fil";
|
||||
|
||||
class State extends React.Component {
|
||||
byCode = {
|
||||
[code.init]: InitState,
|
||||
[code.power]: PowerState,
|
||||
[code.market]: MarketState,
|
||||
[code.miner]: MinerState,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {Balance: -2, State: {}}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const tipset = this.props.tipset || await this.props.client.call("Filecoin.ChainHead", [])
|
||||
const actstate = await this.props.client.call('Filecoin.StateReadState', [this.props.addr, tipset.Cids])
|
||||
|
||||
const c = new CID(this.props.actor.Code['/'])
|
||||
const mh = multihash.decode(c.multihash)
|
||||
let code = mh.digest.toString()
|
||||
|
||||
this.setState({...actstate, code: code})
|
||||
}
|
||||
|
||||
render() {
|
||||
let state
|
||||
if(this.byCode[this.state.code]) {
|
||||
const Stelem = this.byCode[this.state.code]
|
||||
state = <Stelem addr={this.props.addr} actor={this.props.actor} client={this.props.client} mountWindow={this.props.mountWindow} tipset={this.props.tipset}/>
|
||||
} else {
|
||||
state = <div>{Object.keys(this.state.State || {}).map(k => <div key={k}>{k}: <span>{JSON.stringify(this.state.State[k])}</span></div>)}</div>
|
||||
}
|
||||
|
||||
const content = <div className="State">
|
||||
<div>Balance: <Fil>{this.state.Balance}</Fil></div>
|
||||
<div>---</div>
|
||||
{state}
|
||||
</div>
|
||||
return <Window initialSize={{width: 850, height: 400}} onClose={this.props.onClose} title={`Actor ${this.props.addr} ${this.props.tipset && this.props.tipset.Height || ''} ${this.state.code}`}>
|
||||
{content}
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
class InitState extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {actors: []}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const tipset = await this.props.client.call("Filecoin.ChainHead", []) // TODO: from props
|
||||
const actors = await this.props.client.call("Filecoin.StateListActors", [tipset.Cids])
|
||||
this.setState({actors: actors})
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.state.actors.sort((a, b) => (Number(a.substr(1)) > Number(b.substr(1))))
|
||||
.map(addr => <div key={addr}><Address addr={addr} client={this.props.client} mountWindow={this.props.mountWindow}/></div>)
|
||||
}
|
||||
}
|
||||
|
||||
class PowerState extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {actors: [], state: {State: {}}}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const tipset = await this.props.client.call("Filecoin.ChainHead", []) // TODO: from props
|
||||
const actors = await this.props.client.call("Filecoin.StateListMiners", [tipset.Cids])
|
||||
const state = await this.props.client.call('Filecoin.StateReadState', [this.props.addr, tipset.Cids])
|
||||
|
||||
this.setState({actors, state})
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<div>
|
||||
<div>Total Power: <b>{this.state.state.State.TotalStorage}</b></div>
|
||||
</div>
|
||||
<div>---</div>
|
||||
<div>{this.state.actors.sort((a, b) => (Number(a.substr(1)) > Number(b.substr(1))))
|
||||
.map(addr => <div key={addr}><Address miner={true} addr={addr} client={this.props.client} mountWindow={this.props.mountWindow}/></div>)}</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
class MarketState extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {participants: {}, deals: []}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const tipset = await this.props.client.call("Filecoin.ChainHead", []) // TODO: from props
|
||||
const participants = await this.props.client.call("Filecoin.StateMarketParticipants", [tipset.Cids])
|
||||
const deals = await this.props.client.call("Filecoin.StateMarketDeals", [tipset.Cids])
|
||||
const state = await this.props.client.call('Filecoin.StateReadState', [this.props.addr, tipset.Cids])
|
||||
this.setState({participants, deals, nextDeal: state.State.NextDealID})
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<div>
|
||||
<div>Participants:</div>
|
||||
<table>
|
||||
<tr><td>Address</td><td>Available</td><td>Locked</td></tr>
|
||||
{Object.keys(this.state.participants).map(p => <tr>
|
||||
<td><Address addr={p} client={this.props.client} mountWindow={this.props.mountWindow}/></td>
|
||||
<td>{this.state.participants[p].Available}</td>
|
||||
<td>{this.state.participants[p].Locked}</td>
|
||||
</tr>)}
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<div>---</div>
|
||||
<div>Deals ({this.state.nextDeal} Total):</div>
|
||||
<table>
|
||||
<tr><td>id</td><td>Started</td><td>Client</td><td>Provider</td><td>Size</td><td>Price</td><td>Duration</td></tr>
|
||||
{Object.keys(this.state.deals).map(d => <tr>
|
||||
<td>{d}</td>
|
||||
<td>{this.state.deals[d].State.SectorStartEpoch || "No"}</td>
|
||||
<td><Address short={true} addr={this.state.deals[d].Proposal.Client} client={this.props.client} mountWindow={this.props.mountWindow}/></td>
|
||||
<td><Address short={true} addr={this.state.deals[d].Proposal.Provider} client={this.props.client} mountWindow={this.props.mountWindow}/></td>
|
||||
<td>{this.state.deals[d].Proposal.PieceSize}B</td>
|
||||
<td>{this.state.deals[d].Proposal.StoragePricePerEpoch*(this.state.deals[d].Proposal.EndEpoch-this.state.deals[d].Proposal.StartEpoch)}</td>
|
||||
<td>{this.state.deals[d].Proposal.EndEpoch-this.state.deals[d].Proposal.StartEpoch}</td>
|
||||
</tr>)}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
class MinerState extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {state: {}, sectorSize: -1, worker: "", networkPower: 0, sectors: {}}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const tipset = await this.props.client.call("Filecoin.ChainHead", []) // TODO: from props
|
||||
|
||||
const state = await this.props.client.call('Filecoin.StateReadState', [this.props.addr, tipset.Cids])
|
||||
const sectorSize = await this.props.client.call("Filecoin.StateMinerSectorSize", [this.props.addr, tipset.Cids])
|
||||
const worker = await this.props.client.call("Filecoin.StateMinerWorker", [this.props.addr, tipset.Cids])
|
||||
|
||||
const tpow = await this.props.client.call("Filecoin.StateMinerPower", [this.props.addr, tipset.Cids])
|
||||
const networkPower = tpow.TotalPower
|
||||
|
||||
let sectors = {}
|
||||
|
||||
const sset = await this.props.client.call("Filecoin.StateMinerSectors", [this.props.addr, tipset.Cids]) || []
|
||||
const pset = await this.props.client.call("Filecoin.StateMinerProvingSet", [this.props.addr, tipset.Cids]) || []
|
||||
|
||||
sset.forEach(s => sectors[s.SectorID] = {...s, sectorSet: true})
|
||||
pset.forEach(s => sectors[s.SectorID] = {...(sectors[s.SectorID] || s), provingSet: true})
|
||||
|
||||
this.setState({state, sectorSize, worker, networkPower, sectors})
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.worker) {
|
||||
return <span>(...)</span>
|
||||
}
|
||||
|
||||
let state = this.state.state.State
|
||||
|
||||
return <div>
|
||||
<div>Worker: <Address addr={this.state.worker} client={this.props.client} mountWindow={this.props.mountWindow}/></div>
|
||||
<div>Sector Size: <b>{this.state.sectorSize/1024}</b> KiB</div>
|
||||
<div>Power: <b>todoPower</b> (<b>{1/this.state.networkPower*100}</b>%)</div>
|
||||
<div>Election Period Start: <b>{state.ElectionPeriodStart}</b></div>
|
||||
<div>Slashed: <b>{state.SlashedAt === 0 ? "NO" : state.SlashedAt}</b></div>
|
||||
<div>
|
||||
<div>----</div>
|
||||
<div>Sectors:</div>
|
||||
<table style={{overflowY: "scroll"}}>
|
||||
<thead>
|
||||
<tr><td>ID</td><td>CommD</td><td>CommR</td><td>SectorSet</td><td>Proving</td></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.keys(this.state.sectors).map(sid => <tr key={sid} style={{whiteSpace: 'nowrap'}}>
|
||||
<td>{sid}</td>
|
||||
<td>{this.state.sectors[sid].CommD}</td>
|
||||
<td>{this.state.sectors[sid].CommR}</td>
|
||||
<td>{this.state.sectors[sid].sectorSet ? 'X' : ' '}</td>
|
||||
<td>{this.state.sectors[sid].provingSet ? 'X' : ' '}</td>
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default State
|
@ -1,161 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Client } from 'rpc-websockets'
|
||||
import Address from "./Address";
|
||||
import Window from "./Window";
|
||||
|
||||
const stateConnected = 'connected'
|
||||
const stateConnecting = 'connecting'
|
||||
const stateGettingToken = 'getting-token'
|
||||
|
||||
let sealCodes = [
|
||||
"UndefinedSectorState",
|
||||
"Empty",
|
||||
"Packing",
|
||||
"Unsealed",
|
||||
"PreCommitting",
|
||||
"WaitSeed",
|
||||
"Committing",
|
||||
"CommitWait",
|
||||
"FinalizeSector",
|
||||
"Proving",
|
||||
|
||||
"SealFailed",
|
||||
"PreCommitFailed",
|
||||
"SealCommitFailed",
|
||||
"CommitFailed",
|
||||
"PackingFailed",
|
||||
|
||||
"FailedUnrecoverable",
|
||||
"Faulty",
|
||||
"FaultReported",
|
||||
"FaultedFinal",
|
||||
]
|
||||
|
||||
class StorageNode extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
state: stateGettingToken,
|
||||
id: "~",
|
||||
|
||||
mining: false,
|
||||
|
||||
statusCounts: [0, 0, 0, 0, 0]
|
||||
}
|
||||
|
||||
this.loadInfo = this.loadInfo.bind(this)
|
||||
this.pledgeSector = this.pledgeSector.bind(this)
|
||||
this.stop = this.stop.bind(this)
|
||||
|
||||
this.connect()
|
||||
}
|
||||
|
||||
async connect() {
|
||||
const token = await this.props.pondClient.call('Pond.TokenFor', [this.props.node.ID])
|
||||
|
||||
this.setState(() => ({
|
||||
state: stateConnecting,
|
||||
token: token,
|
||||
}))
|
||||
|
||||
const client = new Client(`ws://127.0.0.1:${this.props.node.APIPort}/rpc/v0?token=${token}`)
|
||||
client.on('open', async () => {
|
||||
this.setState(() => ({
|
||||
state: stateConnected,
|
||||
client: client,
|
||||
|
||||
version: {Version: "~version~"},
|
||||
id: "~peerid~",
|
||||
peers: -1,
|
||||
balances: []
|
||||
}))
|
||||
|
||||
const id = await this.state.client.call("Filecoin.ID", [])
|
||||
this.setState(() => ({id: id}))
|
||||
|
||||
// this.props.onConnect(client, id) // TODO: dedupe connecting part
|
||||
|
||||
let updates = setInterval(this.loadInfo, 1050)
|
||||
client.on('close', () => clearInterval(updates))
|
||||
})
|
||||
|
||||
console.log(token) // todo: use
|
||||
}
|
||||
|
||||
async loadInfo() {
|
||||
const version = await this.state.client.call("Filecoin.Version", [])
|
||||
const peers = await this.state.client.call("Filecoin.NetPeers", [])
|
||||
const actor = await this.state.client.call("Filecoin.ActorAddress", [])
|
||||
|
||||
const actorState = await this.props.fullConn.call('Filecoin.StateReadState', [actor, null])
|
||||
|
||||
this.setState({version: version, peers: peers.length, actor: actor, actorState: actorState})
|
||||
await this.stagedList()
|
||||
}
|
||||
|
||||
async stagedList() {
|
||||
let stagedList = await this.state.client.call("Filecoin.SectorsList", [])
|
||||
let staged = await stagedList
|
||||
.map(sector => this.state.client.call("Filecoin.SectorsStatus", [sector, false]))
|
||||
.reduce(async (p, n) => [...await p, await n], Promise.resolve([]))
|
||||
|
||||
let statusCounts = staged.reduce((p, n) => p.map((e, i) => e + (i === n.State ? 1 : 0) ), [0, 0, 0, 0, 0])
|
||||
|
||||
this.setState({staged, statusCounts})
|
||||
}
|
||||
|
||||
async pledgeSector() {
|
||||
await this.state.client.call("Filecoin.PledgeSector", [])
|
||||
}
|
||||
|
||||
sealStaged = async () => {
|
||||
await this.state.client.call("Filecoin.SectorsStagedSeal", [])
|
||||
}
|
||||
|
||||
async stop() {
|
||||
await this.props.stop()
|
||||
}
|
||||
|
||||
render() {
|
||||
let runtime = <div></div>
|
||||
if (this.state.actor) {
|
||||
const pledgeSector = <a href="#" onClick={this.pledgeSector}>[Pledge Sector]</a>
|
||||
const sealStaged = <a href="#" onClick={this.sealStaged}>[Seal Staged]</a>
|
||||
|
||||
runtime = (
|
||||
<div>
|
||||
<div>v{this.state.version.Version}, <abbr title={this.state.id}>{this.state.id.substr(-8)}</abbr>, {this.state.peers} peers</div>
|
||||
<div>Repo: LOTUS_MINER_PATH={this.props.node.Repo}</div>
|
||||
<div>
|
||||
{pledgeSector} {sealStaged}
|
||||
</div>
|
||||
<div>
|
||||
<Address client={this.props.fullConn} addr={this.state.actor} mountWindow={this.props.mountWindow}/>
|
||||
<span> <abbr title="Proving period end">EPS:</abbr> <b>{this.state.actorState.State.ElectionPeriodStart}</b></span>
|
||||
</div>
|
||||
<div>{this.state.statusCounts.map((c, i) => <span key={i}>{sealCodes[i]}: {c} | </span>)}</div>
|
||||
<div>
|
||||
{this.state.staged ? this.state.staged.map((s, i) => (
|
||||
<div key={i}>{s.SectorID} {sealCodes[s.State] || `unk ${s.State}`}</div>
|
||||
)) : <div/>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return <Window
|
||||
title={"Miner Node " + this.props.node.ID}
|
||||
initialPosition={{x: this.props.node.ID*30, y: this.props.node.ID * 30}}
|
||||
onClose={this.stop} >
|
||||
<div className="CristalScroll">
|
||||
<div className="StorageNode">
|
||||
{runtime}
|
||||
</div>
|
||||
</div>
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
export default StorageNode
|
@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
import Window from "./Window";
|
||||
|
||||
class StorageNodeInit extends React.Component {
|
||||
async componentDidMount() {
|
||||
const info = await this.props.node
|
||||
|
||||
this.props.onClose()
|
||||
//this.props.mountWindow((onClose) => <StorageNode node={info} fullRepo={this.props.fullRepo} fullConn={this.props.fullConn} pondClient={this.props.pondClient} onClose={onClose} mountWindow={this.props.mountWindow}/>)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Window
|
||||
title={"Miner initializing"}
|
||||
initialPosition={'center'}>
|
||||
<div className="CristalScroll">
|
||||
<div className="StorageNodeInit">
|
||||
Waiting for init, make sure at least one miner is enabled
|
||||
</div>
|
||||
</div>
|
||||
</Window>
|
||||
}
|
||||
}
|
||||
|
||||
export default StorageNodeInit
|
@ -1,15 +0,0 @@
|
||||
import React from 'react'
|
||||
import {Cristal} from "react-cristal";
|
||||
|
||||
class Window extends React.Component {
|
||||
render() {
|
||||
let props = {className: '', ...this.props}
|
||||
props.className = `${props.className} Window`
|
||||
|
||||
return <Cristal {...props}>
|
||||
{this.props.children}
|
||||
</Cristal>
|
||||
}
|
||||
}
|
||||
|
||||
export default Window
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"account": "fil/1/account",
|
||||
"cron": "fil/1/cron",
|
||||
"init": "fil/1/init",
|
||||
"market": "fil/1/storagemarket",
|
||||
"miner": "fil/1/storageminer",
|
||||
"multisig": "fil/1/multisig",
|
||||
"paych": "fil/1/paymentchannel",
|
||||
"power": "fil/1/storagepower",
|
||||
"reward": "fil/1/reward",
|
||||
"system": "fil/1/system",
|
||||
"verifreg": "fil/1/verifiedregistry"
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if _, err := os.Stat("code.json"); err != nil {
|
||||
panic(err) // note: must run in lotuspond/front/src/chain
|
||||
}
|
||||
|
||||
// TODO: ActorUpgrade: this is going to be a problem.
|
||||
names := map[string]string{
|
||||
"system": "fil/1/system",
|
||||
"init": "fil/1/init",
|
||||
"cron": "fil/1/cron",
|
||||
"account": "fil/1/account",
|
||||
"power": "fil/1/storagepower",
|
||||
"miner": "fil/1/storageminer",
|
||||
"market": "fil/1/storagemarket",
|
||||
"paych": "fil/1/paymentchannel",
|
||||
"multisig": "fil/1/multisig",
|
||||
"reward": "fil/1/reward",
|
||||
"verifreg": "fil/1/verifiedregistry",
|
||||
}
|
||||
|
||||
{
|
||||
b, err := json.MarshalIndent(names, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile("code.json", b, 0664); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
out := map[string][]string{}
|
||||
|
||||
for c, methods := range filcns.NewActorRegistry().Methods {
|
||||
name := builtin.ActorNameByCode(c)
|
||||
remaining := len(methods)
|
||||
|
||||
// iterate over actor methods in order.
|
||||
for i := abi.MethodNum(0); remaining > 0; i++ {
|
||||
m, ok := methods[i]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
out[name] = append(out[name], m.Num)
|
||||
remaining--
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
b, err := json.MarshalIndent(out, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile("methods.json", b, 0664); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,955 +0,0 @@
|
||||
{
|
||||
"fil/1/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/1/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/1/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/1/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/1/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/1/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/1/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/1/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20"
|
||||
],
|
||||
"fil/1/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/1/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/1/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
],
|
||||
"fil/2/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/2/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/2/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/2/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/2/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/2/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/2/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/2/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23"
|
||||
],
|
||||
"fil/2/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/2/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/2/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
],
|
||||
"fil/3/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/3/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/3/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/3/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/3/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/3/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/3/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/3/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24"
|
||||
],
|
||||
"fil/3/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/3/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/3/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
],
|
||||
"fil/4/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/4/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/4/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/4/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/4/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/4/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/4/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/4/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24"
|
||||
],
|
||||
"fil/4/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/4/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/4/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
],
|
||||
"fil/5/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/5/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/5/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/5/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/5/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/5/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/5/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/5/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26"
|
||||
],
|
||||
"fil/5/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/5/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/5/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
],
|
||||
"fil/6/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/6/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/6/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/6/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/6/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/6/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/6/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/6/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26"
|
||||
],
|
||||
"fil/6/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/6/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/6/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6"
|
||||
],
|
||||
"fil/7/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/7/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/7/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/7/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/7/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/7/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/7/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/7/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26",
|
||||
"27"
|
||||
],
|
||||
"fil/7/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/7/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/7/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7"
|
||||
],
|
||||
"fil/8/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/8/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/8/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/8/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/8/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/8/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/8/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/8/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26",
|
||||
"27"
|
||||
],
|
||||
"fil/8/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/8/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/8/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7"
|
||||
],
|
||||
"fil/9/account": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3"
|
||||
],
|
||||
"fil/9/cron": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/9/init": [
|
||||
"0",
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"fil/9/multisig": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/9/paymentchannel": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/9/reward": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4"
|
||||
],
|
||||
"fil/9/storagemarket": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/9/storageminer": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"24",
|
||||
"25",
|
||||
"26",
|
||||
"27",
|
||||
"28",
|
||||
"29",
|
||||
"30",
|
||||
"31"
|
||||
],
|
||||
"fil/9/storagepower": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"8",
|
||||
"9"
|
||||
],
|
||||
"fil/9/system": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"fil/9/verifiedregistry": [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7"
|
||||
]
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import util from 'ipld-dag-cbor'
|
||||
import { Buffer } from 'buffer'
|
||||
import { Tagged } from 'borc'
|
||||
|
||||
async function pushMessage(client, from, inmsg) {
|
||||
if(!inmsg.Params) {
|
||||
inmsg.Params = "oA==" // 0b101_00000: empty cbor map: {}
|
||||
}
|
||||
if(!inmsg.Value) {
|
||||
inmsg.Value = "0"
|
||||
}
|
||||
if(!inmsg.Method) {
|
||||
inmsg.Method = 0
|
||||
}
|
||||
|
||||
/* const msg = [
|
||||
inmsg.To,
|
||||
inmsg.From,
|
||||
|
||||
inmsg.Nonce,
|
||||
|
||||
inmsg.Value,
|
||||
|
||||
inmsg.GasPrice,
|
||||
inmsg.GasLimit,
|
||||
|
||||
inmsg.Method,
|
||||
Buffer.from(inmsg.Params, 'base64'),
|
||||
]*/
|
||||
|
||||
console.log(inmsg)
|
||||
|
||||
await client.call('Filecoin.MpoolPushMessage', [inmsg, null])
|
||||
}
|
||||
|
||||
export default pushMessage
|
@ -1,18 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
@ -1,162 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/filecoin-project/go-jsonrpc"
|
||||
)
|
||||
|
||||
const listenAddr = "127.0.0.1:2222"
|
||||
|
||||
type runningNode struct {
|
||||
cmd *exec.Cmd
|
||||
meta nodeInfo
|
||||
|
||||
mux *outmux
|
||||
stop func()
|
||||
}
|
||||
|
||||
var onCmd = &cli.Command{
|
||||
Name: "on",
|
||||
Usage: "run a command on a given node",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
client, err := apiClient(cctx.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nd, err := strconv.ParseInt(cctx.Args().Get(0), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
node := nodeByID(client.Nodes(), int(nd))
|
||||
var cmd *exec.Cmd
|
||||
if !node.Storage {
|
||||
cmd = exec.Command("./lotus", cctx.Args().Slice()[1:]...)
|
||||
cmd.Env = []string{
|
||||
"LOTUS_PATH=" + node.Repo,
|
||||
}
|
||||
} else {
|
||||
cmd = exec.Command("./lotus-miner")
|
||||
cmd.Env = []string{
|
||||
"LOTUS_MINER_PATH=" + node.Repo,
|
||||
"LOTUS_PATH=" + node.FullNode,
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err = cmd.Run()
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
var shCmd = &cli.Command{
|
||||
Name: "sh",
|
||||
Usage: "spawn shell with node shell variables set",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
client, err := apiClient(cctx.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nd, err := strconv.ParseInt(cctx.Args().Get(0), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
node := nodeByID(client.Nodes(), int(nd))
|
||||
shcmd := exec.Command("/bin/bash")
|
||||
if !node.Storage {
|
||||
shcmd.Env = []string{
|
||||
"LOTUS_PATH=" + node.Repo,
|
||||
}
|
||||
} else {
|
||||
shcmd.Env = []string{
|
||||
"LOTUS_MINER_PATH=" + node.Repo,
|
||||
"LOTUS_PATH=" + node.FullNode,
|
||||
}
|
||||
}
|
||||
|
||||
shcmd.Env = append(os.Environ(), shcmd.Env...)
|
||||
|
||||
shcmd.Stdin = os.Stdin
|
||||
shcmd.Stdout = os.Stdout
|
||||
shcmd.Stderr = os.Stderr
|
||||
|
||||
fmt.Printf("Entering shell for Node %d\n", nd)
|
||||
err = shcmd.Run()
|
||||
fmt.Printf("Closed pond shell\n")
|
||||
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
func nodeByID(nodes []nodeInfo, i int) nodeInfo {
|
||||
for _, n := range nodes {
|
||||
if n.ID == int32(i) {
|
||||
return n
|
||||
}
|
||||
}
|
||||
panic("no node with this id")
|
||||
}
|
||||
|
||||
func logHandler(api *api) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
id, err := strconv.ParseInt(path.Base(req.URL.Path), 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
api.runningLk.Lock()
|
||||
n := api.running[int32(id)]
|
||||
api.runningLk.Unlock()
|
||||
|
||||
n.mux.ServeHTTP(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
var runCmd = &cli.Command{
|
||||
Name: "run",
|
||||
Usage: "run lotuspond daemon",
|
||||
Action: func(cctx *cli.Context) error {
|
||||
rpcServer := jsonrpc.NewServer()
|
||||
a := &api{running: map[int32]*runningNode{}}
|
||||
rpcServer.Register("Pond", a)
|
||||
|
||||
http.Handle("/", http.FileServer(http.Dir("lotuspond/front/build")))
|
||||
http.HandleFunc("/app/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, "lotuspond/front/build/index.html")
|
||||
})
|
||||
|
||||
http.Handle("/rpc/v0", rpcServer)
|
||||
http.HandleFunc("/logs/", logHandler(a))
|
||||
|
||||
fmt.Printf("Listening on http://%s\n", listenAddr)
|
||||
return http.ListenAndServe(listenAddr, nil)
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "pond",
|
||||
Commands: []*cli.Command{
|
||||
runCmd,
|
||||
shCmd,
|
||||
onCmd,
|
||||
},
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
)
|
||||
|
||||
type outmux struct {
|
||||
errpw *io.PipeWriter
|
||||
outpw *io.PipeWriter
|
||||
|
||||
errpr *io.PipeReader
|
||||
outpr *io.PipeReader
|
||||
|
||||
n uint64
|
||||
outs map[uint64]*websocket.Conn
|
||||
|
||||
new chan *websocket.Conn
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
func newWsMux() *outmux {
|
||||
out := &outmux{
|
||||
n: 0,
|
||||
outs: map[uint64]*websocket.Conn{},
|
||||
new: make(chan *websocket.Conn),
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
|
||||
out.outpr, out.outpw = io.Pipe()
|
||||
out.errpr, out.errpw = io.Pipe()
|
||||
|
||||
go out.run()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *outmux) msgsToChan(r *io.PipeReader, ch chan []byte) {
|
||||
defer close(ch)
|
||||
br := bufio.NewReader(r)
|
||||
|
||||
for {
|
||||
buf, _, err := br.ReadLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out := make([]byte, len(buf)+1)
|
||||
copy(out, buf)
|
||||
out[len(out)-1] = '\n'
|
||||
|
||||
select {
|
||||
case ch <- out:
|
||||
case <-m.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *outmux) run() {
|
||||
stdout := make(chan []byte)
|
||||
stderr := make(chan []byte)
|
||||
go m.msgsToChan(m.outpr, stdout)
|
||||
go m.msgsToChan(m.errpr, stderr)
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg := <-stdout:
|
||||
for k, out := range m.outs {
|
||||
if err := out.WriteMessage(websocket.BinaryMessage, msg); err != nil {
|
||||
_ = out.Close()
|
||||
fmt.Printf("outmux write failed: %s\n", err)
|
||||
delete(m.outs, k)
|
||||
}
|
||||
}
|
||||
case msg := <-stderr:
|
||||
for k, out := range m.outs {
|
||||
if err := out.WriteMessage(websocket.BinaryMessage, msg); err != nil {
|
||||
out.Close()
|
||||
fmt.Printf("outmux write failed: %s\n", err)
|
||||
delete(m.outs, k)
|
||||
}
|
||||
}
|
||||
case c := <-m.new:
|
||||
m.n++
|
||||
m.outs[m.n] = c
|
||||
case <-m.stop:
|
||||
for _, out := range m.outs {
|
||||
out.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func (m *outmux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if !strings.Contains(r.Header.Get("Connection"), "Upgrade") {
|
||||
fmt.Println("noupgrade")
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
if r.Header.Get("Sec-WebSocket-Protocol") != "" {
|
||||
w.Header().Set("Sec-WebSocket-Protocol", r.Header.Get("Sec-WebSocket-Protocol"))
|
||||
}
|
||||
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
|
||||
m.new <- c
|
||||
}
|
@ -1,280 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
|
||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||
"github.com/filecoin-project/lotus/chain/gen"
|
||||
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
|
||||
"github.com/filecoin-project/lotus/genesis"
|
||||
)
|
||||
|
||||
func init() {
|
||||
policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1)
|
||||
}
|
||||
|
||||
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(genesis2.MinerStart)
|
||||
if err != nil {
|
||||
return nodeInfo{}, err
|
||||
}
|
||||
|
||||
sbroot := filepath.Join(dir, "preseal")
|
||||
spt, err := miner.SealProofTypeFromSectorSize(2<<10, build.NewestNetworkVersion)
|
||||
if err != nil {
|
||||
return nodeInfo{}, err
|
||||
}
|
||||
|
||||
genm, ki, err := seed.PreSeal(genMiner, spt, 0, 2, sbroot, []byte("8"), nil, false)
|
||||
if err != nil {
|
||||
return nodeInfo{}, xerrors.Errorf("preseal failed: %w", err)
|
||||
}
|
||||
|
||||
if err := seed.WriteGenesisMiner(genMiner, sbroot, genm, ki); err != nil {
|
||||
return nodeInfo{}, xerrors.Errorf("failed to write genminer info: %w", err)
|
||||
}
|
||||
params = append(params, "--import-key="+filepath.Join(dir, "preseal", "pre-seal-t01000.key"))
|
||||
params = append(params, "--genesis-template="+filepath.Join(dir, "preseal", "genesis-template.json"))
|
||||
|
||||
// Create template
|
||||
|
||||
var template genesis.Template
|
||||
template.Miners = append(template.Miners, *genm)
|
||||
template.Accounts = append(template.Accounts, genesis.Actor{
|
||||
Type: genesis.TAccount,
|
||||
Balance: types.FromFil(5000000),
|
||||
Meta: (&genesis.AccountMeta{Owner: genm.Owner}).ActorMeta(),
|
||||
})
|
||||
template.VerifregRootKey = gen.DefaultVerifregRootkeyActor
|
||||
template.RemainderAccount = gen.DefaultRemainderAccountActor
|
||||
template.NetworkName = "pond-" + uuid.New().String()
|
||||
template.NetworkVersion = build.NewestNetworkVersion
|
||||
|
||||
tb, err := json.Marshal(&template)
|
||||
if err != nil {
|
||||
return nodeInfo{}, xerrors.Errorf("marshal genesis template: %w", err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "preseal", "genesis-template.json"), tb, 0664); err != nil {
|
||||
return nodeInfo{}, xerrors.Errorf("write genesis template: %w", err)
|
||||
}
|
||||
|
||||
// make genesis
|
||||
genf, err := ioutil.TempFile(os.TempDir(), "lotus-genesis-")
|
||||
if err != nil {
|
||||
return nodeInfo{}, err
|
||||
}
|
||||
|
||||
api.genesis = genf.Name()
|
||||
genParam = "--lotus-make-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()
|
||||
},
|
||||
}
|
||||
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", "--nosync"}
|
||||
if fullNodeRepo == api.running[1].meta.Repo {
|
||||
presealPrefix := filepath.Join(fullNodeRepo, "preseal")
|
||||
initArgs = []string{"init", "--actor=t01000", "--genesis-miner", "--pre-sealed-sectors=" + presealPrefix, "--pre-sealed-metadata=" + filepath.Join(presealPrefix, "pre-seal-t01000.json")}
|
||||
}
|
||||
|
||||
id := atomic.AddInt32(&api.cmds, 1)
|
||||
cmd := exec.Command("./lotus-miner", initArgs...)
|
||||
cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile)
|
||||
cmd.Stdout = io.MultiWriter(os.Stdout, logfile)
|
||||
cmd.Env = append(os.Environ(), "LOTUS_MINER_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-miner", "run", "--miner-api", fmt.Sprintf("%d", 2500+id), "--nosync")
|
||||
cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile, mux.errpw)
|
||||
cmd.Stdout = io.MultiWriter(os.Stdout, logfile, mux.outpw)
|
||||
cmd.Env = append(os.Environ(), "LOTUS_MINER_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()
|
||||
},
|
||||
}
|
||||
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-miner", "run", "--miner-api", fmt.Sprintf("%d", 2500+id), "--nosync")
|
||||
} 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()
|
||||
}
|
||||
|
||||
nd.meta.State = NodeRunning
|
||||
|
||||
time.Sleep(time.Millisecond * 750) // TODO: Something less terrible
|
||||
|
||||
return nd.meta, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user