Merge pull request #182 from filecoin-project/feat/mining-refactor

Mining refactor and correct election proofs
This commit is contained in:
Whyrusleeping 2019-09-10 16:14:53 +10:00 committed by GitHub
commit 450ce5df07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1091 additions and 621 deletions

View File

@ -48,16 +48,11 @@ type FullNode interface {
ChainNotify(context.Context) (<-chan *store.HeadChange, error) ChainNotify(context.Context) (<-chan *store.HeadChange, error)
ChainHead(context.Context) (*types.TipSet, error) // TODO: check serialization ChainHead(context.Context) (*types.TipSet, error) // TODO: check serialization
ChainSubmitBlock(ctx context.Context, blk *chain.BlockMsg) error // TODO: check serialization ChainSubmitBlock(ctx context.Context, blk *chain.BlockMsg) error // TODO: check serialization
ChainGetRandomness(context.Context, *types.TipSet) ([]byte, error) ChainGetRandomness(context.Context, *types.TipSet, []*types.Ticket, int) ([]byte, error)
ChainWaitMsg(context.Context, cid.Cid) (*MsgWait, error) ChainWaitMsg(context.Context, cid.Cid) (*MsgWait, error)
ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error) ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error) ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
ChainGetBlockReceipts(context.Context, cid.Cid) ([]*types.MessageReceipt, 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)
// messages // messages
@ -106,6 +101,11 @@ type FullNode interface {
StateMinerSectors(context.Context, address.Address) ([]*SectorInfo, error) StateMinerSectors(context.Context, address.Address) ([]*SectorInfo, error)
StateMinerProvingSet(context.Context, address.Address) ([]*SectorInfo, error) StateMinerProvingSet(context.Context, address.Address) ([]*SectorInfo, error)
StateMinerPower(context.Context, address.Address, *types.TipSet) (MinerPower, error) StateMinerPower(context.Context, address.Address, *types.TipSet) (MinerPower, error)
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
// if tipset is nil, we'll use heaviest
StateCall(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error)
StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error)
StateReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*ActorState, error)
PaychCreate(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, error) PaychCreate(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, error)
PaychList(context.Context) ([]address.Address, error) PaychList(context.Context) ([]address.Address, error)

View File

@ -39,17 +39,14 @@ type FullNodeStruct struct {
CommonStruct CommonStruct
Internal struct { Internal struct {
ChainNotify func(context.Context) (<-chan *store.HeadChange, error) `perm:"read"` ChainNotify func(context.Context) (<-chan *store.HeadChange, error) `perm:"read"`
ChainSubmitBlock func(ctx context.Context, blk *chain.BlockMsg) error `perm:"write"` ChainSubmitBlock func(ctx context.Context, blk *chain.BlockMsg) error `perm:"write"`
ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"` ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"`
ChainGetRandomness func(context.Context, *types.TipSet) ([]byte, error) `perm:"read"` ChainGetRandomness func(context.Context, *types.TipSet, []*types.Ticket, int) ([]byte, error) `perm:"read"`
ChainWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"` ChainWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"`
ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"` ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"`
ChainGetBlockMessages func(context.Context, cid.Cid) (*BlockMessages, error) `perm:"read"` ChainGetBlockMessages func(context.Context, cid.Cid) (*BlockMessages, error) `perm:"read"`
ChainGetBlockReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, 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"` MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"`
MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"` MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"`
@ -75,9 +72,13 @@ type FullNodeStruct struct {
ClientStartDeal func(ctx context.Context, data cid.Cid, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) `perm:"admin"` ClientStartDeal func(ctx context.Context, data cid.Cid, miner address.Address, price types.BigInt, blocksDuration uint64) (*cid.Cid, error) `perm:"admin"`
ClientRetrieve func(ctx context.Context, order RetrievalOrder, path string) error `perm:"admin"` ClientRetrieve func(ctx context.Context, order RetrievalOrder, path string) error `perm:"admin"`
StateMinerSectors func(context.Context, address.Address) ([]*SectorInfo, error) `perm:"read"` StateMinerSectors func(context.Context, address.Address) ([]*SectorInfo, error) `perm:"read"`
StateMinerProvingSet func(context.Context, address.Address) ([]*SectorInfo, error) `perm:"read"` StateMinerProvingSet func(context.Context, address.Address) ([]*SectorInfo, error) `perm:"read"`
StateMinerPower func(context.Context, address.Address, *types.TipSet) (MinerPower, error) `perm:"read"` StateMinerPower func(context.Context, address.Address, *types.TipSet) (MinerPower, error) `perm:"read"`
StateMinerWorker func(context.Context, address.Address, *types.TipSet) (address.Address, error) `perm:"read"`
StateCall func(context.Context, *types.Message, *types.TipSet) (*types.MessageReceipt, error) `perm:"read"`
StateGetActor func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) `perm:"read"`
StateReadState func(context.Context, *types.Actor, *types.TipSet) (*ActorState, error) `perm:"read"`
PaychCreate func(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, error) `perm:"sign"` PaychCreate func(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, error) `perm:"sign"`
PaychList func(context.Context) ([]address.Address, error) `perm:"read"` PaychList func(context.Context) ([]address.Address, error) `perm:"read"`
@ -203,26 +204,14 @@ func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) {
return c.Internal.ChainHead(ctx) return c.Internal.ChainHead(ctx)
} }
func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts *types.TipSet) ([]byte, error) { func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts *types.TipSet, ticks []*types.Ticket, lb int) ([]byte, error) {
return c.Internal.ChainGetRandomness(ctx, pts) return c.Internal.ChainGetRandomness(ctx, pts, ticks, lb)
} }
func (c *FullNodeStruct) ChainWaitMsg(ctx context.Context, msgc cid.Cid) (*MsgWait, error) { func (c *FullNodeStruct) ChainWaitMsg(ctx context.Context, msgc cid.Cid) (*MsgWait, error) {
return c.Internal.ChainWaitMsg(ctx, msgc) return c.Internal.ChainWaitMsg(ctx, msgc)
} }
func (c *FullNodeStruct) ChainCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
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) { func (c *FullNodeStruct) WalletNew(ctx context.Context, typ string) (address.Address, error) {
return c.Internal.WalletNew(ctx, typ) return c.Internal.WalletNew(ctx, typ)
} }
@ -283,6 +272,22 @@ func (c *FullNodeStruct) StateMinerPower(ctx context.Context, a address.Address,
return c.Internal.StateMinerPower(ctx, a, ts) return c.Internal.StateMinerPower(ctx, a, ts)
} }
func (c *FullNodeStruct) StateMinerWorker(ctx context.Context, m address.Address, ts *types.TipSet) (address.Address, error) {
return c.Internal.StateMinerWorker(ctx, m, ts)
}
func (c *FullNodeStruct) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
return c.Internal.StateCall(ctx, msg, ts)
}
func (c *FullNodeStruct) StateGetActor(ctx context.Context, actor address.Address, ts *types.TipSet) (*types.Actor, error) {
return c.Internal.StateGetActor(ctx, actor, ts)
}
func (c *FullNodeStruct) StateReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*ActorState, error) {
return c.Internal.StateReadState(ctx, act, ts)
}
func (c *FullNodeStruct) PaychCreate(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, error) { func (c *FullNodeStruct) PaychCreate(ctx context.Context, from, to address.Address, amt types.BigInt) (address.Address, error) {
return c.Internal.PaychCreate(ctx, from, to, amt) return c.Internal.PaychCreate(ctx, from, to, amt)
} }

View File

@ -12,5 +12,6 @@ const PaymentChannelClosingDelay = 6 * 60 * 2 // six hours
const DealVoucherSkewLimit = 10 const DealVoucherSkewLimit = 10
const ForkLengthThreshold = 20 const ForkLengthThreshold = 20
const RandomnessLookback = 20
// TODO: Move other important consts here // TODO: Move other important consts here

View File

@ -245,8 +245,10 @@ func (bs *BlockSync) processStatus(req *BlockSyncRequest, res *BlockSyncResponse
panic("not handled") panic("not handled")
case 203: // Internal Error case 203: // Internal Error
return fmt.Errorf("block sync peer errored: %s", res.Message) return fmt.Errorf("block sync peer errored: %s", res.Message)
case 204:
return fmt.Errorf("block sync request invalid: %s", res.Message)
default: default:
return fmt.Errorf("unrecognized response code") return fmt.Errorf("unrecognized response code: %d", res.Status)
} }
} }
@ -261,10 +263,11 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tipset []cid.Cid, count int)
Options: BSOptBlocks, Options: BSOptBlocks,
} }
var err error var oerr error
for _, p := range perm { for _, p := range perm {
res, err := bs.sendRequestToPeer(ctx, peers[p], req) res, err := bs.sendRequestToPeer(ctx, peers[p], req)
if err != nil { if err != nil {
oerr = err
log.Warnf("BlockSync request failed for peer %s: %s", peers[p].String(), err) log.Warnf("BlockSync request failed for peer %s: %s", peers[p].String(), err)
continue continue
} }
@ -272,12 +275,12 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tipset []cid.Cid, count int)
if res.Status == 0 { if res.Status == 0 {
return bs.processBlocksResponse(req, res) return bs.processBlocksResponse(req, res)
} }
err = bs.processStatus(req, res) oerr = bs.processStatus(req, res)
if err != nil { if oerr != nil {
log.Warnf("BlockSync peer %s response was an error: %s", peers[p].String(), err) log.Warnf("BlockSync peer %s response was an error: %s", peers[p].String(), err)
} }
} }
return nil, xerrors.Errorf("GetBlocks failed with all peers: %w", err) return nil, xerrors.Errorf("GetBlocks failed with all peers: %w", oerr)
} }
func (bs *BlockSync) GetFullTipSet(ctx context.Context, p peer.ID, h []cid.Cid) (*store.FullTipSet, error) { func (bs *BlockSync) GetFullTipSet(ctx context.Context, p peer.ID, h []cid.Cid) (*store.FullTipSet, error) {
@ -396,7 +399,7 @@ func (bs *BlockSync) processBlocksResponse(req *BlockSyncRequest, res *BlockSync
return nil, err return nil, err
} }
if !cidArrsEqual(cur.Parents(), nts.Cids()) { if !types.CidArrsEqual(cur.Parents(), nts.Cids()) {
return nil, fmt.Errorf("parents of tipset[%d] were not tipset[%d]", bi-1, bi) return nil, fmt.Errorf("parents of tipset[%d] were not tipset[%d]", bi-1, bi)
} }
@ -406,18 +409,6 @@ func (bs *BlockSync) processBlocksResponse(req *BlockSyncRequest, res *BlockSync
return out, nil return out, nil
} }
func cidArrsEqual(a, b []cid.Cid) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if b[i] != v {
return false
}
}
return true
}
func (bs *BlockSync) GetBlock(ctx context.Context, c cid.Cid) (*types.BlockHeader, error) { func (bs *BlockSync) GetBlock(ctx context.Context, c cid.Cid) (*types.BlockHeader, error) {
sb, err := bs.bserv.GetBlock(ctx, c) sb, err := bs.bserv.GetBlock(ctx, c)
if err != nil { if err != nil {

View File

@ -77,7 +77,7 @@ func (h *Handler) sendSignedResponse(resp StorageDealResponse) error {
From: h.actor, From: h.actor,
Method: actors.MAMethods.GetWorkerAddr, Method: actors.MAMethods.GetWorkerAddr,
} }
r, err := h.full.ChainCall(context.TODO(), getworker, nil) r, err := h.full.StateCall(context.TODO(), getworker, nil)
if err != nil { if err != nil {
return xerrors.Errorf("getting worker address: %w", err) return xerrors.Errorf("getting worker address: %w", err)
} }

View File

@ -3,16 +3,20 @@ package gen
import ( import (
"bytes" "bytes"
"context" "context"
"fmt"
"sync/atomic" "sync/atomic"
"github.com/ipfs/go-blockservice" "github.com/ipfs/go-blockservice"
"github.com/ipfs/go-car" "github.com/ipfs/go-car"
offline "github.com/ipfs/go-ipfs-exchange-offline" offline "github.com/ipfs/go-ipfs-exchange-offline"
"github.com/ipfs/go-merkledag" "github.com/ipfs/go-merkledag"
peer "github.com/libp2p/go-libp2p-peer"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/build" "github.com/filecoin-project/go-lotus/build"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/wallet" "github.com/filecoin-project/go-lotus/chain/wallet"
@ -38,13 +42,15 @@ type ChainGen struct {
cs *store.ChainStore cs *store.ChainStore
genesis *types.BlockHeader sm *stmgr.StateManager
curBlock *types.FullBlock
genesis *types.BlockHeader
curTipset *store.FullTipSet
w *wallet.Wallet w *wallet.Wallet
miner address.Address miners []address.Address
mworker address.Address mworkers []address.Address
receivers []address.Address receivers []address.Address
banker address.Address banker address.Address
bankerNonce uint64 bankerNonce uint64
@ -116,8 +122,9 @@ func NewGenerator() (*ChainGen, error) {
} }
minercfg := &GenMinerCfg{ minercfg := &GenMinerCfg{
Worker: worker, Workers: []address.Address{worker, worker},
Owner: worker, Owners: []address.Address{worker, worker},
PeerIDs: []peer.ID{"peerID1", "peerID2"},
} }
genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{ genb, err := MakeGenesisBlock(bs, map[address.Address]types.BigInt{
@ -131,28 +138,32 @@ func NewGenerator() (*ChainGen, error) {
cs := store.NewChainStore(bs, ds) cs := store.NewChainStore(bs, ds)
genfb := &types.FullBlock{Header: genb.Genesis} genfb := &types.FullBlock{Header: genb.Genesis}
gents := store.NewFullTipSet([]*types.FullBlock{genfb})
if err := cs.SetGenesis(genb.Genesis); err != nil { if err := cs.SetGenesis(genb.Genesis); err != nil {
return nil, xerrors.Errorf("set genesis failed: %w", err) return nil, xerrors.Errorf("set genesis failed: %w", err)
} }
if minercfg.MinerAddr == address.Undef { if len(minercfg.MinerAddrs) == 0 {
return nil, xerrors.Errorf("MakeGenesisBlock failed to set miner address") return nil, xerrors.Errorf("MakeGenesisBlock failed to set miner address")
} }
sm := stmgr.NewStateManager(cs)
gen := &ChainGen{ gen := &ChainGen{
bs: bs, bs: bs,
cs: cs, cs: cs,
sm: sm,
msgsPerBlock: msgsPerBlock, msgsPerBlock: msgsPerBlock,
genesis: genb.Genesis, genesis: genb.Genesis,
w: w, w: w,
miner: minercfg.MinerAddr, miners: minercfg.MinerAddrs,
mworker: worker, mworkers: minercfg.Workers,
banker: banker, banker: banker,
receivers: receievers, receivers: receievers,
curBlock: genfb, curTipset: gents,
r: mr, r: mr,
lr: lr, lr: lr,
@ -179,19 +190,34 @@ func (cg *ChainGen) GenesisCar() ([]byte, error) {
return out.Bytes(), nil return out.Bytes(), nil
} }
func (cg *ChainGen) nextBlockProof(ctx context.Context) (address.Address, types.ElectionProof, []*types.Ticket, error) { func (cg *ChainGen) nextBlockProof(ctx context.Context, m address.Address, ticks []*types.Ticket) (types.ElectionProof, *types.Ticket, error) {
pts := cg.curTipset.TipSet()
ticks := cg.curBlock.Header.Tickets var lastTicket *types.Ticket
lastTicket := ticks[len(ticks)-1] if len(ticks) == 0 {
lastTicket = pts.MinTicket()
} else {
lastTicket = ticks[len(ticks)-1]
}
vrfout, err := ComputeVRF(ctx, cg.w.Sign, cg.mworker, lastTicket.VDFResult) st, err := cg.sm.TipSetState(pts.Cids())
if err != nil { if err != nil {
return address.Undef, nil, nil, err return nil, nil, err
}
worker, err := stmgr.GetMinerWorker(ctx, cg.sm, st, m)
if err != nil {
return nil, nil, err
}
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, lastTicket.VDFResult)
if err != nil {
return nil, nil, err
} }
out, proof, err := vdf.Run(vrfout) out, proof, err := vdf.Run(vrfout)
if err != nil { if err != nil {
return address.Undef, nil, nil, err return nil, nil, err
} }
tick := &types.Ticket{ tick := &types.Ticket{
@ -200,17 +226,77 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context) (address.Address, types.
VDFResult: out, VDFResult: out,
} }
return cg.miner, []byte("cat in a box"), []*types.Ticket{tick}, nil win, eproof, err := IsRoundWinner(ctx, pts, append(ticks, tick), m, &mca{w: cg.w, sm: cg.sm})
}
func (cg *ChainGen) NextBlock() (*types.FullBlock, []*types.SignedMessage, error) {
miner, proof, tickets, err := cg.nextBlockProof(context.TODO())
if err != nil { if err != nil {
return nil, nil, err return nil, nil, xerrors.Errorf("checking round winner failed: %w", err)
}
if !win {
return nil, tick, nil
} }
// make some transfers from banker return eproof, tick, nil
}
type MinedTipSet struct {
TipSet *store.FullTipSet
Messages []*types.SignedMessage
}
func (cg *ChainGen) NextTipSet() (*MinedTipSet, error) {
var blks []*types.FullBlock
ticketSets := make([][]*types.Ticket, len(cg.miners))
msgs, err := cg.getRandomMessages()
if err != nil {
return nil, err
}
base := cg.curTipset.TipSet()
for len(blks) == 0 {
for i, m := range cg.miners {
proof, t, err := cg.nextBlockProof(context.TODO(), m, ticketSets[i])
if err != nil {
return nil, err
}
ticketSets[i] = append(ticketSets[i], t)
if proof != nil {
fblk, err := cg.makeBlock(base, m, proof, ticketSets[i], msgs)
if err != nil {
return nil, xerrors.Errorf("making a block for next tipset failed: %w", err)
}
if err := cg.cs.AddBlock(fblk.Header); err != nil {
return nil, err
}
blks = append(blks, fblk)
}
}
}
cg.curTipset = store.NewFullTipSet(blks)
return &MinedTipSet{
TipSet: cg.curTipset,
Messages: msgs,
}, nil
}
func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof types.ElectionProof, tickets []*types.Ticket, msgs []*types.SignedMessage) (*types.FullBlock, error) {
ts := parents.MinTimestamp() + (uint64(len(tickets)) * build.BlockDelay)
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, tickets, eproof, msgs, ts)
if err != nil {
return nil, err
}
return fblk, err
}
func (cg *ChainGen) getRandomMessages() ([]*types.SignedMessage, error) {
msgs := make([]*types.SignedMessage, cg.msgsPerBlock) msgs := make([]*types.SignedMessage, cg.msgsPerBlock)
for m := range msgs { for m := range msgs {
msg := types.Message{ msg := types.Message{
@ -229,12 +315,12 @@ func (cg *ChainGen) NextBlock() (*types.FullBlock, []*types.SignedMessage, error
unsigned, err := msg.Serialize() unsigned, err := msg.Serialize()
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
sig, err := cg.w.Sign(context.TODO(), cg.banker, unsigned) sig, err := cg.w.Sign(context.TODO(), cg.banker, unsigned)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
msgs[m] = &types.SignedMessage{ msgs[m] = &types.SignedMessage{
@ -243,31 +329,11 @@ func (cg *ChainGen) NextBlock() (*types.FullBlock, []*types.SignedMessage, error
} }
if _, err := cg.cs.PutMessage(msgs[m]); err != nil { if _, err := cg.cs.PutMessage(msgs[m]); err != nil {
return nil, nil, err return nil, err
} }
} }
// create block return msgs, nil
parents, err := types.NewTipSet([]*types.BlockHeader{cg.curBlock.Header})
if err != nil {
return nil, nil, err
}
ts := parents.MinTimestamp() + (uint64(len(tickets)) * build.BlockDelay)
fblk, err := MinerCreateBlock(context.TODO(), cg.cs, cg.w, miner, parents, tickets, proof, msgs, ts)
if err != nil {
return nil, nil, err
}
if err := cg.cs.AddBlock(fblk.Header); err != nil {
return nil, nil, err
}
cg.curBlock = fblk
return fblk, msgs, nil
} }
func (cg *ChainGen) YieldRepo() (repo.Repo, error) { func (cg *ChainGen) YieldRepo() (repo.Repo, error) {
@ -276,3 +342,86 @@ func (cg *ChainGen) YieldRepo() (repo.Repo, error) {
} }
return cg.r, nil return cg.r, nil
} }
type MiningCheckAPI interface {
ChainGetRandomness(context.Context, *types.TipSet, []*types.Ticket, int) ([]byte, error)
StateMinerPower(context.Context, address.Address, *types.TipSet) (api.MinerPower, error)
StateMinerWorker(context.Context, address.Address, *types.TipSet) (address.Address, error)
WalletSign(context.Context, address.Address, []byte) (*types.Signature, error)
}
type mca struct {
w *wallet.Wallet
sm *stmgr.StateManager
}
func (mca mca) ChainGetRandomness(ctx context.Context, pts *types.TipSet, ticks []*types.Ticket, lb int) ([]byte, error) {
return mca.sm.ChainStore().GetRandomness(ctx, pts, ticks, lb)
}
func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) {
mpow, tpow, err := stmgr.GetPower(ctx, mca.sm, ts, maddr)
if err != nil {
return api.MinerPower{}, err
}
return api.MinerPower{
MinerPower: mpow,
TotalPower: tpow,
}, err
}
func (mca mca) StateMinerWorker(ctx context.Context, maddr address.Address, ts *types.TipSet) (address.Address, error) {
st, err := mca.sm.TipSetState(ts.Cids())
if err != nil {
return address.Undef, err
}
return stmgr.GetMinerWorker(ctx, mca.sm, st, maddr)
}
func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*types.Signature, error) {
return mca.w.Sign(ctx, a, v)
}
func IsRoundWinner(ctx context.Context, ts *types.TipSet, ticks []*types.Ticket, miner address.Address, a MiningCheckAPI) (bool, types.ElectionProof, error) {
r, err := a.ChainGetRandomness(ctx, ts, ticks, build.RandomnessLookback)
if err != nil {
return false, nil, err
}
mworker, err := a.StateMinerWorker(ctx, miner, ts)
if err != nil {
return false, nil, xerrors.Errorf("failed to get miner worker: %w", err)
}
vrfout, err := ComputeVRF(ctx, a.WalletSign, mworker, r)
if err != nil {
return false, nil, xerrors.Errorf("failed to compute VRF: %w", err)
}
pow, err := a.StateMinerPower(ctx, miner, ts)
if err != nil {
return false, nil, xerrors.Errorf("failed to check power: %w", err)
}
return types.PowerCmp(vrfout, pow.MinerPower, pow.TotalPower), vrfout, nil
}
type SignFunc func(context.Context, address.Address, []byte) (*types.Signature, error)
func ComputeVRF(ctx context.Context, sign SignFunc, w address.Address, input []byte) ([]byte, error) {
sig, err := sign(ctx, w, input)
if err != nil {
return nil, err
}
if sig.Type != types.KTBLS {
return nil, fmt.Errorf("miner worker address was not a BLS key")
}
return sig.Data, nil
}

View File

@ -12,14 +12,18 @@ func testGeneration(t testing.TB, n int, msgs int) {
g.msgsPerBlock = msgs g.msgsPerBlock = msgs
var height int
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
b, _, err := g.NextBlock() mts, err := g.NextTipSet()
if err != nil { if err != nil {
t.Fatalf("error at H:%d, %s", i, err) t.Fatalf("error at H:%d, %s", i, err)
} }
if b.Header.Height != uint64(i+1) {
t.Fatal("wrong height") ts := mts.TipSet.TipSet()
if ts.Height() != uint64(height+len(ts.Blocks()[0].Tickets)) {
t.Fatal("wrong height", ts.Height(), i, len(ts.Blocks()[0].Tickets), len(ts.Blocks()))
} }
height += len(ts.Blocks()[0].Tickets)
} }
} }

View File

@ -2,40 +2,41 @@ package gen
import ( import (
"context" "context"
"github.com/filecoin-project/go-bls-sigs"
"github.com/ipfs/go-cid" bls "github.com/filecoin-project/go-bls-sigs"
"github.com/ipfs/go-hamt-ipld" cid "github.com/ipfs/go-cid"
hamt "github.com/ipfs/go-hamt-ipld"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/whyrusleeping/sharray" "github.com/whyrusleeping/sharray"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm" "github.com/filecoin-project/go-lotus/chain/vm"
"github.com/filecoin-project/go-lotus/chain/wallet" "github.com/filecoin-project/go-lotus/chain/wallet"
) )
func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, w *wallet.Wallet, miner address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, timestamp uint64) (*types.FullBlock, error) { func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wallet, miner address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, timestamp uint64) (*types.FullBlock, error) {
st, err := cs.TipSetState(parents.Cids()) st, err := sm.TipSetState(parents.Cids())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to load tipset state") return nil, errors.Wrap(err, "failed to load tipset state")
} }
height := parents.Height() + uint64(len(tickets)) height := parents.Height() + uint64(len(tickets))
vmi, err := vm.NewVM(st, height, miner, cs) vmi, err := vm.NewVM(st, height, miner, sm.ChainStore())
if err != nil { if err != nil {
return nil, err return nil, err
} }
owner, err := getMinerOwner(ctx, cs, st, miner) owner, err := stmgr.GetMinerOwner(ctx, sm, st, miner)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to get miner owner: %w", err) return nil, xerrors.Errorf("failed to get miner owner: %w", err)
} }
worker, err := getMinerWorker(ctx, cs, st, miner) worker, err := stmgr.GetMinerWorker(ctx, sm, st, miner)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to get miner worker: %w", err) return nil, xerrors.Errorf("failed to get miner worker: %w", err)
} }
@ -46,11 +47,12 @@ func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, w *wallet.Walle
} }
next := &types.BlockHeader{ next := &types.BlockHeader{
Miner: miner, Miner: miner,
Parents: parents.Cids(), Parents: parents.Cids(),
Tickets: tickets, Tickets: tickets,
Height: height, Height: height,
Timestamp: timestamp, Timestamp: timestamp,
ElectionProof: proof,
} }
var blsMessages []*types.Message var blsMessages []*types.Message
@ -63,7 +65,7 @@ func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, w *wallet.Walle
blsSigs = append(blsSigs, msg.Signature) blsSigs = append(blsSigs, msg.Signature)
blsMessages = append(blsMessages, &msg.Message) blsMessages = append(blsMessages, &msg.Message)
c, err := cs.PutMessage(&msg.Message) c, err := sm.ChainStore().PutMessage(&msg.Message)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -93,7 +95,7 @@ func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, w *wallet.Walle
receipts = append(receipts, rec.MessageReceipt) receipts = append(receipts, rec.MessageReceipt)
} }
cst := hamt.CSTFromBstore(cs.Blockstore()) cst := hamt.CSTFromBstore(sm.ChainStore().Blockstore())
blsmsgroot, err := sharray.Build(context.TODO(), 4, toIfArr(blsMsgCids), cst) blsmsgroot, err := sharray.Build(context.TODO(), 4, toIfArr(blsMsgCids), cst)
if err != nil { if err != nil {
return nil, err return nil, err
@ -130,7 +132,7 @@ func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, w *wallet.Walle
next.BLSAggregate = aggSig next.BLSAggregate = aggSig
next.StateRoot = stateRoot next.StateRoot = stateRoot
pweight := cs.Weight(parents) pweight := sm.ChainStore().Weight(parents)
next.ParentWeight = types.NewInt(pweight) next.ParentWeight = types.NewInt(pweight)
// TODO: set timestamp // TODO: set timestamp
@ -161,40 +163,6 @@ func MinerCreateBlock(ctx context.Context, cs *store.ChainStore, w *wallet.Walle
return fullBlock, nil return fullBlock, nil
} }
func getMinerWorker(ctx context.Context, cs *store.ChainStore, state cid.Cid, maddr address.Address) (address.Address, error) {
rec, err := vm.CallRaw(ctx, cs, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetWorkerAddr,
}, state, 0)
if err != nil {
return address.Undef, err
}
if rec.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getWorker failed with exit code %d", rec.ExitCode)
}
return address.NewFromBytes(rec.Return)
}
func getMinerOwner(ctx context.Context, cs *store.ChainStore, state cid.Cid, maddr address.Address) (address.Address, error) {
rec, err := vm.CallRaw(ctx, cs, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetOwner,
}, state, 0)
if err != nil {
return address.Undef, err
}
if rec.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getOwner failed with exit code %d", rec.ExitCode)
}
return address.NewFromBytes(rec.Return)
}
func aggregateSignatures(sigs []types.Signature) (types.Signature, error) { func aggregateSignatures(sigs []types.Signature) (types.Signature, error) {
var blsSigs []bls.Signature var blsSigs []bls.Signature
for _, s := range sigs { for _, s := range sigs {

View File

@ -140,16 +140,16 @@ func SetupStorageMarketActor(bs bstore.Blockstore) (*types.Actor, error) {
} }
type GenMinerCfg struct { type GenMinerCfg struct {
Owner address.Address Owners []address.Address
Worker address.Address Workers []address.Address
// not quite generating real sectors yet, but this will be necessary // not quite generating real sectors yet, but this will be necessary
//SectorDir string //SectorDir string
// The address of the created miner, this is set by the genesis setup // The addresses of the created miner, this is set by the genesis setup
MinerAddr address.Address MinerAddrs []address.Address
PeerID peer.ID PeerIDs []peer.ID
} }
func mustEnc(i interface{}) []byte { func mustEnc(i interface{}) []byte {
@ -166,67 +166,78 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err) return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err)
} }
params := mustEnc(actors.CreateStorageMinerParams{ for i := 0; i < len(gmcfg.Workers); i++ {
Owner: gmcfg.Owner, owner := gmcfg.Owners[i]
Worker: gmcfg.Worker, worker := gmcfg.Workers[i]
SectorSize: types.NewInt(build.SectorSize), pid := gmcfg.PeerIDs[i]
PeerID: gmcfg.PeerID,
})
rval, err := doExec(ctx, vm, actors.StorageMarketAddress, gmcfg.Owner, actors.SMAMethods.CreateStorageMiner, params) params := mustEnc(actors.CreateStorageMinerParams{
if err != nil { Owner: owner,
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err) Worker: worker,
SectorSize: types.NewInt(build.SectorSize),
PeerID: pid,
})
rval, err := doExec(ctx, vm, actors.StorageMarketAddress, owner, actors.SMAMethods.CreateStorageMiner, params)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to create genesis miner: %w", err)
}
maddr, err := address.NewFromBytes(rval)
if err != nil {
return cid.Undef, err
}
gmcfg.MinerAddrs = append(gmcfg.MinerAddrs, maddr)
params = mustEnc(actors.UpdateStorageParams{Delta: types.NewInt(5000)})
_, err = doExec(ctx, vm, actors.StorageMarketAddress, maddr, actors.SMAMethods.UpdateStorage, params)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to update total storage: %w", err)
}
// UGLY HACKY MODIFICATION OF MINER POWER
// we have to flush the vm here because it buffers stuff internally for perf reasons
if _, err := vm.Flush(ctx); err != nil {
return cid.Undef, xerrors.Errorf("vm.Flush failed: %w", err)
}
st := vm.StateTree()
mact, err := st.GetActor(maddr)
if err != nil {
return cid.Undef, xerrors.Errorf("get miner actor failed: %w", err)
}
cst := hamt.CSTFromBstore(cs.Blockstore())
var mstate actors.StorageMinerActorState
if err := cst.Get(ctx, mact.Head, &mstate); err != nil {
return cid.Undef, xerrors.Errorf("getting miner actor state failed: %w", err)
}
mstate.Power = types.NewInt(5000)
nstate, err := cst.Put(ctx, mstate)
if err != nil {
return cid.Undef, err
}
mact.Head = nstate
if err := st.SetActor(maddr, mact); err != nil {
return cid.Undef, err
}
// End of super haxx
} }
maddr, err := address.NewFromBytes(rval)
if err != nil {
return cid.Undef, err
}
gmcfg.MinerAddr = maddr
params = mustEnc(actors.UpdateStorageParams{Delta: types.NewInt(5000)})
_, err = doExec(ctx, vm, actors.StorageMarketAddress, maddr, actors.SMAMethods.UpdateStorage, params)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to update total storage: %w", err)
}
// UGLY HACKY MODIFICATION OF MINER POWER
// we have to flush the vm here because it buffers stuff internally for perf reasons
if _, err := vm.Flush(ctx); err != nil {
return cid.Undef, xerrors.Errorf("vm.Flush failed: %w", err)
}
st := vm.StateTree()
mact, err := st.GetActor(maddr)
if err != nil {
return cid.Undef, xerrors.Errorf("get miner actor failed: %w", err)
}
cst := hamt.CSTFromBstore(cs.Blockstore())
var mstate actors.StorageMinerActorState
if err := cst.Get(ctx, mact.Head, &mstate); err != nil {
return cid.Undef, xerrors.Errorf("getting miner actor state failed: %w", err)
}
mstate.Power = types.NewInt(5000)
nstate, err := cst.Put(ctx, mstate)
if err != nil {
return cid.Undef, err
}
mact.Head = nstate
if err := st.SetActor(maddr, mact); err != nil {
return cid.Undef, err
}
// End of super haxx
return vm.Flush(ctx) return vm.Flush(ctx)
} }
func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uint64, params []byte) ([]byte, error) { func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uint64, params []byte) ([]byte, error) {
act, err := vm.StateTree().GetActor(from)
if err != nil {
return nil, xerrors.Errorf("doExec failed to get from actor: %w", err)
}
ret, err := vm.ApplyMessage(context.TODO(), &types.Message{ ret, err := vm.ApplyMessage(context.TODO(), &types.Message{
To: to, To: to,
From: from, From: from,
@ -235,6 +246,7 @@ func doExec(ctx context.Context, vm *vm.VM, to, from address.Address, method uin
GasLimit: types.NewInt(1000000), GasLimit: types.NewInt(1000000),
GasPrice: types.NewInt(0), GasPrice: types.NewInt(0),
Value: types.NewInt(0), Value: types.NewInt(0),
Nonce: act.Nonce,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -322,19 +334,3 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B
Genesis: b, Genesis: b,
}, nil }, nil
} }
type SignFunc func(context.Context, address.Address, []byte) (*types.Signature, error)
func ComputeVRF(ctx context.Context, sign SignFunc, w address.Address, input []byte) ([]byte, error) {
sig, err := sign(ctx, w, input)
if err != nil {
return nil, err
}
if sig.Type != types.KTBLS {
return nil, fmt.Errorf("miner worker address was not a BLS key")
}
return sig.Data, nil
}

View File

@ -5,7 +5,7 @@ import (
"sync" "sync"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -15,7 +15,7 @@ type MessagePool struct {
pending map[address.Address]*msgSet pending map[address.Address]*msgSet
cs *store.ChainStore sm *stmgr.StateManager
} }
type msgSet struct { type msgSet struct {
@ -36,12 +36,12 @@ func (ms *msgSet) add(m *types.SignedMessage) {
ms.msgs[m.Message.Nonce] = m ms.msgs[m.Message.Nonce] = m
} }
func NewMessagePool(cs *store.ChainStore) *MessagePool { func NewMessagePool(sm *stmgr.StateManager) *MessagePool {
mp := &MessagePool{ mp := &MessagePool{
pending: make(map[address.Address]*msgSet), pending: make(map[address.Address]*msgSet),
cs: cs, sm: sm,
} }
cs.SubscribeHeadChanges(mp.HeadChange) sm.ChainStore().SubscribeHeadChanges(mp.HeadChange)
return mp return mp
} }
@ -61,7 +61,7 @@ func (mp *MessagePool) Add(m *types.SignedMessage) error {
return err return err
} }
if _, err := mp.cs.PutMessage(m); err != nil { if _, err := mp.sm.ChainStore().PutMessage(m); err != nil {
return err return err
} }
@ -84,7 +84,7 @@ func (mp *MessagePool) GetNonce(addr address.Address) (uint64, error) {
return mset.startNonce + uint64(len(mset.msgs)), nil return mset.startNonce + uint64(len(mset.msgs)), nil
} }
act, err := mp.cs.GetActor(addr) act, err := mp.sm.GetActor(addr)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -130,7 +130,7 @@ func (mp *MessagePool) Pending() []*types.SignedMessage {
func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) error { func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) error {
for _, ts := range revert { for _, ts := range revert {
for _, b := range ts.Blocks() { for _, b := range ts.Blocks() {
bmsgs, smsgs, err := mp.cs.MessagesForBlock(b) bmsgs, smsgs, err := mp.sm.ChainStore().MessagesForBlock(b)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to get messages for revert block %s(height %d)", b.Cid(), b.Height) return errors.Wrapf(err, "failed to get messages for revert block %s(height %d)", b.Cid(), b.Height)
} }
@ -155,7 +155,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet)
for _, ts := range apply { for _, ts := range apply {
for _, b := range ts.Blocks() { for _, b := range ts.Blocks() {
bmsgs, smsgs, err := mp.cs.MessagesForBlock(b) bmsgs, smsgs, err := mp.sm.ChainStore().MessagesForBlock(b)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to get messages for apply block %s(height %d) (msgroot = %s)", b.Cid(), b.Height, b.Messages) return errors.Wrapf(err, "failed to get messages for apply block %s(height %d) (msgroot = %s)", b.Cid(), b.Height, b.Messages)
} }

View File

@ -1,17 +1,18 @@
package vm package stmgr
import ( import (
"context" "context"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
cid "github.com/ipfs/go-cid" cid "github.com/ipfs/go-cid"
"golang.org/x/xerrors" "golang.org/x/xerrors"
) )
func CallRaw(ctx context.Context, cs *store.ChainStore, msg *types.Message, bstate cid.Cid, bheight uint64) (*types.MessageReceipt, error) { func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, bheight uint64) (*types.MessageReceipt, error) {
vmi, err := NewVM(bstate, bheight, actors.NetworkAddress, cs) vmi, err := vm.NewVM(bstate, bheight, actors.NetworkAddress, sm.cs)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to set up vm: %w", err) return nil, xerrors.Errorf("failed to set up vm: %w", err)
} }
@ -32,7 +33,7 @@ func CallRaw(ctx context.Context, cs *store.ChainStore, msg *types.Message, bsta
} }
} }
fromActor, err := vmi.cstate.GetActor(msg.From) fromActor, err := vmi.StateTree().GetActor(msg.From)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -52,15 +53,15 @@ func CallRaw(ctx context.Context, cs *store.ChainStore, msg *types.Message, bsta
} }
func Call(ctx context.Context, cs *store.ChainStore, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) { func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
if ts == nil { if ts == nil {
ts = cs.GetHeaviestTipSet() ts = sm.cs.GetHeaviestTipSet()
} }
state, err := cs.TipSetState(ts.Cids()) state, err := sm.TipSetState(ts.Cids())
if err != nil { if err != nil {
return nil, err return nil, err
} }
return CallRaw(ctx, cs, msg, state, ts.Height()) return sm.CallRaw(ctx, msg, state, ts.Height())
} }

165
chain/stmgr/stmgr.go Normal file
View File

@ -0,0 +1,165 @@
package stmgr
import (
"context"
"sync"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
"golang.org/x/xerrors"
"github.com/ipfs/go-cid"
hamt "github.com/ipfs/go-hamt-ipld"
logging "github.com/ipfs/go-log"
)
var log = logging.Logger("statemgr")
type StateManager struct {
cs *store.ChainStore
stCache map[string]cid.Cid
stlk sync.Mutex
}
func NewStateManager(cs *store.ChainStore) *StateManager {
return &StateManager{
cs: cs,
stCache: make(map[string]cid.Cid),
}
}
func cidsToKey(cids []cid.Cid) string {
var out string
for _, c := range cids {
out += c.KeyString()
}
return out
}
func (sm *StateManager) TipSetState(cids []cid.Cid) (cid.Cid, error) {
ck := cidsToKey(cids)
sm.stlk.Lock()
cached, ok := sm.stCache[ck]
sm.stlk.Unlock()
if ok {
return cached, nil
}
out, err := sm.computeTipSetState(cids)
if err != nil {
return cid.Undef, err
}
sm.stlk.Lock()
sm.stCache[ck] = out
sm.stlk.Unlock()
return out, nil
}
func (sm *StateManager) computeTipSetState(cids []cid.Cid) (cid.Cid, error) {
ctx := context.TODO()
ts, err := sm.cs.LoadTipSet(cids)
if err != nil {
log.Error("failed loading tipset: ", cids)
return cid.Undef, err
}
if len(ts.Blocks()) == 1 {
return ts.Blocks()[0].StateRoot, nil
}
pstate, err := sm.TipSetState(ts.Parents())
if err != nil {
return cid.Undef, xerrors.Errorf("recursive TipSetState failed: %w", err)
}
vmi, err := vm.NewVM(pstate, ts.Height(), address.Undef, sm.cs)
if err != nil {
return cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err)
}
applied := make(map[cid.Cid]bool)
for _, b := range ts.Blocks() {
vmi.SetBlockMiner(b.Miner)
bms, sms, err := sm.cs.MessagesForBlock(b)
if err != nil {
return cid.Undef, xerrors.Errorf("failed to get messages for block: %w", err)
}
for _, m := range bms {
if applied[m.Cid()] {
continue
}
applied[m.Cid()] = true
_, err := vmi.ApplyMessage(ctx, m)
if err != nil {
return cid.Undef, err
}
}
for _, sm := range sms {
if applied[sm.Cid()] {
continue
}
applied[sm.Cid()] = true
_, err := vmi.ApplyMessage(ctx, &sm.Message)
if err != nil {
return cid.Undef, err
}
}
}
return vmi.Flush(ctx)
}
func (sm *StateManager) GetActor(addr address.Address) (*types.Actor, error) {
ts := sm.cs.GetHeaviestTipSet()
stcid, err := sm.TipSetState(ts.Cids())
if err != nil {
return nil, err
}
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
state, err := state.LoadStateTree(cst, stcid)
if err != nil {
return nil, err
}
return state.GetActor(addr)
}
func (sm *StateManager) GetBalance(addr address.Address) (types.BigInt, error) {
act, err := sm.GetActor(addr)
if err != nil {
return types.BigInt{}, err
}
return act.Balance, nil
}
func (sm *StateManager) ChainStore() *store.ChainStore {
return sm.cs
}
func (sm *StateManager) LoadActorState(ctx context.Context, a address.Address, out interface{}) (*types.Actor, error) {
act, err := sm.GetActor(a)
if err != nil {
return nil, err
}
cst := hamt.CSTFromBstore(sm.cs.Blockstore())
if err := cst.Get(ctx, act.Head, out); err != nil {
return nil, err
}
return act, nil
}

106
chain/stmgr/utils.go Normal file
View File

@ -0,0 +1,106 @@
package stmgr
import (
"context"
"github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/types"
cid "github.com/ipfs/go-cid"
"golang.org/x/xerrors"
)
func GetMinerWorker(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) {
recp, err := sm.CallRaw(ctx, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetWorkerAddr,
}, st, 0)
if err != nil {
return address.Undef, xerrors.Errorf("callRaw failed: %w", err)
}
if recp.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getting miner worker addr failed (exit code %d)", recp.ExitCode)
}
worker, err := address.NewFromBytes(recp.Return)
if err != nil {
return address.Undef, err
}
if worker.Protocol() == address.ID {
return address.Undef, xerrors.Errorf("need to resolve worker address to a pubkeyaddr")
}
return worker, nil
}
func GetMinerOwner(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) {
recp, err := sm.CallRaw(ctx, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetOwner,
}, st, 0)
if err != nil {
return address.Undef, xerrors.Errorf("callRaw failed: %w", err)
}
if recp.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getting miner owner addr failed (exit code %d)", recp.ExitCode)
}
owner, err := address.NewFromBytes(recp.Return)
if err != nil {
return address.Undef, err
}
if owner.Protocol() == address.ID {
return address.Undef, xerrors.Errorf("need to resolve owner address to a pubkeyaddr")
}
return owner, nil
}
func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (types.BigInt, types.BigInt, error) {
var err error
enc, err := actors.SerializeParams(&actors.PowerLookupParams{maddr})
if err != nil {
return types.EmptyInt, types.EmptyInt, err
}
var mpow types.BigInt
if maddr != address.Undef {
ret, err := sm.Call(ctx, &types.Message{
From: maddr,
To: actors.StorageMarketAddress,
Method: actors.SMAMethods.PowerLookup,
Params: enc,
}, ts)
if err != nil {
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get miner power from chain: %w", err)
}
if ret.ExitCode != 0 {
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get miner power from chain (exit code %d)", ret.ExitCode)
}
mpow = types.BigFromBytes(ret.Return)
}
ret, err := sm.Call(ctx, &types.Message{
From: actors.StorageMarketAddress,
To: actors.StorageMarketAddress,
Method: actors.SMAMethods.GetTotalStorage,
}, ts)
if err != nil {
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get total power from chain: %w", err)
}
if ret.ExitCode != 0 {
return types.EmptyInt, types.EmptyInt, xerrors.Errorf("failed to get total power from chain (exit code %d)", ret.ExitCode)
}
tpow := types.BigFromBytes(ret.Return)
return mpow, tpow, nil
}

View File

@ -2,13 +2,13 @@ package store
import ( import (
"context" "context"
"crypto/sha256"
"encoding/json" "encoding/json"
"fmt" "fmt"
"sync" "sync"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"golang.org/x/xerrors"
block "github.com/ipfs/go-block-format" block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
@ -35,6 +35,9 @@ type ChainStore struct {
bestTips *pubsub.PubSub bestTips *pubsub.PubSub
tstLk sync.Mutex
tipsets map[uint64][]cid.Cid
headChangeNotifs []func(rev, app []*types.TipSet) error headChangeNotifs []func(rev, app []*types.TipSet) error
} }
@ -43,6 +46,7 @@ func NewChainStore(bs bstore.Blockstore, ds dstore.Batching) *ChainStore {
bs: bs, bs: bs,
ds: ds, ds: ds,
bestTips: pubsub.New(64), bestTips: pubsub.New(64),
tipsets: make(map[uint64][]cid.Cid),
} }
hcnf := func(rev, app []*types.TipSet) error { hcnf := func(rev, app []*types.TipSet) error {
@ -175,7 +179,13 @@ func (cs *ChainStore) PutTipSet(ts *FullTipSet) error {
} }
} }
if err := cs.MaybeTakeHeavierTipSet(ts.TipSet()); err != nil { expanded, err := cs.expandTipset(ts.TipSet().Blocks()[0])
if err != nil {
return xerrors.Errorf("errored while expanding tipset: %w", err)
}
fmt.Printf("expanded %s into %s\n", ts.TipSet().Cids(), expanded.Cids())
if err := cs.MaybeTakeHeavierTipSet(expanded); err != nil {
return errors.Wrap(err, "MaybeTakeHeavierTipSet failed in PutTipSet") return errors.Wrap(err, "MaybeTakeHeavierTipSet failed in PutTipSet")
} }
return nil return nil
@ -317,12 +327,35 @@ func (cs *ChainStore) GetHeaviestTipSet() *types.TipSet {
return cs.heaviest return cs.heaviest
} }
func (cs *ChainStore) addToTipSetTracker(b *types.BlockHeader) error {
cs.tstLk.Lock()
defer cs.tstLk.Unlock()
tss := cs.tipsets[b.Height]
for _, oc := range tss {
if oc == b.Cid() {
log.Warn("tried to add block to tipset tracker that was already there")
return nil
}
}
cs.tipsets[b.Height] = append(tss, b.Cid())
// TODO: do we want to look for slashable submissions here? might as well...
return nil
}
func (cs *ChainStore) PersistBlockHeader(b *types.BlockHeader) error { func (cs *ChainStore) PersistBlockHeader(b *types.BlockHeader) error {
sb, err := b.ToStorageBlock() sb, err := b.ToStorageBlock()
if err != nil { if err != nil {
return err return err
} }
if err := cs.addToTipSetTracker(b); err != nil {
return xerrors.Errorf("failed to insert new block (%s) into tipset tracker: %w", b.Cid(), err)
}
return cs.bs.Put(sb) return cs.bs.Put(sb)
} }
@ -365,12 +398,49 @@ func (cs *ChainStore) PutMessage(m storable) (cid.Cid, error) {
return PutMessage(cs.bs, m) return PutMessage(cs.bs, m)
} }
func (cs *ChainStore) expandTipset(b *types.BlockHeader) (*types.TipSet, error) {
// Hold lock for the whole function for now, if it becomes a problem we can
// fix pretty easily
cs.tstLk.Lock()
defer cs.tstLk.Unlock()
all := []*types.BlockHeader{b}
tsets, ok := cs.tipsets[b.Height]
if !ok {
return types.NewTipSet(all)
}
for _, bhc := range tsets {
if bhc == b.Cid() {
continue
}
h, err := cs.GetBlock(bhc)
if err != nil {
return nil, xerrors.Errorf("failed to load block (%s) for tipset expansion: %w", bhc, err)
}
if types.CidArrsEqual(h.Parents, b.Parents) {
all = append(all, h)
}
}
// TODO: other validation...?
return types.NewTipSet(all)
}
func (cs *ChainStore) AddBlock(b *types.BlockHeader) error { func (cs *ChainStore) AddBlock(b *types.BlockHeader) error {
if err := cs.PersistBlockHeader(b); err != nil { if err := cs.PersistBlockHeader(b); err != nil {
return err return err
} }
ts, _ := types.NewTipSet([]*types.BlockHeader{b}) ts, err := cs.expandTipset(b)
if err != nil {
return err
}
if err := cs.MaybeTakeHeavierTipSet(ts); err != nil { if err := cs.MaybeTakeHeavierTipSet(ts); err != nil {
return errors.Wrap(err, "MaybeTakeHeavierTipSet failed") return errors.Wrap(err, "MaybeTakeHeavierTipSet failed")
} }
@ -397,21 +467,6 @@ func (cs *ChainStore) GetGenesis() (*types.BlockHeader, error) {
return types.DecodeBlock(genb.RawData()) return types.DecodeBlock(genb.RawData())
} }
func (cs *ChainStore) TipSetState(cids []cid.Cid) (cid.Cid, error) {
ts, err := cs.LoadTipSet(cids)
if err != nil {
log.Error("failed loading tipset: ", cids)
return cid.Undef, err
}
if len(ts.Blocks()) == 1 {
return ts.Blocks()[0].StateRoot, nil
}
panic("cant handle multiblock tipsets yet")
}
func (cs *ChainStore) GetMessage(c cid.Cid) (*types.Message, error) { func (cs *ChainStore) GetMessage(c cid.Cid) (*types.Message, error) {
sb, err := cs.bs.Get(c) sb, err := cs.bs.Get(c)
if err != nil { if err != nil {
@ -460,7 +515,7 @@ func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message,
cst := hamt.CSTFromBstore(cs.bs) cst := hamt.CSTFromBstore(cs.bs)
var msgmeta types.MsgMeta var msgmeta types.MsgMeta
if err := cst.Get(context.TODO(), b.Messages, &msgmeta); err != nil { if err := cst.Get(context.TODO(), b.Messages, &msgmeta); err != nil {
return nil, nil, err return nil, nil, xerrors.Errorf("failed to load msgmeta: %w", err)
} }
blscids, err := cs.readSharrayCids(msgmeta.BlsMessages) blscids, err := cs.readSharrayCids(msgmeta.BlsMessages)
@ -539,31 +594,6 @@ func (cs *ChainStore) LoadSignedMessagesFromCids(cids []cid.Cid) ([]*types.Signe
return msgs, nil return msgs, nil
} }
func (cs *ChainStore) GetBalance(addr address.Address) (types.BigInt, error) {
act, err := cs.GetActor(addr)
if err != nil {
return types.BigInt{}, err
}
return act.Balance, nil
}
func (cs *ChainStore) GetActor(addr address.Address) (*types.Actor, error) {
ts := cs.GetHeaviestTipSet()
stcid, err := cs.TipSetState(ts.Cids())
if err != nil {
return nil, err
}
cst := hamt.CSTFromBstore(cs.bs)
state, err := state.LoadStateTree(cst, stcid)
if err != nil {
return nil, err
}
return state.GetActor(addr)
}
func (cs *ChainStore) WaitForMessage(ctx context.Context, mcid cid.Cid) (cid.Cid, *types.MessageReceipt, error) { func (cs *ChainStore) WaitForMessage(ctx context.Context, mcid cid.Cid) (cid.Cid, *types.MessageReceipt, error) {
tsub := cs.SubHeadChanges(ctx) tsub := cs.SubHeadChanges(ctx)
@ -672,3 +702,47 @@ func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) {
} }
return NewFullTipSet(out), nil return NewFullTipSet(out), nil
} }
func (cs *ChainStore) GetRandomness(ctx context.Context, pts *types.TipSet, tickets []*types.Ticket, lb int) ([]byte, error) {
if lb < len(tickets) {
log.Warn("self sampling randomness. this should be extremely rare, if you see this often it may be a bug")
t := tickets[len(tickets)-(1+lb)]
return t.VDFResult, nil
}
nv := lb - len(tickets)
nextCids := pts.Cids()
for {
nts, err := cs.LoadTipSet(nextCids)
if err != nil {
return nil, err
}
mtb := nts.MinTicketBlock()
if nv < len(mtb.Tickets) {
t := mtb.Tickets[len(mtb.Tickets)-(1+nv)]
return t.VDFResult, nil
}
nv -= len(mtb.Tickets)
// special case for lookback behind genesis block
// TODO(spec): this is not in the spec, need to sync that
if mtb.Height == 0 {
t := mtb.Tickets[0]
rval := t.VDFResult
for i := 0; i < nv; i++ {
h := sha256.Sum256(rval)
rval = h[:]
}
return rval, nil
}
nextCids = mtb.Parents
}
}

View File

@ -11,6 +11,7 @@ import (
"github.com/filecoin-project/go-lotus/build" "github.com/filecoin-project/go-lotus/build"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm" "github.com/filecoin-project/go-lotus/chain/vm"
@ -34,6 +35,9 @@ type Syncer struct {
// The interface for accessing and putting tipsets into local storage // The interface for accessing and putting tipsets into local storage
store *store.ChainStore store *store.ChainStore
// the state manager handles making state queries
sm *stmgr.StateManager
// The known Genesis tipset // The known Genesis tipset
Genesis *types.TipSet Genesis *types.TipSet
@ -53,8 +57,8 @@ type Syncer struct {
peerHeadsLk sync.Mutex peerHeadsLk sync.Mutex
} }
func NewSyncer(cs *store.ChainStore, bsync *BlockSync, self peer.ID) (*Syncer, error) { func NewSyncer(sm *stmgr.StateManager, bsync *BlockSync, self peer.ID) (*Syncer, error) {
gen, err := cs.GetGenesis() gen, err := sm.ChainStore().GetGenesis()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -68,7 +72,8 @@ func NewSyncer(cs *store.ChainStore, bsync *BlockSync, self peer.ID) (*Syncer, e
Genesis: gent, Genesis: gent,
Bsync: bsync, Bsync: bsync,
peerHeads: make(map[peer.ID]*types.TipSet), peerHeads: make(map[peer.ID]*types.TipSet),
store: cs, store: sm.ChainStore(),
sm: sm,
self: self, self: self,
}, nil }, nil
} }
@ -320,7 +325,7 @@ func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, b
return err return err
} }
ret, err := vm.Call(ctx, syncer.store, &types.Message{ ret, err := syncer.sm.Call(ctx, &types.Message{
To: actors.StorageMarketAddress, To: actors.StorageMarketAddress,
From: maddr, From: maddr,
Method: actors.SMAMethods.IsMiner, Method: actors.SMAMethods.IsMiner,
@ -353,7 +358,6 @@ func (syncer *Syncer) validateTickets(ctx context.Context, mworker address.Addre
Data: next.VRFProof, Data: next.VRFProof,
} }
log.Infof("About to verify signature: ", sig.Data, mworker, cur.VDFResult)
if err := sig.Verify(mworker, cur.VDFResult); err != nil { if err := sig.Verify(mworker, cur.VDFResult); err != nil {
return xerrors.Errorf("invalid ticket, VRFProof invalid: %w", err) return xerrors.Errorf("invalid ticket, VRFProof invalid: %w", err)
} }
@ -369,62 +373,10 @@ func (syncer *Syncer) validateTickets(ctx context.Context, mworker address.Addre
return nil return nil
} }
func getMinerWorker(ctx context.Context, cs *store.ChainStore, st cid.Cid, maddr address.Address) (address.Address, error) {
recp, err := vm.CallRaw(ctx, cs, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetWorkerAddr,
}, st, 0)
if err != nil {
return address.Undef, xerrors.Errorf("callRaw failed: %w", err)
}
if recp.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getting miner worker addr failed (exit code %d)", recp.ExitCode)
}
worker, err := address.NewFromBytes(recp.Return)
if err != nil {
return address.Undef, err
}
if worker.Protocol() == address.ID {
return address.Undef, xerrors.Errorf("need to resolve worker address to a pubkeyaddr")
}
return worker, nil
}
func getMinerOwner(ctx context.Context, cs *store.ChainStore, st cid.Cid, maddr address.Address) (address.Address, error) {
recp, err := vm.CallRaw(ctx, cs, &types.Message{
To: maddr,
From: maddr,
Method: actors.MAMethods.GetOwner,
}, st, 0)
if err != nil {
return address.Undef, xerrors.Errorf("callRaw failed: %w", err)
}
if recp.ExitCode != 0 {
return address.Undef, xerrors.Errorf("getting miner owner addr failed (exit code %d)", recp.ExitCode)
}
owner, err := address.NewFromBytes(recp.Return)
if err != nil {
return address.Undef, err
}
if owner.Protocol() == address.ID {
return address.Undef, xerrors.Errorf("need to resolve owner address to a pubkeyaddr")
}
return owner, nil
}
// Should match up with 'Semantical Validation' in validation.md in the spec // Should match up with 'Semantical Validation' in validation.md in the spec
func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error { func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error {
h := b.Header h := b.Header
stateroot, err := syncer.store.TipSetState(h.Parents) stateroot, err := syncer.sm.TipSetState(h.Parents)
if err != nil { if err != nil {
return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err) return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err)
} }
@ -447,21 +399,39 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
return xerrors.Errorf("minerIsValid failed: %w", err) return xerrors.Errorf("minerIsValid failed: %w", err)
} }
waddr, err := getMinerWorker(ctx, syncer.store, stateroot, h.Miner) waddr, err := stmgr.GetMinerWorker(ctx, syncer.sm, stateroot, h.Miner)
if err != nil { if err != nil {
return xerrors.Errorf("getMinerWorker failed: %w", err) return xerrors.Errorf("GetMinerWorker failed: %w", err)
} }
if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil { if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil {
return xerrors.Errorf("validating block tickets failed: %w", err) return xerrors.Errorf("validating block tickets failed: %w", err)
} }
rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs, h.Tickets, build.RandomnessLookback)
if err != nil {
return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err)
}
if err := VerifyElectionProof(ctx, h.ElectionProof, rand, waddr); err != nil {
return xerrors.Errorf("checking eproof failed: %w", err)
}
mpow, tpow, err := stmgr.GetPower(ctx, syncer.sm, baseTs, h.Miner)
if err != nil {
return xerrors.Errorf("failed getting power: %w", err)
}
if !types.PowerCmp(h.ElectionProof, mpow, tpow) {
return xerrors.Errorf("miner created a block but was not a winner")
}
vmi, err := vm.NewVM(stateroot, h.Height, h.Miner, syncer.store) vmi, err := vm.NewVM(stateroot, h.Height, h.Miner, syncer.store)
if err != nil { if err != nil {
return xerrors.Errorf("failed to instantiate VM: %w", err) return xerrors.Errorf("failed to instantiate VM: %w", err)
} }
owner, err := getMinerOwner(ctx, syncer.store, stateroot, b.Header.Miner) owner, err := stmgr.GetMinerOwner(ctx, syncer.sm, stateroot, b.Header.Miner)
if err != nil { if err != nil {
return xerrors.Errorf("getting miner owner for block miner failed: %w", err) return xerrors.Errorf("getting miner owner for block miner failed: %w", err)
} }
@ -508,7 +478,6 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
} }
return nil return nil
} }
func (syncer *Syncer) collectHeaders(from *types.TipSet, to *types.TipSet) ([]*types.TipSet, error) { func (syncer *Syncer) collectHeaders(from *types.TipSet, to *types.TipSet) ([]*types.TipSet, error) {
@ -536,12 +505,16 @@ func (syncer *Syncer) collectHeaders(from *types.TipSet, to *types.TipSet) ([]*t
at = ts.Parents() at = ts.Parents()
} }
loop:
for blockSet[len(blockSet)-1].Height() > untilHeight { for blockSet[len(blockSet)-1].Height() > untilHeight {
// NB: GetBlocks validates that the blocks are in-fact the ones we // NB: GetBlocks validates that the blocks are in-fact the ones we
// requested, and that they are correctly linked to eachother. It does // requested, and that they are correctly linked to eachother. It does
// not validate any state transitions // not validate any state transitions
fmt.Println("Get blocks") window := 10
blks, err := syncer.Bsync.GetBlocks(context.TODO(), at, 10) if gap := int(blockSet[len(blockSet)-1].Height() - untilHeight); gap < window {
window = gap
}
blks, err := syncer.Bsync.GetBlocks(context.TODO(), at, window)
if err != nil { if err != nil {
// Most likely our peers aren't fully synced yet, but forwarded // Most likely our peers aren't fully synced yet, but forwarded
// new block message (ideally we'd find better peers) // new block message (ideally we'd find better peers)
@ -554,7 +527,7 @@ func (syncer *Syncer) collectHeaders(from *types.TipSet, to *types.TipSet) ([]*t
for _, b := range blks { for _, b := range blks {
if b.Height() < untilHeight { if b.Height() < untilHeight {
break break loop
} }
blockSet = append(blockSet, b) blockSet = append(blockSet, b)
} }
@ -562,9 +535,18 @@ func (syncer *Syncer) collectHeaders(from *types.TipSet, to *types.TipSet) ([]*t
at = blks[len(blks)-1].Parents() at = blks[len(blks)-1].Parents()
} }
if !cidArrsEqual(blockSet[len(blockSet)-1].Parents(), to.Cids()) { // We have now ascertained that this is *not* a 'fast forward'
if !types.CidArrsEqual(blockSet[len(blockSet)-1].Parents(), to.Cids()) {
last := blockSet[len(blockSet)-1]
if types.CidArrsEqual(last.Parents(), to.Parents()) {
// common case: receiving a block thats potentially part of the same tipset as our best block
return blockSet, nil
}
// TODO: handle the case where we are on a fork and its not a simple fast forward // TODO: handle the case where we are on a fork and its not a simple fast forward
return nil, xerrors.Errorf("synced header chain does not link to our best block") // need to walk back to either a common ancestor, or until we hit the fork length threshold.
return nil, xerrors.Errorf("(fork detected) synced header chain (%s - %d) does not link to our best block (%s - %d)", from.Cids(), from.Height(), to.Cids(), to.Height())
} }
return blockSet, nil return blockSet, nil
@ -572,11 +554,12 @@ func (syncer *Syncer) collectHeaders(from *types.TipSet, to *types.TipSet) ([]*t
func (syncer *Syncer) syncMessagesAndCheckState(headers []*types.TipSet) error { func (syncer *Syncer) syncMessagesAndCheckState(headers []*types.TipSet) error {
return syncer.iterFullTipsets(headers, func(fts *store.FullTipSet) error { return syncer.iterFullTipsets(headers, func(fts *store.FullTipSet) error {
log.Warn("validating tipset: ", fts.TipSet().Height()) log.Warnf("validating tipset (heigt=%d, size=%d)", fts.TipSet().Height(), len(fts.TipSet().Cids()))
if err := syncer.ValidateTipSet(context.TODO(), fts); err != nil { if err := syncer.ValidateTipSet(context.TODO(), fts); err != nil {
log.Errorf("failed to validate tipset: %s", err) log.Errorf("failed to validate tipset: %s", err)
return xerrors.Errorf("message processing failed: %w", err) return xerrors.Errorf("message processing failed: %w", err)
} }
return nil return nil
}) })
} }
@ -602,10 +585,6 @@ func (syncer *Syncer) iterFullTipsets(headers []*types.TipSet, cb func(*store.Fu
windowSize := 10 windowSize := 10
for i := len(headers) - 1; i >= 0; i -= windowSize { for i := len(headers) - 1; i >= 0; i -= windowSize {
// temp storage so we don't persist data we dont want to
ds := dstore.NewMapDatastore()
bs := bstore.NewBlockstore(ds)
cst := hamt.CSTFromBstore(bs)
batchSize := windowSize batchSize := windowSize
if i < batchSize { if i < batchSize {
@ -619,6 +598,11 @@ func (syncer *Syncer) iterFullTipsets(headers []*types.TipSet, cb func(*store.Fu
} }
for bsi := 0; bsi < len(bstips); bsi++ { for bsi := 0; bsi < len(bstips); bsi++ {
// temp storage so we don't persist data we dont want to
ds := dstore.NewMapDatastore()
bs := bstore.NewBlockstore(ds)
cst := hamt.CSTFromBstore(bs)
this := headers[i-bsi] this := headers[i-bsi]
bstip := bstips[len(bstips)-(bsi+1)] bstip := bstips[len(bstips)-(bsi+1)]
fts, err := zipTipSetAndMessages(cst, this, bstip.BlsMessages, bstip.SecpkMessages, bstip.BlsMsgIncludes, bstip.SecpkMsgIncludes) fts, err := zipTipSetAndMessages(cst, this, bstip.BlsMessages, bstip.SecpkMessages, bstip.BlsMsgIncludes, bstip.SecpkMsgIncludes)
@ -634,38 +618,37 @@ func (syncer *Syncer) iterFullTipsets(headers []*types.TipSet, cb func(*store.Fu
if err := cb(fts); err != nil { if err := cb(fts); err != nil {
return err return err
} }
if err := persistMessages(bs, bstip); err != nil {
return err
}
if err := copyBlockstore(bs, syncer.store.Blockstore()); err != nil {
return xerrors.Errorf("message processing failed: %w", err)
}
} }
if err := persistMessages(bs, bstips); err != nil {
return err
}
if err := copyBlockstore(bs, syncer.store.Blockstore()); err != nil {
return xerrors.Errorf("message processing failed: %w", err)
}
} }
return nil return nil
} }
func persistMessages(bs bstore.Blockstore, bstips []*BSTipSet) error { func persistMessages(bs bstore.Blockstore, bst *BSTipSet) error {
for _, bst := range bstips { for _, m := range bst.BlsMessages {
for _, m := range bst.BlsMessages { //log.Infof("putting BLS message: %s", m.Cid())
//log.Infof("putting BLS message: %s", m.Cid()) if _, err := store.PutMessage(bs, m); err != nil {
if _, err := store.PutMessage(bs, m); err != nil { log.Error("failed to persist messages: ", err)
log.Error("failed to persist messages: ", err) return xerrors.Errorf("BLS message processing failed: %w", err)
return xerrors.Errorf("BLS message processing failed: %w", err)
}
} }
for _, m := range bst.SecpkMessages { }
if m.Signature.Type != types.KTSecp256k1 { for _, m := range bst.SecpkMessages {
return xerrors.Errorf("unknown signature type on message %s: %q", m.Cid(), m.Signature.TypeCode) if m.Signature.Type != types.KTSecp256k1 {
} return xerrors.Errorf("unknown signature type on message %s: %q", m.Cid(), m.Signature.TypeCode)
//log.Infof("putting secp256k1 message: %s", m.Cid()) }
if _, err := store.PutMessage(bs, m); err != nil { //log.Infof("putting secp256k1 message: %s", m.Cid())
log.Error("failed to persist messages: ", err) if _, err := store.PutMessage(bs, m); err != nil {
return xerrors.Errorf("secp256k1 message processing failed: %w", err) log.Error("failed to persist messages: ", err)
} return xerrors.Errorf("secp256k1 message processing failed: %w", err)
} }
} }
@ -692,3 +675,16 @@ func (syncer *Syncer) collectChain(fts *store.FullTipSet) error {
return nil return nil
} }
func VerifyElectionProof(ctx context.Context, eproof []byte, rand []byte, worker address.Address) error {
sig := types.Signature{
Data: eproof,
Type: types.KTBLS,
}
if err := sig.Verify(worker, rand); err != nil {
return xerrors.Errorf("failed to verify election proof signature: %w", err)
}
return nil
}

View File

@ -12,26 +12,27 @@ import (
"github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/chain" "github.com/filecoin-project/go-lotus/chain"
"github.com/filecoin-project/go-lotus/chain/gen" "github.com/filecoin-project/go-lotus/chain/gen"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/node" "github.com/filecoin-project/go-lotus/node"
"github.com/filecoin-project/go-lotus/node/impl"
"github.com/filecoin-project/go-lotus/node/modules" "github.com/filecoin-project/go-lotus/node/modules"
"github.com/filecoin-project/go-lotus/node/repo" "github.com/filecoin-project/go-lotus/node/repo"
) )
const source = 0 const source = 0
func (tu *syncTestUtil) repoWithChain(t testing.TB, h int) (repo.Repo, []byte, []*types.FullBlock) { func (tu *syncTestUtil) repoWithChain(t testing.TB, h int) (repo.Repo, []byte, []*store.FullTipSet) {
blks := make([]*types.FullBlock, h) blks := make([]*store.FullTipSet, h)
for i := 0; i < h; i++ { for i := 0; i < h; i++ {
var err error mts, err := tu.g.NextTipSet()
blks[i], _, err = tu.g.NextBlock()
require.NoError(t, err) require.NoError(t, err)
fmt.Printf("block at H:%d: %s\n", blks[i].Header.Height, blks[i].Cid()) blks[i] = mts.TipSet
ts := mts.TipSet.TipSet()
fmt.Printf("tipset at H:%d: %s\n", ts.Height(), ts.Cids())
require.Equal(t, uint64(i+1), blks[i].Header.Height, "wrong height")
} }
r, err := tu.g.YieldRepo() r, err := tu.g.YieldRepo()
@ -54,7 +55,7 @@ type syncTestUtil struct {
g *gen.ChainGen g *gen.ChainGen
genesis []byte genesis []byte
blocks []*types.FullBlock blocks []*store.FullTipSet
nds []api.FullNode nds []api.FullNode
} }
@ -79,7 +80,7 @@ func prepSyncTest(t testing.TB, h int) *syncTestUtil {
} }
tu.addSourceNode(h) tu.addSourceNode(h)
tu.checkHeight("source", source, h) //tu.checkHeight("source", source, h)
// separate logs // separate logs
fmt.Println("\x1b[31m///////////////////////////////////////////////////\x1b[39b") fmt.Println("\x1b[31m///////////////////////////////////////////////////\x1b[39b")
@ -92,14 +93,16 @@ func (tu *syncTestUtil) Shutdown() {
} }
func (tu *syncTestUtil) mineNewBlock(src int) { func (tu *syncTestUtil) mineNewBlock(src int) {
fblk, msgs, err := tu.g.NextBlock() mts, err := tu.g.NextTipSet()
require.NoError(tu.t, err) require.NoError(tu.t, err)
for _, msg := range msgs { for _, msg := range mts.Messages {
require.NoError(tu.t, tu.nds[src].MpoolPush(context.TODO(), msg)) require.NoError(tu.t, tu.nds[src].MpoolPush(context.TODO(), msg))
} }
require.NoError(tu.t, tu.nds[src].ChainSubmitBlock(context.TODO(), fblkToBlkMsg(fblk))) for _, fblk := range mts.TipSet.Blocks {
require.NoError(tu.t, tu.nds[src].ChainSubmitBlock(context.TODO(), fblkToBlkMsg(fblk)))
}
} }
func fblkToBlkMsg(fb *types.FullBlock) *chain.BlockMsg { func fblkToBlkMsg(fb *types.FullBlock) *chain.BlockMsg {
@ -177,6 +180,17 @@ func (tu *syncTestUtil) checkHeight(name string, n int, h int) {
} }
func (tu *syncTestUtil) compareSourceState(with int) { func (tu *syncTestUtil) compareSourceState(with int) {
sourceHead, err := tu.nds[source].ChainHead(tu.ctx)
require.NoError(tu.t, err)
targetHead, err := tu.nds[with].ChainHead(tu.ctx)
require.NoError(tu.t, err)
if !sourceHead.Equals(targetHead) {
fmt.Println("different chains: ", sourceHead.Height(), targetHead.Height())
tu.t.Fatalf("nodes were not synced correctly: %s != %s", sourceHead.Cids(), targetHead.Cids())
}
sourceAccounts, err := tu.nds[source].WalletList(tu.ctx) sourceAccounts, err := tu.nds[source].WalletList(tu.ctx)
require.NoError(tu.t, err) require.NoError(tu.t, err)
@ -215,6 +229,7 @@ func (tu *syncTestUtil) waitUntilSync(from, to int) {
} }
} }
/*
func (tu *syncTestUtil) submitSourceBlock(to int, h int) { func (tu *syncTestUtil) submitSourceBlock(to int, h int) {
// utility to simulate incoming blocks without miner process // utility to simulate incoming blocks without miner process
// TODO: should call syncer directly, this won't work correctly in all cases // TODO: should call syncer directly, this won't work correctly in all cases
@ -238,29 +253,30 @@ func (tu *syncTestUtil) submitSourceBlocks(to int, h int, n int) {
tu.submitSourceBlock(to, h+i) tu.submitSourceBlock(to, h+i)
} }
} }
*/
func TestSyncSimple(t *testing.T) { func TestSyncSimple(t *testing.T) {
H := 50 H := 50
tu := prepSyncTest(t, H) tu := prepSyncTest(t, H)
client := tu.addClientNode() client := tu.addClientNode()
tu.checkHeight("client", client, 0) //tu.checkHeight("client", client, 0)
require.NoError(t, tu.mn.LinkAll()) require.NoError(t, tu.mn.LinkAll())
tu.connect(1, 0) tu.connect(1, 0)
tu.waitUntilSync(0, client) tu.waitUntilSync(0, client)
tu.checkHeight("client", client, H) //tu.checkHeight("client", client, H)
tu.compareSourceState(client) tu.compareSourceState(client)
} }
func TestSyncMining(t *testing.T) { func TestSyncMining(t *testing.T) {
H := 100 H := 50
tu := prepSyncTest(t, H) tu := prepSyncTest(t, H)
client := tu.addClientNode() client := tu.addClientNode()
tu.checkHeight("client", client, 0) //tu.checkHeight("client", client, 0)
require.NoError(t, tu.mn.LinkAll()) require.NoError(t, tu.mn.LinkAll())
tu.connect(client, 0) tu.connect(client, 0)
@ -269,7 +285,7 @@ func TestSyncMining(t *testing.T) {
fmt.Println("after wait until sync") fmt.Println("after wait until sync")
tu.checkHeight("client", client, H) //tu.checkHeight("client", client, H)
tu.compareSourceState(client) tu.compareSourceState(client)

View File

@ -2,6 +2,8 @@ package types
import ( import (
"bytes" "bytes"
"crypto/sha256"
"math/big"
block "github.com/ipfs/go-block-format" block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
@ -87,6 +89,10 @@ func (blk *BlockHeader) Serialize() ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
func (blk *BlockHeader) LastTicket() *Ticket {
return blk.Tickets[len(blk.Tickets)-1]
}
type MsgMeta struct { type MsgMeta struct {
BlsMessages cid.Cid BlsMessages cid.Cid
SecpkMessages cid.Cid SecpkMessages cid.Cid
@ -114,3 +120,40 @@ func (mm *MsgMeta) ToStorageBlock() (block.Block, error) {
return block.NewBlockWithCid(buf.Bytes(), c) return block.NewBlockWithCid(buf.Bytes(), c)
} }
func CidArrsEqual(a, b []cid.Cid) bool {
if len(a) != len(b) {
return false
}
// order ignoring compare...
s := make(map[cid.Cid]bool)
for _, c := range a {
s[c] = true
}
for _, c := range b {
if !s[c] {
return false
}
}
return true
}
func PowerCmp(eproof ElectionProof, mpow, totpow BigInt) bool {
/*
Need to check that
h(vrfout) / 2^256 < minerPower / totalPower
*/
h := sha256.Sum256(eproof)
// 2^256
rden := BigInt{big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil)}
top := BigMul(rden, mpow)
out := BigDiv(top, totpow)
return BigCmp(BigFromBytes(h[:]), out) < 0
}

View File

@ -96,19 +96,13 @@ func (ts *TipSet) Equals(ots *TipSet) bool {
return true return true
} }
func (ts *TipSet) MinTicket() *Ticket { func (t *Ticket) Less(o *Ticket) bool {
if len(ts.Blocks()) == 0 { return bytes.Compare(t.VDFResult, o.VDFResult) < 0
panic("tipset has no blocks!") }
}
var minTicket *Ticket
for _, b := range ts.Blocks() {
lastTicket := b.Tickets[len(b.Tickets)-1]
if minTicket == nil || bytes.Compare(lastTicket.VDFResult, minTicket.VDFResult) < 0 {
minTicket = lastTicket
}
}
return minTicket func (ts *TipSet) MinTicket() *Ticket {
b := ts.MinTicketBlock()
return b.Tickets[len(b.Tickets)-1]
} }
func (ts *TipSet) MinTimestamp() uint64 { func (ts *TipSet) MinTimestamp() uint64 {
@ -120,3 +114,17 @@ func (ts *TipSet) MinTimestamp() uint64 {
} }
return minTs return minTs
} }
func (ts *TipSet) MinTicketBlock() *BlockHeader {
blks := ts.Blocks()
min := blks[0]
for _, b := range blks[1:] {
if b.LastTicket().Less(min.LastTicket()) {
min = b
}
}
return min
}

View File

@ -454,6 +454,10 @@ func (vm *VM) ApplyMessage(ctx context.Context, msg *types.Message) (*ApplyRet,
}, nil }, nil
} }
func (vm *VM) SetBlockMiner(m address.Address) {
vm.blockMiner = m
}
func (vm *VM) ActorBalance(addr address.Address) (types.BigInt, aerrors.ActorError) { func (vm *VM) ActorBalance(addr address.Address) (types.BigInt, aerrors.ActorError) {
act, err := vm.cstate.GetActor(addr) act, err := vm.cstate.GetActor(addr)
if err != nil { if err != nil {

View File

@ -143,7 +143,7 @@ func configureStorageMiner(ctx context.Context, api api.FullNode, addr address.A
} }
// This really just needs to be an api call at this point... // This really just needs to be an api call at this point...
recp, err := api.ChainCall(ctx, &types.Message{ recp, err := api.StateCall(ctx, &types.Message{
To: addr, To: addr,
From: addr, From: addr,
Method: actors.MAMethods.GetWorkerAddr, Method: actors.MAMethods.GetWorkerAddr,

2
go.mod
View File

@ -15,7 +15,7 @@ require (
github.com/gorilla/websocket v1.4.0 github.com/gorilla/websocket v1.4.0
github.com/ipfs/go-bitswap v0.1.8 github.com/ipfs/go-bitswap v0.1.8
github.com/ipfs/go-block-format v0.0.2 github.com/ipfs/go-block-format v0.0.2
github.com/ipfs/go-blockservice v0.1.2 github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c
github.com/ipfs/go-car v0.0.0-20190823083746-79984a8632b4 github.com/ipfs/go-car v0.0.0-20190823083746-79984a8632b4
github.com/ipfs/go-cid v0.0.3 github.com/ipfs/go-cid v0.0.3
github.com/ipfs/go-datastore v0.1.0 github.com/ipfs/go-datastore v0.1.0

2
go.sum
View File

@ -148,6 +148,8 @@ github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJ
github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
github.com/ipfs/go-blockservice v0.1.2 h1:fqFeeu1EG0lGVrqUo+BVJv7LZV31I4ZsyNthCOMAJRc= github.com/ipfs/go-blockservice v0.1.2 h1:fqFeeu1EG0lGVrqUo+BVJv7LZV31I4ZsyNthCOMAJRc=
github.com/ipfs/go-blockservice v0.1.2/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I= github.com/ipfs/go-blockservice v0.1.2/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I=
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c h1:lN5IQA07VtLiTLAp/Scezp1ljFhXErC6yq4O1cu+yJ0=
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I=
github.com/ipfs/go-car v0.0.0-20190823083746-79984a8632b4 h1:qYLz/x/d1SOiiFGS8dwCBCFJ5Oh64Y8HMBrS+MbaU8c= github.com/ipfs/go-car v0.0.0-20190823083746-79984a8632b4 h1:qYLz/x/d1SOiiFGS8dwCBCFJ5Oh64Y8HMBrS+MbaU8c=
github.com/ipfs/go-car v0.0.0-20190823083746-79984a8632b4/go.mod h1:NSSM0pxlhej9rSFXQmB/lDru7TYNoDjKgmvqzlsd06Y= github.com/ipfs/go-car v0.0.0-20190823083746-79984a8632b4/go.mod h1:NSSM0pxlhej9rSFXQmB/lDru7TYNoDjKgmvqzlsd06Y=
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=

View File

@ -2,8 +2,6 @@ package miner
import ( import (
"context" "context"
"crypto/sha256"
"math/big"
"sync" "sync"
"time" "time"
@ -200,7 +198,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*chain.BlockMsg,
return nil, errors.Wrap(err, "scratching ticket failed") return nil, errors.Wrap(err, "scratching ticket failed")
} }
win, proof, err := m.isWinnerNextRound(ctx, base) win, proof, err := gen.IsRoundWinner(ctx, base.ts, base.tickets, m.addresses[0], &m.api)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to check if we win next round") return nil, errors.Wrap(err, "failed to check if we win next round")
} }
@ -234,7 +232,7 @@ func (m *Miner) computeVRF(ctx context.Context, input []byte) ([]byte, error) {
} }
func (m *Miner) getMinerWorker(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { func (m *Miner) getMinerWorker(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) {
ret, err := m.api.ChainCall(ctx, &types.Message{ ret, err := m.api.StateCall(ctx, &types.Message{
From: addr, From: addr,
To: addr, To: addr,
Method: actors.MAMethods.GetWorkerAddr, Method: actors.MAMethods.GetWorkerAddr,
@ -255,43 +253,6 @@ func (m *Miner) getMinerWorker(ctx context.Context, addr address.Address, ts *ty
return w, nil return w, nil
} }
func (m *Miner) isWinnerNextRound(ctx context.Context, base *MiningBase) (bool, types.ElectionProof, error) {
r, err := m.api.ChainGetRandomness(context.TODO(), base.ts)
if err != nil {
return false, nil, err
}
vrfout, err := m.computeVRF(ctx, r)
if err != nil {
return false, nil, xerrors.Errorf("failed to compute VRF: %w", err)
}
pow, err := m.api.StateMinerPower(ctx, m.addresses[0], base.ts)
if err != nil {
return false, nil, xerrors.Errorf("failed to check power: %w", err)
}
return powerCmp(vrfout, pow.MinerPower, pow.TotalPower), vrfout, nil
}
func powerCmp(vrfout []byte, mpow, totpow types.BigInt) bool {
/*
Need to check that
h(vrfout) / 2^256 < minerPower / totalPower
*/
h := sha256.Sum256(vrfout)
// 2^256
rden := types.BigInt{big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil)}
top := types.BigMul(rden, mpow)
out := types.BigDiv(top, totpow)
return types.BigCmp(types.BigFromBytes(h[:]), out) < 0
}
func (m *Miner) runVDF(ctx context.Context, input []byte) ([]byte, []byte, error) { func (m *Miner) runVDF(ctx context.Context, input []byte) ([]byte, []byte, error) {
select { select {
case <-ctx.Done(): case <-ctx.Done():

View File

@ -20,6 +20,7 @@ import (
"github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/chain" "github.com/filecoin-project/go-lotus/chain"
"github.com/filecoin-project/go-lotus/chain/deals" "github.com/filecoin-project/go-lotus/chain/deals"
"github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/wallet" "github.com/filecoin-project/go-lotus/chain/wallet"
@ -201,6 +202,7 @@ func Online() Option {
Override(HandleIncomingMessagesKey, modules.HandleIncomingMessages), Override(HandleIncomingMessagesKey, modules.HandleIncomingMessages),
Override(new(*store.ChainStore), modules.ChainStore), Override(new(*store.ChainStore), modules.ChainStore),
Override(new(*stmgr.StateManager), stmgr.NewStateManager),
Override(new(*wallet.Wallet), wallet.NewWallet), Override(new(*wallet.Wallet), wallet.NewWallet),
Override(new(dtypes.ChainGCLocker), blockstore.NewGCLocker), Override(new(dtypes.ChainGCLocker), blockstore.NewGCLocker),

View File

@ -5,17 +5,11 @@ import (
"github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/chain" "github.com/filecoin-project/go-lotus/chain"
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/gen"
"github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
"github.com/filecoin-project/go-lotus/lib/bufbstore"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/ipfs/go-hamt-ipld"
pubsub "github.com/libp2p/go-libp2p-pubsub" pubsub "github.com/libp2p/go-libp2p-pubsub"
"go.uber.org/fx" "go.uber.org/fx"
) )
@ -51,9 +45,8 @@ func (a *ChainAPI) ChainHead(context.Context) (*types.TipSet, error) {
return a.Chain.GetHeaviestTipSet(), nil return a.Chain.GetHeaviestTipSet(), nil
} }
func (a *ChainAPI) ChainGetRandomness(ctx context.Context, pts *types.TipSet) ([]byte, error) { func (a *ChainAPI) ChainGetRandomness(ctx context.Context, pts *types.TipSet, tickets []*types.Ticket, lb int) ([]byte, error) {
// TODO: this needs to look back in the chain for the right random beacon value return a.Chain.GetRandomness(ctx, pts, tickets, lb)
return []byte("foo bar random"), nil
} }
func (a *ChainAPI) ChainWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgWait, error) { func (a *ChainAPI) ChainWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgWait, error) {
@ -113,72 +106,3 @@ func (a *ChainAPI) ChainGetBlockReceipts(ctx context.Context, bcid cid.Cid) ([]*
return out, nil return out, nil
} }
func (a *ChainAPI) ChainCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
return vm.Call(ctx, a.Chain, msg, ts)
}
func (a *ChainAPI) 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 *ChainAPI) 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 *ChainAPI) ChainReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*api.ActorState, error) {
state, err := a.stateForTs(ts)
if err != nil {
return nil, err
}
blk, err := state.Store.Blocks.GetBlock(ctx, act.Head)
if err != nil {
return nil, err
}
oif, err := vm.DumpActorState(act.Code, blk.RawData())
if err != nil {
return nil, err
}
return &api.ActorState{
Balance: act.Balance,
State: oif,
}, nil
}
// This is on ChainAPI because miner.Miner requires this, and MinerAPI requires miner.Miner
func (a *ChainAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, ts uint64) (*chain.BlockMsg, error) {
fblk, err := gen.MinerCreateBlock(ctx, a.Chain, a.Wallet, addr, parents, tickets, proof, msgs, ts)
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())
}
return &out, nil
}

View File

@ -35,6 +35,7 @@ type ClientAPI struct {
fx.In fx.In
ChainAPI ChainAPI
StateAPI
WalletAPI WalletAPI
PaychAPI PaychAPI
@ -62,7 +63,7 @@ func (a *ClientAPI) ClientStartDeal(ctx context.Context, data cid.Cid, miner add
Method: actors.MAMethods.GetPeerID, Method: actors.MAMethods.GetPeerID,
} }
r, err := a.ChainCall(ctx, msg, nil) r, err := a.StateCall(ctx, msg, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3,16 +3,22 @@ package full
import ( import (
"context" "context"
"fmt" "fmt"
"strconv"
"github.com/filecoin-project/go-lotus/chain"
"github.com/filecoin-project/go-lotus/chain/gen"
"github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm" "github.com/filecoin-project/go-lotus/chain/vm"
"github.com/filecoin-project/go-lotus/chain/wallet"
"github.com/filecoin-project/go-lotus/lib/bufbstore"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"strconv"
"github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/api"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/state" "github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/ipfs/go-hamt-ipld" "github.com/ipfs/go-hamt-ipld"
cbor "github.com/ipfs/go-ipld-cbor" cbor "github.com/ipfs/go-ipld-cbor"
@ -22,18 +28,23 @@ import (
type StateAPI struct { type StateAPI struct {
fx.In fx.In
Chain *store.ChainStore // TODO: the wallet here is only needed because we have the MinerCreateBlock
// API attached to the state API. It probably should live somewhere better
Wallet *wallet.Wallet
StateManager *stmgr.StateManager
Chain *store.ChainStore
} }
func (a *StateAPI) StateMinerSectors(ctx context.Context, addr address.Address) ([]*api.SectorInfo, error) { func (a *StateAPI) StateMinerSectors(ctx context.Context, addr address.Address) ([]*api.SectorInfo, error) {
ts := a.Chain.GetHeaviestTipSet() ts := a.StateManager.ChainStore().GetHeaviestTipSet()
stc, err := a.Chain.TipSetState(ts.Cids()) stc, err := a.StateManager.TipSetState(ts.Cids())
if err != nil { if err != nil {
return nil, err return nil, err
} }
cst := hamt.CSTFromBstore(a.Chain.Blockstore()) cst := hamt.CSTFromBstore(a.StateManager.ChainStore().Blockstore())
st, err := state.LoadStateTree(cst, stc) st, err := state.LoadStateTree(cst, stc)
if err != nil { if err != nil {
@ -86,7 +97,7 @@ func (a *StateAPI) StateMinerSectors(ctx context.Context, addr address.Address)
func (a *StateAPI) StateMinerProvingSet(ctx context.Context, addr address.Address) ([]*api.SectorInfo, error) { func (a *StateAPI) StateMinerProvingSet(ctx context.Context, addr address.Address) ([]*api.SectorInfo, error) {
ts := a.Chain.GetHeaviestTipSet() ts := a.Chain.GetHeaviestTipSet()
stc, err := a.Chain.TipSetState(ts.Cids()) stc, err := a.StateManager.TipSetState(ts.Cids())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -142,47 +153,104 @@ func (a *StateAPI) StateMinerProvingSet(ctx context.Context, addr address.Addres
} }
func (a *StateAPI) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) { func (a *StateAPI) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) {
var err error mpow, tpow, err := stmgr.GetPower(ctx, a.StateManager, ts, maddr)
enc, err := actors.SerializeParams(&actors.PowerLookupParams{maddr})
if err != nil { if err != nil {
return api.MinerPower{}, err return api.MinerPower{}, err
} }
var mpow types.BigInt
if maddr != address.Undef {
ret, err := vm.Call(ctx, a.Chain, &types.Message{
From: maddr,
To: actors.StorageMarketAddress,
Method: actors.SMAMethods.PowerLookup,
Params: enc,
}, ts)
if err != nil {
return api.MinerPower{}, xerrors.Errorf("failed to get miner power from chain: %w", err)
}
if ret.ExitCode != 0 {
return api.MinerPower{}, xerrors.Errorf("failed to get miner power from chain (exit code %d)", ret.ExitCode)
}
mpow = types.BigFromBytes(ret.Return)
}
ret, err := vm.Call(ctx, a.Chain, &types.Message{
From: actors.StorageMarketAddress,
To: actors.StorageMarketAddress,
Method: actors.SMAMethods.GetTotalStorage,
}, ts)
if err != nil {
return api.MinerPower{}, xerrors.Errorf("failed to get total power from chain: %w", err)
}
if ret.ExitCode != 0 {
return api.MinerPower{}, xerrors.Errorf("failed to get total power from chain (exit code %d)", ret.ExitCode)
}
tpow := types.BigFromBytes(ret.Return)
return api.MinerPower{ return api.MinerPower{
MinerPower: mpow, MinerPower: mpow,
TotalPower: tpow, TotalPower: tpow,
}, nil }, nil
} }
func (a *StateAPI) StateMinerWorker(ctx context.Context, m address.Address, ts *types.TipSet) (address.Address, error) {
ret, err := a.StateManager.Call(ctx, &types.Message{
From: m,
To: m,
Method: actors.MAMethods.GetWorkerAddr,
}, ts)
if err != nil {
return address.Undef, xerrors.Errorf("failed to get miner worker addr: %w", err)
}
if ret.ExitCode != 0 {
return address.Undef, xerrors.Errorf("failed to get miner worker addr (exit code %d)", ret.ExitCode)
}
w, err := address.NewFromBytes(ret.Return)
if err != nil {
return address.Undef, xerrors.Errorf("GetWorkerAddr returned malformed address: %w", err)
}
return w, nil
}
func (a *StateAPI) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
return a.StateManager.Call(ctx, msg, ts)
}
func (a *StateAPI) stateForTs(ts *types.TipSet) (*state.StateTree, error) {
if ts == nil {
ts = a.Chain.GetHeaviestTipSet()
}
st, err := a.StateManager.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 *StateAPI) StateGetActor(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 *StateAPI) StateReadState(ctx context.Context, act *types.Actor, ts *types.TipSet) (*api.ActorState, error) {
state, err := a.stateForTs(ts)
if err != nil {
return nil, err
}
blk, err := state.Store.Blocks.GetBlock(ctx, act.Head)
if err != nil {
return nil, err
}
oif, err := vm.DumpActorState(act.Code, blk.RawData())
if err != nil {
return nil, err
}
return &api.ActorState{
Balance: act.Balance,
State: oif,
}, nil
}
// This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner
func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, ts uint64) (*chain.BlockMsg, error) {
fblk, err := gen.MinerCreateBlock(ctx, a.StateManager, a.Wallet, addr, parents, tickets, proof, msgs, ts)
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())
}
return &out, nil
}

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/wallet" "github.com/filecoin-project/go-lotus/chain/wallet"
@ -15,8 +15,8 @@ import (
type WalletAPI struct { type WalletAPI struct {
fx.In fx.In
Chain *store.ChainStore StateManager *stmgr.StateManager
Wallet *wallet.Wallet Wallet *wallet.Wallet
} }
func (a *WalletAPI) WalletNew(ctx context.Context, typ string) (address.Address, error) { func (a *WalletAPI) WalletNew(ctx context.Context, typ string) (address.Address, error) {
@ -32,7 +32,7 @@ func (a *WalletAPI) WalletList(ctx context.Context) ([]address.Address, error) {
} }
func (a *WalletAPI) WalletBalance(ctx context.Context, addr address.Address) (types.BigInt, error) { func (a *WalletAPI) WalletBalance(ctx context.Context, addr address.Address) (types.BigInt, error) {
return a.Chain.GetBalance(addr) return a.StateManager.GetBalance(addr)
} }
func (a *WalletAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) { func (a *WalletAPI) WalletSign(ctx context.Context, k address.Address, msg []byte) (*types.Signature, error) {

View File

@ -1,7 +1,7 @@
package modules package modules
import ( import (
"github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/node/modules/dtypes" "github.com/filecoin-project/go-lotus/node/modules/dtypes"
"github.com/filecoin-project/go-lotus/paych" "github.com/filecoin-project/go-lotus/paych"
) )
@ -10,6 +10,6 @@ func PaychStore(ds dtypes.MetadataDS) *paych.Store {
return paych.NewStore(ds) return paych.NewStore(ds)
} }
func PaymentChannelManager(chain *store.ChainStore, store *paych.Store) (*paych.Manager, error) { func PaymentChannelManager(sm *stmgr.StateManager, store *paych.Store) (*paych.Manager, error) {
return paych.NewManager(chain, store), nil return paych.NewManager(sm, store), nil
} }

View File

@ -12,6 +12,7 @@ import (
offline "github.com/ipfs/go-ipfs-exchange-offline" offline "github.com/ipfs/go-ipfs-exchange-offline"
logging "github.com/ipfs/go-log" logging "github.com/ipfs/go-log"
"github.com/ipfs/go-merkledag" "github.com/ipfs/go-merkledag"
peer "github.com/libp2p/go-libp2p-peer"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/gen" "github.com/filecoin-project/go-lotus/chain/gen"
@ -34,8 +35,9 @@ func MakeGenesisMem(out io.Writer) func(bs dtypes.ChainBlockstore, w *wallet.Wal
} }
gmc := &gen.GenMinerCfg{ gmc := &gen.GenMinerCfg{
Owner: w, Owners: []address.Address{w},
Worker: w, Workers: []address.Address{w},
PeerIDs: []peer.ID{"peerID 1"},
} }
alloc := map[address.Address]types.BigInt{ alloc := map[address.Address]types.BigInt{
w: types.NewInt(100000), w: types.NewInt(100000),
@ -68,8 +70,9 @@ func MakeGenesis(outFile string) func(bs dtypes.ChainBlockstore, w *wallet.Walle
} }
gmc := &gen.GenMinerCfg{ gmc := &gen.GenMinerCfg{
Owner: minerAddr, Owners: []address.Address{minerAddr},
Worker: minerAddr, Workers: []address.Address{minerAddr},
PeerIDs: []peer.ID{"peer ID 1"},
} }
addrs := map[address.Address]types.BigInt{ addrs := map[address.Address]types.BigInt{
@ -81,7 +84,7 @@ func MakeGenesis(outFile string) func(bs dtypes.ChainBlockstore, w *wallet.Walle
return nil, err return nil, err
} }
fmt.Println("GENESIS MINER ADDRESS: ", gmc.MinerAddr.String()) fmt.Println("GENESIS MINER ADDRESS: ", gmc.MinerAddrs[0].String())
f, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY, 0644) f, err := os.OpenFile(outFile, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {

View File

@ -4,28 +4,25 @@ import (
"context" "context"
"fmt" "fmt"
hamt "github.com/ipfs/go-hamt-ipld"
logging "github.com/ipfs/go-log" logging "github.com/ipfs/go-log"
"github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/actors"
"github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/state" "github.com/filecoin-project/go-lotus/chain/stmgr"
"github.com/filecoin-project/go-lotus/chain/store"
"github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/types"
"github.com/filecoin-project/go-lotus/chain/vm"
) )
var log = logging.Logger("paych") var log = logging.Logger("paych")
type Manager struct { type Manager struct {
chain *store.ChainStore
store *Store store *Store
sm *stmgr.StateManager
} }
func NewManager(chain *store.ChainStore, pchstore *Store) *Manager { func NewManager(sm *stmgr.StateManager, pchstore *Store) *Manager {
return &Manager{ return &Manager{
chain: chain,
store: pchstore, store: pchstore,
sm: sm,
} }
} }
@ -148,7 +145,7 @@ func (pm *Manager) CheckVoucherSpendable(ctx context.Context, ch address.Address
return false, err return false, err
} }
ret, err := vm.Call(ctx, pm.chain, &types.Message{ ret, err := pm.sm.Call(ctx, &types.Message{
From: owner, From: owner,
To: ch, To: ch,
Method: actors.PCAMethods.UpdateChannelState, Method: actors.PCAMethods.UpdateChannelState,
@ -166,24 +163,9 @@ func (pm *Manager) CheckVoucherSpendable(ctx context.Context, ch address.Address
} }
func (pm *Manager) loadPaychState(ctx context.Context, ch address.Address) (*types.Actor, *actors.PaymentChannelActorState, error) { func (pm *Manager) loadPaychState(ctx context.Context, ch address.Address) (*types.Actor, *actors.PaymentChannelActorState, error) {
st, err := pm.chain.TipSetState(pm.chain.GetHeaviestTipSet().Cids())
if err != nil {
return nil, nil, err
}
cst := hamt.CSTFromBstore(pm.chain.Blockstore())
tree, err := state.LoadStateTree(cst, st)
if err != nil {
return nil, nil, err
}
act, err := tree.GetActor(ch)
if err != nil {
return nil, nil, err
}
var pcast actors.PaymentChannelActorState var pcast actors.PaymentChannelActorState
if err := cst.Get(ctx, act.Head, &pcast); err != nil { act, err := pm.sm.LoadActorState(ctx, ch, &pcast)
if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -191,7 +173,7 @@ func (pm *Manager) loadPaychState(ctx context.Context, ch address.Address) (*typ
} }
func (pm *Manager) getPaychOwner(ctx context.Context, ch address.Address) (address.Address, error) { func (pm *Manager) getPaychOwner(ctx context.Context, ch address.Address) (address.Address, error) {
ret, err := vm.Call(ctx, pm.chain, &types.Message{ ret, err := pm.sm.Call(ctx, &types.Message{
From: ch, From: ch,
To: ch, To: ch,
Method: actors.PCAMethods.GetOwner, Method: actors.PCAMethods.GetOwner,

View File

@ -41,7 +41,7 @@ type storageMinerApi interface {
//ReadState(ctx context.Context, addr address.Address) (????, error) //ReadState(ctx context.Context, addr address.Address) (????, error)
// Call a read only method on actors (no interaction with the chain required) // Call a read only method on actors (no interaction with the chain required)
ChainCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) StateCall(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error)
MpoolPush(context.Context, *types.SignedMessage) error MpoolPush(context.Context, *types.SignedMessage) error
MpoolGetNonce(context.Context, address.Address) (uint64, error) MpoolGetNonce(context.Context, address.Address) (uint64, error)
@ -201,7 +201,7 @@ func (m *Miner) getWorkerAddr(ctx context.Context) (address.Address, error) {
Params: actors.EmptyStructCBOR, Params: actors.EmptyStructCBOR,
} }
recpt, err := m.api.ChainCall(ctx, msg, nil) recpt, err := m.api.StateCall(ctx, msg, nil)
if err != nil { if err != nil {
return address.Undef, errors.Wrapf(err, "calling getWorker(%s)", m.maddr) return address.Undef, errors.Wrapf(err, "calling getWorker(%s)", m.maddr)
} }