on chain deals: Expose more chain state in pond

This commit is contained in:
Łukasz Magiera 2019-10-22 21:29:05 +02:00
parent 64bfb38834
commit 213ac77d08
15 changed files with 298 additions and 27 deletions

View File

@ -135,6 +135,8 @@ type FullNode interface {
StateListMiners(context.Context, *types.TipSet) ([]address.Address, error)
StateListActors(context.Context, *types.TipSet) ([]address.Address, error)
StateMarketBalance(context.Context, address.Address, *types.TipSet) (actors.StorageParticipantBalance, error)
StateMarketParticipants(context.Context, *types.TipSet) (map[string]actors.StorageParticipantBalance, error)
StateMarketDeals(context.Context, *types.TipSet) (map[string]actors.OnChainDeal, error)
PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error)
PaychList(context.Context) ([]address.Address, error)

View File

@ -101,6 +101,8 @@ type FullNodeStruct struct {
StateListMiners func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"`
StateListActors func(context.Context, *types.TipSet) ([]address.Address, error) `perm:"read"`
StateMarketBalance func(context.Context, address.Address, *types.TipSet) (actors.StorageParticipantBalance, error) `perm:"read"`
StateMarketParticipants func(context.Context, *types.TipSet) (map[string]actors.StorageParticipantBalance, error) `perm:"read"`
StateMarketDeals func(context.Context, *types.TipSet) (map[string]actors.OnChainDeal, error) `perm:"read"`
PaychGet func(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error) `perm:"sign"`
PaychList func(context.Context) ([]address.Address, error) `perm:"read"`
@ -394,6 +396,14 @@ func (c *FullNodeStruct) StateMarketBalance(ctx context.Context, addr address.Ad
return c.Internal.StateMarketBalance(ctx, addr, ts)
}
func (c *FullNodeStruct) StateMarketParticipants(ctx context.Context, ts *types.TipSet) (map[string]actors.StorageParticipantBalance, error) {
return c.Internal.StateMarketParticipants(ctx, ts)
}
func (c *FullNodeStruct) StateMarketDeals(ctx context.Context, ts *types.TipSet) (map[string]actors.OnChainDeal, error) {
return c.Internal.StateMarketDeals(ctx, ts)
}
func (c *FullNodeStruct) PaychGet(ctx context.Context, from, to address.Address, ensureFunds types.BigInt) (*ChannelInfo, error) {
return c.Internal.PaychGet(ctx, from, to, ensureFunds)
}

View File

@ -3476,3 +3476,61 @@ func (t *ProcessStorageDealsPaymentParams) UnmarshalCBOR(r io.Reader) error {
return nil
}
func (t *OnChainDeal) MarshalCBOR(w io.Writer) error {
if t == nil {
_, err := w.Write(cbg.CborNull)
return err
}
if _, err := w.Write([]byte{130}); err != nil {
return err
}
// t.t.Deal (actors.StorageDeal)
if err := t.Deal.MarshalCBOR(w); err != nil {
return err
}
// t.t.ActivationEpoch (uint64)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.ActivationEpoch)); err != nil {
return err
}
return nil
}
func (t *OnChainDeal) UnmarshalCBOR(r io.Reader) error {
br := cbg.GetPeeker(r)
maj, extra, err := cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajArray {
return fmt.Errorf("cbor input should be of type array")
}
if extra != 2 {
return fmt.Errorf("cbor input had wrong number of fields")
}
// t.t.Deal (actors.StorageDeal)
{
if err := t.Deal.UnmarshalCBOR(br); err != nil {
return err
}
}
// t.t.ActivationEpoch (uint64)
maj, extra, err = cbg.CborReadHeader(br)
if err != nil {
return err
}
if maj != cbg.MajUnsignedInt {
return fmt.Errorf("wrong type for uint64 field")
}
t.ActivationEpoch = extra
return nil
}

View File

@ -2,12 +2,10 @@ package deals
import (
"context"
"math"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
cbor "github.com/ipfs/go-ipld-cbor"
logging "github.com/ipfs/go-log"
"github.com/libp2p/go-libp2p-core/host"
inet "github.com/libp2p/go-libp2p-core/network"
@ -185,7 +183,7 @@ func (c *Client) Start(ctx context.Context, p ClientDealProposal) (cid.Cid, erro
return cid.Undef, err
}
proposalNd, err := cbor.WrapObject(proposal, math.MaxUint64, -1)
proposalNd, err := cborrpc.AsIpld(proposal)
if err != nil {
return cid.Undef, err
}

View File

@ -2,13 +2,11 @@ package deals
import (
"context"
"math"
"sync"
cid "github.com/ipfs/go-cid"
datastore "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
cbor "github.com/ipfs/go-ipld-cbor"
inet "github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/peer"
"golang.org/x/xerrors"
@ -17,6 +15,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/address"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/cborrpc"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/storage/commitment"
"github.com/filecoin-project/lotus/storage/sectorblocks"
@ -192,7 +191,7 @@ func (p *Provider) onUpdated(ctx context.Context, update minerDealUpdate) {
}
func (p *Provider) newDeal(s inet.Stream, proposal actors.StorageDealProposal) (MinerDeal, error) {
proposalNd, err := cbor.WrapObject(proposal, math.MaxUint64, -1)
proposalNd, err := cborrpc.AsIpld(proposal)
if err != nil {
return MinerDeal{}, err
}

View File

@ -5,6 +5,14 @@ import (
"fmt"
amt "github.com/filecoin-project/go-amt-ipld"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
hamt "github.com/ipfs/go-hamt-ipld"
bstore "github.com/ipfs/go-ipfs-blockstore"
peer "github.com/libp2p/go-libp2p-peer"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/build"
actors "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/address"
@ -12,14 +20,6 @@ import (
"github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
"golang.org/x/xerrors"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
hamt "github.com/ipfs/go-hamt-ipld"
bstore "github.com/ipfs/go-ipfs-blockstore"
peer "github.com/libp2p/go-libp2p-peer"
cbg "github.com/whyrusleeping/cbor-gen"
)
type GenesisBootstrap struct {

View File

@ -86,6 +86,7 @@ func main() {
actors.PublishStorageDealResponse{},
actors.ActivateStorageDealsParams{},
actors.ProcessStorageDealsPaymentParams{},
actors.OnChainDeal{},
)
if err != nil {
fmt.Println(err)

View File

@ -4,8 +4,10 @@ import (
"bytes"
"encoding/hex"
"io"
"math"
cbor "github.com/ipfs/go-ipld-cbor"
ipld "github.com/ipfs/go-ipld-format"
logging "github.com/ipfs/go-log"
cbg "github.com/whyrusleeping/cbor-gen"
)
@ -52,3 +54,15 @@ func Dump(obj interface{}) ([]byte, error) {
}
return out.Bytes(), nil
}
// TODO: this is a bit ugly, and this package is not exactly the best place
func AsIpld(obj interface{}) (ipld.Node, error) {
if m, ok := obj.(cbg.CBORMarshaler); ok {
b, err := Dump(m)
if err != nil {
return nil, err
}
return cbor.Decode(b, math.MaxUint64, -1)
}
return cbor.WrapObject(obj, math.MaxUint64, -1)
}

View File

@ -3457,6 +3457,11 @@
}
}
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"clean-css": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
@ -10663,6 +10668,15 @@
"workbox-webpack-plugin": "4.2.0"
}
},
"react-tooltip": {
"version": "3.11.1",
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-3.11.1.tgz",
"integrity": "sha512-YCMVlEC2KuHIzOQhPplTK5jmBBwoL+PYJJdJKXj7M/h7oevupd/QSVq6z5U7/ehIGXyHsAqvwpdxexDfyQ0o3A==",
"requires": {
"classnames": "^2.2.5",
"prop-types": "^15.6.0"
}
},
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",

View File

@ -13,6 +13,7 @@
"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",

View File

@ -1,8 +1,9 @@
import React from 'react'
import CID from 'cids'
import * as multihash from "multihashes";
import State from "./State";
import methods from "./chain/methods";
import ReactTooltip from 'react-tooltip'
import * as multihash from "multihashes"
import State from "./State"
import methods from "./chain/methods"
function truncAddr(addr, len) {
if (addr.length > len) {
@ -11,6 +12,17 @@ function truncAddr(addr, len) {
return addr
}
function filStr(raw) {
if(typeof raw !== 'string') {
return raw
}
if(raw.length < 18) {
raw = '0'.repeat(18 - raw.length)
}
let out = (raw.substring(0, raw.length - 18) + '.' + raw.substring(raw.length - 18, raw.length)).replace(/\.0+|0+$/g, '');
return out ? out : '0'
}
let sheet = document.createElement('style')
document.body.appendChild(sheet);
@ -62,7 +74,7 @@ class Address extends React.Component {
}
openState() {
this.props.mountWindow((onClose) => <State addr={this.props.addr} actor={this.state.actor} client={this.props.client} onClose={onClose}/>)
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) {
@ -117,12 +129,12 @@ class Address extends React.Component {
nonce = <span>&nbsp;<abbr title={"Next nonce"}>Nc:{this.state.nonce}</abbr>{nonce}</span>
}
let balance = <span>:&nbsp;{this.state.balance}&nbsp;</span>
let balance = <span>:&nbsp;{filStr(this.state.balance)}&nbsp;</span>
if(this.props.nobalance) {
balance = <span/>
}
if(this.props.short) {
actInfo = <span/>
actInfo = <ReactTooltip id={this.props.addr} place="top" type="dark" effect="solid">{actInfo}: {filStr(this.state.balance)}</ReactTooltip>
balance = <span/>
}
@ -136,7 +148,7 @@ class Address extends React.Component {
minerInfo = <span>&nbsp;Power: {this.state.minerInfo.MinerPower} ({this.state.minerInfo.MinerPower/this.state.minerInfo.TotalPower*100}%)</span>
}
return <span>{addr}{balance}{actInfo}{nonce}{add20k}{transfer}{minerInfo}</span>
return <span data-tip data-for={this.props.addr}>{addr}{balance}{actInfo}{nonce}{add20k}{transfer}{minerInfo}</span>
}
}

View File

@ -64,7 +64,14 @@ class Block extends React.Component {
<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>Parent State Root:&nbsp;{head.ParentStateRoot['/']}</div>
<div>
<span>Parent State Root:&nbsp;{head.ParentStateRoot['/']}</span>
<span>&nbsp;<Address client={this.props.conn} short={true} addr="t00" mountWindow={this.props.mountWindow}/></span>
<span>&nbsp;<Address client={this.props.conn} short={true} addr="t01" mountWindow={this.props.mountWindow}/></span>
<span>&nbsp;<Address client={this.props.conn} short={true} addr="t02" mountWindow={this.props.mountWindow}/></span>
<span>&nbsp;<Address client={this.props.conn} short={true} addr="t03" mountWindow={this.props.mountWindow}/></span>
<span>&nbsp;<Address client={this.props.conn} short={true} addr="t099" mountWindow={this.props.mountWindow}/></span>
</div>
<div>----</div>
<div>{messages}</div>
</div>

View File

@ -1,7 +1,17 @@
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";
class State extends React.Component {
byCode = {
[code.init]: InitState,
[code.power]: PowerState,
[code.market]: MarketState,
}
constructor(props) {
super(props)
@ -9,22 +19,99 @@ class State extends React.Component {
}
async componentDidMount() {
const tipset = await this.props.client.call("Filecoin.ChainHead", []) // TODO: from props
const tipset = this.props.tipset || await this.props.client.call("Filecoin.ChainHead", [])
const actstate = await this.props.client.call('Filecoin.StateReadState', [this.props.actor, tipset])
this.setState(actstate)
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 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: {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>
{state}
</div>
return <Window onClose={this.props.onClose} title={`Actor ${this.props.addr} @{this.props.ts.Height}`}>
return <Window initialSize={{width: 700, 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])
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: []}
}
async componentDidMount() {
const tipset = await this.props.client.call("Filecoin.ChainHead", []) // TODO: from props
const actors = await this.props.client.call("Filecoin.StateListMiners", [tipset])
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 miner={true} addr={addr} client={this.props.client} mountWindow={this.props.mountWindow}/></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])
const deals = await this.props.client.call("Filecoin.StateMarketDeals", [tipset])
this.setState({participants, deals})
}
render() {
return <div>
<div>
<div>Participants:</div>
{Object.keys(this.state.participants).map(p => <span>{p}</span>)}
</div>
<div>
<div>Deals:</div>
{this.state.deals.map(d => <span>{d}</span>)}
</div>
</div>
}
}
export default State

View File

@ -0,0 +1,5 @@
export default {
init: 'filecoin/1.0/InitActor',
power: 'filecoin/1.0/StoragePowerActor',
market: 'filecoin/1.0/StorageMarketActor'
}

View File

@ -1,11 +1,15 @@
package full
import (
"bytes"
"context"
"github.com/filecoin-project/go-amt-ipld"
"strconv"
cid "github.com/ipfs/go-cid"
"github.com/ipfs/go-hamt-ipld"
"github.com/libp2p/go-libp2p-core/peer"
cbg "github.com/whyrusleeping/cbor-gen"
"go.uber.org/fx"
"golang.org/x/xerrors"
@ -240,3 +244,62 @@ func (a *StateAPI) StateMarketBalance(ctx context.Context, addr address.Address,
return b[0], nil
}
func (a *StateAPI) StateMarketParticipants(ctx context.Context, ts *types.TipSet) (map[string]actors.StorageParticipantBalance, error) {
out := map[string]actors.StorageParticipantBalance{}
var state actors.StorageMarketState
if _, err := a.StateManager.LoadActorState(ctx, actors.StoragePowerAddress, &state, ts); err != nil {
return nil, err
}
cst := hamt.CSTFromBstore(a.StateManager.ChainStore().Blockstore())
nd, err := hamt.LoadNode(ctx, cst, state.Balances)
if err != nil {
return nil, err
}
err = nd.ForEach(ctx, func(k string, val interface{}) error {
cv := val.(*cbg.Deferred)
a, err := address.NewFromBytes([]byte(k))
if err != nil {
return err
}
var b actors.StorageParticipantBalance
if err := b.UnmarshalCBOR(bytes.NewReader(cv.Raw)); err != nil {
return err
}
out[a.String()] = b
return nil
})
if err != nil {
return nil, err
}
return out, nil
}
func (a *StateAPI) StateMarketDeals(ctx context.Context, ts *types.TipSet) (map[string]actors.OnChainDeal, error) {
out := map[string]actors.OnChainDeal{}
var state actors.StorageMarketState
if _, err := a.StateManager.LoadActorState(ctx, actors.StoragePowerAddress, &state, ts); err != nil {
return nil, err
}
blks := amt.WrapBlockstore(a.StateManager.ChainStore().Blockstore())
da, err := amt.LoadAMT(blks, state.Deals)
if err != nil {
return nil, err
}
if err := da.ForEach(func(i uint64, v *cbg.Deferred) error {
var d actors.OnChainDeal
if err := d.UnmarshalCBOR(bytes.NewReader(v.Raw)); err != nil {
return err
}
out[strconv.FormatInt(int64(i), 10)] = d
return nil
}); err != nil {
return nil, err
}
return out, nil
}