From 91d2c027656a2974cab41506224d91a96897e958 Mon Sep 17 00:00:00 2001
From: Aayush <arajasek94@gmail.com>
Date: Thu, 10 Aug 2023 16:07:08 -0400
Subject: [PATCH] feat: miner: implement FRC-0051

---
 build/params_mainnet.go |  1 +
 miner/miner.go          | 43 ++++++++++++++++++++++++++++++++++++-----
 2 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/build/params_mainnet.go b/build/params_mainnet.go
index 0b3c0a254..9026056c0 100644
--- a/build/params_mainnet.go
+++ b/build/params_mainnet.go
@@ -105,6 +105,7 @@ var SupportedProofTypes = []abi.RegisteredSealProof{
 var ConsensusMinerMinPower = abi.NewStoragePower(10 << 40)
 var PreCommitChallengeDelay = abi.ChainEpoch(150)
 var PropagationDelaySecs = uint64(10)
+var EquivocationDelaySecs = uint64(2)
 
 func init() {
 	if os.Getenv("LOTUS_USE_TEST_ADDRESSES") != "1" {
diff --git a/miner/miner.go b/miner/miner.go
index 104cf35fc..b02aba69d 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -10,7 +10,7 @@ import (
 	"sync"
 	"time"
 
-	"github.com/hashicorp/golang-lru/arc/v2"
+	"github.com/ipfs/go-cid"
 	logging "github.com/ipfs/go-log/v2"
 	"go.opencensus.io/trace"
 	"golang.org/x/xerrors"
@@ -373,8 +373,9 @@ minerLoop:
 // MiningBase is the tipset on top of which we plan to construct our next block.
 // Refer to godocs on GetBestMiningCandidate.
 type MiningBase struct {
-	TipSet     *types.TipSet
-	NullRounds abi.ChainEpoch
+	TipSet      *types.TipSet
+	ComputeTime time.Time
+	NullRounds  abi.ChainEpoch
 }
 
 // GetBestMiningCandidate implements the fork choice rule from a miner's
@@ -412,7 +413,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
 }
 
@@ -560,6 +561,37 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type
 		return nil, err
 	}
 
+	tEquivocateWait := build.Clock.Now()
+
+	// TODO: make param
+	m.niceSleep(time.Until(base.ComputeTime.Add(2 * time.Second)))
+	newBase, err := m.GetBestMiningCandidate(ctx)
+	if err != nil {
+		err = xerrors.Errorf("failed to refresh best mining candidate: %w", err)
+		return nil, err
+	}
+
+	// Only factor in equivocated blocks if doing so will not risk us missing a block
+	if newBase.TipSet.Height() == base.TipSet.Height() && newBase.TipSet.MinTicket().Equals(base.TipSet.MinTicket()) {
+		newBaseMap := map[cid.Cid]struct{}{}
+		for _, newBaseBlk := range newBase.TipSet.Cids() {
+			newBaseMap[newBaseBlk] = struct{}{}
+		}
+
+		refreshedBase := make([]*types.BlockHeader, 0, len(base.TipSet.Cids()))
+		for _, baseBlk := range base.TipSet.Blocks() {
+			if _, ok := newBaseMap[baseBlk.Cid()]; ok {
+				refreshedBase = append(refreshedBase, baseBlk)
+			}
+		}
+
+		base.TipSet, err = types.NewTipSet(refreshedBase)
+		if err != nil {
+			err = xerrors.Errorf("failed to create new tipset when refreshing: %w", err)
+			return nil, err
+		}
+	}
+
 	tPending := build.Clock.Now()
 
 	// TODO: winning post proof
@@ -582,7 +614,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type
 			"tTicket ", tTicket.Sub(tPowercheck),
 			"tSeed ", tSeed.Sub(tTicket),
 			"tProof ", tProof.Sub(tSeed),
-			"tPending ", tPending.Sub(tProof),
+			"tEquivocateWait ", tEquivocateWait.Sub(tProof),
+			"tPending ", tPending.Sub(tEquivocateWait),
 			"tCreateBlock ", tCreateBlock.Sub(tPending))
 	}