miner: Initial event system integration

This commit is contained in:
Łukasz Magiera 2019-09-18 15:32:21 +02:00
parent 76ce3d9bb2
commit cc82cc9675
5 changed files with 69 additions and 67 deletions

View File

@ -31,6 +31,7 @@ type heightHandler struct {
type eventApi interface {
ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
}
type Events struct {
@ -49,7 +50,7 @@ type Events struct {
func NewEvents(api eventApi) *Events {
gcConfidence := 2 * build.ForkLengthThreshold
tsc := newTSCache(gcConfidence)
tsc := newTSCache(gcConfidence, api.ChainGetTipSetByHeight)
e := &Events{
api: api,

View File

@ -39,6 +39,10 @@ type fakeCS struct {
sub func(rev, app []*types.TipSet)
}
func (fcs *fakeCS) ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error) {
panic("Not Implemented")
}
func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
ts, err := types.NewTipSet([]*types.BlockHeader{
{
@ -152,7 +156,7 @@ func TestAt(t *testing.T) {
fcs := &fakeCS{
t: t,
h: 1,
tsc: newTSCache(2 * build.ForkLengthThreshold),
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
@ -211,7 +215,7 @@ func TestAtStart(t *testing.T) {
fcs := &fakeCS{
t: t,
h: 1,
tsc: newTSCache(2 * build.ForkLengthThreshold),
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
@ -245,7 +249,7 @@ func TestAtStartConfidence(t *testing.T) {
fcs := &fakeCS{
t: t,
h: 1,
tsc: newTSCache(2 * build.ForkLengthThreshold),
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
@ -278,7 +282,7 @@ func TestCalled(t *testing.T) {
msgs: map[cid.Cid]fakeMsg{},
blkMsgs: map[cid.Cid]cid.Cid{},
tsc: newTSCache(2 * build.ForkLengthThreshold),
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
@ -476,7 +480,7 @@ func TestCalledTimeout(t *testing.T) {
msgs: map[cid.Cid]fakeMsg{},
blkMsgs: map[cid.Cid]cid.Cid{},
tsc: newTSCache(2 * build.ForkLengthThreshold),
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
@ -516,7 +520,7 @@ func TestCalledTimeout(t *testing.T) {
msgs: map[cid.Cid]fakeMsg{},
blkMsgs: map[cid.Cid]cid.Cid{},
tsc: newTSCache(2 * build.ForkLengthThreshold),
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))
@ -550,7 +554,7 @@ func TestCalledOrder(t *testing.T) {
msgs: map[cid.Cid]fakeMsg{},
blkMsgs: map[cid.Cid]cid.Cid{},
tsc: newTSCache(2 * build.ForkLengthThreshold),
tsc: newTSCache(2*build.ForkLengthThreshold, nil),
}
require.NoError(t, fcs.tsc.add(makeTs(t, 1, dummyCid)))

View File

@ -1,24 +1,31 @@
package events
import (
"context"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-lotus/chain/types"
)
type tsByHFunc func(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
// tipSetCache implements a simple ring-buffer cache to keep track of recent
// tipsets
type tipSetCache struct {
cache []*types.TipSet
start int
len int
storage tsByHFunc
}
func newTSCache(cap int) *tipSetCache {
func newTSCache(cap int, storage tsByHFunc) *tipSetCache {
return &tipSetCache{
cache: make([]*types.TipSet, cap),
start: 0,
len: 0,
storage: storage,
}
}
@ -66,9 +73,7 @@ func (tsc *tipSetCache) get(height uint64) (*types.TipSet, error) {
tailH := tsc.cache[(tsc.start-tsc.len+1)%len(tsc.cache)].Height()
if height < tailH {
// TODO: we can try to walk parents, but that shouldn't happen in
// practice, so it's probably not worth implementing
return nil, xerrors.Errorf("tipSetCache.get: requested tipset not in cache (req: %d, cache tail: %d)", height, tailH)
return tsc.storage(context.TODO(), height, tsc.cache[tailH])
}
return tsc.cache[int(height-tailH+1)%len(tsc.cache)], nil

View File

@ -109,5 +109,5 @@ func (a *ChainAPI) ChainGetBlockReceipts(ctx context.Context, bcid cid.Cid) ([]*
}
func (a *ChainAPI) ChainGetTipSetByHeight(ctx context.Context, h uint64, ts *types.TipSet) (*types.TipSet, error) {
panic("NYI")
return a.Chain.GetTipsetByHeight(ctx, h, ts)
}

View File

@ -2,11 +2,10 @@ package storage
import (
"context"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
logging "github.com/ipfs/go-log"
host "github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/host"
"github.com/pkg/errors"
"golang.org/x/xerrors"
@ -24,6 +23,8 @@ import (
var log = logging.Logger("storageminer")
const PoStConfidence = 0
type Miner struct {
api storageMinerApi
events *events.Events
@ -53,6 +54,7 @@ type storageMinerApi interface {
MpoolPush(context.Context, *types.SignedMessage) error
MpoolGetNonce(context.Context, address.Address) (uint64, error)
ChainHead(context.Context) (*types.TipSet, error)
ChainWaitMsg(context.Context, cid.Cid) (*api.MsgWait, error)
ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
ChainGetRandomness(context.Context, *types.TipSet, []*types.Ticket, int) ([]byte, error)
@ -82,8 +84,13 @@ func (m *Miner) Run(ctx context.Context) error {
return errors.Wrap(err, "miner preflight checks failed")
}
ts, err := m.api.ChainHead(ctx)
if err != nil {
return err
}
go m.handlePostingSealedSectors(ctx)
go m.runPoSt(ctx)
go m.schedulePoSt(ctx, ts)
return nil
}
@ -179,70 +186,54 @@ func (m *Miner) commitSector(ctx context.Context, sinfo sectorbuilder.SectorSeal
return nil
}
func (m *Miner) runPoSt(ctx context.Context) {
// TODO: most of this method can probably be replaced by the events module once it works on top of the api
notifs, err := m.api.ChainNotify(ctx)
func (m *Miner) schedulePoSt(ctx context.Context, baseTs *types.TipSet) {
ppe, err := m.api.StateMinerProvingPeriodEnd(ctx, m.maddr, baseTs)
if err != nil {
// TODO: this is probably 'crash the node' level serious
log.Errorf("POST ROUTINE FAILED: failed to get chain notifications stream: %s", err)
log.Errorf("failed to get proving period end for miner: %s", err)
return
}
curhead := <-notifs
if curhead[0].Type != store.HCCurrent {
// TODO: this is probably 'crash the node' level serious
log.Warning("expected to get current best tipset from chain notifications stream")
if ppe == 0 {
log.Errorf("Proving period end == 0")
// TODO: we probably want to call schedulePoSt after the first commitSector call
return
}
postCtx, cancel := context.WithCancel(ctx)
postWaitCh, onBlock, err := m.maybeDoPost(postCtx, curhead[0].Val)
log.Infof("Scheduling post at height %d", ppe)
// TODO: Should we set confidence to randomness lookback?
err = m.events.ChainAt(m.startPost, func(ts *types.TipSet) error { // Revert
// TODO: Cancel post
return nil
}, PoStConfidence, ppe)
if err != nil {
log.Errorf("initial 'maybeDoPost' call failed: %s", err)
// TODO: This is BAD, figure something out
log.Errorf("scheduling PoSt failed: %s", err)
return
}
}
for {
select {
case <-ctx.Done():
case notif, ok := <-notifs:
for _, ch := range notif {
if !ok {
log.Warning("chain notifications stream terminated")
// TODO: attempt to restart it if the context isnt cancelled
return
}
func (m *Miner) startPost(ts *types.TipSet, curH uint64) error {
postWaitCh, _, err := m.maybeDoPost(context.TODO(), ts)
if err != nil {
return err
}
switch ch.Type {
case store.HCApply:
postWaitCh, onBlock, err = m.maybeDoPost(postCtx, ch.Val)
if err != nil {
log.Errorf("maybeDoPost failed: %s", err)
return
}
case store.HCRevert:
if onBlock != nil {
if ch.Val.Contains(onBlock.Cid()) {
// Our post may now be invalid!
cancel() // probably the right thing to do?
}
}
case store.HCCurrent:
log.Warn("got 'current' chain notification in middle of stream")
}
}
case perr := <-postWaitCh:
if perr != nil {
log.Errorf("got error back from postWaitCh: %s", err)
// TODO: what do we even do here?
return
}
postWaitCh = nil
onBlock = nil
// yay?
log.Infof("post successfully submitted")
if postWaitCh == nil {
return errors.New("PoSt didn't start")
}
go func() {
err := <-postWaitCh
if err != nil {
log.Errorf("got error back from postWaitCh: %s", err)
return
}
}
log.Infof("post successfully submitted")
m.schedulePoSt(context.TODO(), ts)
}()
return nil
}
func (m *Miner) maybeDoPost(ctx context.Context, ts *types.TipSet) (<-chan error, *types.BlockHeader, error) {
@ -252,6 +243,7 @@ func (m *Miner) maybeDoPost(ctx context.Context, ts *types.TipSet) (<-chan error
}
if ppe < ts.Height() {
log.Warnf("skipping post, supplied tipset too high: ppe=%d, ts.H=%d", ppe, ts.Height())
return nil, nil, nil
}