Merge pull request #149 from filecoin-project/feat/pond-gas-updates

pond: Gas-related updates
This commit is contained in:
Łukasz Magiera 2019-08-20 13:54:30 +02:00 committed by GitHub
commit 78b548ab2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 275 additions and 32 deletions

View File

@ -15,7 +15,7 @@ import (
)
func main() {
logging.SetLogLevel("*", "DEBUG")
logging.SetLogLevel("*", "INFO")
local := []*cli.Command{
DaemonCmd,
}

1
go.mod
View File

@ -59,6 +59,7 @@ require (
github.com/multiformats/go-multiaddr-dns v0.0.3
github.com/multiformats/go-multiaddr-net v0.0.1
github.com/multiformats/go-multihash v0.0.6
github.com/opentracing/opentracing-go v1.1.0
github.com/pkg/errors v0.8.1
github.com/polydawn/refmt v0.0.0-20190809202753-05966cbd336a
github.com/smartystreets/assertions v1.0.1 // indirect

View File

@ -65,9 +65,11 @@ func (api *api) Spawn() (nodeInfo, error) {
return nodeInfo{}, err
}
mux := newWsMux()
cmd := exec.Command("./lotus", "daemon", genParam, "--api", fmt.Sprintf("%d", 2500+id))
cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile)
cmd.Stdout = io.MultiWriter(os.Stdout, logfile)
cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile, mux.errpw)
cmd.Stdout = io.MultiWriter(os.Stdout, logfile, mux.outpw)
cmd.Env = []string{"LOTUS_PATH=" + dir}
if err := cmd.Start(); err != nil {
return nodeInfo{}, err
@ -84,7 +86,9 @@ func (api *api) Spawn() (nodeInfo, error) {
cmd: cmd,
meta: info,
mux: mux,
stop: func() {
defer close(mux.stop)
defer errlogfile.Close()
defer logfile.Close()
},
@ -156,9 +160,11 @@ func (api *api) SpawnStorage(fullNodeRepo string) (nodeInfo, error) {
time.Sleep(time.Millisecond * 300)
mux := newWsMux()
cmd = exec.Command("./lotus-storage-miner", "run", "--api", fmt.Sprintf("%d", 2500+id))
cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile)
cmd.Stdout = io.MultiWriter(os.Stdout, logfile)
cmd.Stderr = io.MultiWriter(os.Stderr, errlogfile, mux.errpw)
cmd.Stdout = io.MultiWriter(os.Stdout, logfile, mux.outpw)
cmd.Env = []string{"LOTUS_STORAGE_PATH=" + dir, "LOTUS_PATH=" + fullNodeRepo}
if err := cmd.Start(); err != nil {
return nodeInfo{}, err
@ -178,7 +184,9 @@ func (api *api) SpawnStorage(fullNodeRepo string) (nodeInfo, error) {
cmd: cmd,
meta: info,
mux: mux,
stop: func() {
defer close(mux.stop)
defer errlogfile.Close()
defer logfile.Close()
},

View File

@ -13075,6 +13075,21 @@
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
"xterm": {
"version": "3.14.5",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-3.14.5.tgz",
"integrity": "sha512-DVmQ8jlEtL+WbBKUZuMxHMBgK/yeIZwkXB81bH+MGaKKnJGYwA+770hzhXPfwEIokK9On9YIFPRleVp/5G7z9g=="
},
"xterm-addon-attach": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/xterm-addon-attach/-/xterm-addon-attach-0.1.0.tgz",
"integrity": "sha512-vImYAP+AVoW/gnr4CIESrOr2MplzNxnrPX4YEkdk0EEkBOg3Pwnndu1xy7HnY0XZsfGRz7rfn71sAXfJDGLvUQ=="
},
"xterm-addon-fit": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.1.0.tgz",
"integrity": "sha512-DzYThnR5rXYX7JrOZ8rHGMU36BiTwYNFUOhhNwrDSFvoUR2MgwQrfA/JrqLE62KRj0D8bkRR7+xe7qGBp1O4Rw=="
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",

View File

@ -13,7 +13,10 @@
"react-dom": "^16.8.6",
"react-scripts": "3.0.1",
"rpc-websockets": "^4.5.1",
"styled-components": "^3.3.3"
"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",

View File

@ -4,9 +4,9 @@ import * as multihash from "multihashes";
import State from "./State";
import methods from "./chain/methods";
function truncAddr(addr) {
if (addr.length > 21) {
return <abbr title={addr}>{addr.substr(0, 18) + '..'}</abbr>
function truncAddr(addr, len) {
if (addr.length > len) {
return <abbr title={addr}>{addr.substr(0, len - 3) + '..'}</abbr>
}
return addr
}
@ -72,7 +72,7 @@ class Address extends React.Component {
if(this.props.add1k) {
add1k = <span>&nbsp;<a href="#" onClick={() => this.props.add1k(this.props.addr)}>[+1k]</a></span>
}
let addr = truncAddr(this.props.addr)
let addr = truncAddr(this.props.addr, this.props.short ? 12 : 17)
let actInfo = <span>(?)</span>
if(this.state.balance >= 0) {
@ -80,17 +80,21 @@ class Address extends React.Component {
addr = <a href="#" onClick={this.openState}>{addr}</a>
}
let balance = <span>:&nbsp;{this.state.balance}</span>
let balance = <span>:&nbsp;{this.state.balance}&nbsp;</span>
if(this.props.nobalance) {
balance = <span></span>
balance = <span/>
}
if(this.props.short) {
actInfo = <span/>
balance = <span/>
}
let transfer = <span></span>
let transfer = <span/>
if(this.props.transfer) {
transfer = <span>&nbsp;{this.props.transfer}FIL</span>
}
return <span>{addr}{balance}&nbsp;{actInfo}{add1k}{transfer}</span>
return <span>{addr}{balance}{actInfo}{add1k}{transfer}</span>
}
}

View File

@ -64,6 +64,7 @@
}
.ChainExplorer-at {
min-width: 40em;
background: #77ff77;
}
@ -74,3 +75,12 @@
.ChainExplorer-before {
background: #cccc00
}
.Logs {
width: 100%;
height: 100%;
}
.Logs-window :nth-child(2) {
height: 100%;
}

View File

@ -14,8 +14,16 @@ class Block extends React.Component {
async loadHeader() {
const header = await this.props.conn.call('Filecoin.ChainGetBlock', [this.props.cid])
const messages = await this.props.conn.call('Filecoin.ChainGetBlockMessages', [this.props.cid])
console.log(messages)
let messages = await this.props.conn.call('Filecoin.ChainGetBlockMessages', [this.props.cid])
let receipts = await this.props.conn.call('Filecoin.ChainGetBlockReceipts', [this.props.cid])
messages = [
...(messages.BlsMessages.map(m => ({...m, type: 'BLS'}))),
...(messages.SecpkMessages.map(m => ({...(m.Message), type: 'Secpk'})))
]
messages = messages.map((msg, k) => ({...msg, receipt: receipts[k]}))
this.setState({header: header, messages: messages})
}
@ -24,15 +32,12 @@ class Block extends React.Component {
if (this.state.header) {
let head = this.state.header
let messages = [
...(this.state.messages.BlsMessages.map(m => ({...m, type: 'BLS'}))),
...(this.state.messages.SecpkMessages.map(m => ({...(m.Message), type: 'Secpk'})))
].map(m => (
const messages = this.state.messages.map(m => (
<div>
<Address client={this.props.conn} addr={m.From} mountWindow={this.props.mountWindow}/><b>&nbsp;=>&nbsp;</b>
<Address client={this.props.conn} addr={m.To} mountWindow={this.props.mountWindow} transfer={m.Value} method={m.Method}/>
<span>&nbsp;{m.receipt.GasUsed}Gas</span>
{m.receipt.ExitCode !== 0 ? <span>&nbsp;<b>EXIT:{m.receipt.ExitCode}</b></span> : <span/>}
</div>
))

View File

@ -1,10 +1,19 @@
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 => <BlockLink key={c} conn={this.props.conn} cid={c} mountWindow={this.props.mountWindow}/>)
return this.props.cids.map((c, k) => {
let block
if(this.props.blocks) {
block = this.props.blocks[k]
}
return <BlockLink key={c} block={block} conn={this.props.conn} cid={c} mountWindow={this.props.mountWindow}/>
})
}
}
@ -20,7 +29,12 @@ class BlockLink extends React.Component {
}
render() {
return <a href="#" onClick={this.openBlockViewer}><abbr title={this.props.cid['/']}>{this.props.cid['/'].substr(-8)}</abbr></a>
let info = <span></span>
if(this.props.block) {
info = <span>&nbsp;(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>
}
}

View File

@ -114,12 +114,15 @@ class ChainExplorer extends React.Component {
const ts = this.state.cache[row]
let msgc = -1
if(ts.Cids[0] && this.state.messages[ts.Cids[0]['/']]) {
if(ts.Cids[0] && this.state.messages[ts.Cids[0]['/']]) { // TODO: get from all blks
msgc = this.state.messages[ts.Cids[0]['/']].SecpkMessages.length + this.state.messages[ts.Cids[0]['/']].BlsMessages.length
}
if(msgc > 0) {
msgc = <b>{msgc}</b>
}
info = <span>
<BlockLinks cids={ts.Cids} conn={this.props.client} mountWindow={this.props.mountWindow} /> Msgs: <b>{msgc}</b>
<BlockLinks cids={ts.Cids} blocks={ts.Blocks} conn={this.props.client} mountWindow={this.props.mountWindow} /> Msgs: {msgc}
</span>
}

View File

@ -63,10 +63,10 @@ class FullNode extends React.Component {
// TODO: Use actual miner address
// see cli/miner.go
this.setState({mining: true})
let addr = "t0523423423" // in case we have no wallets
if (this.state.defaultAddr) {
let addr = "t0101" // in case we have no wallets
/*if (this.state.defaultAddr) {
addr = this.state.defaultAddr
}
}*/
this.setState({mining: true})
await this.props.client.call("Filecoin.MinerStart", [addr])

View File

@ -0,0 +1,31 @@
import React from 'react'
import {Cristal} from "react-cristal";
import { Terminal } from 'xterm';
import { AttachAddon } from "xterm-addon-attach";
import 'xterm/dist/xterm.css';
import * as fit from 'xterm/lib/addons/fit/fit';
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 <Cristal className="Logs-window" onClose={this.props.onClose} initialSize={{width: 1000, height: 480}} title={`Node ${this.props.node} Logs`}>
<div ref={this.termRef} className="Logs"/>
</Cristal>
}
}
export default Logs

View File

@ -6,6 +6,7 @@ import {Cristal} from "react-cristal";
import StorageNode from "./StorageNode";
import {Client} from "rpc-websockets";
import pushMessage from "./chain/send";
import Logs from "./Logs";
class NodeList extends React.Component {
constructor(props) {
@ -130,13 +131,15 @@ class NodeList extends React.Component {
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>
}
return <div key={n}>
{n} {type} {info}
{n} {type} {logs} {info}
</div>
})}
</div>

View File

@ -4,7 +4,7 @@ import { Tagged } from 'borc'
async function pushMessage(client, from, inmsg) {
if(!inmsg.GasLimit) {
inmsg.GasLimit = "0"
inmsg.GasLimit = "1000"
}
if(!inmsg.GasPrice) {
inmsg.GasPrice = "0"

View File

@ -5,6 +5,7 @@ import (
"net/http"
"os"
"os/exec"
"path"
"strconv"
"github.com/filecoin-project/go-lotus/lib/jsonrpc"
@ -18,6 +19,7 @@ type runningNode struct {
cmd *exec.Cmd
meta nodeInfo
mux *outmux
stop func()
}
@ -107,15 +109,33 @@ func nodeById(nodes []nodeInfo, i int) nodeInfo {
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)
return
}
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()
rpcServer.Register("Pond", &api{running: map[int32]runningNode{}})
a := &api{running: map[int32]runningNode{}}
rpcServer.Register("Pond", a)
http.Handle("/", http.FileServer(http.Dir("lotuspond/front/build")))
http.Handle("/rpc/v0", rpcServer)
http.HandleFunc("/logs/", logHandler(a))
fmt.Printf("Listening on http://%s\n", listenAddr)
return http.ListenAndServe(listenAddr, nil)

124
lotuspond/outmux.go Normal file
View File

@ -0,0 +1,124 @@
package main
import (
"fmt"
"github.com/gorilla/websocket"
"github.com/opentracing/opentracing-go/log"
"io"
"net/http"
"strings"
"sync"
)
type outmux struct {
lk sync.Mutex
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)
for {
buf := make([]byte, 1)
n, err := r.Read(buf)
if err != nil {
return
}
select {
case ch <- buf[:n]:
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
return
}

View File

@ -80,6 +80,8 @@ func (m *Miner) Mine(ctx context.Context) {
b, err := m.mineOne(ctx, base)
if err != nil {
log.Errorf("mining block failed: %s", err)
log.Warn("waiting 400ms before attempting to mine a block")
time.Sleep(400 * time.Millisecond)
continue
}