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-chainwatch
|
||||||
/lotus-shed
|
/lotus-shed
|
||||||
/lotus-sim
|
/lotus-sim
|
||||||
/lotus-pond
|
|
||||||
/lotus-townhall
|
/lotus-townhall
|
||||||
/lotus-fountain
|
/lotus-fountain
|
||||||
/lotus-stats
|
/lotus-stats
|
||||||
@ -21,8 +20,6 @@
|
|||||||
/docgen-md
|
/docgen-md
|
||||||
/docgen-openrpc
|
/docgen-openrpc
|
||||||
/bench.json
|
/bench.json
|
||||||
/lotuspond/front/node_modules
|
|
||||||
/lotuspond/front/build
|
|
||||||
/cmd/lotus-townhall/townhall/node_modules
|
/cmd/lotus-townhall/townhall/node_modules
|
||||||
/cmd/lotus-townhall/townhall/build
|
/cmd/lotus-townhall/townhall/build
|
||||||
/cmd/lotus-townhall/townhall/package-lock.json
|
/cmd/lotus-townhall/townhall/package-lock.json
|
||||||
|
@ -43,9 +43,6 @@ issues:
|
|||||||
|
|
||||||
exclude-use-default: false
|
exclude-use-default: false
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
- path: lotuspond
|
|
||||||
linters:
|
|
||||||
- errcheck
|
|
||||||
|
|
||||||
- path: node/modules/lp2p
|
- path: node/modules/lp2p
|
||||||
linters:
|
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}"
|
@curl -X POST 'http://benchmark.kittyhawk.wtf/benchmark' -d '@bench.json' -u "${benchmark_http_cred}"
|
||||||
.PHONY: benchmarks
|
.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:
|
lotus-fountain:
|
||||||
rm -f lotus-fountain
|
rm -f lotus-fountain
|
||||||
$(GOCC) build $(GOFLAGS) -o lotus-fountain ./cmd/lotus-fountain
|
$(GOCC) build $(GOFLAGS) -o lotus-fountain ./cmd/lotus-fountain
|
||||||
@ -299,9 +287,6 @@ type-gen: api-gen
|
|||||||
$(GOCC) generate -x ./...
|
$(GOCC) generate -x ./...
|
||||||
goimports -w api/
|
goimports -w api/
|
||||||
|
|
||||||
method-gen: api-gen
|
|
||||||
(cd ./lotuspond/front/src/chain && $(GOCC) run ./methodgen.go)
|
|
||||||
|
|
||||||
actors-code-gen:
|
actors-code-gen:
|
||||||
$(GOCC) run ./gen/inline-gen . gen/inlinegen-data.json
|
$(GOCC) run ./gen/inline-gen . gen/inlinegen-data.json
|
||||||
$(GOCC) run ./chain/actors/agen
|
$(GOCC) run ./chain/actors/agen
|
||||||
@ -367,7 +352,7 @@ docsgen-openrpc-gateway: docsgen-openrpc-bin
|
|||||||
fiximports:
|
fiximports:
|
||||||
./scripts/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"
|
@echo ">>> IF YOU'VE MODIFIED THE CLI OR CONFIG, REMEMBER TO ALSO MAKE docsgen-cli"
|
||||||
.PHONY: gen
|
.PHONY: gen
|
||||||
|
|
||||||
|
4
go.mod
4
go.mod
@ -63,7 +63,6 @@ require (
|
|||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/mux v1.7.4
|
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/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026
|
||||||
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e
|
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
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-multihash v0.2.1
|
||||||
github.com/multiformats/go-varint v0.0.6
|
github.com/multiformats/go-varint v0.0.6
|
||||||
github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333
|
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/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e
|
||||||
github.com/prometheus/client_golang v1.12.1
|
github.com/prometheus/client_golang v1.12.1
|
||||||
github.com/raulk/clock v1.1.0
|
github.com/raulk/clock v1.1.0
|
||||||
@ -215,6 +213,7 @@ require (
|
|||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/go-cmp v0.5.8 // indirect
|
github.com/google/go-cmp v0.5.8 // indirect
|
||||||
github.com/google/gopacket v1.1.19 // 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/hannahhoward/cbor-gen-for v0.0.0-20200817222906-ea96cece81f1 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/huin/goupnp v1.0.3 // indirect
|
github.com/huin/goupnp v1.0.3 // indirect
|
||||||
@ -288,6 +287,7 @@ require (
|
|||||||
github.com/nxadm/tail v1.4.8 // indirect
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||||
github.com/opencontainers/runtime-spec v1.0.2 // 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/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||||
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
|
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // 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