lotus/node/impl/full.go

454 lines
11 KiB
Go
Raw Normal View History

package impl
2019-07-08 19:07:16 +00:00
import (
"context"
"fmt"
2019-08-10 01:54:45 +00:00
"github.com/filecoin-project/go-lotus/lib/bufbstore"
"strconv"
2019-07-26 04:54:22 +00:00
2019-07-08 19:07:16 +00:00
"github.com/filecoin-project/go-lotus/api"
2019-07-09 15:19:27 +00:00
"github.com/filecoin-project/go-lotus/chain"
"github.com/filecoin-project/go-lotus/chain/actors"
2019-07-11 02:36:43 +00:00
"github.com/filecoin-project/go-lotus/chain/address"
2019-08-01 17:12:41 +00:00
"github.com/filecoin-project/go-lotus/chain/deals"
2019-07-25 22:15:33 +00:00
"github.com/filecoin-project/go-lotus/chain/gen"
"github.com/filecoin-project/go-lotus/chain/state"
2019-07-26 04:54:22 +00:00
"github.com/filecoin-project/go-lotus/chain/store"
2019-07-18 20:26:04 +00:00
"github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
2019-07-25 22:15:33 +00:00
"github.com/filecoin-project/go-lotus/chain/wallet"
2019-07-11 02:36:43 +00:00
"github.com/filecoin-project/go-lotus/miner"
2019-07-12 09:59:18 +00:00
"github.com/filecoin-project/go-lotus/node/client"
2019-07-23 20:27:05 +00:00
"github.com/ipfs/go-cid"
2019-08-10 01:54:45 +00:00
"github.com/ipfs/go-hamt-ipld"
cbor "github.com/ipfs/go-ipld-cbor"
2019-07-23 17:27:45 +00:00
logging "github.com/ipfs/go-log"
2019-08-02 14:09:54 +00:00
"github.com/libp2p/go-libp2p-core/peer"
2019-07-09 15:19:27 +00:00
pubsub "github.com/libp2p/go-libp2p-pubsub"
2019-08-07 20:06:10 +00:00
"golang.org/x/xerrors"
2019-07-08 19:07:16 +00:00
)
2019-07-23 17:27:45 +00:00
var log = logging.Logger("node")
type FullNodeAPI struct {
2019-07-12 09:59:18 +00:00
client.LocalStorage
2019-07-24 00:58:31 +00:00
CommonAPI
2019-08-01 17:12:41 +00:00
DealClient *deals.Client
Chain *store.ChainStore
PubSub *pubsub.PubSub
Mpool *chain.MessagePool
Wallet *wallet.Wallet
}
2019-08-07 20:06:10 +00:00
func (a *FullNodeAPI) ClientStartDeal(ctx context.Context, data cid.Cid, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) {
2019-08-02 16:25:10 +00:00
self, err := a.WalletDefaultAddress(ctx)
if err != nil {
2019-08-07 20:06:10 +00:00
return nil, err
2019-08-02 16:25:10 +00:00
}
2019-08-02 14:09:54 +00:00
msg := &types.Message{
To: miner,
2019-08-02 16:25:10 +00:00
From: miner,
2019-08-02 14:09:54 +00:00
Method: actors.MAMethods.GetPeerID,
}
r, err := a.ChainCall(ctx, msg, nil)
if err != nil {
2019-08-07 20:06:10 +00:00
return nil, err
2019-08-02 14:09:54 +00:00
}
pid, err := peer.IDFromBytes(r.Return)
if err != nil {
2019-08-07 20:06:10 +00:00
return nil, err
2019-08-02 14:09:54 +00:00
}
2019-08-07 20:06:10 +00:00
total := types.BigMul(price, types.NewInt(blocksDuration))
c, err := a.DealClient.Start(ctx, data, total, self, miner, pid, blocksDuration)
return &c, err
2019-07-09 15:19:27 +00:00
}
func (a *FullNodeAPI) ChainNotify(ctx context.Context) (<-chan *store.HeadChange, error) {
return a.Chain.SubHeadChanges(ctx), nil
}
func (a *FullNodeAPI) ChainSubmitBlock(ctx context.Context, blk *chain.BlockMsg) error {
2019-07-11 02:36:43 +00:00
if err := a.Chain.AddBlock(blk.Header); err != nil {
return err
}
2019-07-09 15:19:27 +00:00
b, err := blk.Serialize()
if err != nil {
return err
}
// TODO: anything else to do here?
return a.PubSub.Publish("/fil/blocks", b)
}
2019-07-26 04:54:22 +00:00
func (a *FullNodeAPI) ChainHead(context.Context) (*types.TipSet, error) {
2019-07-11 02:36:43 +00:00
return a.Chain.GetHeaviestTipSet(), nil
}
2019-07-26 04:54:22 +00:00
func (a *FullNodeAPI) ChainGetRandomness(ctx context.Context, pts *types.TipSet) ([]byte, error) {
2019-07-11 02:36:43 +00:00
// TODO: this needs to look back in the chain for the right random beacon value
return []byte("foo bar random"), nil
2019-07-08 19:07:16 +00:00
}
func (a *FullNodeAPI) ChainWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgWait, error) {
2019-07-28 02:32:09 +00:00
blkcid, recpt, err := a.Chain.WaitForMessage(ctx, msg)
if err != nil {
return nil, err
}
return &api.MsgWait{
InBlock: blkcid,
Receipt: *recpt,
2019-07-28 02:32:09 +00:00
}, nil
}
func (a *FullNodeAPI) ChainGetBlock(ctx context.Context, msg cid.Cid) (*types.BlockHeader, error) {
2019-07-23 00:54:27 +00:00
return a.Chain.GetBlock(msg)
}
2019-08-02 03:51:34 +00:00
func (a *FullNodeAPI) ChainGetBlockMessages(ctx context.Context, msg cid.Cid) (*api.BlockMessages, error) {
2019-07-23 00:54:27 +00:00
b, err := a.Chain.GetBlock(msg)
if err != nil {
return nil, err
}
2019-08-02 03:51:34 +00:00
bmsgs, smsgs, err := a.Chain.MessagesForBlock(b)
if err != nil {
return nil, err
2019-07-23 00:54:27 +00:00
}
2019-08-02 03:51:34 +00:00
return &api.BlockMessages{
BlsMessages: bmsgs,
SecpkMessages: smsgs,
}, nil
2019-07-23 00:54:27 +00:00
}
func (a *FullNodeAPI) ChainGetBlockReceipts(ctx context.Context, bcid cid.Cid) ([]*types.MessageReceipt, error) {
b, err := a.Chain.GetBlock(bcid)
if err != nil {
return nil, err
}
// TODO: need to get the number of messages better than this
bm, sm, err := a.Chain.MessagesForBlock(b)
if err != nil {
return nil, err
}
var out []*types.MessageReceipt
for i := 0; i < len(bm)+len(sm); i++ {
r, err := a.Chain.GetReceipt(b, i)
if err != nil {
return nil, err
}
out = append(out, r)
}
return out, nil
}
2019-07-31 16:54:28 +00:00
func (a *FullNodeAPI) ChainCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
if ts == nil {
ts = a.Chain.GetHeaviestTipSet()
}
state, err := a.Chain.TipSetState(ts.Cids())
if err != nil {
return nil, err
}
2019-07-31 16:54:28 +00:00
vmi, err := vm.NewVM(state, ts.Height(), ts.Blocks()[0].Miner, a.Chain)
if err != nil {
return nil, xerrors.Errorf("failed to set up vm: %w", err)
}
if msg.GasLimit == types.EmptyInt {
msg.GasLimit = types.NewInt(10000000000)
}
if msg.GasPrice == types.EmptyInt {
msg.GasPrice = types.NewInt(0)
}
if msg.Value == types.EmptyInt {
msg.Value = types.NewInt(0)
}
2019-08-02 14:09:54 +00:00
if msg.Params == nil {
msg.Params, err = actors.SerializeParams(struct{}{})
if err != nil {
return nil, err
}
}
// TODO: maybe just use the invoker directly?
ret, err := vmi.ApplyMessage(ctx, msg)
if ret.ActorErr != nil {
log.Warnf("chain call failed: %s", ret.ActorErr)
}
return &ret.MessageReceipt, err
}
2019-08-10 01:54:45 +00:00
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
}
2019-07-26 04:54:22 +00:00
func (a *FullNodeAPI) MpoolPending(ctx context.Context, ts *types.TipSet) ([]*types.SignedMessage, error) {
2019-07-11 02:36:43 +00:00
// 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()
2019-07-09 22:58:51 +00:00
return a.Mpool.Pending(), nil
}
func (a *FullNodeAPI) MpoolPush(ctx context.Context, smsg *types.SignedMessage) error {
msgb, err := smsg.Serialize()
if err != nil {
return err
}
if err := a.Mpool.Add(smsg); err != nil {
return err
}
return a.PubSub.Publish("/fil/messages", msgb)
}
func (a *FullNodeAPI) MpoolGetNonce(ctx context.Context, addr address.Address) (uint64, error) {
return a.Mpool.GetNonce(addr)
}
func (a *FullNodeAPI) MinerStart(ctx context.Context, addr address.Address) error {
2019-07-11 02:36:43 +00:00
// hrm...
m := miner.NewMiner(a, addr)
go m.Mine(context.TODO())
return nil
}
2019-07-26 04:54:22 +00:00
func (a *FullNodeAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) {
2019-07-25 22:15:33 +00:00
fblk, err := gen.MinerCreateBlock(ctx, a.Chain, addr, parents, tickets, proof, msgs)
2019-07-11 02:36:43 +00:00
if err != nil {
return nil, err
}
var out chain.BlockMsg
out.Header = fblk.Header
for _, msg := range fblk.BlsMessages {
out.BlsMessages = append(out.BlsMessages, msg.Cid())
}
for _, msg := range fblk.SecpkMessages {
out.SecpkMessages = append(out.SecpkMessages, msg.Cid())
2019-07-11 02:36:43 +00:00
}
return &out, nil
}
func (a *FullNodeAPI) WalletNew(ctx context.Context, typ string) (address.Address, error) {
2019-07-13 00:41:32 +00:00
return a.Wallet.GenerateKey(typ)
}
2019-08-08 17:29:23 +00:00
func (a *FullNodeAPI) WalletHas(ctx context.Context, addr address.Address) (bool, error) {
return a.Wallet.HasKey(addr)
}
func (a *FullNodeAPI) WalletList(ctx context.Context) ([]address.Address, error) {
return a.Wallet.ListAddrs()
2019-07-13 00:41:32 +00:00
}
func (a *FullNodeAPI) WalletBalance(ctx context.Context, addr address.Address) (types.BigInt, error) {
2019-07-18 20:26:04 +00:00
return a.Chain.GetBalance(addr)
}
func (a *FullNodeAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) {
return a.Wallet.Sign(k, msg)
}
2019-08-09 15:59:12 +00:00
func (a *FullNodeAPI) WalletSignMessage(ctx context.Context, k address.Address, msg *types.Message) (*types.SignedMessage, error) {
msgbytes, err := msg.Serialize()
if err != nil {
return nil, err
}
sig, err := a.WalletSign(ctx, k, msgbytes)
if err != nil {
return nil, xerrors.Errorf("failed to sign message: %w", err)
}
return &types.SignedMessage{
Message: *msg,
Signature: *sig,
}, nil
}
func (a *FullNodeAPI) WalletDefaultAddress(ctx context.Context) (address.Address, error) {
2019-07-18 19:12:30 +00:00
addrs, err := a.Wallet.ListAddrs()
if err != nil {
return address.Undef, err
}
if len(addrs) == 0 {
return address.Undef, xerrors.New("no addresses in wallet")
}
// TODO: store a default address in the config or 'wallet' portion of the repo
return addrs[0], nil
}
func (a *FullNodeAPI) StateMinerSectors(ctx context.Context, addr address.Address) ([]*api.SectorInfo, error) {
ts := a.Chain.GetHeaviestTipSet()
stc, err := a.Chain.TipSetState(ts.Cids())
if err != nil {
return nil, err
}
cst := hamt.CSTFromBstore(a.Chain.Blockstore())
st, err := state.LoadStateTree(cst, stc)
if err != nil {
return nil, err
}
act, err := st.GetActor(addr)
if err != nil {
return nil, err
}
var minerState actors.StorageMinerActorState
if err := cst.Get(ctx, act.Head, &minerState); err != nil {
return nil, err
}
nd, err := hamt.LoadNode(ctx, cst, minerState.Sectors)
if err != nil {
return nil, err
}
log.Info("miner sector count: ", minerState.SectorSetSize)
var sinfos []*api.SectorInfo
// Note to self: the hamt isnt a great data structure to use here... need to implement the sector set
err = nd.ForEach(ctx, func(k string, val interface{}) error {
sid, err := strconv.ParseUint(k, 10, 64)
if err != nil {
return err
}
bval, ok := val.([]byte)
if !ok {
return fmt.Errorf("expected to get bytes in sector set hamt")
}
var comms [][]byte
if err := cbor.DecodeInto(bval, &comms); err != nil {
return err
}
sinfos = append(sinfos, &api.SectorInfo{
SectorID: sid,
CommR: comms[0],
CommD: comms[1],
})
return nil
})
return sinfos, nil
}
func (a *FullNodeAPI) StateMinerProvingSet(ctx context.Context, addr address.Address) ([]*api.SectorInfo, error) {
ts := a.Chain.GetHeaviestTipSet()
stc, err := a.Chain.TipSetState(ts.Cids())
if err != nil {
return nil, err
}
cst := hamt.CSTFromBstore(a.Chain.Blockstore())
st, err := state.LoadStateTree(cst, stc)
if err != nil {
return nil, err
}
act, err := st.GetActor(addr)
if err != nil {
return nil, err
}
var minerState actors.StorageMinerActorState
if err := cst.Get(ctx, act.Head, &minerState); err != nil {
return nil, err
}
nd, err := hamt.LoadNode(ctx, cst, minerState.ProvingSet)
if err != nil {
return nil, err
}
var sinfos []*api.SectorInfo
// Note to self: the hamt isnt a great data structure to use here... need to implement the sector set
err = nd.ForEach(ctx, func(k string, val interface{}) error {
sid, err := strconv.ParseUint(k, 10, 64)
if err != nil {
return err
}
bval, ok := val.([]byte)
if !ok {
return fmt.Errorf("expected to get bytes in sector set hamt")
}
var comms [][]byte
if err := cbor.DecodeInto(bval, &comms); err != nil {
return err
}
sinfos = append(sinfos, &api.SectorInfo{
SectorID: sid,
CommR: comms[0],
CommD: comms[1],
})
return nil
})
return sinfos, nil
}
var _ api.FullNode = &FullNodeAPI{}