Fantom-foundation/go-lachesis

View on GitHub
gossip/scores.go

Summary

Maintainability
A
35 mins
Test Coverage
package gossip

import (
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"

    "github.com/Fantom-foundation/go-lachesis/evmcore"
    "github.com/Fantom-foundation/go-lachesis/inter"
    "github.com/Fantom-foundation/go-lachesis/inter/idx"
)

const (
    minGasPowerRefund = 800
)

// updateOriginationScores calculates the origination scores
func (s *Service) updateOriginationScores(block *inter.Block, evmBlock *evmcore.EvmBlock, receipts types.Receipts, txPositions map[common.Hash]TxPosition, sealEpoch bool) {
    epoch := s.engine.GetEpoch()
    // Calc origination scores
    for i, tx := range evmBlock.Transactions {
        txEventPos := txPositions[receipts[i].TxHash]
        // sanity check
        if txEventPos.Block != block.Index {
            s.Log.Crit("Incorrect tx block position", "tx", receipts[i].TxHash,
                "block", txEventPos.Block, "block_got", block.Index)
        }

        txEvent := s.store.GetEventHeader(txEventPos.Event.Epoch(), txEventPos.Event)
        // sanity check
        if txEvent == nil {
            s.Log.Crit("Incorrect tx event position", "tx", receipts[i].TxHash, "event", txEventPos.Event, "reason", "event has no transactions")
        }

        txFee := new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), tx.GasPrice())

        s.app.AddDirtyOriginationScore(txEvent.Creator, txFee)

        { // logic for gas power refunds
            if tx.Gas() < receipts[i].GasUsed {
                s.Log.Crit("Transaction gas used is higher than tx gas limit", "tx", receipts[i].TxHash, "event", txEventPos.Event)
            }
            notUsedGas := tx.Gas() - receipts[i].GasUsed
            if notUsedGas >= minGasPowerRefund { // do not refund if refunding is more costly than refunded value
                s.app.IncGasPowerRefund(epoch, txEvent.Creator, notUsedGas)
            }
        }
    }

    if sealEpoch {
        s.app.DelAllActiveOriginationScores()
        s.app.MoveDirtyOriginationScoresToActive()
        // prune not needed gas power records
        s.app.DelGasPowerRefunds(epoch - 1)
    }
}

// updateValidationScores calculates the validation scores
func (s *Service) updateValidationScores(block *inter.Block, sealEpoch bool) {
    blockTimeDiff := block.Time - s.store.GetBlock(block.Index-1).Time

    // Calc validation scores
    for _, it := range s.GetActiveSfcStakers() {
        // validators only
        if !s.engine.GetValidators().Exists(it.StakerID) {
            continue
        }

        // Check if validator has confirmed events by this Atropos
        missedBlock := !s.blockParticipated[it.StakerID]

        // If have no confirmed events by this Atropos - just add missed blocks for validator
        if missedBlock {
            s.app.IncBlocksMissed(it.StakerID, blockTimeDiff)
            continue
        }

        missedNum := s.app.GetBlocksMissed(it.StakerID).Num
        if missedNum > s.config.Net.Economy.BlockMissedLatency {
            missedNum = s.config.Net.Economy.BlockMissedLatency
        }

        // Add score for previous blocks, but no more than FrameLatency prev blocks
        s.app.AddDirtyValidationScore(it.StakerID, new(big.Int).SetUint64(uint64(blockTimeDiff)))
        for i := idx.Block(1); i <= missedNum && i < block.Index; i++ {
            blockTime := s.store.GetBlock(block.Index - i).Time
            prevBlockTime := s.store.GetBlock(block.Index - i - 1).Time
            timeDiff := blockTime - prevBlockTime
            s.app.AddDirtyValidationScore(it.StakerID, new(big.Int).SetUint64(uint64(timeDiff)))
        }
        s.app.ResetBlocksMissed(it.StakerID)
    }

    if sealEpoch {
        s.app.DelAllActiveValidationScores()
        s.app.MoveDirtyValidationScoresToActive()
    }
}