Merge pull request #11157 from filecoin-project/asr/add-time-to-base

feat: miner: implement FRC-0051
This commit is contained in:
Aayush Rajasekaran 2023-09-01 15:30:41 -04:00 committed by GitHub
commit d590c53400
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 10 deletions

View File

@ -132,6 +132,8 @@ const BlockDelaySecs = uint64(4)
const PropagationDelaySecs = uint64(1) const PropagationDelaySecs = uint64(1)
var EquivocationDelaySecs = uint64(0)
// SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after // SlashablePowerDelay is the number of epochs after ElectionPeriodStart, after
// which the miner is slashed // which the miner is slashed
// //

View File

@ -86,6 +86,8 @@ const BlockDelaySecs = uint64(builtin2.EpochDurationSeconds)
const PropagationDelaySecs = uint64(6) const PropagationDelaySecs = uint64(6)
var EquivocationDelaySecs = uint64(2)
// BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start // BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 2 const BootstrapPeerThreshold = 2

View File

@ -123,6 +123,8 @@ const BlockDelaySecs = uint64(builtin2.EpochDurationSeconds)
var PropagationDelaySecs = uint64(10) var PropagationDelaySecs = uint64(10)
var EquivocationDelaySecs = uint64(2)
// BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start // BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 4 const BootstrapPeerThreshold = 4

View File

@ -121,6 +121,8 @@ const BlockDelaySecs = uint64(builtin2.EpochDurationSeconds)
const PropagationDelaySecs = uint64(6) const PropagationDelaySecs = uint64(6)
var EquivocationDelaySecs = uint64(2)
// BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start // BootstrapPeerThreshold is the minimum number peers we need to track for a sync worker to start
const BootstrapPeerThreshold = 2 const BootstrapPeerThreshold = 2

View File

@ -106,6 +106,8 @@ var ConsensusMinerMinPower = abi.NewStoragePower(10 << 40)
var PreCommitChallengeDelay = abi.ChainEpoch(150) var PreCommitChallengeDelay = abi.ChainEpoch(150)
var PropagationDelaySecs = uint64(10) var PropagationDelaySecs = uint64(10)
var EquivocationDelaySecs = uint64(2)
func init() { func init() {
if os.Getenv("LOTUS_USE_TEST_ADDRESSES") != "1" { if os.Getenv("LOTUS_USE_TEST_ADDRESSES") != "1" {
SetAddressNetwork(address.Mainnet) SetAddressNetwork(address.Mainnet)

View File

@ -9,7 +9,6 @@ package build
import ( import (
"math/big" "math/big"
"time"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
@ -34,6 +33,7 @@ var (
MinimumBaseFee = int64(100) MinimumBaseFee = int64(100)
BlockDelaySecs = uint64(builtin2.EpochDurationSeconds) BlockDelaySecs = uint64(builtin2.EpochDurationSeconds)
PropagationDelaySecs = uint64(6) PropagationDelaySecs = uint64(6)
EquivocationDelaySecs = uint64(2)
SupportedProofTypes = []abi.RegisteredSealProof{ SupportedProofTypes = []abi.RegisteredSealProof{
abi.RegisteredSealProof_StackedDrg32GiBV1, abi.RegisteredSealProof_StackedDrg32GiBV1,
abi.RegisteredSealProof_StackedDrg64GiBV1, abi.RegisteredSealProof_StackedDrg64GiBV1,
@ -139,7 +139,3 @@ const BootstrapPeerThreshold = 1
// ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint. // ChainId defines the chain ID used in the Ethereum JSON-RPC endpoint.
// As per https://github.com/ethereum-lists/chains // As per https://github.com/ethereum-lists/chains
const Eip155ChainId = 31415926 const Eip155ChainId = 31415926
// Reducing the delivery delay for equivocation of
// consistent broadcast to just half a second.
var CBDeliveryDelay = 500 * time.Millisecond

View File

@ -169,6 +169,8 @@ func NewEnsemble(t *testing.T, opts ...EnsembleOpt) *Ensemble {
require.NoError(t, build.UseNetworkBundle("testing")) require.NoError(t, build.UseNetworkBundle("testing"))
} }
build.EquivocationDelaySecs = 0
return n return n
} }

View File

@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/hashicorp/golang-lru/arc/v2" "github.com/hashicorp/golang-lru/arc/v2"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
"go.opencensus.io/trace" "go.opencensus.io/trace"
"golang.org/x/xerrors" "golang.org/x/xerrors"
@ -373,8 +374,9 @@ minerLoop:
// MiningBase is the tipset on top of which we plan to construct our next block. // MiningBase is the tipset on top of which we plan to construct our next block.
// Refer to godocs on GetBestMiningCandidate. // Refer to godocs on GetBestMiningCandidate.
type MiningBase struct { type MiningBase struct {
TipSet *types.TipSet TipSet *types.TipSet
NullRounds abi.ChainEpoch ComputeTime time.Time
NullRounds abi.ChainEpoch
} }
// GetBestMiningCandidate implements the fork choice rule from a miner's // GetBestMiningCandidate implements the fork choice rule from a miner's
@ -412,7 +414,7 @@ func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error)
} }
} }
m.lastWork = &MiningBase{TipSet: bts} m.lastWork = &MiningBase{TipSet: bts, ComputeTime: time.Now()}
return m.lastWork, nil return m.lastWork, nil
} }
@ -554,12 +556,71 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type
tProof := build.Clock.Now() tProof := build.Clock.Now()
// get pending messages early, // get pending messages early,
msgs, err := m.api.MpoolSelect(context.TODO(), base.TipSet.Key(), ticket.Quality()) msgs, err := m.api.MpoolSelect(ctx, base.TipSet.Key(), ticket.Quality())
if err != nil { if err != nil {
err = xerrors.Errorf("failed to select messages for block: %w", err) err = xerrors.Errorf("failed to select messages for block: %w", err)
return nil, err return nil, err
} }
tEquivocateWait := build.Clock.Now()
// This next block exists to "catch" equivocating miners,
// who submit 2 blocks at the same height at different times in order to split the network.
// To safeguard against this, we make sure it's been EquivocationDelaySecs since our base was calculated,
// then re-calculate it.
// If the daemon detected equivocated blocks, those blocks will no longer be in the new base.
m.niceSleep(time.Until(base.ComputeTime.Add(time.Duration(build.EquivocationDelaySecs) * time.Second)))
newBase, err := m.GetBestMiningCandidate(ctx)
if err != nil {
err = xerrors.Errorf("failed to refresh best mining candidate: %w", err)
return nil, err
}
// If the base has changed, we take the _intersection_ of our old base and new base,
// thus ejecting blocks from any equivocating miners, without taking any new blocks.
if newBase.TipSet.Height() == base.TipSet.Height() && !newBase.TipSet.Equals(base.TipSet) {
log.Warnf("base changed from %s to %s, taking intersection", base.TipSet.Key(), newBase.TipSet.Key())
newBaseMap := map[cid.Cid]struct{}{}
for _, newBaseBlk := range newBase.TipSet.Cids() {
newBaseMap[newBaseBlk] = struct{}{}
}
refreshedBaseBlocks := make([]*types.BlockHeader, 0, len(base.TipSet.Cids()))
for _, baseBlk := range base.TipSet.Blocks() {
if _, ok := newBaseMap[baseBlk.Cid()]; ok {
refreshedBaseBlocks = append(refreshedBaseBlocks, baseBlk)
}
}
if len(refreshedBaseBlocks) != len(base.TipSet.Blocks()) {
refreshedBase, err := types.NewTipSet(refreshedBaseBlocks)
if err != nil {
err = xerrors.Errorf("failed to create new tipset when refreshing: %w", err)
return nil, err
}
if !base.TipSet.MinTicket().Equals(refreshedBase.MinTicket()) {
log.Warn("recomputing ticket due to base refresh")
ticket, err = m.computeTicket(ctx, &rbase, round, refreshedBase.MinTicket(), mbi)
if err != nil {
err = xerrors.Errorf("failed to refresh ticket: %w", err)
return nil, err
}
}
log.Warn("re-selecting messages due to base refresh")
// refresh messages, as the selected messages may no longer be valid
msgs, err = m.api.MpoolSelect(ctx, refreshedBase.Key(), ticket.Quality())
if err != nil {
err = xerrors.Errorf("failed to re-select messages for block: %w", err)
return nil, err
}
base.TipSet = refreshedBase
}
}
tPending := build.Clock.Now() tPending := build.Clock.Now()
// TODO: winning post proof // TODO: winning post proof
@ -582,7 +643,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type
"tTicket ", tTicket.Sub(tPowercheck), "tTicket ", tTicket.Sub(tPowercheck),
"tSeed ", tSeed.Sub(tTicket), "tSeed ", tSeed.Sub(tTicket),
"tProof ", tProof.Sub(tSeed), "tProof ", tProof.Sub(tSeed),
"tPending ", tPending.Sub(tProof), "tEquivocateWait ", tEquivocateWait.Sub(tProof),
"tPending ", tPending.Sub(tEquivocateWait),
"tCreateBlock ", tCreateBlock.Sub(tPending)) "tCreateBlock ", tCreateBlock.Sub(tPending))
} }