Skip to content
This repository has been archived by the owner on Jun 17, 2022. It is now read-only.

POI calculations by ABCI-app #444

Merged
merged 12 commits into from Mar 15, 2020
33 changes: 22 additions & 11 deletions app/app.go
Expand Up @@ -21,7 +21,7 @@ type (
App struct {
config lachesis.Config
store *Store
*blockContext
ctx *blockContext

logger.Instance
}
Expand All @@ -30,6 +30,7 @@ type (
statedb *state.StateDB
evmProcessor *evmcore.StateProcessor
sealEpoch bool
totalFee *big.Int
}
)

Expand All @@ -45,7 +46,7 @@ func New(cfg lachesis.Config, s *Store) *App {

// BeginBlock is a prototype of ABCIApplication.BeginBlock
func (a *App) BeginBlock(block *inter.Block, cheaters inter.Cheaters, stateHash common.Hash, stateReader evmcore.DummyChain) {
a.blockContext = &blockContext{
a.ctx = &blockContext{
statedb: a.store.StateDB(stateHash),
evmProcessor: evmcore.NewStateProcessor(a.config.EvmChainConfig(), stateReader),
sealEpoch: a.shouldSealEpoch(block, cheaters),
Expand All @@ -65,15 +66,16 @@ func (a *App) DeliverTxs(
bool,
) {
// Process txs
receipts, _, gasUsed, totalFee, skipped, err := a.blockContext.evmProcessor.
Process(evmBlock, a.blockContext.statedb, vm.Config{}, false)
receipts, _, gasUsed, totalFee, skipped, err := a.ctx.evmProcessor.
Process(evmBlock, a.ctx.statedb, vm.Config{}, false)
if err != nil {
a.Log.Crit("Shouldn't happen ever because it's not strict", "err", err)
}
a.ctx.totalFee = totalFee
block.SkippedTxs = skipped
block.GasUsed = gasUsed

a.blockContext.sealEpoch = a.blockContext.sealEpoch || sfctype.EpochIsForceSealed(receipts)
a.ctx.sealEpoch = a.ctx.sealEpoch || sfctype.EpochIsForceSealed(receipts)

// Filter skipped transactions
evmBlock = filterSkippedTxs(block, evmBlock)
Expand All @@ -88,30 +90,39 @@ func (a *App) DeliverTxs(
a.store.IndexLogs(r.Logs...)
}

return block, evmBlock, totalFee, receipts, a.blockContext.sealEpoch
return block, evmBlock, a.ctx.totalFee, receipts, a.ctx.sealEpoch
}

// EndBlock is a prototype of ABCIApplication.EndBlock
func (a *App) EndBlock(
epoch idx.Epoch,
block *inter.Block,
evmBlock *evmcore.EvmBlock,
receipts types.Receipts,
cheaters inter.Cheaters,
stats *sfctype.EpochStats,
txPositions map[common.Hash]TxPosition,
blockTime func(n idx.Block) inter.Timestamp,
blockParticipated map[idx.StakerID]bool,
) common.Hash {

a.processSfc(epoch, block, receipts, a.blockContext.sealEpoch, cheaters, stats)
newStateHash, err := a.blockContext.statedb.Commit(true)
// Process PoI/score changes
a.updateOriginationScores(epoch, evmBlock, receipts, txPositions)
a.updateValidationScores(epoch, block, blockParticipated, blockTime)
a.updateUsersPOI(block, evmBlock, receipts)
a.updateStakersPOI(block, blockTime)

a.processSfc(epoch, block, receipts, cheaters, stats)
newStateHash, err := a.ctx.statedb.Commit(true)
if err != nil {
a.Log.Crit("Failed to commit state", "err", err)
}

if a.blockContext.sealEpoch {
if a.ctx.sealEpoch {
a.store.SetLastVoting(block.Index, block.Time)
}

// free resources
a.blockContext = nil
a.ctx = nil
a.store.FlushState()

return newStateHash
Expand Down
115 changes: 115 additions & 0 deletions app/poi.go
@@ -0,0 +1,115 @@
package app

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"
"github.com/Fantom-foundation/go-lachesis/lachesis"
)

// PoiPeriod calculate POI period from int64 unix time
func PoiPeriod(t inter.Timestamp, config *lachesis.EconomyConfig) uint64 {
return uint64(t) / uint64(config.PoiPeriodDuration)
}

// updateUsersPOI calculates the Proof Of Importance weights for users
func (a *App) updateUsersPOI(block *inter.Block, evmBlock *evmcore.EvmBlock, receipts types.Receipts) {
// User POI calculations
poiPeriod := PoiPeriod(block.Time, &a.config.Economy)
a.store.AddPoiFee(poiPeriod, a.ctx.totalFee)

for i, tx := range evmBlock.Transactions {
txFee := new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), tx.GasPrice())

signer := types.NewEIP155Signer(a.config.EvmChainConfig().ChainID)
sender, err := signer.Sender(tx)
if err != nil {
a.Log.Crit("Failed to get sender from transaction", "err", err)
}

senderLastTxTime := a.store.GetAddressLastTxTime(sender)
prevUserPoiPeriod := PoiPeriod(senderLastTxTime, &a.config.Economy)
senderTotalFee := a.store.GetAddressFee(sender, prevUserPoiPeriod)

delegator := a.store.GetSfcDelegator(sender)
if delegator != nil {
staker := a.store.GetSfcStaker(delegator.ToStakerID)
if staker != nil {
prevWeightedTxFee := a.store.GetWeightedDelegatorsFee(delegator.ToStakerID)

weightedTxFee := new(big.Int).Mul(txFee, delegator.Amount)
weightedTxFee.Div(weightedTxFee, staker.CalcTotalStake())

weightedTxFee.Add(weightedTxFee, prevWeightedTxFee)
a.store.SetWeightedDelegatorsFee(delegator.ToStakerID, weightedTxFee)
}
}

if prevUserPoiPeriod != poiPeriod {
a.updateAddressPOI(sender, senderTotalFee, prevUserPoiPeriod)
senderTotalFee = big.NewInt(0)
}

a.store.SetAddressLastTxTime(sender, block.Time)
senderTotalFee.Add(senderTotalFee, txFee)
a.store.SetAddressFee(sender, poiPeriod, senderTotalFee)
}

}

// updateAddressPOI calculate and save POI for user
func (a *App) updateAddressPOI(address common.Address, senderTotalFee *big.Int, poiPeriod uint64) {
/*if senderTotalFee.Sign() == 0 {
a.store.SetAddressPOI(address, common.Big0)
return // avoid division by 0
}
poi := new(big.Int).Mul(senderTotalFee, lachesis.PercentUnit)
poi.Div(poi, a.store.GetPoiFee(poiPeriod)) // rebase user's PoI as <= 1.0 ratio
a.store.SetAddressPOI(address, poi)*/
}

// updateStakerPOI calculate and save POI for staker
func (a *App) updateStakerPOI(stakerID idx.StakerID, stakerAddress common.Address, poiPeriod uint64) {
staker := a.store.GetSfcStaker(stakerID)

vFee := a.store.GetAddressFee(stakerAddress, poiPeriod)
weightedDFee := a.store.GetWeightedDelegatorsFee(stakerID)
if vFee.Sign() == 0 && weightedDFee.Sign() == 0 {
a.store.SetStakerPOI(stakerID, common.Big0)
return // optimization
}

weightedVFee := new(big.Int).Mul(vFee, staker.StakeAmount)
weightedVFee.Div(weightedVFee, staker.CalcTotalStake())

weightedFee := new(big.Int).Add(weightedDFee, weightedVFee)

if weightedFee.Sign() == 0 {
a.store.SetStakerPOI(stakerID, common.Big0)
return // avoid division by 0
}
poi := weightedFee // no need to rebase validator's PoI as <= 1.0 ratio
/*poi := new(big.Int).Mul(weightedFee, lachesis.PercentUnit)
poi.Div(poi, s.store.GetPoiFee(poiPeriod))*/
a.store.SetStakerPOI(stakerID, poi)
}

// updateStakersPOI calculates the Proof Of Importance weights for stakers
func (a *App) updateStakersPOI(block *inter.Block, blockTime func(n idx.Block) inter.Timestamp) {
// Stakers POI calculations
poiPeriod := PoiPeriod(block.Time, &a.config.Economy)
prevBlockPoiPeriod := PoiPeriod(blockTime(block.Index-1), &a.config.Economy)

if poiPeriod != prevBlockPoiPeriod {
for _, it := range a.store.GetActiveSfcStakers() {
a.updateStakerPOI(it.StakerID, it.Staker.Address, prevBlockPoiPeriod)
}
// clear StakersDelegatorsFee counters
a.store.DelAllWeightedDelegatorsFee()
}
}
118 changes: 118 additions & 0 deletions app/provided_store.go
@@ -0,0 +1,118 @@
package app

import (
"math/big"

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

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

/*
* NOTE: all the methods are temporary and will be refactored during Tendermint implementation.
*/

// GetEpochValidators provides store's method.
func (a App) GetEpochValidators(epoch idx.Epoch) []sfctype.SfcStakerAndID {
return a.store.GetEpochValidators(epoch)
}

// GetSfcConstants provides store's method.
func (a App) GetSfcConstants(epoch idx.Epoch) SfcConstants {
return a.store.GetSfcConstants(epoch)
}

// SetReceipts provides store's method.
func (a App) SetReceipts(n idx.Block, receipts types.Receipts) {
a.store.SetReceipts(n, receipts)
}

// provides store's method.
func (a App) GetReceipts(n idx.Block) types.Receipts {
return a.store.GetReceipts(n)
}

// StateDB provides store's method.
func (a App) StateDB(from common.Hash) *state.StateDB {
return a.store.StateDB(from)
}

// EvmTable provides store's method.
func (a App) EvmTable() ethdb.Database {
return a.store.EvmTable()
}

// provides store's method.
func (a App) EvmLogs() *topicsdb.Index {
return a.store.EvmLogs()
}

// HasSfcStaker provides store's method.
func (a App) HasSfcStaker(stakerID idx.StakerID) bool {
return a.store.HasSfcStaker(stakerID)
}

// HasEpochValidator provides store's method.
func (a App) HasEpochValidator(epoch idx.Epoch, stakerID idx.StakerID) bool {
return a.store.HasEpochValidator(epoch, stakerID)
}

// provides store's method.
func (a App) GetActiveValidationScore(stakerID idx.StakerID) *big.Int {
return a.store.GetActiveValidationScore(stakerID)
}

// GetActiveOriginationScore provides store's method.
func (a App) GetActiveOriginationScore(stakerID idx.StakerID) *big.Int {
return a.store.GetActiveOriginationScore(stakerID)
}

// provides store's method.
func (a App) GetStakerPOI(stakerID idx.StakerID) *big.Int {
return a.store.GetStakerPOI(stakerID)
}

// GetBlocksMissed provides store's method.
func (a App) GetBlocksMissed(stakerID idx.StakerID) BlocksMissed {
return a.store.GetBlocksMissed(stakerID)
}

// GetSfcStaker provides store's method.
func (a App) GetSfcStaker(stakerID idx.StakerID) *sfctype.SfcStaker {
return a.store.GetSfcStaker(stakerID)
}

// ForEachSfcStaker provides store's method.
func (a App) ForEachSfcStaker(do func(sfctype.SfcStakerAndID)) {
a.store.ForEachSfcStaker(do)
}

// ForEachSfcDelegator provides store's method.
func (a App) ForEachSfcDelegator(do func(sfctype.SfcDelegatorAndAddr)) {
a.store.ForEachSfcDelegator(do)
}

// GetSfcDelegator provides store's method.
func (a App) GetSfcDelegator(addr common.Address) *sfctype.SfcDelegator {
return a.store.GetSfcDelegator(addr)
}

// GetDelegatorClaimedRewards provides store's method.
func (a App) GetDelegatorClaimedRewards(addr common.Address) *big.Int {
return a.store.GetDelegatorClaimedRewards(addr)
}

// GetStakerClaimedRewards provides store's method.
func (a App) GetStakerClaimedRewards(stakerID idx.StakerID) *big.Int {
return a.store.GetStakerClaimedRewards(stakerID)
}

// GetStakerDelegatorsClaimedRewards provides store's method.
func (a App) GetStakerDelegatorsClaimedRewards(stakerID idx.StakerID) *big.Int {
return a.store.GetStakerDelegatorsClaimedRewards(stakerID)
}