erigon-pulse/consensus/bor/finality/whitelist/milestone.go

292 lines
8.5 KiB
Go
Raw Normal View History

package whitelist
import (
"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/metrics"
"github.com/ledgerwatch/erigon/consensus/bor/finality/flags"
"github.com/ledgerwatch/erigon/consensus/bor/finality/rawdb"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/log/v3"
)
type milestone struct {
finality[*rawdb.Milestone]
LockedMilestoneNumber uint64 // Locked sprint number
LockedMilestoneHash common.Hash //Hash for the locked endBlock
Locked bool //
LockedMilestoneIDs map[string]struct{} //list of milestone ids
FutureMilestoneList map[uint64]common.Hash // Future Milestone list
FutureMilestoneOrder []uint64 // Future Milestone Order
MaxCapacity int //Capacity of future Milestone list
}
type milestoneService interface {
finalityService
GetMilestoneIDsList() []string
RemoveMilestoneID(milestoneId string)
LockMutex(endBlockNum uint64) bool
UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash)
UnlockSprint(endBlockNum uint64)
ProcessFutureMilestone(num uint64, hash common.Hash)
}
2023-10-06 04:23:04 +00:00
var (
//Metrics for collecting the whitelisted milestone number
whitelistedMilestoneMeter = metrics.GetOrCreateCounter("chain_milestone_latest", true)
2023-10-06 04:23:04 +00:00
//Metrics for collecting the future milestone number
FutureMilestoneMeter = metrics.GetOrCreateCounter("chain_milestone_future", true)
2023-10-06 04:23:04 +00:00
//Metrics for collecting the length of the MilestoneIds map
MilestoneIdsLengthMeter = metrics.GetOrCreateCounter("chain_milestone_idslength", true)
2023-10-06 04:23:04 +00:00
//Metrics for collecting the number of valid chains received
MilestoneChainMeter = metrics.GetOrCreateCounter("chain_milestone_isvalidchain")
)
// IsValidChain checks the validity of chain by comparing it
// against the local milestone entries
func (m *milestone) IsValidChain(currentHeader uint64, chain []*types.Header) bool {
//Checking for the milestone flag
if !flags.Milestone {
return true
}
m.finality.RLock()
defer m.finality.RUnlock()
2023-10-06 04:23:04 +00:00
var isValid bool = false
defer func() {
if isValid {
MilestoneChainMeter.Add(1)
} else {
MilestoneChainMeter.Add(-1)
}
}()
res := m.finality.IsValidChain(currentHeader, chain)
if !res {
2023-10-06 04:23:04 +00:00
isValid = false
return false
}
if m.Locked && !m.IsReorgAllowed(chain, m.LockedMilestoneNumber, m.LockedMilestoneHash) {
2023-10-06 04:23:04 +00:00
isValid = false
return false
}
if !m.IsFutureMilestoneCompatible(chain) {
2023-10-06 04:23:04 +00:00
isValid = false
return false
}
2023-10-06 04:23:04 +00:00
isValid = true
return true
}
func (m *milestone) Process(block uint64, hash common.Hash) {
m.finality.Lock()
defer m.finality.Unlock()
m.finality.Process(block, hash)
for i := 0; i < len(m.FutureMilestoneOrder); i++ {
if m.FutureMilestoneOrder[i] <= block {
m.dequeueFutureMilestone()
} else {
break
}
}
2023-10-06 04:23:04 +00:00
whitelistedMilestoneMeter.Set(block)
m.UnlockSprint(block)
}
// This function will Lock the mutex at the time of voting
func (m *milestone) LockMutex(endBlockNum uint64) bool {
m.finality.Lock()
if m.doExist && endBlockNum <= m.Number { //if endNum is less than whitelisted milestone, then we won't lock the sprint
log.Debug("endBlockNumber is less than or equal to latesMilestoneNumber", "endBlock Number", endBlockNum, "LatestMilestone Number", m.Number)
return false
}
if m.Locked && endBlockNum < m.LockedMilestoneNumber {
log.Debug("endBlockNum is less than locked milestone number", "endBlock Number", endBlockNum, "Locked Milestone Number", m.LockedMilestoneNumber)
return false
}
return true
}
// This function will unlock the mutex locked in LockMutex
func (m *milestone) UnlockMutex(doLock bool, milestoneId string, endBlockNum uint64, endBlockHash common.Hash) {
m.Locked = m.Locked || doLock
if doLock {
m.UnlockSprint(m.LockedMilestoneNumber)
m.Locked = true
m.LockedMilestoneHash = endBlockHash
m.LockedMilestoneNumber = endBlockNum
m.LockedMilestoneIDs[milestoneId] = struct{}{}
}
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
2023-10-06 04:23:04 +00:00
milestoneIDLength := uint64(len(m.LockedMilestoneIDs))
MilestoneIdsLengthMeter.Set(milestoneIDLength)
m.finality.Unlock()
}
// This function will unlock the locked sprint
func (m *milestone) UnlockSprint(endBlockNum uint64) {
if endBlockNum < m.LockedMilestoneNumber {
return
}
m.Locked = false
m.purgeMilestoneIDsList()
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
}
// This function will remove the stored milestoneID
func (m *milestone) RemoveMilestoneID(milestoneId string) {
m.finality.Lock()
defer m.finality.Unlock()
delete(m.LockedMilestoneIDs, milestoneId)
if len(m.LockedMilestoneIDs) == 0 {
m.Locked = false
}
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
}
// This will check whether the incoming chain matches the locked sprint hash
func (m *milestone) IsReorgAllowed(chain []*types.Header, lockedMilestoneNumber uint64, lockedMilestoneHash common.Hash) bool {
if chain[len(chain)-1].Number.Uint64() <= lockedMilestoneNumber { //Can't reorg if the end block of incoming
return false //chain is less than locked sprint number
}
for i := 0; i < len(chain); i++ {
if chain[i].Number.Uint64() == lockedMilestoneNumber {
return chain[i].Hash() == lockedMilestoneHash
}
}
return true
}
// This will return the list of milestoneIDs stored.
func (m *milestone) GetMilestoneIDsList() []string {
m.finality.RLock()
defer m.finality.RUnlock()
// fixme: use generics :)
keys := make([]string, 0, len(m.LockedMilestoneIDs))
for key := range m.LockedMilestoneIDs {
keys = append(keys, key)
}
return keys
}
// This is remove the milestoneIDs stored in the list.
func (m *milestone) purgeMilestoneIDsList() {
m.LockedMilestoneIDs = make(map[string]struct{})
}
func (m *milestone) IsFutureMilestoneCompatible(chain []*types.Header) bool {
//Tip of the received chain
chainTipNumber := chain[len(chain)-1].Number.Uint64()
for i := len(m.FutureMilestoneOrder) - 1; i >= 0; i-- {
//Finding out the highest future milestone number
//which is less or equal to received chain tip
if chainTipNumber >= m.FutureMilestoneOrder[i] {
//Looking for the received chain 's particular block number(matching future milestone number)
for j := len(chain) - 1; j >= 0; j-- {
if chain[j].Number.Uint64() == m.FutureMilestoneOrder[i] {
endBlockNum := m.FutureMilestoneOrder[i]
endBlockHash := m.FutureMilestoneList[endBlockNum]
//Checking the received chain matches with future milestone
return chain[j].Hash() == endBlockHash
}
}
}
}
return true
}
func (m *milestone) ProcessFutureMilestone(num uint64, hash common.Hash) {
if len(m.FutureMilestoneOrder) < m.MaxCapacity {
m.enqueueFutureMilestone(num, hash)
}
if num < m.LockedMilestoneNumber {
return
}
m.Locked = false
m.purgeMilestoneIDsList()
err := rawdb.WriteLockField(m.db, m.Locked, m.LockedMilestoneNumber, m.LockedMilestoneHash, m.LockedMilestoneIDs)
if err != nil {
log.Error("Error in writing lock data of milestone to db", "err", err)
}
}
// EnqueueFutureMilestone add the future milestone to the list
func (m *milestone) enqueueFutureMilestone(key uint64, hash common.Hash) {
if _, ok := m.FutureMilestoneList[key]; ok {
log.Debug("Future milestone already exist", "endBlockNumber", key, "futureMilestoneHash", hash)
return
}
log.Debug("Enqueing new future milestone", "endBlockNumber", key, "futureMilestoneHash", hash)
m.FutureMilestoneList[key] = hash
m.FutureMilestoneOrder = append(m.FutureMilestoneOrder, key)
err := rawdb.WriteFutureMilestoneList(m.db, m.FutureMilestoneOrder, m.FutureMilestoneList)
if err != nil {
log.Error("Error in writing future milestone data to db", "err", err)
}
2023-10-06 04:23:04 +00:00
FutureMilestoneMeter.Set(key)
}
// DequeueFutureMilestone remove the future milestone entry from the list.
func (m *milestone) dequeueFutureMilestone() {
delete(m.FutureMilestoneList, m.FutureMilestoneOrder[0])
m.FutureMilestoneOrder = m.FutureMilestoneOrder[1:]
err := rawdb.WriteFutureMilestoneList(m.db, m.FutureMilestoneOrder, m.FutureMilestoneList)
if err != nil {
log.Error("Error in writing future milestone data to db", "err", err)
}
}