add godocs to miner objects.

This commit is contained in:
Raúl Kripalani 2020-06-30 11:26:27 +01:00 committed by Łukasz Magiera
parent 8f78066d4f
commit 87efc9d536

View File

@ -37,7 +37,13 @@ const (
evtTypeBlockMined = iota evtTypeBlockMined = iota
) )
// returns a callback reporting whether we mined a blocks in this round // waitFunc is expected to pace block mining at the configured network rate.
//
// baseTime is the timestamp of the mining base, i.e. the timestamp
// of the tipset we're planning to construct upon.
//
// Upon each mining loop iteration, the returned callback is called reporting
// whether we mined a block in this round or not.
type waitFunc func(ctx context.Context, baseTime uint64) (func(bool, abi.ChainEpoch, error), abi.ChainEpoch, error) type waitFunc func(ctx context.Context, baseTime uint64) (func(bool, abi.ChainEpoch, error), abi.ChainEpoch, error)
func randTimeOffset(width time.Duration) time.Duration { func randTimeOffset(width time.Duration) time.Duration {
@ -48,6 +54,8 @@ func randTimeOffset(width time.Duration) time.Duration {
return val - (width / 2) return val - (width / 2)
} }
// NewMiner instantiates a miner with a concrete WinningPoStProver and a miner
// address (which can be different from the worker's address).
func NewMiner(api api.FullNode, epp gen.WinningPoStProver, addr address.Address, sf *slashfilter.SlashFilter, j journal.Journal) *Miner { func NewMiner(api api.FullNode, epp gen.WinningPoStProver, addr address.Address, sf *slashfilter.SlashFilter, j journal.Journal) *Miner {
arc, err := lru.NewARC(10000) arc, err := lru.NewARC(10000)
if err != nil { if err != nil {
@ -59,7 +67,16 @@ func NewMiner(api api.FullNode, epp gen.WinningPoStProver, addr address.Address,
epp: epp, epp: epp,
address: addr, address: addr,
waitFunc: func(ctx context.Context, baseTime uint64) (func(bool, abi.ChainEpoch, error), abi.ChainEpoch, error) { waitFunc: func(ctx context.Context, baseTime uint64) (func(bool, abi.ChainEpoch, error), abi.ChainEpoch, error) {
// Wait around for half the block time in case other parents come in // wait around for half the block time in case other parents come in
//
// if we're mining a block in the past via catch-up/rush mining,
// such as when recovering from a network halt, this sleep will be
// for a negative duration, and therefore **will return
// immediately**.
//
// the result is that we WILL NOT wait, therefore fast-forwarding
// and thus healing the chain by backfilling it with null rounds
// rapidly.
deadline := baseTime + build.PropagationDelaySecs deadline := baseTime + build.PropagationDelaySecs
baseT := time.Unix(int64(deadline), 0) baseT := time.Unix(int64(deadline), 0)
@ -79,6 +96,9 @@ func NewMiner(api api.FullNode, epp gen.WinningPoStProver, addr address.Address,
} }
} }
// Miner encapsulates the mining processes of the system.
//
// Refer to the godocs on mineOne and mine methods for more detail.
type Miner struct { type Miner struct {
api api.FullNode api api.FullNode
@ -91,15 +111,20 @@ type Miner struct {
waitFunc waitFunc waitFunc waitFunc
// lastWork holds the last MiningBase we built upon.
lastWork *MiningBase lastWork *MiningBase
sf *slashfilter.SlashFilter sf *slashfilter.SlashFilter
// minedBlockHeights is a safeguard that caches the last heights we mined.
// It is consulted before publishing a newly mined block, for a sanity check
// intended to avoid slashings in case of a bug.
minedBlockHeights *lru.ARCCache minedBlockHeights *lru.ARCCache
evtTypes [1]journal.EventType evtTypes [1]journal.EventType
journal journal.Journal journal journal.Journal
} }
// Address returns the address of the miner.
func (m *Miner) Address() address.Address { func (m *Miner) Address() address.Address {
m.lk.Lock() m.lk.Lock()
defer m.lk.Unlock() defer m.lk.Unlock()
@ -107,7 +132,9 @@ func (m *Miner) Address() address.Address {
return m.address return m.address
} }
func (m *Miner) Start(ctx context.Context) error { // Start starts the mining operation. It spawns a goroutine and returns
// immediately. Start is not idempotent.
func (m *Miner) Start(_ context.Context) error {
m.lk.Lock() m.lk.Lock()
defer m.lk.Unlock() defer m.lk.Unlock()
if m.stop != nil { if m.stop != nil {
@ -118,6 +145,8 @@ func (m *Miner) Start(ctx context.Context) error {
return nil return nil
} }
// Stop stops the mining operation. It is not idempotent, and multiple adjacent
// calls to Stop will fail.
func (m *Miner) Stop(ctx context.Context) error { func (m *Miner) Stop(ctx context.Context) error {
m.lk.Lock() m.lk.Lock()
@ -145,6 +174,28 @@ func (m *Miner) niceSleep(d time.Duration) bool {
} }
} }
// mine runs the mining loop. It performs the following:
//
// 1. Queries our current best currently-known mining candidate (tipset to
// build upon).
// 2. Waits until the propagation delay of the network has elapsed (currently
// 6 seconds). The waiting is done relative to the timestamp of the best
// candidate, which means that if it's way in the past, we won't wait at
// all (e.g. in catch-up or rush mining).
// 3. After the wait, we query our best mining candidate. This will be the one
// we'll work with.
// 4. Sanity check that we _actually_ have a new mining base to mine on. If
// not, wait one epoch + propagation delay, and go back to the top.
// 5. We attempt to mine a block, by calling mineOne (refer to godocs). This
// method will either return a block if we were eligible to mine, or nil
// if we weren't.
// 6a. If we mined a block, we update our state and push it out to the network
// via gossipsub.
// 6b. If we didn't mine a block, we consider this to be a nil round on top of
// the mining base we selected. If other miner or miners on the network
// were eligible to mine, we will receive their blocks via gossipsub and
// we will select that tipset on the next iteration of the loop, thus
// discarding our null round.
func (m *Miner) mine(ctx context.Context) { func (m *Miner) mine(ctx context.Context) {
ctx, span := trace.StartSpan(ctx, "/mine") ctx, span := trace.StartSpan(ctx, "/mine")
defer span.End() defer span.End()
@ -305,11 +356,19 @@ minerLoop:
} }
} }
// MiningBase is the tipset on top of which we plan to construct our next block.
// Refer to godocs on GetBestMiningCandidate.
type MiningBase struct { type MiningBase struct {
TipSet *types.TipSet TipSet *types.TipSet
NullRounds abi.ChainEpoch NullRounds abi.ChainEpoch
} }
// GetBestMiningCandidate implements the fork choice rule from a miner's
// perspective.
//
// It obtains the current chain head (HEAD), and compares it to the last tipset
// we selected as our mining base (LAST). If HEAD's weight is larger than
// LAST's weight, it selects HEAD to build on. Else, it selects LAST.
func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error) { func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error) {
m.lk.Lock() m.lk.Lock()
defer m.lk.Unlock() defer m.lk.Unlock()