Merge pull request #11157 from filecoin-project/asr/add-time-to-base
feat: miner: implement FRC-0051
This commit is contained in:
commit
d590c53400
@ -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
|
||||||
//
|
//
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
@ -374,6 +375,7 @@ minerLoop:
|
|||||||
// Refer to godocs on GetBestMiningCandidate.
|
// Refer to godocs on GetBestMiningCandidate.
|
||||||
type MiningBase struct {
|
type MiningBase struct {
|
||||||
TipSet *types.TipSet
|
TipSet *types.TipSet
|
||||||
|
ComputeTime time.Time
|
||||||
NullRounds abi.ChainEpoch
|
NullRounds abi.ChainEpoch
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user