pond: basic state inspection

This commit is contained in:
Łukasz Magiera 2019-08-10 03:54:45 +02:00
parent e430f86b69
commit 2229fae79d
10 changed files with 194 additions and 34 deletions

View File

@ -52,6 +52,11 @@ type SectorInfo struct {
CommR []byte
}
type ActorState struct {
Balance types.BigInt
State interface{}
}
type Common interface {
// Auth
AuthVerify(ctx context.Context, token string) ([]string, error)
@ -85,6 +90,8 @@ type FullNode interface {
ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
ChainGetBlockReceipts(context.Context, cid.Cid) ([]*types.MessageReceipt, error)
ChainGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
ChainReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*ActorState, error)
// if tipset is nil, we'll use heaviest
ChainCall(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error)
@ -136,6 +143,8 @@ type FullNode interface {
type StorageMiner interface {
Common
ActorAddresses(context.Context) ([]address.Address, error)
// Temp api for testing
StoreGarbageData(context.Context) (uint64, error)

View File

@ -48,6 +48,8 @@ type FullNodeStruct struct {
ChainGetBlockMessages func(context.Context, cid.Cid) (*BlockMessages, error) `perm:"read"`
ChainGetBlockReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
ChainCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
ChainGetActor func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) `perm:"read"`
ChainReadState func(context.Context, *types.Actor, *types.TipSet) (*ActorState, error) `perm:"read"`
MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"`
MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"`
@ -77,6 +79,8 @@ type StorageMinerStruct struct {
CommonStruct
Internal struct {
ActorAddresses func(context.Context) ([]address.Address, error) `perm:"read"`
StoreGarbageData func(context.Context) (uint64, error) `perm:"write"`
SectorsStatus func(context.Context, uint64) (sectorbuilder.SectorSealingStatus, error) `perm:"read"`
@ -171,6 +175,14 @@ func (c *FullNodeStruct) ChainCall(ctx context.Context, msg *types.Message, ts *
return c.Internal.ChainCall(ctx, msg, ts)
}
func (c *FullNodeStruct) ChainGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error) {
return c.Internal.ChainGetActor(ctx, actor, ts)
}
func (c *FullNodeStruct) ChainReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*ActorState, error) {
return c.Internal.ChainReadState(ctx, act, ts)
}
func (c *FullNodeStruct) WalletNew(ctx context.Context, typ string) (address.Address, error) {
return c.Internal.WalletNew(ctx, typ)
}
@ -227,6 +239,10 @@ func (c *FullNodeStruct) StateMinerProvingSet(ctx context.Context, addr address.
return c.Internal.StateMinerProvingSet(ctx, addr)
}
func (c *StorageMinerStruct) ActorAddresses(ctx context.Context) ([]address.Address, error) {
return c.Internal.ActorAddresses(ctx)
}
func (c *StorageMinerStruct) StoreGarbageData(ctx context.Context) (uint64, error) {
return c.Internal.StoreGarbageData(ctx)
}

View File

@ -4,8 +4,10 @@
"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",

View File

@ -0,0 +1,66 @@
import React from 'react'
import CID from 'cids'
import * as multihash from "multihashes";
import State from "./State";
function truncAddr(addr) {
if (addr.length > 21) {
return <abbr title={addr}>{addr.substr(0, 18) + '..'}</abbr>
}
return addr
}
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)
setInterval(this.refresh, 2050)
}
async refresh() {
let balance = 0
let actor = {}
try {
balance = await this.props.client.call('Filecoin.WalletBalance', [this.props.addr])
actor = await this.props.client.call('Filecoin.ChainGetActor', [this.props.addr, this.props.ts || null])
} catch (err) {
balance = -1
}
this.setState({balance, actor})
}
openState() {
this.props.mountWindow((onClose) => <State addr={this.props.addr} actor={this.state.actor} client={this.props.client} onClose={onClose}/>)
}
render() {
let add1k = <span/>
if(this.props.add1k) {
add1k = <a href="#" onClick={() => this.props.add1k(this.props.addr)}>[+1k]</a>
}
let addr = truncAddr(this.props.addr)
let actInfo = <span>(?)</span>
if(this.state.balance >= 0) {
const c = new CID(this.state.actor.Code['/'])
const mh = multihash.decode(c.multihash) // TODO: check identity
actInfo = <span>({mh.digest.toString()})</span>
addr = <a href="#" onClick={this.openState}>{addr}</a>
}
return <span>{addr}:&nbsp;{this.state.balance}&nbsp;{actInfo}&nbsp;{add1k}</span>
}
}
export default Address

View File

@ -1,6 +1,7 @@
import React from 'react';
import {Cristal} from "react-cristal";
import {BlockLinks} from "./BlockLink";
import Address from "./Address";
class Block extends React.Component {
constructor(props) {
@ -30,7 +31,9 @@ class Block extends React.Component {
...(this.state.messages.SecpkMessages.map(m => ({...(m.Message), type: 'Secpk'})))
].map(m => (
<div>
{m.From}<b> => </b>{m.To} {m.Value}FIL M{m.Method}
<Address client={this.props.conn} addr={m.From} mountWindow={this.props.mountWindow}/><b>=>&nbsp;</b>
<Address client={this.props.conn} addr={m.To} mountWindow={this.props.mountWindow}/>
{m.Value}FIL&nbsp;M{m.Method}
</div>
))
@ -39,7 +42,7 @@ class Block extends React.Component {
<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: {head.Miner}</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>Receipts: {head.MessageReceipts['/']}</div>
<div>State Root:&nbsp;{head.StateRoot['/']}</div>

View File

@ -3,18 +3,12 @@ import { Client } from 'rpc-websockets'
import Cristal from 'react-cristal'
import { BlockLinks } from "./BlockLink";
import StorageNodeInit from "./StorageNodeInit";
import Address from "./Address";
async function awaitListReducer(prev, c) {
return [...await prev, await c]
}
function truncAddr(addr) {
if (addr.length > 41) {
return <abbr title={addr}>{addr.substr(0, 38) + '...'}</abbr>
}
return addr
}
class FullNode extends React.Component {
constructor(props) {
super(props)
@ -42,13 +36,13 @@ class FullNode extends React.Component {
const tipset = await this.props.client.call("Filecoin.ChainHead", [])
const addrss = await this.props.client.call('Filecoin.WalletList', [])
const addrs = await this.props.client.call('Filecoin.WalletList', [])
let defaultAddr = ""
if (addrss.length > 0) {
if (addrs.length > 0) {
defaultAddr = await this.props.client.call('Filecoin.WalletDefaultAddress', [])
}
const balances = await addrss.map(async addr => {
/* const balances = await addrss.map(async addr => {
let balance = 0
try {
balance = await this.props.client.call('Filecoin.WalletBalance', [addr])
@ -56,7 +50,7 @@ class FullNode extends React.Component {
balance = -1
}
return [addr, balance]
}).reduce(awaitListReducer, Promise.resolve([]))
}).reduce(awaitListReducer, Promise.resolve([]))*/
this.setState(() => ({
id: id,
@ -64,7 +58,7 @@ class FullNode extends React.Component {
peers: peers.length,
tipset: tipset,
balances: balances,
addrs: addrs,
defaultAddr: defaultAddr}))
}
@ -117,10 +111,8 @@ class FullNode extends React.Component {
let storageMine = <a href="#" onClick={this.startStorageMiner}>[Spawn Storage Miner]</a>
let balances = this.state.balances.map(([addr, balance]) => {
let add1k = <a href="#" onClick={() => this.add1k(addr)}>[+1k]</a>
let line = <span>{truncAddr(addr)}:&nbsp;{balance}&nbsp;(ActTyp) {add1k}</span>
let addresses = this.state.addrs.map((addr) => {
let line = <Address client={this.props.client} add1k={this.add1k} addr={addr} mountWindow={this.props.mountWindow}/>
if (this.state.defaultAddr === addr) {
line = <b>{line}</b>
}
@ -137,7 +129,7 @@ class FullNode extends React.Component {
</div>
<div>
<div>Balances: [New <a href="#" onClick={this.newScepAddr}>[Secp256k1]</a>]</div>
<div>{balances}</div>
<div>{addresses}</div>
</div>
</div>

View File

@ -0,0 +1,30 @@
import React from 'react'
import {Cristal} from "react-cristal";
class State extends React.Component {
constructor(props) {
super(props)
this.state = {Balance: -2, State: {}}
}
async componentDidMount() {
const tipset = await this.props.client.call("Filecoin.ChainHead", []) // TODO: from props
const actstate = await this.props.client.call('Filecoin.ChainReadState', [this.props.actor, tipset])
this.setState(actstate)
}
render() {
const content = <div>
<div>Balance: {this.state.Balance}</div>
<div>---</div>
<div>{Object.keys(this.state.State).map(k => <div key={k}>{k}: <span>{JSON.stringify(this.state.State[k])}</span></div>)}</div>
</div>
return <Cristal onClose={this.props.onClose} title={`Actor ${this.props.addr} @{this.props.ts.Height}`}>
{content}
</Cristal>
}
}
export default State

View File

@ -1,6 +1,7 @@
import React from 'react';
import {Cristal} from "react-cristal";
import { Client } from 'rpc-websockets'
import Address from "./Address";
const stateConnected = 'connected'
const stateConnecting = 'connecting'
@ -66,20 +67,10 @@ class StorageNode extends React.Component {
async loadInfo() {
const version = await this.state.client.call("Filecoin.Version", [])
this.setState(() => ({version: version}))
const peers = await this.state.client.call("Filecoin.NetPeers", [])
this.setState(() => ({peers: peers.length}))
/*const addrss = await this.state.client.call('Filecoin.WalletList', [])
let defaultAddr = ""
if (addrss.length > 0) {
defaultAddr = await this.state.client.call('Filecoin.WalletDefaultAddress', [])
}
this.setState(() => ({defaultAddr: defaultAddr}))
*/
const [actor] = await this.state.client.call("Filecoin.ActorAddresses", [])
this.setState({version: version, peers: peers.length, actor: actor})
await this.stagedList()
}
@ -100,7 +91,7 @@ class StorageNode extends React.Component {
render() {
let runtime = <div></div>
if (this.state.state === stateConnected) {
if (this.state.actor) {
const sealGarbage = <a href="#" onClick={this.sealGarbage}>[Seal Garbage]</a>
runtime = (
@ -110,6 +101,9 @@ class StorageNode extends React.Component {
<div>
{sealGarbage}
</div>
<div>
<Address client={this.props.fullConn} addr={this.state.actor} mountWindow={this.props.mountWindow}/>
</div>
<div>{this.state.statusCounts.map((c, i) => <span>{sealCodes[i]}: {c} | </span>)}</div>
<div>
{this.state.staged ? this.state.staged.map(s => (

View File

@ -3,6 +3,7 @@ package impl
import (
"context"
"fmt"
"github.com/filecoin-project/go-lotus/lib/bufbstore"
"strconv"
"github.com/filecoin-project/go-lotus/api"
@ -20,7 +21,7 @@ import (
"github.com/filecoin-project/go-lotus/node/client"
"github.com/ipfs/go-cid"
hamt "github.com/ipfs/go-hamt-ipld"
"github.com/ipfs/go-hamt-ipld"
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log"
"github.com/libp2p/go-libp2p-core/peer"
@ -191,6 +192,47 @@ func (a *FullNodeAPI) ChainCall(ctx context.Context, msg *types.Message, ts *typ
return &ret.MessageReceipt, err
}
func (a *FullNodeAPI) stateForTs(ts *types.TipSet) (*state.StateTree, error) {
if ts == nil {
ts = a.Chain.GetHeaviestTipSet()
}
st, err := a.Chain.TipSetState(ts.Cids())
if err != nil {
return nil, err
}
buf := bufbstore.NewBufferedBstore(a.Chain.Blockstore())
cst := hamt.CSTFromBstore(buf)
return state.LoadStateTree(cst, st)
}
func (a *FullNodeAPI) ChainGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error) {
state, err := a.stateForTs(ts)
if err != nil {
return nil, err
}
return state.GetActor(actor)
}
func (a *FullNodeAPI) ChainReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*api.ActorState, error) {
state, err := a.stateForTs(ts)
if err != nil {
return nil, err
}
var oif interface{}
if err := state.Store.Get(context.TODO(), act.Head, &oif); err != nil {
return nil, err
}
return &api.ActorState{
Balance: act.Balance,
State: oif,
}, nil
}
func (a *FullNodeAPI) MpoolPending(ctx context.Context, ts *types.TipSet) ([]*types.SignedMessage, error) {
// TODO: need to make sure we don't return messages that were already included in the referenced chain
// also need to accept ts == nil just fine, assume nil == chain.Head()

View File

@ -3,6 +3,7 @@ package impl
import (
"context"
"fmt"
"github.com/filecoin-project/go-lotus/chain/address"
"io/ioutil"
"math/rand"
@ -14,11 +15,16 @@ import (
type StorageMinerAPI struct {
CommonAPI
SectorBuilder *sectorbuilder.SectorBuilder
SectorBuilderConfig *sectorbuilder.SectorBuilderConfig
SectorBuilder *sectorbuilder.SectorBuilder
Miner *storage.Miner
}
func (sm *StorageMinerAPI) ActorAddresses(context.Context) ([]address.Address, error) {
return []address.Address{sm.SectorBuilderConfig.Miner}, nil
}
func (sm *StorageMinerAPI) StoreGarbageData(ctx context.Context) (uint64, error) {
maxSize := uint64(1016) // this is the most data we can fit in a 1024 byte sector
data := make([]byte, maxSize)