diff --git a/cmd/node/config/economics.toml b/cmd/node/config/economics.toml index 4913944041d..e096dc50d1b 100644 --- a/cmd/node/config/economics.toml +++ b/cmd/node/config/economics.toml @@ -18,15 +18,25 @@ Denomination = 18 # represents the smallest eGLD subdivision (10^-X eGLD for a denomination of X) GenesisMintingSenderAddress = "erd17rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rcqqkhty3" + [GlobalSettings.TailInflation] + EnableEpoch = 1 + StartYearInflation = 0.08757 + DecayPercentage = 0.0025 + MinimumInflation = 0.02 + [RewardsSettings] [[RewardsSettings.RewardsConfigByEpoch]] EpochEnable = 0 LeaderPercentage = 0.1 #fraction of value 0.1 - 10% DeveloperPercentage = 0.3 #fraction of value 0.3 - 30% ProtocolSustainabilityPercentage = 0.1 #fraction of value 0.1 - 10% - ProtocolSustainabilityAddress = "erd1j25xk97yf820rgdp3mj5scavhjkn6tjyn0t63pmv5qyjj7wxlcfqqe2rw5" - TopUpGradientPoint = "3000000000000000000000000" # 3MIL eGLD (eligible topUp) - TopUpFactor = 0.25 # fraction of value 0.25 - 25% + ProtocolSustainabilityAddress = "erd1e5cw5pegj9w2zfpzeq94wf3d5tp6g943xuhgnyysdwyr63rlkspqr0u05s" + EcosystemGrowthPercentage = 0.0 + EcosystemGrowthAddress = "erd1k8d58eve7aa64wjad35sx6z8ckgr738wl7jyz54pez0t4zr98fuqz6hxvs" + GrowthDividendPercentage = 0.0 + GrowthDividendAddress = "erd1enva49yfy9ja0e4ttncfmzr0vjv9k9rawuyg2h2g8nepqwmz8ltssvkglr" + TopUpGradientPoint = "2000000000000000000000000" # 2MIL eGLD (eligible topUp) + TopUpFactor = 0.5 # fraction of value 0.5 - 50% [[RewardsSettings.RewardsConfigByEpoch]] EpochEnable = 1 @@ -34,6 +44,10 @@ DeveloperPercentage = 0.3 #fraction of value 0.3 - 30% ProtocolSustainabilityPercentage = 0.1 #fraction of value 0.1 - 10% ProtocolSustainabilityAddress = "erd1e5cw5pegj9w2zfpzeq94wf3d5tp6g943xuhgnyysdwyr63rlkspqr0u05s" + EcosystemGrowthPercentage = 0.2 #fraction of value 0.2 - 20% + EcosystemGrowthAddress = "erd1k8d58eve7aa64wjad35sx6z8ckgr738wl7jyz54pez0t4zr98fuqz6hxvs" + GrowthDividendPercentage = 0.2 #fraction of value 0.2 - 20% + GrowthDividendAddress = "erd1enva49yfy9ja0e4ttncfmzr0vjv9k9rawuyg2h2g8nepqwmz8ltssvkglr" TopUpGradientPoint = "2000000000000000000000000" # 2MIL eGLD (eligible topUp) TopUpFactor = 0.5 # fraction of value 0.5 - 50% diff --git a/config/economicsConfig.go b/config/economicsConfig.go index 0d7ec5fad07..ab51ce21404 100644 --- a/config/economicsConfig.go +++ b/config/economicsConfig.go @@ -7,6 +7,15 @@ type GlobalSettings struct { YearSettings []*YearSetting Denomination int GenesisMintingSenderAddress string + TailInflation TailInflationSettings +} + +// TailInflationSettings will hold the tail inflation settings +type TailInflationSettings struct { + EnableEpoch uint32 + StartYearInflation float64 + DecayPercentage float64 + MinimumInflation float64 } // YearSetting will hold the maximum inflation rate for year @@ -26,6 +35,10 @@ type EpochRewardSettings struct { DeveloperPercentage float64 ProtocolSustainabilityPercentage float64 ProtocolSustainabilityAddress string + EcosystemGrowthPercentage float64 + EcosystemGrowthAddress string + GrowthDividendPercentage float64 + GrowthDividendAddress string TopUpGradientPoint string TopUpFactor float64 EpochEnable uint32 diff --git a/epochStart/interface.go b/epochStart/interface.go index 0e4e4d8a759..400ab104017 100644 --- a/epochStart/interface.go +++ b/epochStart/interface.go @@ -7,10 +7,9 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/state" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) // TriggerHandler defines the functionalities for an start of epoch trigger @@ -186,11 +185,19 @@ type EpochEconomicsDataProvider interface { SetLeadersFees(fees *big.Int) SetRewardsToBeDistributed(rewards *big.Int) SetRewardsToBeDistributedForBlocks(rewards *big.Int) + SetRewardsForProtocolSustainability(rewards *big.Int) + SetRewardsForEcosystemGrowth(rewards *big.Int) + SetRewardsForGrowthDividend(rewards *big.Int) NumberOfBlocks() uint64 NumberOfBlocksPerShard() map[uint32]uint64 LeaderFees() *big.Int RewardsToBeDistributed() *big.Int RewardsToBeDistributedForBlocks() *big.Int + RewardsForProtocolSustainability() *big.Int + RewardsForEcosystemGrowth() *big.Int + RewardsForGrowthDividend() *big.Int + RewardsForAccelerator() *big.Int + Clean() IsInterfaceNil() bool } @@ -208,7 +215,7 @@ type RewardsCreator interface { computedEconomics *block.Economics, prevBlockExecutionResults data.BaseMetaExecutionResultHandler, ) (block.MiniBlockSlice, error) - GetProtocolSustainabilityRewards() *big.Int + GetAcceleratorRewards() *big.Int GetLocalTxCache() TransactionCacher CreateMarshalledData(body *block.Body) map[string][][]byte GetRewardsTxs(body *block.Body) map[string]data.TransactionHandler diff --git a/epochStart/metachain/baseRewards.go b/epochStart/metachain/baseRewards.go index 41c15ffd9be..453cce98a44 100644 --- a/epochStart/metachain/baseRewards.go +++ b/epochStart/metachain/baseRewards.go @@ -42,6 +42,7 @@ type BaseRewardsCreatorArgs struct { EnableEpochsHandler common.EnableEpochsHandler ExecutionOrderHandler common.TxExecutionOrderHandler RewardsHandler process.RewardsHandler + EconomicsData epochStart.EpochEconomicsDataProvider } type baseRewardsCreator struct { @@ -56,7 +57,6 @@ type baseRewardsCreator struct { dataPool dataRetriever.PoolsHolder mapBaseRewardsPerBlockPerValidator map[uint32]*big.Int accumulatedRewards *big.Int - protocolSustainabilityValue *big.Int flagDelegationSystemSCEnabled atomic.Flag // nolint userAccountsDB state.AccountsAdapter enableEpochsHandler common.EnableEpochsHandler @@ -84,7 +84,6 @@ func NewBaseRewardsCreator(args BaseRewardsCreatorArgs) (*baseRewardsCreator, er dataPool: args.DataPool, nodesConfigProvider: args.NodesConfigProvider, accumulatedRewards: big.NewInt(0), - protocolSustainabilityValue: big.NewInt(0), userAccountsDB: args.UserAccountsDB, mapBaseRewardsPerBlockPerValidator: make(map[uint32]*big.Int), enableEpochsHandler: args.EnableEpochsHandler, @@ -95,14 +94,6 @@ func NewBaseRewardsCreator(args BaseRewardsCreatorArgs) (*baseRewardsCreator, er return brc, nil } -// GetProtocolSustainabilityRewards returns the sum of all rewards -func (brc *baseRewardsCreator) GetProtocolSustainabilityRewards() *big.Int { - brc.mutRewardsData.RLock() - defer brc.mutRewardsData.RUnlock() - - return brc.protocolSustainabilityValue -} - // GetLocalTxCache returns the local tx cache which holds all the rewards func (brc *baseRewardsCreator) GetLocalTxCache() epochStart.TransactionCacher { return brc.currTxs @@ -321,7 +312,6 @@ func (brc *baseRewardsCreator) clean() { brc.mapBaseRewardsPerBlockPerValidator = make(map[uint32]*big.Int) brc.currTxs.Clean() brc.accumulatedRewards = big.NewInt(0) - brc.protocolSustainabilityValue = big.NewInt(0) } func (brc *baseRewardsCreator) isSystemDelegationSC(address []byte) bool { @@ -346,14 +336,14 @@ func (brc *baseRewardsCreator) isSystemDelegationSC(address []byte) bool { func (brc *baseRewardsCreator) createProtocolSustainabilityRewardTransaction( epoch uint32, round uint64, - computedEconomics *block.Economics, + protocolSustainability *big.Int, ) (*rewardTx.RewardTx, uint32, error) { protocolSustainabilityAddressForEpoch := brc.rewardsHandler.ProtocolSustainabilityAddressInEpoch(epoch) protocolSustainabilityShardID := brc.shardCoordinator.ComputeId([]byte(protocolSustainabilityAddressForEpoch)) protocolSustainabilityRwdTx := &rewardTx.RewardTx{ Round: round, - Value: big.NewInt(0).Set(computedEconomics.RewardsForProtocolSustainability), + Value: big.NewInt(0).Set(protocolSustainability), RcvAddr: []byte(protocolSustainabilityAddressForEpoch), Epoch: epoch, } @@ -404,19 +394,25 @@ func (brc *baseRewardsCreator) initializeRewardsMiniBlocks() block.MiniBlockSlic return miniBlocks } -func (brc *baseRewardsCreator) addProtocolRewardToMiniBlocks( - protocolSustainabilityRwdTx *rewardTx.RewardTx, +func (brc *baseRewardsCreator) addAcceleratorRewardToMiniBlocks( + acceleratorRewardTx *rewardTx.RewardTx, miniBlocks block.MiniBlockSlice, - protocolSustainabilityShardId uint32, + shardID uint32, ) error { - protocolSustainabilityRwdHash, errHash := core.CalculateHash(brc.marshalizer, brc.hasher, protocolSustainabilityRwdTx) + acceleratorRwdHash, errHash := core.CalculateHash(brc.marshalizer, brc.hasher, acceleratorRewardTx) if errHash != nil { return errHash } + if acceleratorRewardTx.Value.Cmp(zero) < 0 { + return errNegativeAcceleratorReward + } + if acceleratorRewardTx.Value.Cmp(zero) == 0 { + // do not add to the miniblock + return nil + } - brc.currTxs.AddTx(protocolSustainabilityRwdHash, protocolSustainabilityRwdTx) - miniBlocks[protocolSustainabilityShardId].TxHashes = append(miniBlocks[protocolSustainabilityShardId].TxHashes, protocolSustainabilityRwdHash) - brc.protocolSustainabilityValue.Set(protocolSustainabilityRwdTx.Value) + brc.currTxs.AddTx(acceleratorRwdHash, acceleratorRewardTx) + miniBlocks[shardID].TxHashes = append(miniBlocks[shardID].TxHashes, acceleratorRwdHash) return nil } diff --git a/epochStart/metachain/baseRewards_test.go b/epochStart/metachain/baseRewards_test.go index a134de9f1dc..b0e1118b84e 100644 --- a/epochStart/metachain/baseRewards_test.go +++ b/epochStart/metachain/baseRewards_test.go @@ -185,14 +185,12 @@ func TestBaseRewardsCreator_clean(t *testing.T) { require.Nil(t, err) rwd.accumulatedRewards = big.NewInt(1000) - rwd.protocolSustainabilityValue = big.NewInt(100) rwd.mapBaseRewardsPerBlockPerValidator[0] = big.NewInt(10) txHash := []byte("txHash") rwd.currTxs.AddTx(txHash, &rewardTx.RewardTx{}) rwd.clean() require.Equal(t, big.NewInt(0), rwd.accumulatedRewards) - require.Equal(t, big.NewInt(0), rwd.protocolSustainabilityValue) require.Equal(t, 0, len(rwd.mapBaseRewardsPerBlockPerValidator)) tx, err := rwd.currTxs.GetTx(txHash) require.Nil(t, tx) @@ -220,7 +218,7 @@ func TestBaseRewardsCreator_GetLocalTxCache(t *testing.T) { require.False(t, check.IfNil(txCache)) } -func TestBaseRewardsCreator_GetProtocolSustainabilityRewards(t *testing.T) { +func TestBaseRewardsCreator_addAcceleratorRewardToMiniBlocks(t *testing.T) { t.Parallel() args := getBaseRewardsArguments() @@ -228,20 +226,7 @@ func TestBaseRewardsCreator_GetProtocolSustainabilityRewards(t *testing.T) { require.Nil(t, err) require.NotNil(t, rwd) - // should return 0 as just initialized - rewards := rwd.GetProtocolSustainabilityRewards() - require.Zero(t, big.NewInt(0).Cmp(rewards)) -} - -func TestBaseRewardsCreator_addProtocolRewardToMiniblocks(t *testing.T) { - t.Parallel() - - args := getBaseRewardsArguments() - rwd, err := NewBaseRewardsCreator(args) - require.Nil(t, err) - require.NotNil(t, rwd) - - initialProtRewardValue := big.NewInt(-100) + initialProtRewardValue := big.NewInt(100) protRwAddr, _ := args.PubkeyConverter.Decode(args.RewardsHandler.ProtocolSustainabilityAddressInEpoch(0)) protRwTx := &rewardTx.RewardTx{ Round: 100, @@ -257,7 +242,7 @@ func TestBaseRewardsCreator_addProtocolRewardToMiniblocks(t *testing.T) { protRwShard := args.ShardCoordinator.ComputeId(protRwAddr) mbSlice := createDefaultMiniBlocksSlice() - err = rwd.addProtocolRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) + err = rwd.addAcceleratorRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) require.Nil(t, err) found := false @@ -898,7 +883,7 @@ func TestBaseRewardsCreator_createProtocolSustainabilityRewardTransaction(t *tes DevFeesInEpoch: big.NewInt(0), } - rwTx, _, err := rwd.createProtocolSustainabilityRewardTransaction(metaBlk.GetEpoch(), metaBlk.GetRound(), &metaBlk.EpochStart.Economics) + rwTx, _, err := rwd.createProtocolSustainabilityRewardTransaction(metaBlk.GetEpoch(), metaBlk.GetRound(), metaBlk.EpochStart.Economics.GetRewardsForProtocolSustainability()) require.Nil(t, err) require.NotNil(t, rwTx) require.Equal(t, metaBlk.EpochStart.Economics.RewardsForProtocolSustainability, rwTx.Value) diff --git a/epochStart/metachain/economics.go b/epochStart/metachain/economics.go index 88cd37650d9..b80d969ba1a 100644 --- a/epochStart/metachain/economics.go +++ b/epochStart/metachain/economics.go @@ -153,6 +153,8 @@ func (e *economics) createLegacyEconomicsArgs(metaBlock data.MetaHeaderHandler) return nil, epochStart.ErrNotEpochStartBlock } + e.economicsDataNotified.Clean() + noncesPerShardPrevEpoch, prevEpochStart, err := e.startNoncePerShardFromEpochStart(metaBlock.GetEpoch() - 1) if err != nil { return nil, err @@ -179,8 +181,18 @@ func (e *economics) baseComputeEconomics(args *argsComputeEconomics) (*block.Eco maxBlocksInEpoch := core.MaxUint64(1, roundsPassedInEpoch*uint64(e.shardCoordinator.NumberOfShards()+1)) totalNumBlocksInEpoch := e.computeNumOfTotalCreatedBlocks(args.lastNoncesPerShardPrevEpoch, args.lastNoncesPerShardCurrEpoch) + supplyToUseForRewardsPerBlock := e.genesisTotalSupply + if e.rewardsHandler.IsTailInflationEnabled(args.computationData.newEpoch) { + supplyToUseForRewardsPerBlock = args.prevEpochStart.GetEpochStartHandler().GetEconomicsHandler().GetTotalSupply() + } + inflationRate := e.computeInflationRate(&args.computationData) - rwdPerBlock := e.computeRewardsPerBlock(e.genesisTotalSupply, maxBlocksInEpoch, inflationRate, args.computationData.newEpoch) + rwdPerBlock := e.computeRewardsPerBlock( + supplyToUseForRewardsPerBlock, + maxBlocksInEpoch, + inflationRate, + args.computationData.newEpoch, + ) totalRewardsToBeDistributed := big.NewInt(0).Mul(rwdPerBlock, big.NewInt(0).SetUint64(totalNumBlocksInEpoch)) newTokens := big.NewInt(0).Sub(totalRewardsToBeDistributed, args.computationData.accumulatedFeesInEpoch) @@ -194,10 +206,14 @@ func (e *economics) baseComputeEconomics(args *argsComputeEconomics) (*block.Eco e.adjustRewardsPerBlockWithDeveloperFees(rwdPerBlock, args.computationData.devFeesInEpoch, totalNumBlocksInEpoch) rewardsForLeaders := e.adjustRewardsPerBlockWithLeaderPercentage(rwdPerBlock, args.computationData.accumulatedFeesInEpoch, args.computationData.devFeesInEpoch, totalNumBlocksInEpoch, args.computationData.newEpoch) remainingToBeDistributed = big.NewInt(0).Sub(remainingToBeDistributed, rewardsForLeaders) - rewardsForProtocolSustainability := e.computeRewardsForProtocolSustainability(totalRewardsToBeDistributed, args.computationData.newEpoch) - remainingToBeDistributed = big.NewInt(0).Sub(remainingToBeDistributed, rewardsForProtocolSustainability) + rewardsForAccelerator, err := e.computeRewardsForAccelerator(totalRewardsToBeDistributed, args.computationData.GetEpoch()) + if err != nil { + return nil, err + } + + remainingToBeDistributed = big.NewInt(0).Sub(remainingToBeDistributed, rewardsForAccelerator) // adjust rewards per block taking into consideration protocol sustainability rewards - e.adjustRewardsPerBlockWithProtocolSustainabilityRewards(rwdPerBlock, rewardsForProtocolSustainability, totalNumBlocksInEpoch) + e.adjustRewardsPerBlockWithAcceleratorRewards(rwdPerBlock, rewardsForAccelerator, totalNumBlocksInEpoch) if big.NewInt(0).Cmp(totalRewardsToBeDistributed) > 0 { totalRewardsToBeDistributed = big.NewInt(0) @@ -219,7 +235,7 @@ func (e *economics) baseComputeEconomics(args *argsComputeEconomics) (*block.Eco TotalToDistribute: big.NewInt(0).Set(totalRewardsToBeDistributed), TotalNewlyMinted: big.NewInt(0).Set(newTokens), RewardsPerBlock: rwdPerBlock, - RewardsForProtocolSustainability: rewardsForProtocolSustainability, + RewardsForProtocolSustainability: rewardsForAccelerator, NodePrice: big.NewInt(0).Set(prevEpochEconomics.GetNodePrice()), PrevEpochStartRound: args.prevEpochStart.GetRound(), PrevEpochStartHash: prevEpochStartHash, @@ -234,7 +250,7 @@ func (e *economics) baseComputeEconomics(args *argsComputeEconomics) (*block.Eco totalRewardsToBeDistributed, totalNumBlocksInEpoch, rwdPerBlock, - rewardsForProtocolSustainability, + rewardsForAccelerator, ) maxPossibleNotarizedBlocks := e.maxPossibleNotarizedBlocks(args.computationData.round, args.prevEpochStart) @@ -325,7 +341,7 @@ func (e *economics) printEconomicsData( totalRewardsToBeDistributed *big.Int, totalNumBlocksInEpoch uint64, rwdPerBlock *big.Int, - rewardsForProtocolSustainability *big.Int, + rewardsForAccelerator *big.Int, ) { header := []string{"identifier", "", "value"} @@ -363,7 +379,7 @@ func (e *economics) printEconomicsData( e.newDisplayLine("percent for protocol sustainability", "(9)", e.alignRight(fmt.Sprintf("%.6f", e.rewardsHandler.ProtocolSustainabilityPercentageInEpoch(computationData.newEpoch)), maxSupplyLength)), e.newDisplayLine("reward for protocol sustainability", "(4 * 9)", - e.alignRight(rewardsForProtocolSustainability.String(), maxSupplyLength)), + e.alignRight(rewardsForAccelerator.String(), maxSupplyLength)), } str, err := display.CreateTableString(header, lines) @@ -389,21 +405,57 @@ func (e *economics) newDisplayLine(values ...string) *display.LineData { // compute the rewards for protocol sustainability - percentage from total rewards func (e *economics) computeRewardsForProtocolSustainability(totalRewards *big.Int, epoch uint32) *big.Int { + var protocolSustainability *big.Int if epoch > e.stakingV2EnableEpoch { - return core.GetIntTrimmedPercentageOfValue(totalRewards, e.rewardsHandler.ProtocolSustainabilityPercentageInEpoch(epoch)) + protocolSustainability = core.GetIntTrimmedPercentageOfValue(totalRewards, e.rewardsHandler.ProtocolSustainabilityPercentageInEpoch(epoch)) + } else { + protocolSustainability = core.GetApproximatePercentageOfValue(totalRewards, e.rewardsHandler.ProtocolSustainabilityPercentageInEpoch(epoch)) + } + + e.economicsDataNotified.SetRewardsForProtocolSustainability(protocolSustainability) + return protocolSustainability +} + +func (e *economics) computeRewardsForAccelerator(totalRewards *big.Int, epoch uint32) (*big.Int, error) { + if !e.rewardsHandler.IsTailInflationEnabled(epoch) { + return e.computeRewardsForProtocolSustainability(totalRewards, epoch), nil } - return core.GetApproximatePercentageOfValue(totalRewards, e.rewardsHandler.ProtocolSustainabilityPercentageInEpoch(epoch)) + protocolSustainability := core.GetIntTrimmedPercentageOfValue(totalRewards, e.rewardsHandler.ProtocolSustainabilityPercentageInEpoch(epoch)) + ecosystemGrowth := core.GetIntTrimmedPercentageOfValue(totalRewards, e.rewardsHandler.EcosystemGrowthPercentageInEpoch(epoch)) + growthDividend := core.GetIntTrimmedPercentageOfValue(totalRewards, e.rewardsHandler.GrowthDividendPercentageInEpoch(epoch)) + + e.economicsDataNotified.SetRewardsForProtocolSustainability(protocolSustainability) + e.economicsDataNotified.SetRewardsForEcosystemGrowth(ecosystemGrowth) + e.economicsDataNotified.SetRewardsForGrowthDividend(growthDividend) + + acceleratorRewards := big.NewInt(0).Add(protocolSustainability, ecosystemGrowth) + acceleratorRewards = big.NewInt(0).Add(acceleratorRewards, growthDividend) + + if protocolSustainability.Cmp(zero) < 0 || + ecosystemGrowth.Cmp(zero) < 0 || + growthDividend.Cmp(zero) < 0 { + return nil, errNegativeAcceleratorReward + } + + if acceleratorRewards.Cmp(totalRewards) > 0 { + return nil, errAcceleratorRewardsMoreThanTotalRewards + } + + return acceleratorRewards, nil } // adjustment for rewards given for each proposed block taking protocol sustainability rewards into consideration -func (e *economics) adjustRewardsPerBlockWithProtocolSustainabilityRewards( +func (e *economics) adjustRewardsPerBlockWithAcceleratorRewards( rwdPerBlock *big.Int, - protocolSustainabilityRewards *big.Int, + acceleratorRewards *big.Int, blocksInEpoch uint64, ) { - protocolSustainabilityRewardsPerBlock := big.NewInt(0).Div(protocolSustainabilityRewards, big.NewInt(0).SetUint64(blocksInEpoch)) - rwdPerBlock.Sub(rwdPerBlock, protocolSustainabilityRewardsPerBlock) + if blocksInEpoch == 0 { + return + } + acceleratorRewardsPerBlock := big.NewInt(0).Div(acceleratorRewards, big.NewInt(0).SetUint64(blocksInEpoch)) + rwdPerBlock.Sub(rwdPerBlock, acceleratorRewardsPerBlock) } // adjustment for rewards given for each proposed block taking developer fees into consideration @@ -440,12 +492,11 @@ func (e *economics) adjustRewardsPerBlockWithLeaderPercentage( // compute inflation rate from genesisTotalSupply and economics settings for that year func (e *economics) computeInflationBeforeSupernova(currentRound uint64, epoch uint32) float64 { - roundsPerDay := common.ComputeRoundsPerDay(e.roundTime.TimeDuration(), e.enableEpochsHandler, epoch) - + roundsPerDay := numberOfSecondsInDay / uint64(e.roundTime.TimeDuration().Seconds()) roundsPerYear := numberOfDaysInYear * roundsPerDay yearsIndex := uint32(currentRound/roundsPerYear) + 1 - return e.rewardsHandler.MaxInflationRate(yearsIndex) + return e.rewardsHandler.MaxInflationRate(yearsIndex, epoch) } func (e *economics) computeInflationRate( @@ -455,14 +506,14 @@ func (e *economics) computeInflationRate( supernovaInEpochActivated := e.enableEpochsHandler.IsFlagEnabledInEpoch(common.SupernovaFlag, prevEpoch) if !supernovaInEpochActivated { - return e.computeInflationBeforeSupernova(computationData.GetRound(), prevEpoch) + return e.computeInflationBeforeSupernova(computationData.GetRound(), computationData.GetEpoch()) } - return e.computeInflationRateAfterSupernova(computationData.GetTimeStamp()) + return e.computeInflationRateAfterSupernova(computationData.GetTimeStamp(), computationData.GetEpoch()) } // currentTimestamp is defined as unix milliseconds after supernova is activated -func (e *economics) computeInflationRateAfterSupernova(currentTimestampMs uint64) float64 { +func (e *economics) computeInflationRateAfterSupernova(currentTimestampMs uint64, epoch uint32) float64 { // genesisTimestamp has to be converted as unix milliseconds genesisTimestamp := common.ConvertTimeStampSecToMs(e.genesisTimestamp) @@ -476,7 +527,7 @@ func (e *economics) computeInflationRateAfterSupernova(currentTimestampMs uint64 } yearsIndex := (currentTimestampMs-genesisTimestamp)/numberOfMillisecondsInYear + 1 - return e.rewardsHandler.MaxInflationRate(uint32(yearsIndex)) + return e.rewardsHandler.MaxInflationRate(uint32(yearsIndex), epoch) } func (e *economics) getPreviousEpoch(epoch uint32) uint32 { @@ -511,7 +562,11 @@ func (e *economics) computeInflationForEpoch( epoch uint32, ) float64 { prevEpoch := e.getPreviousEpoch(epoch) - chainParameters, _ := e.chainParamsHandler.ChainParametersForEpoch(prevEpoch) + chainParameters, err := e.chainParamsHandler.ChainParametersForEpoch(prevEpoch) + if err != nil { + log.Warn("could not get rounds per epoch for epoch, returned current chain paramters", "prevEpoch", prevEpoch, "error", err) + chainParameters = e.chainParamsHandler.CurrentChainParameters() + } roundDuration := time.Duration(chainParameters.RoundDuration) * time.Millisecond inflationRatePerDay := inflationRate / numberOfDaysInYear @@ -638,7 +693,7 @@ func (e *economics) checkEconomicsInvariants( return nil } - maxAllowedInflation := e.rewardsHandler.MaxInflationRate(1) + maxAllowedInflation := e.rewardsHandler.MaxInflationRate(1, epoch) if !core.IsInRangeInclusiveFloat64(inflationRate, 0, maxAllowedInflation) { return fmt.Errorf("%w, computed inflation %s, max allowed %s", epochStart.ErrInvalidInflationRate, diff --git a/epochStart/metachain/economicsDataProvider.go b/epochStart/metachain/economicsDataProvider.go index ec165ffe80a..957e0610997 100644 --- a/epochStart/metachain/economicsDataProvider.go +++ b/epochStart/metachain/economicsDataProvider.go @@ -6,24 +6,41 @@ import ( ) type epochEconomicsStatistics struct { - numberOfBlocks uint64 - numberOfBlocksPerShard map[uint32]uint64 - leaderFees *big.Int - rewardsToBeDistributed *big.Int - rewardsToBeDistributedForBlocks *big.Int // without leader fees, protocol sustainability and developer fees - mutEconomicsStatistics sync.RWMutex + numberOfBlocks uint64 + numberOfBlocksPerShard map[uint32]uint64 + leaderFees *big.Int + rewardsToBeDistributed *big.Int + rewardsToBeDistributedForBlocks *big.Int // without leader fees, protocol sustainability and developer fees + rewardsForProtocolSustainability *big.Int + rewardsForEcosystemGrowth *big.Int + rewardsForGrowthDividend *big.Int + mutEconomicsStatistics sync.RWMutex } // NewEpochEconomicsStatistics creates the end of epoch economics statistics func NewEpochEconomicsStatistics() *epochEconomicsStatistics { return &epochEconomicsStatistics{ - numberOfBlocksPerShard: make(map[uint32]uint64), - leaderFees: big.NewInt(0), - rewardsToBeDistributed: big.NewInt(0), - rewardsToBeDistributedForBlocks: big.NewInt(0), + numberOfBlocksPerShard: make(map[uint32]uint64), + leaderFees: big.NewInt(0), + rewardsToBeDistributed: big.NewInt(0), + rewardsToBeDistributedForBlocks: big.NewInt(0), + rewardsForProtocolSustainability: big.NewInt(0), + rewardsForEcosystemGrowth: big.NewInt(0), + rewardsForGrowthDividend: big.NewInt(0), } } +// Clean clears the previous data +func (es *epochEconomicsStatistics) Clean() { + es.numberOfBlocksPerShard = make(map[uint32]uint64) + es.leaderFees = big.NewInt(0) + es.rewardsToBeDistributed = big.NewInt(0) + es.rewardsToBeDistributedForBlocks = big.NewInt(0) + es.rewardsForProtocolSustainability = big.NewInt(0) + es.rewardsForEcosystemGrowth = big.NewInt(0) + es.rewardsForGrowthDividend = big.NewInt(0) +} + // SetNumberOfBlocks sets the number of blocks produced in the epoch func (es *epochEconomicsStatistics) SetNumberOfBlocks(nbBlocks uint64) { es.mutEconomicsStatistics.Lock() @@ -115,6 +132,63 @@ func (es *epochEconomicsStatistics) RewardsToBeDistributedForBlocks() *big.Int { return big.NewInt(0).Set(es.rewardsToBeDistributedForBlocks) } +// SetRewardsForProtocolSustainability sets the rewards for protocol sustainability +func (es *epochEconomicsStatistics) SetRewardsForProtocolSustainability(rewards *big.Int) { + es.mutEconomicsStatistics.Lock() + defer es.mutEconomicsStatistics.Unlock() + + es.rewardsForProtocolSustainability = big.NewInt(0).Set(rewards) +} + +// SetRewardsForEcosystemGrowth sets the rewards for ecosystem growth +func (es *epochEconomicsStatistics) SetRewardsForEcosystemGrowth(rewards *big.Int) { + es.mutEconomicsStatistics.Lock() + defer es.mutEconomicsStatistics.Unlock() + + es.rewardsForEcosystemGrowth = big.NewInt(0).Set(rewards) +} + +// SetRewardsForGrowthDividend sets the rewards for growth dividend +func (es *epochEconomicsStatistics) SetRewardsForGrowthDividend(rewards *big.Int) { + es.mutEconomicsStatistics.Lock() + defer es.mutEconomicsStatistics.Unlock() + + es.rewardsForGrowthDividend = big.NewInt(0).Set(rewards) +} + +// RewardsForProtocolSustainability returns the rewards for protocol sustainability +func (es *epochEconomicsStatistics) RewardsForProtocolSustainability() *big.Int { + es.mutEconomicsStatistics.RLock() + defer es.mutEconomicsStatistics.RUnlock() + + return big.NewInt(0).Set(es.rewardsForProtocolSustainability) +} + +// RewardsForEcosystemGrowth returns the rewards for ecosystem growth +func (es *epochEconomicsStatistics) RewardsForEcosystemGrowth() *big.Int { + es.mutEconomicsStatistics.RLock() + defer es.mutEconomicsStatistics.RUnlock() + + return big.NewInt(0).Set(es.rewardsForEcosystemGrowth) +} + +// RewardsForGrowthDividend returns the rewards for growth dividend +func (es *epochEconomicsStatistics) RewardsForGrowthDividend() *big.Int { + es.mutEconomicsStatistics.RLock() + defer es.mutEconomicsStatistics.RUnlock() + + return big.NewInt(0).Set(es.rewardsForGrowthDividend) +} + +func (es *epochEconomicsStatistics) RewardsForAccelerator() *big.Int { + es.mutEconomicsStatistics.RLock() + defer es.mutEconomicsStatistics.RUnlock() + + accumulatedValue := big.NewInt(0).Add(es.rewardsForGrowthDividend, es.rewardsForEcosystemGrowth) + accumulatedValue = big.NewInt(0).Add(accumulatedValue, es.rewardsForProtocolSustainability) + return accumulatedValue +} + // IsInterfaceNil returns nil if the underlying object is nil func (es *epochEconomicsStatistics) IsInterfaceNil() bool { return es == nil diff --git a/epochStart/metachain/economics_test.go b/epochStart/metachain/economics_test.go index 075abb4c289..3e8242c0f0a 100644 --- a/epochStart/metachain/economics_test.go +++ b/epochStart/metachain/economics_test.go @@ -296,7 +296,7 @@ func TestEconomics_AdjustRewardsPerBlockWithProtocolSustainabilityRewards(t *tes expectedRewardsProtocolSustainabilityAfterAdjustment := big.NewInt(0).Set(protocolSustainabilityRewards) expectedRwdPerBlock := big.NewInt(900) - ec.adjustRewardsPerBlockWithProtocolSustainabilityRewards(rwdPerBlock, protocolSustainabilityRewards, blocksInEpoch) + ec.adjustRewardsPerBlockWithAcceleratorRewards(rwdPerBlock, protocolSustainabilityRewards, blocksInEpoch) assert.Equal(t, expectedRewardsProtocolSustainabilityAfterAdjustment, protocolSustainabilityRewards) assert.Equal(t, expectedRwdPerBlock, rwdPerBlock) @@ -413,7 +413,7 @@ func TestEconomics_ComputeInflationRate(t *testing.T) { lateYearInflation := 2.0 args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(year uint32) float64 { + MaxInflationRateCalled: func(year uint32, _ uint32) float64 { switch year { case 0: errFound = errNotGoodYear @@ -511,7 +511,7 @@ func TestEconomics_ComputeInflationRate(t *testing.T) { lateYearInflation := 2.0 args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(year uint32) float64 { + MaxInflationRateCalled: func(year uint32, _ uint32) float64 { switch year { case 0: errFound = errNotGoodYear @@ -622,7 +622,7 @@ func TestEconomics_ComputeInflationRate(t *testing.T) { lateYearInflation := 2.0 args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(year uint32) float64 { + MaxInflationRateCalled: func(year uint32, _ uint32) float64 { switch year { case 0: errFound = errNotGoodYear @@ -703,10 +703,12 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { return flag == common.GasPriceModifierFlag }, }, - TxVersionChecker: &testscommon.TxVersionCheckerStub{}, - PubkeyConverter: &testscommon.PubkeyConverterStub{}, - ShardCoordinator: &testscommon.ShardsCoordinatorMock{}, + TxVersionChecker: &testscommon.TxVersionCheckerStub{}, + PubkeyConverter: &testscommon.PubkeyConverterStub{}, + ShardCoordinator: &testscommon.ShardsCoordinatorMock{}, + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, } + argsNewEconomicsData.Economics.GlobalSettings.TailInflation.EnableEpoch = 999999 economicsData, _ := processEconomics.NewEconomicsData(argsNewEconomicsData) args := getArguments() @@ -715,7 +717,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { roundsPerDay := uint64(14400) roundsPerYear := uint64(epochsPerYear) * roundsPerDay - supernovaActivationEpoch := uint32(epochsPerYear*5 + 10) + supernovaActivationEpoch := epochsPerYear*5 + 10 supernovaActivationRound := roundsPerYear*5 + 10*roundsPerDay + 50 args.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ @@ -751,7 +753,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { } rate := ec.computeInflationRate(header) assert.Equal(t, cfg.EconomicsConfig.GlobalSettings.YearSettings[0].MaximumInflation, rate) - assert.Equal(t, economicsData.MaxInflationRate(1), rate) + assert.Equal(t, economicsData.MaxInflationRate(1, 1), rate) header = &block.MetaBlock{ Round: roundsPerDay + 1, @@ -760,7 +762,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { } rate = ec.computeInflationRate(header) assert.Equal(t, cfg.EconomicsConfig.GlobalSettings.YearSettings[0].MaximumInflation, rate) - assert.Equal(t, economicsData.MaxInflationRate(1), rate) + assert.Equal(t, economicsData.MaxInflationRate(1, 1), rate) header = &block.MetaBlock{ Round: roundsPerYear + 100, @@ -769,7 +771,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { } rate = ec.computeInflationRate(header) assert.Equal(t, cfg.EconomicsConfig.GlobalSettings.YearSettings[1].MaximumInflation, rate) - assert.Equal(t, economicsData.MaxInflationRate(2), rate) + assert.Equal(t, economicsData.MaxInflationRate(2, 1), rate) ec.SetRoundTimeHandler( &mock.RoundTimeDurationHandler{ @@ -788,7 +790,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + roundDurationBeforeSupernova*(supernovaActivationRound), } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(6), rate) + assert.Equal(t, economicsData.MaxInflationRate(6, 1), rate) // first time slot in year 7 header = &block.MetaBlock{ @@ -797,7 +799,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*6, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(7), rate) + assert.Equal(t, economicsData.MaxInflationRate(7, 1), rate) // last time slot in year 7 header = &block.MetaBlock{ @@ -806,7 +808,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*6 + roundDurationAfterSupernova, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(7), rate) + assert.Equal(t, economicsData.MaxInflationRate(7, 1), rate) // first time slot in year 8 header = &block.MetaBlock{ @@ -815,7 +817,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*7, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(8), rate) + assert.Equal(t, economicsData.MaxInflationRate(8, 1), rate) // last time slot in year 8 header = &block.MetaBlock{ @@ -824,7 +826,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*7 + roundDurationAfterSupernova, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(8), rate) + assert.Equal(t, economicsData.MaxInflationRate(8, 1), rate) // first time slot in year 9 header = &block.MetaBlock{ @@ -833,7 +835,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*8, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(9), rate) + assert.Equal(t, economicsData.MaxInflationRate(9, 1), rate) // last time slot in year 9 header = &block.MetaBlock{ @@ -842,7 +844,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*8 + roundDurationAfterSupernova, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(9), rate) + assert.Equal(t, economicsData.MaxInflationRate(9, 1), rate) // first time slot in year 10 header = &block.MetaBlock{ @@ -851,7 +853,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*9, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(10), rate) + assert.Equal(t, economicsData.MaxInflationRate(10, 1), rate) // last time slot in year 10 header = &block.MetaBlock{ @@ -860,7 +862,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*9 + roundDurationAfterSupernova, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(10), rate) + assert.Equal(t, economicsData.MaxInflationRate(10, 1), rate) // first time slot in year 11 header = &block.MetaBlock{ @@ -869,7 +871,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*10, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(11), rate) + assert.Equal(t, economicsData.MaxInflationRate(11, 1), rate) // first time slot in year 11 header = &block.MetaBlock{ @@ -878,7 +880,7 @@ func TestEconomics_ComputeInflationRate_WithRealConfigData(t *testing.T) { TimeStamp: genesisTimestamp + numberOfMillisecondsInYear*11, } rate = ec.computeInflationRate(header) - assert.Equal(t, economicsData.MaxInflationRate(11), rate) + assert.Equal(t, economicsData.MaxInflationRate(11, 1), rate) } func TestEconomics_ComputeEndOfEpochEconomics(t *testing.T) { @@ -1037,10 +1039,11 @@ func TestEconomics_VerifyRewardsPerBlock_DifferentHitRates(t *testing.T) { accFeesInEpoch := big.NewInt(0) devFeesInEpoch := big.NewInt(0) roundDur := 4 + accRewardsEnableEpoch := uint32(9999999) args := getArguments() args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(_ uint32) float64 { + MaxInflationRateCalled: func(_ uint32, _ uint32) float64 { return 0.1 }, ProtocolSustainabilityAddressInEpochCalled: func(epoch uint32) string { @@ -1049,6 +1052,9 @@ func TestEconomics_VerifyRewardsPerBlock_DifferentHitRates(t *testing.T) { ProtocolSustainabilityPercentageInEpochCalled: func(epoch uint32) float64 { return 0.1 }, + IsTailInflationEnabledCalled: func(epoch uint32) bool { + return epoch >= accRewardsEnableEpoch + }, } args.RoundTime = &mock.RoundTimeDurationHandler{ TimeDurationCalled: func() time.Duration { @@ -1151,6 +1157,7 @@ func TestEconomics_VerifyRewardsPerBlock_DifferentHitRates(t *testing.T) { totalSupply := big.NewInt(20000000000) // 20B accFeesInEpoch := big.NewInt(0) devFeesInEpoch := big.NewInt(0) + accRewardsEnableEpoch := uint32(9999999) roundDur := 4000 args := getArguments() @@ -1169,7 +1176,7 @@ func TestEconomics_VerifyRewardsPerBlock_DifferentHitRates(t *testing.T) { } args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(_ uint32) float64 { + MaxInflationRateCalled: func(_ uint32, _ uint32) float64 { return 0.1 }, ProtocolSustainabilityAddressInEpochCalled: func(epoch uint32) string { @@ -1178,6 +1185,9 @@ func TestEconomics_VerifyRewardsPerBlock_DifferentHitRates(t *testing.T) { ProtocolSustainabilityPercentageInEpochCalled: func(epoch uint32) float64 { return 0.1 }, + IsTailInflationEnabledCalled: func(epoch uint32) bool { + return epoch >= accRewardsEnableEpoch + }, } args.RoundTime = &mock.RoundTimeDurationHandler{ TimeDurationCalled: func() time.Duration { @@ -1284,7 +1294,7 @@ func TestEconomics_VerifyRewardsPerBlock_DifferentFees(t *testing.T) { args := getArguments() args.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(_ uint32) float64 { + MaxInflationRateCalled: func(_ uint32, _ uint32) float64 { return 0.1 }, ProtocolSustainabilityAddressInEpochCalled: func(epoch uint32) string { @@ -1510,7 +1520,7 @@ func TestEconomics_VerifyRewardsPerBlock_MoreFeesThanInflation(t *testing.T) { args := getArguments() args.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(_ uint32) float64 { + MaxInflationRateCalled: func(_ uint32, _ uint32) float64 { return 0.1 }, ProtocolSustainabilityAddressInEpochCalled: func(epoch uint32) string { @@ -1716,7 +1726,7 @@ func TestEconomics_VerifyRewardsPerBlock_InflationZero(t *testing.T) { args := getArguments() args.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(_ uint32) float64 { + MaxInflationRateCalled: func(_ uint32, _ uint32) float64 { return 0.0 }, ProtocolSustainabilityAddressInEpochCalled: func(epoch uint32) string { @@ -2626,7 +2636,7 @@ func createArgsForComputeEndOfEpochEconomics( args := getArguments() args.StakingV2EnableEpoch = stakingV2EnableEpoch args.RewardsHandler = &mock.RewardsHandlerStub{ - MaxInflationRateCalled: func(_ uint32) float64 { + MaxInflationRateCalled: func(_ uint32, _ uint32) float64 { return 0.1 }, ProtocolSustainabilityAddressInEpochCalled: func(_ uint32) string { @@ -2735,6 +2745,282 @@ func verifyEconomicsBlock( assert.Equal(t, adjustedRewardsPerBlock, economicsBlock.RewardsPerBlock) } +func TestEconomics_ComputeRewardsForAccelerator(t *testing.T) { + t.Parallel() + + totalRewards := big.NewInt(10000) + protocolSustainabilityPercentage := 0.1 + ecosystemGrowthPercentage := 0.2 + growthDividendPercentage := 0.3 + accRewardsEnableEpoch := uint32(10) + + args := getArguments() + args.RewardsHandler = &mock.RewardsHandlerStub{ + ProtocolSustainabilityPercentageInEpochCalled: func(epoch uint32) float64 { + return protocolSustainabilityPercentage + }, + EcosystemGrowthPercentageInEpochCalled: func(epoch uint32) float64 { + return ecosystemGrowthPercentage + }, + GrowthDividendPercentageInEpochCalled: func(epoch uint32) float64 { + return growthDividendPercentage + }, + IsTailInflationEnabledCalled: func(epoch uint32) bool { + return epoch >= accRewardsEnableEpoch + }, + } + + ec, _ := NewEndOfEpochEconomicsDataCreator(args) + + // Before accRewardsEnableEpoch + rewards, _ := ec.computeRewardsForAccelerator(totalRewards, accRewardsEnableEpoch-1) + expectedRewards := big.NewInt(1000) // 10000 * 0.1 + assert.Equal(t, expectedRewards, rewards) + + // At accRewardsEnableEpoch + rewards, _ = ec.computeRewardsForAccelerator(totalRewards, accRewardsEnableEpoch) + expectedRewards = big.NewInt(6000) // 10000 * (0.1 + 0.2 + 0.3) + assert.Equal(t, expectedRewards, rewards) + + // After accRewardsEnableEpoch + rewards, _ = ec.computeRewardsForAccelerator(totalRewards, accRewardsEnableEpoch+1) + expectedRewards = big.NewInt(6000) // 10000 * (0.1 + 0.2 + 0.3) + assert.Equal(t, expectedRewards, rewards) +} + +func TestEconomics_LogEconomicsDifferences(t *testing.T) { + t.Parallel() + + logEconomicsDifferences(&block.Economics{}, &block.Economics{}) +} + +func TestEconomics_VerifyRewardsPerBlockError(t *testing.T) { + t.Parallel() + + args := getArguments() + ec, _ := NewEndOfEpochEconomicsDataCreator(args) + + err := ec.VerifyRewardsPerBlock( + &block.MetaBlock{Epoch: 1, EpochStart: block.EpochStart{ + Economics: block.Economics{TotalSupply: big.NewInt(1), RewardsForProtocolSustainability: big.NewInt(0)}, + LastFinalizedHeaders: []block.EpochStartShardData{ + {ShardID: 0, Round: 2, Nonce: 3}, + {ShardID: 1, Round: 2, Nonce: 3}, + }, + }, + }, + big.NewInt(0), + &block.Economics{TotalSupply: big.NewInt(0), RewardsForProtocolSustainability: big.NewInt(0)}, + ) + assert.NotNil(t, err) +} + +func TestEconomics_ComputeEndOfEpochEconomicsWithTailInflation(t *testing.T) { + t.Parallel() + + mbPrevStartEpoch := block.MetaBlock{ + Round: 10, + Nonce: 5, + EpochStart: block.EpochStart{ + Economics: block.Economics{ + TotalSupply: big.NewInt(100000), + TotalToDistribute: big.NewInt(10), + TotalNewlyMinted: big.NewInt(109), + RewardsPerBlock: big.NewInt(10), + NodePrice: big.NewInt(10), + }, + }, + } + + leaderPercentage := 0.1 + args := getArguments() + args.RewardsHandler = &mock.RewardsHandlerStub{ + LeaderPercentageInEpochCalled: func(epoch uint32) float64 { + return leaderPercentage + }, + } + args.Store = &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + return &storageStubs.StorerStub{GetCalled: func(key []byte) ([]byte, error) { + hdr := mbPrevStartEpoch + hdrBytes, _ := json.Marshal(hdr) + return hdrBytes, nil + }}, nil + }, + } + ec, _ := NewEndOfEpochEconomicsDataCreator(args) + + mb := block.MetaBlock{ + Round: 15000, + EpochStart: block.EpochStart{ + LastFinalizedHeaders: []block.EpochStartShardData{ + {ShardID: 0, Round: 2, Nonce: 3}, + {ShardID: 1, Round: 2, Nonce: 3}, + }, + Economics: block.Economics{}, + }, + Epoch: 2, + AccumulatedFeesInEpoch: big.NewInt(10000), + DevFeesInEpoch: big.NewInt(0), + } + + res, err := ec.ComputeEndOfEpochEconomics(&mb) + assert.Nil(t, err) + assert.NotNil(t, res) + + var expectedLeaderFees *big.Int + if mb.Epoch > args.StakingV2EnableEpoch { + expectedLeaderFees = core.GetIntTrimmedPercentageOfValue(mb.AccumulatedFeesInEpoch, leaderPercentage) + } else { + expectedLeaderFees = core.GetApproximatePercentageOfValue(mb.AccumulatedFeesInEpoch, leaderPercentage) + } + + assert.Equal(t, expectedLeaderFees, ec.economicsDataNotified.LeaderFees(), expectedLeaderFees) +} + +func TestEconomics_ComputeEndOfEpochEconomicsWithPrevEpochTotalSupply(t *testing.T) { + t.Parallel() + + totalSupply := big.NewInt(0) + totalSupply.SetString("400000000000", 10) + + mbPrevStartEpoch := block.MetaBlock{ + Round: 10, + Nonce: 5, + EpochStart: block.EpochStart{ + Economics: block.Economics{ + TotalSupply: totalSupply, + NodePrice: big.NewInt(100), + }, + }, + } + + args := getArguments() + args.Store = &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + return &storageStubs.StorerStub{GetCalled: func(key []byte) ([]byte, error) { + hdr := mbPrevStartEpoch + hdrBytes, _ := json.Marshal(hdr) + return hdrBytes, nil + }}, nil + }, + } + ec, _ := NewEndOfEpochEconomicsDataCreator(args) + + mb := block.MetaBlock{ + Round: 15000, + EpochStart: block.EpochStart{ + LastFinalizedHeaders: []block.EpochStartShardData{ + {ShardID: 0, Round: 2, Nonce: 3}, + {ShardID: 1, Round: 2, Nonce: 3}, + }, + }, + Epoch: 2, + AccumulatedFeesInEpoch: big.NewInt(10000), + DevFeesInEpoch: big.NewInt(0), + } + + res, err := ec.ComputeEndOfEpochEconomics(&mb) + require.Nil(t, err) + require.NotNil(t, res) + + // re-calculate expected values for verification + inflationRate := ec.computeInflationRate(&mb) + roundsPassedInEpoch := mb.GetRound() - mbPrevStartEpoch.GetRound() + maxBlocksInEpoch := core.MaxUint64(1, roundsPassedInEpoch*uint64(args.ShardCoordinator.NumberOfShards()+1)) + + noncesPerShardPrevEpoch, _, _ := ec.startNoncePerShardFromEpochStart(mb.Epoch - 1) + noncesPerShardCurrEpoch := ec.startNoncePerShardFromLastCrossNotarized(mb.GetNonce(), &mb.EpochStart) + totalNumBlocksInEpoch := ec.computeNumOfTotalCreatedBlocks(noncesPerShardPrevEpoch, noncesPerShardCurrEpoch) + + rwdPerBlock := ec.computeRewardsPerBlock( + mbPrevStartEpoch.EpochStart.Economics.TotalSupply, + maxBlocksInEpoch, + inflationRate, + mb.Epoch, + ) + totalRewardsToBeDistributed := big.NewInt(0).Mul(rwdPerBlock, big.NewInt(0).SetUint64(totalNumBlocksInEpoch)) + newTokens := big.NewInt(0).Sub(totalRewardsToBeDistributed, mb.AccumulatedFeesInEpoch) + if newTokens.Cmp(big.NewInt(0)) < 0 { + newTokens = big.NewInt(0) + } + + expectedTotalSupply := big.NewInt(0).Add(mbPrevStartEpoch.EpochStart.Economics.TotalSupply, newTokens) + assert.Equal(t, expectedTotalSupply, res.TotalSupply) +} + +func TestEconomics_TotalSupplyCalculation(t *testing.T) { + t.Parallel() + + mbPrevStartEpoch := block.MetaBlock{ + Round: 10, + Nonce: 5, + EpochStart: block.EpochStart{ + Economics: block.Economics{ + TotalSupply: big.NewInt(100000), + NodePrice: big.NewInt(100), + }, + }, + } + + args := getArguments() + args.Store = &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + return &storageStubs.StorerStub{GetCalled: func(key []byte) ([]byte, error) { + hdr := mbPrevStartEpoch + hdrBytes, _ := json.Marshal(hdr) + return hdrBytes, nil + }}, nil + }, + } + + args.RewardsHandler = &mock.RewardsHandlerStub{IsTailInflationEnabledCalled: func(epoch uint32) bool { + return epoch >= 3 // future epoch + }} + ec, _ := NewEndOfEpochEconomicsDataCreator(args) + + mb := block.MetaBlock{ + Round: 15000, + EpochStart: block.EpochStart{ + LastFinalizedHeaders: []block.EpochStartShardData{ + {ShardID: 0, Round: 2, Nonce: 3}, + {ShardID: 1, Round: 2, Nonce: 3}, + }, + }, + Epoch: 2, + AccumulatedFeesInEpoch: big.NewInt(10000), + DevFeesInEpoch: big.NewInt(0), + } + + res, err := ec.ComputeEndOfEpochEconomics(&mb) + require.Nil(t, err) + require.NotNil(t, res) + + // re-calculate expected values for verification + inflationRate := ec.computeInflationRate(&mb) + roundsPassedInEpoch := mb.GetRound() - mbPrevStartEpoch.GetRound() + maxBlocksInEpoch := core.MaxUint64(1, roundsPassedInEpoch*uint64(args.ShardCoordinator.NumberOfShards()+1)) + + noncesPerShardPrevEpoch, _, _ := ec.startNoncePerShardFromEpochStart(mb.Epoch - 1) + noncesPerShardCurrEpoch := ec.startNoncePerShardFromLastCrossNotarized(mb.GetNonce(), &mb.EpochStart) + totalNumBlocksInEpoch := ec.computeNumOfTotalCreatedBlocks(noncesPerShardPrevEpoch, noncesPerShardCurrEpoch) + + rwdPerBlock := ec.computeRewardsPerBlock( + args.GenesisTotalSupply, + maxBlocksInEpoch, + inflationRate, + mb.Epoch, + ) + totalRewardsToBeDistributed := big.NewInt(0).Mul(rwdPerBlock, big.NewInt(0).SetUint64(totalNumBlocksInEpoch)) + newTokens := big.NewInt(0).Sub(totalRewardsToBeDistributed, mb.AccumulatedFeesInEpoch) + if newTokens.Cmp(big.NewInt(0)) < 0 { + newTokens = big.NewInt(0) + } + + expectedTotalSupply := big.NewInt(0).Add(mbPrevStartEpoch.EpochStart.Economics.TotalSupply, newTokens) + assert.Equal(t, expectedTotalSupply, res.TotalSupply) +} + func printEconomicsData(eb *block.Economics, hitRate float64, numBlocksTotal int64) { fmt.Printf("Hit rate per shard %.4f%%, Total block produced: %d \n", hitRate, numBlocksTotal) fmt.Printf("Total supply: %vEGLD, TotalToDistribute %vEGLD, "+ diff --git a/epochStart/metachain/errors.go b/epochStart/metachain/errors.go index 25963fd265d..6bbba9788d1 100644 --- a/epochStart/metachain/errors.go +++ b/epochStart/metachain/errors.go @@ -10,6 +10,10 @@ var errNilAuctionListDisplayHandler = errors.New("nil auction list display handl var errNilTableDisplayHandler = errors.New("nil table display handler provided") +var errNegativeAcceleratorReward = errors.New("negative accelerator reward") + +var errAcceleratorRewardsMoreThanTotalRewards = errors.New("accelerator rewards more than total rewards") + var errHashMismatch = errors.New("hash mismatch") var errMethodNotSupported = errors.New("method not supported") diff --git a/epochStart/metachain/rewards.go b/epochStart/metachain/rewards.go index 9db87c7ed8b..1828cbb234f 100644 --- a/epochStart/metachain/rewards.go +++ b/epochStart/metachain/rewards.go @@ -26,6 +26,7 @@ type ArgsNewRewardsCreator struct { type rewardsCreator struct { *baseRewardsCreator + protocolSustainabilityValue *big.Int } type rewardInfoData struct { @@ -42,12 +43,26 @@ func NewRewardsCreator(args ArgsNewRewardsCreator) (*rewardsCreator, error) { } rc := &rewardsCreator{ - baseRewardsCreator: brc, + baseRewardsCreator: brc, + protocolSustainabilityValue: big.NewInt(0), } return rc, nil } +func (rc *rewardsCreator) clean() { + rc.protocolSustainabilityValue = big.NewInt(0) + rc.baseRewardsCreator.clean() +} + +// GetAcceleratorRewards returns the sum of all rewards +func (rc *rewardsCreator) GetAcceleratorRewards() *big.Int { + rc.mutRewardsData.RLock() + defer rc.mutRewardsData.RUnlock() + + return rc.protocolSustainabilityValue +} + // CreateRewardsMiniBlocks creates the rewards miniblocks according to economics data and validator info func (rc *rewardsCreator) CreateRewardsMiniBlocks( metaBlock data.MetaHeaderHandler, @@ -73,7 +88,7 @@ func (rc *rewardsCreator) CreateRewardsMiniBlocks( miniBlocks := rc.initializeRewardsMiniBlocks() - protSustRwdTx, protSustShardId, err := rc.createProtocolSustainabilityRewardTransaction(metaBlock.GetEpoch(), metaBlock.GetRound(), computedEconomics) + protSustRwdTx, protSustShardId, err := rc.createProtocolSustainabilityRewardTransaction(metaBlock.GetEpoch(), metaBlock.GetRound(), computedEconomics.GetRewardsForProtocolSustainability()) if err != nil { return nil, err } @@ -88,7 +103,8 @@ func (rc *rewardsCreator) CreateRewardsMiniBlocks( difference := big.NewInt(0).Sub(totalWithoutDevelopers, rc.accumulatedRewards) log.Debug("arithmetic difference in end of epoch rewards economics", "epoch", metaBlock.GetEpoch(), "value", difference) rc.adjustProtocolSustainabilityRewards(protSustRwdTx, difference) - err = rc.addProtocolRewardToMiniBlocks(protSustRwdTx, miniBlocks, protSustShardId) + + err = rc.addAcceleratorRewardToMiniBlocks(protSustRwdTx, miniBlocks, protSustShardId) if err != nil { return nil, err } diff --git a/epochStart/metachain/rewardsCreatorProxy.go b/epochStart/metachain/rewardsCreatorProxy.go index d5f67d77a36..ad4da9170bc 100644 --- a/epochStart/metachain/rewardsCreatorProxy.go +++ b/epochStart/metachain/rewardsCreatorProxy.go @@ -101,9 +101,9 @@ func (rcp *rewardsCreatorProxy) VerifyRewardsMiniBlocks( return rcp.rc.VerifyRewardsMiniBlocks(metaBlock, validatorsInfo, computedEconomics) } -// GetProtocolSustainabilityRewards proxies the same method of the configured rewardsCreator instance -func (rcp *rewardsCreatorProxy) GetProtocolSustainabilityRewards() *big.Int { - return rcp.rc.GetProtocolSustainabilityRewards() +// GetAcceleratorRewards proxies the same method of the configured rewardsCreator instance +func (rcp *rewardsCreatorProxy) GetAcceleratorRewards() *big.Int { + return rcp.rc.GetAcceleratorRewards() } // GetLocalTxCache proxies the same method of the configured rewardsCreator instance diff --git a/epochStart/metachain/rewardsCreatorProxy_test.go b/epochStart/metachain/rewardsCreatorProxy_test.go index 638801522d9..916c0e38bac 100644 --- a/epochStart/metachain/rewardsCreatorProxy_test.go +++ b/epochStart/metachain/rewardsCreatorProxy_test.go @@ -144,6 +144,14 @@ func TestRewardsCreatorProxy_CreateRewardsMiniBlocksWithSwitchToRewardsCreatorV1 } metaBlock.Epoch = 3 + metaBlock.EpochStart.Economics = block.Economics{ + TotalSupply: big.NewInt(10000), + TotalToDistribute: big.NewInt(1000000), + TotalNewlyMinted: big.NewInt(1000000), + RewardsPerBlock: big.NewInt(1), + NodePrice: big.NewInt(10000), + RewardsForProtocolSustainability: big.NewInt(50), + } economics := &metaBlock.EpochStart.Economics miniBlocks, err := rewardsCreatorProxy.CreateRewardsMiniBlocks(metaBlock, vInfo, economics) @@ -191,19 +199,19 @@ func TestRewardsCreatorProxy_VerifyRewardsMiniBlocksOK(t *testing.T) { require.Nil(t, err) } -func TestRewardsCreatorProxy_GetProtocolSustainabilityRewards(t *testing.T) { +func TestRewardsCreatorProxy_GetAcceleratorRewards(t *testing.T) { t.Parallel() expectedValue := big.NewInt(12345) rewardCreatorV1 := &testscommon.RewardsCreatorStub{ - GetProtocolSustainabilityRewardsCalled: func() *big.Int { + GetAcceleratorRewardsCalled: func() *big.Int { return expectedValue }, } rewardsCreatorProxy, _, _ := createTestData(rewardCreatorV1, rCreatorV1) - protocolSustainabilityRewards := rewardsCreatorProxy.GetProtocolSustainabilityRewards() + protocolSustainabilityRewards := rewardsCreatorProxy.GetAcceleratorRewards() require.Equal(t, expectedValue, protocolSustainabilityRewards) } diff --git a/epochStart/metachain/rewardsV2.go b/epochStart/metachain/rewardsV2.go index 47d2711fe1c..e50437c02f3 100644 --- a/epochStart/metachain/rewardsV2.go +++ b/epochStart/metachain/rewardsV2.go @@ -80,6 +80,11 @@ func NewRewardsCreatorV2(args RewardsCreatorArgsV2) (*rewardsCreatorV2, error) { return rc, nil } +// GetAcceleratorRewards returns the sum of all rewards +func (rc *rewardsCreatorV2) GetAcceleratorRewards() *big.Int { + return rc.economicsDataProvider.RewardsForAccelerator() +} + // CreateRewardsMiniBlocks creates the rewards miniblocks according to economics data and validator info. // This method applies the rewards according to the economics version 2 proposal, which takes into consideration // stake top-up values per node @@ -156,7 +161,7 @@ func (rc *rewardsCreatorV2) createRewardsMiniBlocks( rc.clean() rc.flagDelegationSystemSCEnabled.SetValue(args.newEpoch >= rc.enableEpochsHandler.GetActivationEpoch(common.StakingV2Flag)) - protRwdTx, protRwdShardId, err := rc.createProtocolSustainabilityRewardTransaction(args.newEpoch, args.round, args.computedEconomics) + protRwdTx, protRwdShardId, err := rc.createProtocolSustainabilityRewardTransaction(args.newEpoch, args.round, args.computedEconomics.GetRewardsForProtocolSustainability()) if err != nil { return nil, err } @@ -173,7 +178,24 @@ func (rc *rewardsCreatorV2) createRewardsMiniBlocks( log.Debug("accumulated dust for protocol sustainability", "value", dust) rc.adjustProtocolSustainabilityRewards(protRwdTx, dust) - err = rc.addProtocolRewardToMiniBlocks(protRwdTx, miniBlocks, protRwdShardId) + err = rc.addAcceleratorRewardToMiniBlocks(protRwdTx, miniBlocks, protRwdShardId) + if err != nil { + return nil, err + } + + ecoGrowthRwdTx, ecoGrowthShardId, err := rc.createEcosystemGrowthRewardTransaction(args.newEpoch, args.round) + if err != nil { + return nil, err + } + growthDivRwdTx, growthDivShardId, err := rc.createGrowthDividendRewardTransaction(args.newEpoch, args.round) + if err != nil { + return nil, err + } + err = rc.addAcceleratorRewardToMiniBlocks(ecoGrowthRwdTx, miniBlocks, ecoGrowthShardId) + if err != nil { + return nil, err + } + err = rc.addAcceleratorRewardToMiniBlocks(growthDivRwdTx, miniBlocks, growthDivShardId) if err != nil { return nil, err } @@ -181,6 +203,43 @@ func (rc *rewardsCreatorV2) createRewardsMiniBlocks( return rc.finalizeMiniBlocks(miniBlocks), nil } +func (rc *rewardsCreatorV2) createEcosystemGrowthRewardTransaction( + epoch uint32, + round uint64, +) (*rewardTx.RewardTx, uint32, error) { + + rwdAddr := rc.rewardsHandler.EcosystemGrowthAddressInEpoch(epoch) + shardId := rc.shardCoordinator.ComputeId([]byte(rwdAddr)) + + rwdTx := &rewardTx.RewardTx{ + Round: round, + Epoch: epoch, + RcvAddr: []byte(rwdAddr), + Value: big.NewInt(0).Set(rc.economicsDataProvider.RewardsForEcosystemGrowth()), + } + + rc.accumulatedRewards.Add(rc.accumulatedRewards, rwdTx.Value) + return rwdTx, shardId, nil +} + +func (rc *rewardsCreatorV2) createGrowthDividendRewardTransaction( + epoch uint32, + round uint64, +) (*rewardTx.RewardTx, uint32, error) { + rwdAddr := rc.rewardsHandler.GrowthDividendAddressInEpoch(epoch) + shardId := rc.shardCoordinator.ComputeId([]byte(rwdAddr)) + + rwdTx := &rewardTx.RewardTx{ + Round: round, + Epoch: epoch, + RcvAddr: []byte(rwdAddr), + Value: big.NewInt(0).Set(rc.economicsDataProvider.RewardsForGrowthDividend()), + } + + rc.accumulatedRewards.Add(rc.accumulatedRewards, rwdTx.Value) + return rwdTx, shardId, nil +} + func (rc *rewardsCreatorV2) adjustProtocolSustainabilityRewards(protocolSustainabilityRwdTx *rewardTx.RewardTx, dustRewards *big.Int) { if protocolSustainabilityRwdTx.Value.Cmp(big.NewInt(0)) < 0 { log.Error("negative rewards protocol sustainability") @@ -199,7 +258,7 @@ func (rc *rewardsCreatorV2) adjustProtocolSustainabilityRewards(protocolSustaina "destination", protocolSustainabilityRwdTx.GetRcvAddr(), "value", protocolSustainabilityRwdTx.GetValue().String()) - rc.protocolSustainabilityValue.Set(protocolSustainabilityRwdTx.Value) + rc.economicsDataProvider.SetRewardsForProtocolSustainability(protocolSustainabilityRwdTx.Value) } // VerifyRewardsMiniBlocks verifies if received rewards miniblocks are correct diff --git a/epochStart/metachain/rewardsV2_test.go b/epochStart/metachain/rewardsV2_test.go index 307c6c89826..f1b9bdddd7d 100644 --- a/epochStart/metachain/rewardsV2_test.go +++ b/epochStart/metachain/rewardsV2_test.go @@ -210,15 +210,16 @@ func TestRewardsCreatorV2_adjustProtocolSustainabilityRewardsPositiveValue(t *te protRwShard := args.ShardCoordinator.ComputeId(protRwAddr) mbSlice := createDefaultMiniBlocksSlice() - _ = rwd.addProtocolRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) + _ = rwd.addAcceleratorRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) dust := big.NewInt(1000) rwd2 := rewardsCreatorV2{ - baseRewardsCreator: rwd, + baseRewardsCreator: rwd, + economicsDataProvider: NewEpochEconomicsStatistics(), } rwd2.adjustProtocolSustainabilityRewards(protRwTx, dust) require.Zero(t, protRwTx.Value.Cmp(big.NewInt(0).Add(dust, initialProtRewardValue))) - setProtValue := rwd.GetProtocolSustainabilityRewards() + setProtValue := rwd2.GetAcceleratorRewards() require.Zero(t, protRwTx.Value.Cmp(setProtValue)) } @@ -241,16 +242,18 @@ func TestRewardsCreatorV2_adjustProtocolSustainabilityRewardsNegValueNotAccepted protRwShard := args.ShardCoordinator.ComputeId(protRwAddr) mbSlice := createDefaultMiniBlocksSlice() - _ = rwd.addProtocolRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) + _ = rwd.addAcceleratorRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) rwd2 := rewardsCreatorV2{ - baseRewardsCreator: rwd, + baseRewardsCreator: rwd, + economicsDataProvider: NewEpochEconomicsStatistics(), } + rwd2.economicsDataProvider.SetRewardsForProtocolSustainability(initialProtRewardValue) dust := big.NewInt(-10) rwd2.adjustProtocolSustainabilityRewards(protRwTx, dust) require.Zero(t, protRwTx.Value.Cmp(initialProtRewardValue)) - setProtValue := rwd.GetProtocolSustainabilityRewards() + setProtValue := rwd2.GetAcceleratorRewards() require.Zero(t, protRwTx.Value.Cmp(setProtValue)) } @@ -273,16 +276,17 @@ func TestRewardsCreatorV2_adjustProtocolSustainabilityRewardsInitialNegativeValu protRwShard := args.ShardCoordinator.ComputeId(protRwAddr) mbSlice := createDefaultMiniBlocksSlice() - _ = rwd.addProtocolRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) + _ = rwd.addAcceleratorRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) rwd2 := rewardsCreatorV2{ - baseRewardsCreator: rwd, + baseRewardsCreator: rwd, + economicsDataProvider: NewEpochEconomicsStatistics(), } dust := big.NewInt(0) rwd2.adjustProtocolSustainabilityRewards(protRwTx, dust) require.Zero(t, protRwTx.Value.Cmp(big.NewInt(0))) - setProtValue := rwd.GetProtocolSustainabilityRewards() + setProtValue := rwd2.GetAcceleratorRewards() require.Zero(t, protRwTx.Value.Cmp(setProtValue)) } @@ -1620,6 +1624,7 @@ func TestNewRewardsCreatorV2_CreateRewardsMiniBlocks(t *testing.T) { EpochStart: getDefaultEpochStart(), DevFeesInEpoch: big.NewInt(0), } + rwd.economicsDataProvider.SetRewardsForProtocolSustainability(metaBlock.EpochStart.Economics.RewardsForProtocolSustainability) var miniBlocks block.MiniBlockSlice miniBlocks, err = rwd.CreateRewardsMiniBlocks(metaBlock, vInfo, &metaBlock.EpochStart.Economics) @@ -1664,6 +1669,8 @@ func TestNewRewardsCreatorV2_CreateRewardsMiniBlocks(t *testing.T) { } } + rwd.economicsDataProvider.SetRewardsForProtocolSustainability(metaBlock.EpochStart.Economics.RewardsForProtocolSustainability) + err = rwd.VerifyRewardsMiniBlocks(metaBlock, vInfo, &metaBlock.EpochStart.Economics) require.Nil(t, err) } @@ -1716,6 +1723,7 @@ func TestNewRewardsCreatorV2_CreateRewardsMiniBlocks2169Nodes(t *testing.T) { DevFeesInEpoch: big.NewInt(0), } + rwd.economicsDataProvider.SetRewardsForProtocolSustainability(metaBlock.EpochStart.Economics.RewardsForProtocolSustainability) var miniBlocks block.MiniBlockSlice miniBlocks, err = rwd.CreateRewardsMiniBlocks(metaBlock, vInfo, &metaBlock.EpochStart.Economics) require.Nil(t, err) @@ -1759,10 +1767,95 @@ func TestNewRewardsCreatorV2_CreateRewardsMiniBlocks2169Nodes(t *testing.T) { } } + rwd.economicsDataProvider.SetRewardsForProtocolSustainability(metaBlock.EpochStart.Economics.RewardsForProtocolSustainability) err = rwd.VerifyRewardsMiniBlocks(metaBlock, vInfo, &metaBlock.EpochStart.Economics) require.Nil(t, err) } +func TestRewardsCreatorV2_CreateRewardsMiniBlocksWithTopUp(t *testing.T) { + t.Parallel() + + args := getRewardsCreatorV2Arguments() + nbEligiblePerShard := uint32(10) + vInfo := createDefaultValidatorInfo(nbEligiblePerShard, args.ShardCoordinator, args.NodesConfigProvider, 100, defaultBlocksPerShard) + + // Set up validators with varying top-up stakes + nodesRewardInfo := make(map[uint32][]*nodeRewardsData) + totalTopUpStake := big.NewInt(0) + for shardID, valList := range vInfo.GetShardValidatorsInfoMap() { + nodesRewardInfo[shardID] = make([]*nodeRewardsData, len(valList)) + for i, valInfo := range valList { + topUp := big.NewInt(int64(i * 100)) // Different top-up for each validator + totalTopUpStake.Add(totalTopUpStake, topUp) + nodesRewardInfo[shardID][i] = &nodeRewardsData{ + valInfo: valInfo, + topUpStake: topUp, + } + } + } + + args.StakingDataProvider = &stakingcommon.StakingDataProviderStub{ + GetTotalTopUpStakeEligibleNodesCalled: func() *big.Int { + return totalTopUpStake + }, + GetNodeStakedTopUpCalled: func(blsKey []byte) (*big.Int, error) { + for _, nodes := range nodesRewardInfo { + for _, node := range nodes { + if bytes.Equal(node.valInfo.GetPublicKey(), blsKey) { + return node.topUpStake, nil + } + } + } + return nil, fmt.Errorf("not found") + }, + } + + nbBlocksPerShard := uint64(14400) + blocksPerShard := make(map[uint32]uint64) + shardMap := createShardsMap(args.ShardCoordinator) + for shardID := range shardMap { + blocksPerShard[shardID] = nbBlocksPerShard + } + args.EconomicsDataProvider.SetNumberOfBlocksPerShard(blocksPerShard) + rewardsForBlocks, _ := big.NewInt(0).SetString("5000000000000000000000", 10) + args.EconomicsDataProvider.SetRewardsToBeDistributedForBlocks(rewardsForBlocks) + + rwd, err := NewRewardsCreatorV2(args) + require.Nil(t, err) + require.NotNil(t, rwd) + + metaBlock := &block.MetaBlock{ + EpochStart: getDefaultEpochStart(), + DevFeesInEpoch: big.NewInt(0), + } + rwd.economicsDataProvider.SetRewardsForProtocolSustainability(metaBlock.EpochStart.Economics.RewardsForProtocolSustainability) + + miniBlocks, err := rwd.CreateRewardsMiniBlocks(metaBlock, vInfo, &metaBlock.EpochStart.Economics) + require.Nil(t, err) + require.NotNil(t, miniBlocks) + + sumRewards := big.NewInt(0) + var tx data.TransactionHandler + for _, mb := range miniBlocks { + for _, txHash := range mb.TxHashes { + tx, err = rwd.currTxs.GetTx(txHash) + require.Nil(t, err) + sumRewards.Add(sumRewards, tx.GetValue()) + } + } + + sumFees := big.NewInt(0) + for _, v := range vInfo.GetAllValidatorsInfo() { + sumFees.Add(sumFees, v.GetAccumulatedFees()) + } + + totalRws := rwd.economicsDataProvider.RewardsToBeDistributedForBlocks() + rewardsForProtocolSustainability := big.NewInt(0).Set(metaBlock.EpochStart.Economics.RewardsForProtocolSustainability) + expectedRewards := big.NewInt(0).Add(sumFees, totalRws) + expectedRewards.Add(expectedRewards, rewardsForProtocolSustainability) + require.Equal(t, expectedRewards, sumRewards) +} + func TestRewardsCreatorV2_CreateRewardsMiniBlocksHeaderV3(t *testing.T) { t.Parallel() diff --git a/epochStart/metachain/rewards_test.go b/epochStart/metachain/rewards_test.go index 25278fa4178..be601783c1d 100644 --- a/epochStart/metachain/rewards_test.go +++ b/epochStart/metachain/rewards_test.go @@ -242,15 +242,16 @@ func TestRewardsCreator_adjustProtocolSustainabilityRewardsPositiveValue(t *test protRwShard := args.ShardCoordinator.ComputeId(protRwAddr) mbSlice := createDefaultMiniBlocksSlice() - _ = rwd.addProtocolRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) + _ = rwd.addAcceleratorRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) dust := big.NewInt(1000) rwd1 := rewardsCreator{ - baseRewardsCreator: rwd, + baseRewardsCreator: rwd, + protocolSustainabilityValue: big.NewInt(0), } rwd1.adjustProtocolSustainabilityRewards(protRwTx, dust) require.Zero(t, protRwTx.Value.Cmp(big.NewInt(0).Add(dust, initialProtRewardValue))) - setProtValue := rwd.GetProtocolSustainabilityRewards() + setProtValue := rwd1.GetAcceleratorRewards() require.Zero(t, protRwTx.Value.Cmp(setProtValue)) } @@ -273,17 +274,18 @@ func TestRewardsCreator_adjustProtocolSustainabilityRewardsNegValueShouldWork(t protRwShard := args.ShardCoordinator.ComputeId(protRwAddr) mbSlice := createDefaultMiniBlocksSlice() - _ = rwd.addProtocolRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) + _ = rwd.addAcceleratorRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) rwd1 := rewardsCreator{ - baseRewardsCreator: rwd, + baseRewardsCreator: rwd, + protocolSustainabilityValue: big.NewInt(0), } dust := big.NewInt(-10) rwd1.adjustProtocolSustainabilityRewards(protRwTx, dust) expected := big.NewInt(0).Add(dust, initialProtRewardValue).String() assert.Equal(t, expected, protRwTx.Value.String()) - setProtValue := rwd.GetProtocolSustainabilityRewards() + setProtValue := rwd1.GetAcceleratorRewards() require.Zero(t, protRwTx.Value.Cmp(setProtValue)) } @@ -306,16 +308,17 @@ func TestRewardsCreator_adjustProtocolSustainabilityRewardsInitialNegativeValue( protRwShard := args.ShardCoordinator.ComputeId(protRwAddr) mbSlice := createDefaultMiniBlocksSlice() - _ = rwd.addProtocolRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) + _ = rwd.addAcceleratorRewardToMiniBlocks(protRwTx, mbSlice, protRwShard) rwd1 := rewardsCreator{ - baseRewardsCreator: rwd, + baseRewardsCreator: rwd, + protocolSustainabilityValue: big.NewInt(0), } dust := big.NewInt(0) rwd1.adjustProtocolSustainabilityRewards(protRwTx, dust) require.Zero(t, protRwTx.Value.Cmp(big.NewInt(0))) - setProtValue := rwd.GetProtocolSustainabilityRewards() + setProtValue := rwd1.GetAcceleratorRewards() require.Zero(t, protRwTx.Value.Cmp(setProtValue)) } @@ -654,7 +657,7 @@ func TestRewardsCreator_CreateProtocolSustainabilityRewardTransaction(t *testing Epoch: 0, } - rwdTx, _, err := rwdc.createProtocolSustainabilityRewardTransaction(mb.GetEpoch(), mb.GetRound(), &mb.EpochStart.Economics) + rwdTx, _, err := rwdc.createProtocolSustainabilityRewardTransaction(mb.GetEpoch(), mb.GetRound(), mb.EpochStart.Economics.GetRewardsForProtocolSustainability()) assert.Equal(t, expectedRewardTx, rwdTx) assert.Nil(t, err) } diff --git a/epochStart/mock/rewardsHandlerStub.go b/epochStart/mock/rewardsHandlerStub.go index 815a05bc82c..87e1192a696 100644 --- a/epochStart/mock/rewardsHandlerStub.go +++ b/epochStart/mock/rewardsHandlerStub.go @@ -7,8 +7,7 @@ type RewardsHandlerStub struct { LeaderPercentageCalled func() float64 ProtocolSustainabilityPercentageCalled func() float64 ProtocolSustainabilityAddressCalled func() string - MinInflationRateCalled func() float64 - MaxInflationRateCalled func(year uint32) float64 + MaxInflationRateCalled func(year uint32, epoch uint32) float64 RewardsTopUpGradientPointCalled func() *big.Int RewardsTopUpFactorCalled func() float64 LeaderPercentageInEpochCalled func(epoch uint32) float64 @@ -17,6 +16,11 @@ type RewardsHandlerStub struct { ProtocolSustainabilityAddressInEpochCalled func(epoch uint32) string RewardsTopUpGradientPointInEpochCalled func(epoch uint32) *big.Int RewardsTopUpFactorInEpochCalled func(epoch uint32) float64 + EcosystemGrowthPercentageInEpochCalled func(epoch uint32) float64 + EcosystemGrowthAddressInEpochCalled func(epoch uint32) string + GrowthDividendPercentageInEpochCalled func(epoch uint32) float64 + GrowthDividendAddressInEpochCalled func(epoch uint32) string + IsTailInflationEnabledCalled func(epoch uint32) bool } // LeaderPercentage - @@ -46,24 +50,23 @@ func (r *RewardsHandlerStub) ProtocolSustainabilityAddress() string { return "1111" } -// MinInflationRate - -func (r *RewardsHandlerStub) MinInflationRate() float64 { - if r.MinInflationRateCalled != nil { - return r.MinInflationRateCalled() - } - - return 1 -} - // MaxInflationRate - -func (r *RewardsHandlerStub) MaxInflationRate(year uint32) float64 { +func (r *RewardsHandlerStub) MaxInflationRate(year uint32, epoch uint32) float64 { if r.MaxInflationRateCalled != nil { - return r.MaxInflationRateCalled(year) + return r.MaxInflationRateCalled(year, epoch) } return 1000000 } +// IsTailInflationEnabled - +func (r *RewardsHandlerStub) IsTailInflationEnabled(epoch uint32) bool { + if r.IsTailInflationEnabledCalled != nil { + return r.IsTailInflationEnabledCalled(epoch) + } + return true +} + // RewardsTopUpGradientPoint - func (r *RewardsHandlerStub) RewardsTopUpGradientPoint() *big.Int { return r.RewardsTopUpGradientPointCalled() @@ -122,6 +125,38 @@ func (r *RewardsHandlerStub) RewardsTopUpFactorInEpoch(epoch uint32) float64 { return 0 } +// EcosystemGrowthPercentageInEpoch - +func (r *RewardsHandlerStub) EcosystemGrowthPercentageInEpoch(epoch uint32) float64 { + if r.EcosystemGrowthPercentageInEpochCalled != nil { + return r.EcosystemGrowthPercentageInEpochCalled(epoch) + } + return 0 +} + +// GrowthDividendPercentageInEpoch - +func (r *RewardsHandlerStub) GrowthDividendPercentageInEpoch(epoch uint32) float64 { + if r.GrowthDividendPercentageInEpochCalled != nil { + return r.GrowthDividendPercentageInEpochCalled(epoch) + } + return 0 +} + +// EcosystemGrowthAddressInEpoch - +func (r *RewardsHandlerStub) EcosystemGrowthAddressInEpoch(epoch uint32) string { + if r.EcosystemGrowthAddressInEpochCalled != nil { + return r.EcosystemGrowthAddressInEpochCalled(epoch) + } + return "" +} + +// GrowthDividendAddressInEpoch - +func (r *RewardsHandlerStub) GrowthDividendAddressInEpoch(epoch uint32) string { + if r.GrowthDividendAddressInEpochCalled != nil { + return r.GrowthDividendAddressInEpochCalled(epoch) + } + return "" +} + // IsInterfaceNil - func (r *RewardsHandlerStub) IsInterfaceNil() bool { return r == nil diff --git a/factory/core/coreComponents.go b/factory/core/coreComponents.go index 7da39d61e35..64160e21a9f 100644 --- a/factory/core/coreComponents.go +++ b/factory/core/coreComponents.go @@ -331,6 +331,7 @@ func (ccf *coreComponentsFactory) Create() (*coreComponents, error) { log.Trace("creating economics data components") argsNewEconomicsData := economics.ArgsNewEconomicsData{ Economics: &ccf.economicsConfig, + ChainParamsHandler: chainParametersHandler, EpochNotifier: epochNotifier, EnableEpochsHandler: enableEpochsHandler, TxVersionChecker: txVersionChecker, diff --git a/factory/interface.go b/factory/interface.go index 847eb7219ce..c528bb70de7 100644 --- a/factory/interface.go +++ b/factory/interface.go @@ -507,8 +507,7 @@ type EconomicsHandler interface { LeaderPercentage() float64 ProtocolSustainabilityPercentage() float64 ProtocolSustainabilityAddress() string - MinInflationRate() float64 - MaxInflationRate(year uint32) float64 + MaxInflationRate(year uint32, epoch uint32) float64 DeveloperPercentage() float64 GenesisTotalSupply() *big.Int MaxGasLimitPerBlock(shardID uint32) uint64 diff --git a/integrationTests/state/stateTrieSync/stateTrieSync_test.go b/integrationTests/state/stateTrieSync/stateTrieSync_test.go index d6a9e4c6b2f..cd416f663c5 100644 --- a/integrationTests/state/stateTrieSync/stateTrieSync_test.go +++ b/integrationTests/state/stateTrieSync/stateTrieSync_test.go @@ -428,7 +428,6 @@ func testSyncMissingSnapshotNodes(t *testing.T, version int) { t.Skip("this is not a short test") } - numExtraAccounts := 2 // protocol sustainability acc & nodes owner acc numAccounts := 1000 numDataTrieLeaves := 50 valSize := 32 @@ -486,7 +485,7 @@ func testSyncMissingSnapshotNodes(t *testing.T, version int) { dataTrieRootHashes := addAccountsToState(t, numAccounts, numDataTrieLeaves, accState, valSize) rootHash, _ := accState.RootHash() numLeaves := getNumLeaves(t, resolverTrie, rootHash) - require.Equal(t, numAccounts+numExtraAccounts, numLeaves) + require.Equal(t, numAccounts, numLeaves) requesterTrie := nRequester.TrieContainer.Get([]byte(dataRetriever.UserAccountsUnit.String())) nilRootHash, _ := requesterTrie.RootHash() @@ -518,7 +517,7 @@ func testSyncMissingSnapshotNodes(t *testing.T, version int) { assert.Equal(t, rootHash, newRootHash) numLeaves = getNumLeaves(t, requesterTrie, rootHash) - assert.Equal(t, numAccounts+numExtraAccounts, numLeaves) + assert.Equal(t, numAccounts, numLeaves) checkAllDataTriesAreSynced(t, numDataTrieLeaves, requesterTrie, dataTrieRootHashes) } diff --git a/integrationTests/testConsensusNode.go b/integrationTests/testConsensusNode.go index ce47b54eef4..ec4177724b5 100644 --- a/integrationTests/testConsensusNode.go +++ b/integrationTests/testConsensusNode.go @@ -16,6 +16,7 @@ import ( crypto "github.com/multiversx/mx-chain-crypto-go" mclMultiSig "github.com/multiversx/mx-chain-crypto-go/signing/mcl/multisig" "github.com/multiversx/mx-chain-crypto-go/signing/multisig" + "github.com/multiversx/mx-chain-go/testscommon/processMocks" "github.com/multiversx/mx-chain-go/common" @@ -751,6 +752,7 @@ func createHasher(consensusType string) hashing.Hasher { func createTestStore() dataRetriever.StorageService { store := dataRetriever.NewChainStorer() store.AddStorer(dataRetriever.TransactionUnit, CreateMemUnit()) + store.AddStorer(dataRetriever.UnsignedTransactionUnit, CreateMemUnit()) store.AddStorer(dataRetriever.MiniBlockUnit, CreateMemUnit()) store.AddStorer(dataRetriever.RewardTransactionUnit, CreateMemUnit()) store.AddStorer(dataRetriever.MetaBlockUnit, CreateMemUnit()) diff --git a/integrationTests/testFullNode.go b/integrationTests/testFullNode.go index 73170f4bdaa..1fdfcc5528a 100644 --- a/integrationTests/testFullNode.go +++ b/integrationTests/testFullNode.go @@ -298,24 +298,22 @@ func (tpn *TestFullNode) initTestNodeWithArgs(args ArgTestProcessorNode, fullArg tpn.initAccountDBs(args.TrieStore) } + chainParam := config.ChainParametersByEpochConfig{ + RoundDuration: uint64(roundDuration.Milliseconds()), + ShardConsensusGroupSize: uint32(fullArgs.ConsensusSize), + MetachainConsensusGroupSize: uint32(fullArgs.ConsensusSize), + RoundsPerEpoch: fullArgs.RoundsPerEpoch, + MinRoundsBetweenEpochs: 1, + } tpn.ChainParametersHandler = &chainParameters.ChainParametersHandlerStub{ ChainParametersForEpochCalled: func(_ uint32) (config.ChainParametersByEpochConfig, error) { - return config.ChainParametersByEpochConfig{ - RoundDuration: uint64(roundDuration.Milliseconds()), - ShardConsensusGroupSize: uint32(fullArgs.ConsensusSize), - MetachainConsensusGroupSize: uint32(fullArgs.ConsensusSize), - RoundsPerEpoch: fullArgs.RoundsPerEpoch, - MinRoundsBetweenEpochs: 1, - }, nil + return chainParam, nil }, CurrentChainParametersCalled: func() config.ChainParametersByEpochConfig { - return config.ChainParametersByEpochConfig{ - RoundDuration: uint64(roundDuration.Milliseconds()), - ShardConsensusGroupSize: uint32(fullArgs.ConsensusSize), - MetachainConsensusGroupSize: uint32(fullArgs.ConsensusSize), - RoundsPerEpoch: fullArgs.RoundsPerEpoch, - MinRoundsBetweenEpochs: 1, - } + return chainParam + }, + AllChainParametersCalled: func() []config.ChainParametersByEpochConfig { + return []config.ChainParametersByEpochConfig{chainParam} }, } diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index 38168d7f0de..e67119763f0 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -843,20 +843,20 @@ func (tpn *TestProcessorNode) initTestNodeWithArgs(args ArgTestProcessorNode) { tpn.initHeaderValidator() tpn.initRoundHandler(roundTime) + chainParam := config.ChainParametersByEpochConfig{ + RoundDuration: uint64(roundTime.Milliseconds()), + RoundsPerEpoch: 1000, + MinRoundsBetweenEpochs: 1, + } tpn.ChainParametersHandler = &chainParameters.ChainParametersHandlerStub{ ChainParametersForEpochCalled: func(_ uint32) (config.ChainParametersByEpochConfig, error) { - return config.ChainParametersByEpochConfig{ - RoundDuration: uint64(roundTime.Milliseconds()), - RoundsPerEpoch: 1000, - MinRoundsBetweenEpochs: 1, - }, nil + return chainParam, nil }, CurrentChainParametersCalled: func() config.ChainParametersByEpochConfig { - return config.ChainParametersByEpochConfig{ - RoundDuration: uint64(roundTime.Milliseconds()), - RoundsPerEpoch: 1000, - MinRoundsBetweenEpochs: 1, - } + return chainParam + }, + AllChainParametersCalled: func() []config.ChainParametersByEpochConfig { + return []config.ChainParametersByEpochConfig{chainParam} }, } @@ -1217,6 +1217,7 @@ func (tpn *TestProcessorNode) initEconomicsData(economicsConfig *config.Economic pubKeyConv, _ := pubkeyConverter.NewBech32PubkeyConverter(32, "erd") argsNewEconomicsData := economics.ArgsNewEconomicsData{ Economics: economicsConfig, + ChainParamsHandler: tpn.ChainParametersHandler, EpochNotifier: tpn.EpochNotifier, EnableEpochsHandler: tpn.EnableEpochsHandler, TxVersionChecker: &testscommon.TxVersionCheckerStub{}, @@ -1252,6 +1253,10 @@ func createDefaultEconomicsConfig() *config.EconomicsConfig { TopUpFactor: 0.25, TopUpGradientPoint: "300000000000000000000", ProtocolSustainabilityPercentage: 0.1, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: testProtocolSustainabilityAddress, + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: testProtocolSustainabilityAddress, }, }, }, diff --git a/integrationTests/vm/testInitializer.go b/integrationTests/vm/testInitializer.go index 79407ce9263..4343fe255b2 100644 --- a/integrationTests/vm/testInitializer.go +++ b/integrationTests/vm/testInitializer.go @@ -19,15 +19,6 @@ import ( "github.com/multiversx/mx-chain-core-go/data/scheduled" dataTransaction "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-core-go/marshal" - logger "github.com/multiversx/mx-chain-logger-go" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - vmcommonBuiltInFunctions "github.com/multiversx/mx-chain-vm-common-go/builtInFunctions" - "github.com/multiversx/mx-chain-vm-common-go/parsers" - datafield "github.com/multiversx/mx-chain-vm-common-go/parsers/dataField" - wasmConfig "github.com/multiversx/mx-chain-vm-go/config" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/enablers" "github.com/multiversx/mx-chain-go/common/forking" @@ -61,6 +52,7 @@ import ( "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" commonMocks "github.com/multiversx/mx-chain-go/testscommon/common" dataRetrieverMock "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" "github.com/multiversx/mx-chain-go/testscommon/dblookupext" @@ -74,6 +66,14 @@ import ( "github.com/multiversx/mx-chain-go/testscommon/txDataBuilder" "github.com/multiversx/mx-chain-go/txcache" "github.com/multiversx/mx-chain-go/vm/systemSmartContracts/defaults" + logger "github.com/multiversx/mx-chain-logger-go" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + vmcommonBuiltInFunctions "github.com/multiversx/mx-chain-vm-common-go/builtInFunctions" + "github.com/multiversx/mx-chain-vm-common-go/parsers" + datafield "github.com/multiversx/mx-chain-vm-common-go/parsers/dataField" + wasmConfig "github.com/multiversx/mx-chain-vm-go/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // EpochGuardianDelay is the test constant for the delay in epochs for the guardian feature @@ -327,7 +327,8 @@ func createEconomicsData(enableEpochsConfig config.EnableEpochs, gasPriceModifie enableEpochsHandler, _ := enablers.NewEnableEpochsHandler(enableEpochsConfig, realEpochNotifier) argsNewEconomicsData := economics.ArgsNewEconomicsData{ - TxVersionChecker: versioning.NewTxVersionChecker(minTransactionVersion), + TxVersionChecker: versioning.NewTxVersionChecker(minTransactionVersion), + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &config.EconomicsConfig{ GlobalSettings: config.GlobalSettings{ GenesisTotalSupply: "2000000000000000000000", @@ -347,6 +348,10 @@ func createEconomicsData(enableEpochsConfig config.EnableEpochs, gasPriceModifie DeveloperPercentage: 0.1, ProtocolSustainabilityAddress: testProtocolSustainabilityAddress, TopUpGradientPoint: "100000", + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: testProtocolSustainabilityAddress, + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: testProtocolSustainabilityAddress, }, }, }, diff --git a/integrationTests/vm/wasm/utils.go b/integrationTests/vm/wasm/utils.go index 5638af63d49..f26bf0a4002 100644 --- a/integrationTests/vm/wasm/utils.go +++ b/integrationTests/vm/wasm/utils.go @@ -23,10 +23,6 @@ import ( "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-core-go/hashing/blake2b" "github.com/multiversx/mx-chain-core-go/marshal" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "github.com/multiversx/mx-chain-vm-common-go/parsers" - "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/enablers" "github.com/multiversx/mx-chain-go/config" @@ -50,6 +46,7 @@ import ( "github.com/multiversx/mx-chain-go/process/transactionLog" "github.com/multiversx/mx-chain-go/state" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" dataRetrieverMock "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" "github.com/multiversx/mx-chain-go/testscommon/dblookupext" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" @@ -59,6 +56,9 @@ import ( storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" "github.com/multiversx/mx-chain-go/txcache" "github.com/multiversx/mx-chain-go/vm/systemSmartContracts/defaults" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/multiversx/mx-chain-vm-common-go/parsers" + "github.com/stretchr/testify/require" ) // VMTypeHex - @@ -215,7 +215,9 @@ func (context *TestContext) initFeeHandlers() { minGasPrice := strconv.FormatUint(1, 10) minGasLimit := strconv.FormatUint(1, 10) testProtocolSustainabilityAddress := "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp" + argsNewEconomicsData := economics.ArgsNewEconomicsData{ + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &config.EconomicsConfig{ GlobalSettings: config.GlobalSettings{ GenesisTotalSupply: "2000000000000000000000", @@ -236,6 +238,10 @@ func (context *TestContext) initFeeHandlers() { ProtocolSustainabilityAddress: testProtocolSustainabilityAddress, TopUpGradientPoint: "1000000", TopUpFactor: 0, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: testProtocolSustainabilityAddress, + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: testProtocolSustainabilityAddress, }, }, }, diff --git a/node/chainSimulator/chainSimulator.go b/node/chainSimulator/chainSimulator.go index 5aefad55334..b2ba68124c5 100644 --- a/node/chainSimulator/chainSimulator.go +++ b/node/chainSimulator/chainSimulator.go @@ -132,6 +132,7 @@ func (s *simulator) createChainHandlers(args ArgsBaseChainSimulator) error { AlterConfigsFunction: args.AlterConfigsFunction, NumNodesWaitingListShard: args.NumNodesWaitingListShard, NumNodesWaitingListMeta: args.NumNodesWaitingListMeta, + InitialRound: args.InitialRound, }) if err != nil { return err diff --git a/node/chainSimulator/chainSimulator_test.go b/node/chainSimulator/chainSimulator_test.go index 19294f8e90f..4c321059cf3 100644 --- a/node/chainSimulator/chainSimulator_test.go +++ b/node/chainSimulator/chainSimulator_test.go @@ -1,13 +1,16 @@ package chainSimulator import ( + "fmt" "math/big" "strings" "testing" "time" "github.com/multiversx/mx-chain-core-go/core" + apiBlock "github.com/multiversx/mx-chain-core-go/data/api" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -171,6 +174,66 @@ func TestChainSimulator_GenerateBlocksShouldWork(t *testing.T) { } +func TestChainSimulator_VerifyBlockTimestampSupernova(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + supernovaActivationRound := uint64(220) + + chainSimulator, err := NewChainSimulator(ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: defaultNumOfShards, + RoundDurationInMillis: defaultRoundDurationInMillis, + SupernovaRoundDurationInMillis: defaultSupernovaRoundDurationInMillis, + RoundsPerEpoch: core.OptionalUint64{ + Value: 20, + HasValue: true, + }, + SupernovaRoundsPerEpoch: defaultSupernovaRoundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: defaultMinNodesPerShard, + MetaChainMinNodes: defaultMetaChainMinNodes, + InitialRound: 200, + InitialEpoch: 100, + InitialNonce: 100, + AlterConfigsFunction: func(cfg *config.Configs) { + // we need to enable this as this test skips a lot of epoch activations events, and it will fail otherwise + // because the owner of a BLS key coming from genesis is not set + // (the owner is not set at genesis anymore because we do not enable the staking v2 in that phase) + cfg.EpochConfig.EnableEpochs.StakingV2EnableEpoch = 0 + cfg.EpochConfig.EnableEpochs.SupernovaEnableEpoch = 100 + cfg.RoundConfig.RoundActivations[string(common.SupernovaRoundFlag)] = config.ActivationRoundByName{ + Round: fmt.Sprintf("%d", supernovaActivationRound), + } + }, + }) + require.Nil(t, err) + require.NotNil(t, chainSimulator) + defer chainSimulator.Close() + + time.Sleep(time.Second) + + err = chainSimulator.GenerateBlocks(30) + require.Nil(t, err) + + blockBeforeSupernovaRound, err := chainSimulator.GetNodeHandler(0).GetFacadeHandler().GetBlockByRound(supernovaActivationRound-1, apiBlock.BlockQueryOptions{}) + require.Nil(t, err) + + blockS, err := chainSimulator.GetNodeHandler(0).GetFacadeHandler().GetBlockByRound(supernovaActivationRound, apiBlock.BlockQueryOptions{}) + require.Nil(t, err) + + blockAfterSupernovaRound, err := chainSimulator.GetNodeHandler(0).GetFacadeHandler().GetBlockByRound(supernovaActivationRound+1, apiBlock.BlockQueryOptions{}) + require.Nil(t, err) + + diff := blockS.TimestampMs - blockBeforeSupernovaRound.TimestampMs + require.Equal(t, int64(6000), diff) + diff = blockAfterSupernovaRound.TimestampMs - blockS.TimestampMs + require.Equal(t, defaultSupernovaRoundDurationInMillis, uint64(diff)) +} + func TestChainSimulator_GenerateBlocksAndEpochChangeShouldWork(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") diff --git a/node/chainSimulator/components/coreComponents.go b/node/chainSimulator/components/coreComponents.go index 57b28accbfd..9ed9d0851a9 100644 --- a/node/chainSimulator/components/coreComponents.go +++ b/node/chainSimulator/components/coreComponents.go @@ -106,7 +106,6 @@ type ArgsCoreComponentsHolder struct { MetaChainConsensusGroupSize uint32 RoundDurationInMs uint64 GenesisTime time.Time - SupernovaGenesisTime time.Time } // CreateCoreComponents will create a new instance of factory.CoreComponentsHolder @@ -161,8 +160,8 @@ func CreateCoreComponents(args ArgsCoreComponentsHolder) (*coreComponentsHolder, instance.syncTimer = &testscommon.SyncTimerStub{} instance.epochStartNotifierWithConfirm = notifier.NewEpochStartSubscriptionHandler() - instance.chainParametersSubscriber = chainparametersnotifier.NewChainParametersNotifier() chainParametersNotifier := chainparametersnotifier.NewChainParametersNotifier() + instance.chainParametersSubscriber = chainParametersNotifier argsChainParametersHandler := sharding.ArgsChainParametersHolder{ EpochStartEventNotifier: instance.epochStartNotifierWithConfirm, ChainParameters: args.Config.GeneralSettings.ChainParametersByEpoch, @@ -225,7 +224,11 @@ func CreateCoreComponents(args ArgsCoreComponentsHolder) (*coreComponentsHolder, roundDuration := time.Millisecond * time.Duration(instance.genesisNodesSetup.GetRoundDuration()) supernovaRound := instance.enableRoundsHandler.GetActivationRound(common.SupernovaRoundFlag) - instance.supernovaGenesisTime = instance.genesisTime.Add(time.Duration(supernovaRound) * roundDuration) + + instance.supernovaGenesisTime = startTime.Add(time.Duration(supernovaRound-uint64(args.InitialRound)) * roundDuration) + if instance.supernovaGenesisTime.Before(instance.genesisTime) { + instance.supernovaGenesisTime = instance.genesisTime + } supernovaActivationEpoch := instance.enableEpochsHandler.GetActivationEpoch(common.SupernovaFlag) chainParamsForSupernova, err := instance.chainParametersHandler.ChainParametersForEpoch(supernovaActivationEpoch) @@ -250,6 +253,7 @@ func CreateCoreComponents(args ArgsCoreComponentsHolder) (*coreComponentsHolder, instance.txVersionChecker = versioning.NewTxVersionChecker(args.Config.GeneralSettings.MinTransactionVersion) argsEconomicsHandler := economics.ArgsNewEconomicsData{ + ChainParamsHandler: instance.chainParametersHandler, TxVersionChecker: instance.txVersionChecker, Economics: &args.EconomicsConfig, EpochNotifier: instance.epochNotifier, diff --git a/node/chainSimulator/components/coreComponents_test.go b/node/chainSimulator/components/coreComponents_test.go index 514344f23d6..a3a94854be1 100644 --- a/node/chainSimulator/components/coreComponents_test.go +++ b/node/chainSimulator/components/coreComponents_test.go @@ -79,6 +79,7 @@ func createArgsCoreComponentsHolder() ArgsCoreComponentsHolder { Hardfork: config.HardforkConfig{ PublicKeyToListenFrom: components.DummyPk, }, + EpochStartConfig: config.EpochStartConfig{}, }, EnableEpochsConfig: config.EnableEpochs{}, RoundsConfig: config.RoundConfig{ @@ -130,6 +131,10 @@ func createArgsCoreComponentsHolder() ArgsCoreComponentsHolder { TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, EpochEnable: 0, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: testingProtocolSustainabilityAddress, + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: testingProtocolSustainabilityAddress, }, }, }, diff --git a/node/chainSimulator/components/testOnlyProcessingNode.go b/node/chainSimulator/components/testOnlyProcessingNode.go index aebcb44e9d8..dc26e0aaee2 100644 --- a/node/chainSimulator/components/testOnlyProcessingNode.go +++ b/node/chainSimulator/components/testOnlyProcessingNode.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "math/big" - "strconv" "time" "github.com/multiversx/mx-chain-core-go/core" @@ -96,13 +95,6 @@ func NewTestOnlyProcessingNode(args ArgsTestOnlyProcessingNode) (*testOnlyProces var err error instance.TransactionFeeHandler = postprocess.NewFeeAccumulator() - supernovaRoundStr := args.Configs.RoundConfig.RoundActivations[string(common.SupernovaRoundFlag)].Round - supernovaRound, err := strconv.ParseUint(supernovaRoundStr, 10, 64) - if err != nil { - return nil, err - } - - supernovaGenesisTime := args.GenesisTime.Add(time.Duration(supernovaRound*args.RoundDurationInMillis) * time.Millisecond) instance.CoreComponentsHolder, err = CreateCoreComponents(ArgsCoreComponentsHolder{ Config: *args.Configs.GeneralConfig, EnableEpochsConfig: args.Configs.EpochConfig.EnableEpochs, @@ -121,7 +113,6 @@ func NewTestOnlyProcessingNode(args ArgsTestOnlyProcessingNode) (*testOnlyProces RoundDurationInMs: args.RoundDurationInMillis, RatingConfig: *args.Configs.RatingsConfig, GenesisTime: args.GenesisTime, - SupernovaGenesisTime: supernovaGenesisTime, }) if err != nil { return nil, err diff --git a/node/chainSimulator/configs/configs.go b/node/chainSimulator/configs/configs.go index 518f0c28bee..bc140aca7bf 100644 --- a/node/chainSimulator/configs/configs.go +++ b/node/chainSimulator/configs/configs.go @@ -41,6 +41,8 @@ const ( // ChainSimulatorConsensusGroupSize defines the size of the consensus group for chain simulator ChainSimulatorConsensusGroupSize = 1 allValidatorsPemFileName = "allValidatorsKeys.pem" + + numRoundsAfterSupernovaEnableEpoch = 5 ) // ArgsChainSimulatorConfigs holds all the components needed to create the chain simulator configs @@ -56,6 +58,7 @@ type ArgsChainSimulatorConfigs struct { MetaChainConsensusGroupSize uint32 Hysteresis float32 InitialEpoch uint32 + InitialRound int64 RoundsPerEpoch core.OptionalUint64 SupernovaRoundsPerEpoch core.OptionalUint64 NumNodesWaitingListShard uint32 @@ -181,14 +184,34 @@ func updateSupernovaConfigs(configs *config.Configs, args ArgsChainSimulatorConf } } - if args.RoundsPerEpoch.HasValue { - // update supernova round for the new rounds per epoch config - newRoundsPerEpoch := args.RoundsPerEpoch.Value - newSupernovaRound := uint64(supernovaEpoch)*newRoundsPerEpoch + 5 // 5 rounds later - if isSupernovaFromGenesis { - // if supernova is from genesis, the round should be 0 as well - newSupernovaRound = 0 + if !args.RoundsPerEpoch.HasValue { + return + } + + hasCorrectActivationRound := false + diff := int(supernovaEpoch) - int(args.InitialEpoch) + if diff >= 0 { + + correctRoundActivationForSupernova := args.InitialRound + int64(diff)*int64(args.RoundsPerEpoch.Value) + numRoundsAfterSupernovaEnableEpoch + supernovaActivationRound, _ := strconv.ParseInt(configs.RoundConfig.RoundActivations[string(common.SupernovaRoundFlag)].Round, 10, 64) + if supernovaActivationRound > correctRoundActivationForSupernova { + diffRounds := supernovaActivationRound - correctRoundActivationForSupernova + diffIsGreaterThanAnEpoch := diffRounds > int64(args.SupernovaRoundsPerEpoch.Value)-numRoundsAfterSupernovaEnableEpoch + if !diffIsGreaterThanAnEpoch { + hasCorrectActivationRound = true + } } + } + + // update supernova round for the new rounds per epoch config + newRoundsPerEpoch := args.RoundsPerEpoch.Value + newSupernovaRound := uint64(supernovaEpoch)*newRoundsPerEpoch + numRoundsAfterSupernovaEnableEpoch + if isSupernovaFromGenesis { + // if supernova is from genesis, the round should be 0 as well + newSupernovaRound = 0 + } + + if !hasCorrectActivationRound || newSupernovaRound == 0 { oldOptions := configs.RoundConfig.RoundActivations[string(common.SupernovaRoundFlag)].Options configs.RoundConfig.RoundActivations[string(common.SupernovaRoundFlag)] = config.ActivationRoundByName{ Round: fmt.Sprintf("%d", newSupernovaRound), diff --git a/node/chainSimulator/configs/configs_test.go b/node/chainSimulator/configs/configs_test.go index d617219049f..f1601230c17 100644 --- a/node/chainSimulator/configs/configs_test.go +++ b/node/chainSimulator/configs/configs_test.go @@ -3,7 +3,10 @@ package configs import ( "testing" + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/integrationTests/realcomponents" + "github.com/multiversx/mx-chain-go/testscommon" "github.com/stretchr/testify/require" ) @@ -29,3 +32,27 @@ func TestNewProcessorRunnerChainArguments(t *testing.T) { pr := realcomponents.NewProcessorRunner(t, outputConfig.Configs) pr.Close(t) } + +func TestUpdateSupernovaConfigs(t *testing.T) { + t.Parallel() + + configs, err := testscommon.CreateTestConfigs(t.TempDir(), "../../../cmd/node/config") + require.Nil(t, err) + + chainSimulatorCfg := ArgsChainSimulatorConfigs{ + RoundsPerEpoch: core.OptionalUint64{ + Value: 20, + HasValue: true, + }, + SupernovaRoundsPerEpoch: core.OptionalUint64{ + Value: 200, + HasValue: true, + }, + SupernovaRoundDurationInMillis: 600, + } + + updateSupernovaConfigs(configs, chainSimulatorCfg) + require.Equal(t, uint64(600), configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[2].RoundDuration) + require.Equal(t, configs.EpochConfig.EnableEpochs.SupernovaEnableEpoch, configs.GeneralConfig.GeneralSettings.ChainParametersByEpoch[2].EnableEpoch) + require.Equal(t, "45", configs.RoundConfig.RoundActivations[string(common.SupernovaRoundFlag)].Round) +} diff --git a/node/chainSimulator/process/processor.go b/node/chainSimulator/process/processor.go index 43058aadd0d..4a3cd4c0904 100644 --- a/node/chainSimulator/process/processor.go +++ b/node/chainSimulator/process/processor.go @@ -47,6 +47,7 @@ func (creator *blocksCreator) IncrementRound() { manual.IncrementIndex() creator.nodeHandler.GetStatusCoreComponents().AppStatusHandler().SetUInt64Value(common.MetricCurrentRound, uint64(roundHandler.Index())) + creator.nodeHandler.GetStatusCoreComponents().AppStatusHandler().SetUInt64Value(common.MetricRoundDuration, uint64(roundHandler.TimeDuration().Milliseconds())) } func (creator *blocksCreator) createHeaderBasedOnRound(round uint64, nonce uint64) (data.HeaderHandler, error) { diff --git a/node/chainSimulator/process/processor_test.go b/node/chainSimulator/process/processor_test.go index fd999dea37d..23b35128b01 100644 --- a/node/chainSimulator/process/processor_test.go +++ b/node/chainSimulator/process/processor_test.go @@ -85,8 +85,10 @@ func TestBlocksCreator_IncrementRound(t *testing.T) { return &testsFactory.StatusCoreComponentsStub{ AppStatusHandlerField: &statusHandler.AppStatusHandlerStub{ SetUInt64ValueHandler: func(key string, value uint64) { - wasSetUInt64ValueCalled = true - require.Equal(t, common.MetricCurrentRound, key) + + if key == common.MetricCurrentRound { + wasSetUInt64ValueCalled = true + } }, }, } diff --git a/node/external/timemachine/fee/feeComputer_test.go b/node/external/timemachine/fee/feeComputer_test.go index c3b2cd49655..6fc95fb2d0d 100644 --- a/node/external/timemachine/fee/feeComputer_test.go +++ b/node/external/timemachine/fee/feeComputer_test.go @@ -12,6 +12,7 @@ import ( "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/economics" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/stretchr/testify/assert" @@ -21,9 +22,10 @@ import ( func createEconomicsData() process.EconomicsDataHandler { economicsConfig := testscommon.GetEconomicsConfig() economicsData, _ := economics.NewEconomicsData(economics.ArgsNewEconomicsData{ - TxVersionChecker: &testscommon.TxVersionCheckerStub{}, - Economics: &economicsConfig, - EpochNotifier: &epochNotifier.EpochNotifierStub{}, + TxVersionChecker: &testscommon.TxVersionCheckerStub{}, + Economics: &economicsConfig, + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, + EpochNotifier: &epochNotifier.EpochNotifierStub{}, EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{ IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { if flag == common.PenalizedTooMuchGasFlag { diff --git a/node/external/timemachine/fee/memoryFootprint/memory_test.go b/node/external/timemachine/fee/memoryFootprint/memory_test.go index 5f3742ffd11..5f653ec0c58 100644 --- a/node/external/timemachine/fee/memoryFootprint/memory_test.go +++ b/node/external/timemachine/fee/memoryFootprint/memory_test.go @@ -11,6 +11,7 @@ import ( "github.com/multiversx/mx-chain-go/node/external/timemachine/fee" "github.com/multiversx/mx-chain-go/process/economics" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/stretchr/testify/require" @@ -30,7 +31,8 @@ func TestFeeComputer_MemoryFootprint(t *testing.T) { economicsConfig := testscommon.GetEconomicsConfig() economicsData, _ := economics.NewEconomicsData(economics.ArgsNewEconomicsData{ - Economics: &economicsConfig, + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, + Economics: &economicsConfig, EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{ IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { if flag == common.PenalizedTooMuchGasFlag { diff --git a/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go b/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go index 4e817f4cf55..aa023c9c499 100644 --- a/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go +++ b/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go @@ -15,6 +15,7 @@ import ( "github.com/multiversx/mx-chain-go/process/economics" "github.com/multiversx/mx-chain-go/process/smartContract" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/stretchr/testify/require" @@ -23,6 +24,7 @@ import ( func createEconomicsData(enableEpochsHandler common.EnableEpochsHandler) process.EconomicsDataHandler { economicsConfig := testscommon.GetEconomicsConfig() economicsData, _ := economics.NewEconomicsData(economics.ArgsNewEconomicsData{ + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &economicsConfig, EnableEpochsHandler: enableEpochsHandler, TxVersionChecker: &testscommon.TxVersionCheckerStub{}, diff --git a/outport/process/transactionsfee/transactionsFeeProcessor_test.go b/outport/process/transactionsfee/transactionsFeeProcessor_test.go index 67a0d98d45f..1c1a1dc7c67 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor_test.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor_test.go @@ -10,17 +10,17 @@ import ( outportcore "github.com/multiversx/mx-chain-core-go/data/outport" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" - "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/outport/mock" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/economics" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/multiversx/mx-chain-go/testscommon/genericMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" + "github.com/stretchr/testify/require" ) var pubKeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(32, "erd") @@ -28,6 +28,7 @@ var pubKeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(32, "erd") func createEconomicsData(enableEpochsHandler common.EnableEpochsHandler) process.EconomicsDataHandler { economicsConfig := testscommon.GetEconomicsConfig() economicsData, _ := economics.NewEconomicsData(economics.ArgsNewEconomicsData{ + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &economicsConfig, EnableEpochsHandler: enableEpochsHandler, TxVersionChecker: &testscommon.TxVersionCheckerStub{}, diff --git a/process/block/metablock.go b/process/block/metablock.go index ac65fefee02..12e9a8ac848 100644 --- a/process/block/metablock.go +++ b/process/block/metablock.go @@ -414,7 +414,7 @@ func (mp *metaProcessor) processEpochStartMetaBlock( return err } - err = mp.epochEconomics.VerifyRewardsPerBlock(header, mp.epochRewardsCreator.GetProtocolSustainabilityRewards(), computedEconomics) + err = mp.epochEconomics.VerifyRewardsPerBlock(header, mp.epochRewardsCreator.GetAcceleratorRewards(), computedEconomics) if err != nil { return err } @@ -872,7 +872,7 @@ func (mp *metaProcessor) processEpochStartMiniBlocks( return nil, err } - computedEconomics.RewardsForProtocolSustainability.Set(mp.epochRewardsCreator.GetProtocolSustainabilityRewards()) + computedEconomics.RewardsForProtocolSustainability.Set(mp.epochRewardsCreator.GetAcceleratorRewards()) err = mp.epochSystemSCProcessor.ProcessDelegationRewards(rewardMiniBlocks, mp.epochRewardsCreator.GetLocalTxCache()) if err != nil { diff --git a/process/block/metablock_test.go b/process/block/metablock_test.go index 7ed8ef7c79e..07ba0cfa28b 100644 --- a/process/block/metablock_test.go +++ b/process/block/metablock_test.go @@ -3773,7 +3773,7 @@ func TestMetaProcessor_CreateEpochStartBodyShouldWork(t *testing.T) { assert.True(t, wasCalled) return rewardMiniBlocks, nil }, - GetProtocolSustainabilityRewardsCalled: func() *big.Int { + GetAcceleratorRewardsCalled: func() *big.Int { return expectedRewardsForProtocolSustain }, } @@ -3843,7 +3843,7 @@ func TestMetaProcessor_CreateEpochStartBodyShouldWork(t *testing.T) { assert.Equal(t, mb, metaBlock) return rewardMiniBlocks, nil }, - GetProtocolSustainabilityRewardsCalled: func() *big.Int { + GetAcceleratorRewardsCalled: func() *big.Int { return expectedRewardsForProtocolSustain }, } diff --git a/process/economics/economicsData.go b/process/economics/economicsData.go index a104dd430f4..1dfddd92491 100644 --- a/process/economics/economicsData.go +++ b/process/economics/economicsData.go @@ -28,10 +28,8 @@ var log = logger.GetOrCreate("process/economics") type economicsData struct { *gasConfigHandler *rewardsConfigHandler + *globalSettingsHandler gasPriceModifier float64 - minInflation float64 - yearSettings map[uint32]*config.YearSetting - mutYearSettings sync.RWMutex statusHandler core.AppStatusHandler enableEpochsHandler common.EnableEpochsHandler txVersionHandler process.TxVersionCheckerHandler @@ -42,6 +40,7 @@ type economicsData struct { type ArgsNewEconomicsData struct { TxVersionChecker process.TxVersionCheckerHandler Economics *config.EconomicsConfig + ChainParamsHandler process.ChainParametersHandler EpochNotifier process.EpochNotifier EnableEpochsHandler common.EnableEpochsHandler PubkeyConverter core.PubkeyConverter @@ -79,21 +78,12 @@ func NewEconomicsData(args ArgsNewEconomicsData) (*economicsData, error) { } ed := &economicsData{ - minInflation: args.Economics.GlobalSettings.MinimumInflation, gasPriceModifier: args.Economics.FeeSettings.GasPriceModifier, statusHandler: statusHandler.NewNilStatusHandler(), enableEpochsHandler: args.EnableEpochsHandler, txVersionHandler: args.TxVersionChecker, } - ed.yearSettings = make(map[uint32]*config.YearSetting) - for _, yearSetting := range args.Economics.GlobalSettings.YearSettings { - ed.yearSettings[yearSetting.Year] = &config.YearSetting{ - Year: yearSetting.Year, - MaximumInflation: yearSetting.MaximumInflation, - } - } - ed.gasConfigHandler, err = newGasConfigHandler(args.Economics) if err != nil { return nil, err @@ -104,6 +94,11 @@ func NewEconomicsData(args ArgsNewEconomicsData) (*economicsData, error) { return nil, err } + ed.globalSettingsHandler, err = newGlobalSettingsHandler(args.Economics, args.ChainParamsHandler) + if err != nil { + return nil, err + } + args.EpochNotifier.RegisterNotifyHandler(ed) return ed, nil @@ -157,22 +152,14 @@ func (ed *economicsData) LeaderPercentageInEpoch(epoch uint32) float64 { return ed.getLeaderPercentage(epoch) } -// MinInflationRate returns the minimum inflation rate -func (ed *economicsData) MinInflationRate() float64 { - return ed.minInflation -} - // MaxInflationRate returns the maximum inflation rate -func (ed *economicsData) MaxInflationRate(year uint32) float64 { - ed.mutYearSettings.RLock() - yearSetting, ok := ed.yearSettings[year] - ed.mutYearSettings.RUnlock() - - if !ok { - return ed.minInflation - } +func (ed *economicsData) MaxInflationRate(year uint32, epoch uint32) float64 { + return ed.globalSettingsHandler.maxInflationRate(year, epoch) +} - return yearSetting.MaximumInflation +// IsTailInflationEnabled returns if the tail inflation is enabled +func (ed *economicsData) IsTailInflationEnabled(epoch uint32) bool { + return ed.globalSettingsHandler.isTailInflationActive(epoch) } // GenesisTotalSupply returns the genesis total supply @@ -652,6 +639,26 @@ func (ed *economicsData) getExtraGasLimitRelayedTx(txInstance *transaction.Trans return 0 } +// EcosystemGrowthPercentageInEpoch returns the ecosystem growth percentage in a specific epoch +func (ed *economicsData) EcosystemGrowthPercentageInEpoch(epoch uint32) float64 { + return ed.rewardsConfigHandler.getEcosystemGrowthPercentage(epoch) +} + +// EcosystemGrowthAddressInEpoch returns the ecosystem growth address in a specific epoch +func (ed *economicsData) EcosystemGrowthAddressInEpoch(epoch uint32) string { + return ed.rewardsConfigHandler.getEcosystemGrowthAddress(epoch) +} + +// GrowthDividendPercentageInEpoch returns the growth dividend percentage in a specific epoch +func (ed *economicsData) GrowthDividendPercentageInEpoch(epoch uint32) float64 { + return ed.rewardsConfigHandler.getGrowthDividendPercentage(epoch) +} + +// GrowthDividendAddressInEpoch returns the growth dividend address in a specific epoch +func (ed *economicsData) GrowthDividendAddressInEpoch(epoch uint32) string { + return ed.rewardsConfigHandler.getGrowthDividendAddress(epoch) +} + // IsInterfaceNil returns true if there is no value under the interface func (ed *economicsData) IsInterfaceNil() bool { return ed == nil diff --git a/process/economics/economicsData_test.go b/process/economics/economicsData_test.go index 91f10f4a36c..9c42f276357 100644 --- a/process/economics/economicsData_test.go +++ b/process/economics/economicsData_test.go @@ -19,6 +19,7 @@ import ( "github.com/multiversx/mx-chain-go/process/economics" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/multiversx/mx-chain-go/testscommon/statusHandler" @@ -48,6 +49,10 @@ func createDummyEconomicsConfig(feeSettings config.FeeSettings) *config.Economic TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, EpochEnable: 0, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, }, }, @@ -101,9 +106,11 @@ func createArgsForEconomicsData(gasModifier float64) economics.ArgsNewEconomicsD feeSettings := feeSettingsDummy(gasModifier) pkConv, _ := pubkeyConverter.NewBech32PubkeyConverter(32, "erd") shardC, _ := sharding.NewMultiShardCoordinator(2, 0) + args := economics.ArgsNewEconomicsData{ - Economics: createDummyEconomicsConfig(feeSettings), - EpochNotifier: &epochNotifier.EpochNotifierStub{}, + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, + Economics: createDummyEconomicsConfig(feeSettings), + EpochNotifier: &epochNotifier.EpochNotifierStub{}, EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{ IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { return flag == common.GasPriceModifierFlag @@ -121,8 +128,9 @@ func createArgsForEconomicsDataRealFees() economics.ArgsNewEconomicsData { pkConv, _ := pubkeyConverter.NewBech32PubkeyConverter(32, "erd") shardC, _ := sharding.NewMultiShardCoordinator(2, 0) args := economics.ArgsNewEconomicsData{ - Economics: createDummyEconomicsConfig(feeSettings), - EpochNotifier: &epochNotifier.EpochNotifierStub{}, + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, + Economics: createDummyEconomicsConfig(feeSettings), + EpochNotifier: &epochNotifier.EpochNotifierStub{}, EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{ IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { return flag == common.GasPriceModifierFlag @@ -769,6 +777,10 @@ func TestEconomicsData_ConfirmedEpochRewardsSettingsChangeOrderedConfigs(t *test TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, EpochEnable: 0, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, { LeaderPercentage: 0.2, @@ -778,6 +790,10 @@ func TestEconomicsData_ConfirmedEpochRewardsSettingsChangeOrderedConfigs(t *test TopUpGradientPoint: "200000000000000000000", TopUpFactor: 0.5, EpochEnable: 2, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, } @@ -857,6 +873,10 @@ func TestEconomicsData_ConfirmedEpochRewardsSettingsChangeUnOrderedConfigs(t *te TopUpGradientPoint: "200000000000000000000", TopUpFactor: 0.5, EpochEnable: 2, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, { LeaderPercentage: 0.1, @@ -866,6 +886,10 @@ func TestEconomicsData_ConfirmedEpochRewardsSettingsChangeUnOrderedConfigs(t *te TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, EpochEnable: 0, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, } expectedRS := getExpectedSettings(rs, args.PubkeyConverter) @@ -1474,18 +1498,6 @@ func TestEconomicsData_SetStatusHandler(t *testing.T) { }) } -func TestEconomicsData_MinInflationRate(t *testing.T) { - t.Parallel() - - args := createArgsForEconomicsData(1) - minInflationRate := 0.40 - args.Economics.GlobalSettings.MinimumInflation = minInflationRate - economicsData, _ := economics.NewEconomicsData(args) - - value := economicsData.MinInflationRate() - assert.Equal(t, minInflationRate, value) -} - func TestEconomicsData_MaxInflationRate(t *testing.T) { t.Parallel() @@ -1494,12 +1506,13 @@ func TestEconomicsData_MaxInflationRate(t *testing.T) { maxInflationRate := 0.99 args.Economics.GlobalSettings.MinimumInflation = minInflationRate args.Economics.GlobalSettings.YearSettings[0].MaximumInflation = maxInflationRate + args.Economics.GlobalSettings.TailInflation.EnableEpoch = 100 economicsData, _ := economics.NewEconomicsData(args) - value := economicsData.MaxInflationRate(0) + value := economicsData.MaxInflationRate(0, 0) assert.Equal(t, maxInflationRate, value) - value = economicsData.MaxInflationRate(1) // missing from GlobalSettings + value = economicsData.MaxInflationRate(1, 1) // missing from GlobalSettings assert.Equal(t, minInflationRate, value) } @@ -1743,6 +1756,22 @@ func TestEconomicsData_RewardsTopUpFactor(t *testing.T) { assert.Equal(t, topUpFactor, value) } +func TestEconomicsData_RewardsSettingsGetters(t *testing.T) { + t.Parallel() + + args := createArgsForEconomicsData(1) + economicsData, _ := economics.NewEconomicsData(args) + + pkConv, _ := pubkeyConverter.NewBech32PubkeyConverter(32, "erd") + expectedAddr, _ := pkConv.Decode("erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp") + + // Test getters for epoch 0 + assert.Equal(t, 0.0, economicsData.EcosystemGrowthPercentageInEpoch(0)) + assert.Equal(t, string(expectedAddr), economicsData.EcosystemGrowthAddressInEpoch(0)) + assert.Equal(t, 0.0, economicsData.GrowthDividendPercentageInEpoch(0)) + assert.Equal(t, string(expectedAddr), economicsData.GrowthDividendAddressInEpoch(0)) +} + func getExpectedSettings(rs []config.EpochRewardSettings, pkConv core.PubkeyConverter) []config.EpochRewardSettings { expectedRS := make([]config.EpochRewardSettings, 0, len(rs)) for _, rewardSettingsPerEpoch := range rs { diff --git a/process/economics/globalSettings.go b/process/economics/globalSettings.go new file mode 100644 index 00000000000..a8148df598b --- /dev/null +++ b/process/economics/globalSettings.go @@ -0,0 +1,141 @@ +package economics + +import ( + "math" + "sort" + "sync" + + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/process" +) + +type inflationForEpochCompound struct { + enableEpoch uint32 + maxInflation float64 +} + +type globalSettingsHandler struct { + minInflation float64 + yearSettings map[uint32]*config.YearSetting + chainParameters process.ChainParametersHandler + tailInflationActivationEpoch uint32 + startYearInflation float64 + inflationForEpoch []inflationForEpochCompound + decayPercentage float64 + mutYearSettings sync.RWMutex +} + +var numMillisecondsPerSeconds = uint64(1000) +var numSecondsPerMinute = uint64(60) +var numMinutesPerHour = uint64(60) +var numHoursPerDay = uint64(24) +var numDaysInYear = uint64(365) +var numberOfMillisecondsInYear = numMillisecondsPerSeconds * numSecondsPerMinute * numMinutesPerHour * numHoursPerDay * numDaysInYear + +// newGlobalSettingsHandler creates a new global settings provider +func newGlobalSettingsHandler( + economics *config.EconomicsConfig, + chainParameters process.ChainParametersHandler, +) (*globalSettingsHandler, error) { + if check.IfNil(chainParameters) { + return nil, process.ErrNilChainParametersHandler + } + + g := &globalSettingsHandler{ + minInflation: economics.GlobalSettings.MinimumInflation, + yearSettings: make(map[uint32]*config.YearSetting), + tailInflationActivationEpoch: economics.GlobalSettings.TailInflation.EnableEpoch, + startYearInflation: economics.GlobalSettings.TailInflation.StartYearInflation, + decayPercentage: economics.GlobalSettings.TailInflation.DecayPercentage, + mutYearSettings: sync.RWMutex{}, + chainParameters: chainParameters, + } + + for _, yearSetting := range economics.GlobalSettings.YearSettings { + g.yearSettings[yearSetting.Year] = &config.YearSetting{ + Year: yearSetting.Year, + MaximumInflation: yearSetting.MaximumInflation, + } + } + + err := g.calculateInflationForEpochCompound() + if err != nil { + return nil, err + } + + if isPercentageInvalid(g.minInflation) || + isPercentageInvalid(g.startYearInflation) || + isPercentageInvalid(g.decayPercentage) { + return nil, process.ErrInvalidInflationPercentages + } + + return g, nil +} + +func (g *globalSettingsHandler) calculateInflationForEpochCompound() error { + allChainParams := g.chainParameters.AllChainParameters() + if len(allChainParams) == 0 { + return process.ErrEmptyChainParametersConfiguration + } + + g.inflationForEpoch = make([]inflationForEpochCompound, len(allChainParams)) + + for index, chainParams := range allChainParams { + roundsPerEpoch := chainParams.RoundsPerEpoch + roundDuration := chainParams.RoundDuration + + epochDurationInMilliseconds := roundDuration * uint64(roundsPerEpoch) + if epochDurationInMilliseconds == 0 { + return process.ErrZeroDurationForEpoch + } + + numberOfEpochsPerYear := float64(numberOfMillisecondsInYear) / float64(epochDurationInMilliseconds) + + inflationForCompound := numberOfEpochsPerYear * (math.Pow(1.0+g.startYearInflation, 1.0/numberOfEpochsPerYear) - 1) + g.inflationForEpoch[index] = inflationForEpochCompound{ + enableEpoch: chainParams.EnableEpoch, + maxInflation: inflationForCompound, + } + + if isPercentageInvalid(inflationForCompound) { + return process.ErrInvalidInflationPercentages + } + } + + sort.SliceStable(g.inflationForEpoch, func(i, j int) bool { + return g.inflationForEpoch[i].enableEpoch > g.inflationForEpoch[j].enableEpoch + }) + + return nil +} + +// TODO: implement decay, implement growth, calculations will change after supernova +func (g *globalSettingsHandler) maxInflationRate(year uint32, epoch uint32) float64 { + if g.isTailInflationActive(epoch) { + for _, inflationForEpoch := range g.inflationForEpoch { + if inflationForEpoch.enableEpoch <= epoch { + return inflationForEpoch.maxInflation + } + } + return 0.0 + } + + return g.yearSettingsInflation(year) +} + +func (g *globalSettingsHandler) yearSettingsInflation(year uint32) float64 { + g.mutYearSettings.RLock() + yearSetting, ok := g.yearSettings[year] + g.mutYearSettings.RUnlock() + + if !ok { + return g.minInflation + } + + return yearSetting.MaximumInflation +} + +func (g *globalSettingsHandler) isTailInflationActive(epoch uint32) bool { + return epoch > g.tailInflationActivationEpoch +} diff --git a/process/economics/globalSettings_test.go b/process/economics/globalSettings_test.go new file mode 100644 index 00000000000..bb911265b65 --- /dev/null +++ b/process/economics/globalSettings_test.go @@ -0,0 +1,182 @@ +package economics + +import ( + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" + "github.com/stretchr/testify/require" + "math" + "testing" +) + +func createGlobalSettingsHandler() *globalSettingsHandler { + tailInflationActivationEpoch := uint32(100) + startYearInflation := 0.08757 + minInflation := 0.01 + economics := config.EconomicsConfig{ + GlobalSettings: config.GlobalSettings{ + GenesisTotalSupply: "2000000000000000000000", + MinimumInflation: minInflation, + YearSettings: []*config.YearSetting{ + { + Year: 1, + MaximumInflation: 0.1, + }, + }, + TailInflation: config.TailInflationSettings{ + EnableEpoch: tailInflationActivationEpoch, + StartYearInflation: startYearInflation, + DecayPercentage: 0, + MinimumInflation: minInflation, + }, + }, + RewardsSettings: config.RewardsSettings{ + RewardsConfigByEpoch: []config.EpochRewardSettings{ + { + LeaderPercentage: 0.1, + DeveloperPercentage: 0.1, + ProtocolSustainabilityPercentage: 0.1, + ProtocolSustainabilityAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + TopUpGradientPoint: "300000000000000000000", + TopUpFactor: 0.25, + EpochEnable: 0, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + }, + }, + }, + FeeSettings: config.FeeSettings{}, + } + + chainParams := config.ChainParametersByEpochConfig{RoundsPerEpoch: 14400, RoundDuration: 6000} + chainParamsHolder := &chainParameters.ChainParametersHandlerStub{ + ChainParametersForEpochCalled: func(epoch uint32) (config.ChainParametersByEpochConfig, error) { + return chainParams, nil + }, + CurrentChainParametersCalled: func() config.ChainParametersByEpochConfig { + return chainParams + }, + AllChainParametersCalled: func() []config.ChainParametersByEpochConfig { + return []config.ChainParametersByEpochConfig{chainParams} + }, + } + + gsh, _ := newGlobalSettingsHandler(&economics, chainParamsHolder) + return gsh +} + +func TestGlobalSettingsHandler_maxInflationRate(t *testing.T) { + t.Parallel() + + tailInflationActivationEpoch := uint32(100) + year1Inflation := 0.1 + minInflation := 0.01 + + gsh := createGlobalSettingsHandler() + gsh.yearSettings[1] = &config.YearSetting{Year: 1, MaximumInflation: year1Inflation} + + // Test before tail inflation activation + rate := gsh.maxInflationRate(1, tailInflationActivationEpoch-1) + require.Equal(t, year1Inflation, rate) + + // Test at tail inflation activation + rate = gsh.maxInflationRate(1, tailInflationActivationEpoch) + require.Equal(t, year1Inflation, rate) + + // Test after tail inflation activation + rate = gsh.maxInflationRate(1, tailInflationActivationEpoch+1) + require.Equal(t, 0.08395550376084304, rate) + + // Test with a year that is not in the map + rate = gsh.maxInflationRate(2, tailInflationActivationEpoch-1) + require.Equal(t, minInflation, rate) +} + +func TestGlobalSettingsMaxInflation(t *testing.T) { + totalSupplyDay0 := 1000000.0 // Initial supply (not actually needed for Y, but included as per query) + x := 0.05 // Annual inflation rate as decimal (e.g., 5%) + + y := math.Pow(1+x, 1.0/365) - 1 + + totalSupplyDay365 := totalSupplyDay0 * math.Pow(1+y, 365) + + totalSupplyOtherCompute := totalSupplyDay0 + totalSupplyDay0*x + + require.True(t, totalSupplyOtherCompute >= totalSupplyDay365) + require.True(t, totalSupplyOtherCompute-totalSupplyDay365 < 1) +} + +func TestGlobalSettingsMaxInflationRate_withSupply(t *testing.T) { + gsh := createGlobalSettingsHandler() + + rate := gsh.maxInflationRate(1, 101) + + oneToken := 1000000000000000000.0 // 10^18 + totalSupplyDay0 := 28781358.0 * oneToken + totalSupplyDay365Yearly := totalSupplyDay0 + totalSupplyDay0*gsh.startYearInflation + + dailyRate := rate / float64(numDaysInYear) + totalSupplyDay365Daily := totalSupplyDay0 * math.Pow(1+dailyRate, float64(numDaysInYear)) + + require.True(t, totalSupplyDay365Yearly >= totalSupplyDay365Daily) + require.True(t, totalSupplyDay365Yearly-totalSupplyDay365Daily < oneToken) +} + +func TestNewGlobalSettingsHandler_ZeroEpochDuration(t *testing.T) { + t.Parallel() + + economics := &config.EconomicsConfig{} + chainParams := config.ChainParametersByEpochConfig{RoundsPerEpoch: 100, RoundDuration: 0} + chainParamsHolder := &chainParameters.ChainParametersHandlerStub{ + AllChainParametersCalled: func() []config.ChainParametersByEpochConfig { + return []config.ChainParametersByEpochConfig{chainParams} + }, + } + + _, err := newGlobalSettingsHandler(economics, chainParamsHolder) + require.Error(t, err) +} + +func TestNewGlobalSettingsHandler_Sorting(t *testing.T) { + t.Parallel() + + economics := &config.EconomicsConfig{} + chainParams1 := config.ChainParametersByEpochConfig{RoundsPerEpoch: 100, RoundDuration: 1000, EnableEpoch: 10} + chainParams2 := config.ChainParametersByEpochConfig{RoundsPerEpoch: 200, RoundDuration: 2000, EnableEpoch: 20} + chainParamsHolder := &chainParameters.ChainParametersHandlerStub{ + AllChainParametersCalled: func() []config.ChainParametersByEpochConfig { + return []config.ChainParametersByEpochConfig{chainParams1, chainParams2} + }, + } + + gsh, err := newGlobalSettingsHandler(economics, chainParamsHolder) + require.NoError(t, err) + + require.Equal(t, uint32(20), gsh.inflationForEpoch[0].enableEpoch) + require.Equal(t, uint32(10), gsh.inflationForEpoch[1].enableEpoch) +} + +func TestNewGlobalSettingsHandler_MultipleChainParameters(t *testing.T) { + t.Parallel() + + economics := &config.EconomicsConfig{} + economics.GlobalSettings.TailInflation.StartYearInflation = 0.1 + + chainParams1 := config.ChainParametersByEpochConfig{RoundsPerEpoch: 14400, RoundDuration: 6000, EnableEpoch: 10} + chainParams2 := config.ChainParametersByEpochConfig{RoundsPerEpoch: 7200, RoundDuration: 3000, EnableEpoch: 20} + chainParamsHolder := &chainParameters.ChainParametersHandlerStub{ + AllChainParametersCalled: func() []config.ChainParametersByEpochConfig { + return []config.ChainParametersByEpochConfig{chainParams1, chainParams2} + }, + } + + gsh, err := newGlobalSettingsHandler(economics, chainParamsHolder) + require.NoError(t, err) + + require.Equal(t, uint32(20), gsh.inflationForEpoch[0].enableEpoch) + require.InDelta(t, 0.0953137, gsh.inflationForEpoch[0].maxInflation, 0.000001) + + require.Equal(t, uint32(10), gsh.inflationForEpoch[1].enableEpoch) + require.InDelta(t, 0.0953227, gsh.inflationForEpoch[1].maxInflation, 0.000001) +} diff --git a/process/economics/rewardsConfigHandler.go b/process/economics/rewardsConfigHandler.go index 60a2dd93960..770904a5a09 100644 --- a/process/economics/rewardsConfigHandler.go +++ b/process/economics/rewardsConfigHandler.go @@ -20,6 +20,10 @@ type rewardsConfig struct { leaderPercentage float64 protocolSustainabilityPercentage float64 protocolSustainabilityAddress string + ecosystemGrowthPercentage float64 + ecosystemGrowthAddress string + growthDividendPercentage float64 + growthDividendAddress string developerPercentage float64 topUpGradientPoint *big.Int topUpFactor float64 @@ -113,6 +117,30 @@ func (handler *rewardsConfigHandler) getRewardsConfigForEpoch(epoch uint32) *rew return rewardsConfigSetting } +// getEcosystemGrowthPercentage returns the ecosystem growth percentage in a specific epoch +func (handler *rewardsConfigHandler) getEcosystemGrowthPercentage(epoch uint32) float64 { + rc := handler.getRewardsConfigForEpoch(epoch) + return rc.ecosystemGrowthPercentage +} + +// getGrowthDividendPercentage returns the growth dividend percentage in a specific epoch +func (handler *rewardsConfigHandler) getGrowthDividendPercentage(epoch uint32) float64 { + rc := handler.getRewardsConfigForEpoch(epoch) + return rc.growthDividendPercentage +} + +// getEcosystemGrowthAddress returns the ecosystem growth address in a specific epoch +func (handler *rewardsConfigHandler) getEcosystemGrowthAddress(epoch uint32) string { + rc := handler.getRewardsConfigForEpoch(epoch) + return rc.ecosystemGrowthAddress +} + +// getGrowthDividendAddress returns the growth dividend address in a specific epoch +func (handler *rewardsConfigHandler) getGrowthDividendAddress(epoch uint32) string { + rc := handler.getRewardsConfigForEpoch(epoch) + return rc.growthDividendAddress +} + func (handler *rewardsConfigHandler) updateRewardsConfigMetrics(epoch uint32) { rc := handler.getRewardsConfigForEpoch(epoch) @@ -148,26 +176,30 @@ func checkAndParseRewardsSettings( topUpGradientPoint, _ := big.NewInt(0).SetString(rewardsCfg.TopUpGradientPoint, 10) - decodedAddress, err := pubkeyConverter.Decode(rewardsCfg.ProtocolSustainabilityAddress) + protocolSustainability, err := decodeAddressAndVerifyShard(pubkeyConverter, shardCoordinator, rewardsCfg.ProtocolSustainabilityAddress) if err != nil { - log.Warn("invalid protocol sustainability reward address", - "err", err, - "provided address", rewardsCfg.ProtocolSustainabilityAddress, - "epoch", rewardsCfg.EpochEnable, - ) return nil, err } - protocolSustainabilityShardID := shardCoordinator.ComputeId(decodedAddress) - if protocolSustainabilityShardID == core.MetachainShardId { - return nil, process.ErrProtocolSustainabilityAddressInMetachain + ecosystemGrowth, err := decodeAddressAndVerifyShard(pubkeyConverter, shardCoordinator, rewardsCfg.EcosystemGrowthAddress) + if err != nil { + return nil, err + } + + growthDividend, err := decodeAddressAndVerifyShard(pubkeyConverter, shardCoordinator, rewardsCfg.GrowthDividendAddress) + if err != nil { + return nil, err } rewardsConfigSlice = append(rewardsConfigSlice, &rewardsConfig{ rewardsSettingEpoch: rewardsCfg.EpochEnable, leaderPercentage: rewardsCfg.LeaderPercentage, protocolSustainabilityPercentage: rewardsCfg.ProtocolSustainabilityPercentage, - protocolSustainabilityAddress: string(decodedAddress), + protocolSustainabilityAddress: string(protocolSustainability), + ecosystemGrowthPercentage: rewardsCfg.EcosystemGrowthPercentage, + ecosystemGrowthAddress: string(ecosystemGrowth), + growthDividendPercentage: rewardsCfg.GrowthDividendPercentage, + growthDividendAddress: string(growthDividend), developerPercentage: rewardsCfg.DeveloperPercentage, topUpGradientPoint: topUpGradientPoint, topUpFactor: rewardsCfg.TopUpFactor, @@ -177,17 +209,52 @@ func checkAndParseRewardsSettings( return rewardsConfigSlice, nil } +func decodeAddressAndVerifyShard( + pubkeyConverter core.PubkeyConverter, + shardCoordinator sharding.Coordinator, + address string, +) ([]byte, error) { + decodedAddress, err := pubkeyConverter.Decode(address) + if err != nil { + log.Warn("invalid reward address", + "err", err, + "provided address", address, + ) + return nil, err + } + + addressShardID := shardCoordinator.ComputeId(decodedAddress) + if addressShardID == core.MetachainShardId { + return nil, process.ErrProtocolSustainabilityAddressInMetachain + } + + return decodedAddress, nil +} + func checkRewardConfig(rewardsCfg config.EpochRewardSettings) error { if isPercentageInvalid(rewardsCfg.LeaderPercentage) || isPercentageInvalid(rewardsCfg.DeveloperPercentage) || isPercentageInvalid(rewardsCfg.ProtocolSustainabilityPercentage) || - isPercentageInvalid(rewardsCfg.TopUpFactor) { + isPercentageInvalid(rewardsCfg.TopUpFactor) || + isPercentageInvalid(rewardsCfg.GrowthDividendPercentage) || + isPercentageInvalid(rewardsCfg.EcosystemGrowthPercentage) { + return process.ErrInvalidRewardsPercentages + } + + accPercentage := rewardsCfg.ProtocolSustainabilityPercentage + rewardsCfg.GrowthDividendPercentage + rewardsCfg.EcosystemGrowthPercentage + if isPercentageInvalid(accPercentage) { return process.ErrInvalidRewardsPercentages } if len(rewardsCfg.ProtocolSustainabilityAddress) == 0 { return process.ErrNilProtocolSustainabilityAddress } + if len(rewardsCfg.EcosystemGrowthAddress) == 0 { + return process.ErrNilEcosystemGrowthAddress + } + if len(rewardsCfg.GrowthDividendAddress) == 0 { + return process.ErrNilGrowthDividendAddress + } _, ok := big.NewInt(0).SetString(rewardsCfg.TopUpGradientPoint, 10) if !ok { diff --git a/process/errors.go b/process/errors.go index 825de6a6aca..a70beb9d81e 100644 --- a/process/errors.go +++ b/process/errors.go @@ -837,6 +837,12 @@ var ErrNilDnsAddresses = errors.New("nil dns addresses map") // ErrNilProtocolSustainabilityAddress signals that a nil protocol sustainability address was provided var ErrNilProtocolSustainabilityAddress = errors.New("nil protocol sustainability address") +// ErrNilEcosystemGrowthAddress signals that a nil ecosystem growth address was provided +var ErrNilEcosystemGrowthAddress = errors.New("nil ecosystem growth address") + +// ErrNilGrowthDividendAddress signals that a nil growth dividend address was provided +var ErrNilGrowthDividendAddress = errors.New("nil growth dividend address") + // ErrUserNameDoesNotMatch signals that username does not match var ErrUserNameDoesNotMatch = errors.New("user name does not match") @@ -1323,6 +1329,12 @@ var ErrInvalidRatingsConfig = errors.New("invalid ratings config") // ErrNilKeyRWMutexHandler signals that a nil KeyRWMutexHandler has been provided var ErrNilKeyRWMutexHandler = errors.New("nil key rw mutex handler") +// ErrZeroDurationForEpoch signals that duration for epoch is zero +var ErrZeroDurationForEpoch = errors.New("zero duration for epoch") + +// ErrInvalidChainParameters signals that invalid chain parameters has been provided +var ErrInvalidChainParameters = errors.New("invalid chain parameters") + // ErrBadSelectionGasBandwidthIncreasePercent signals a bad txcache config var ErrBadSelectionGasBandwidthIncreasePercent = errors.New("bad selection gas bandwidth increase percent") diff --git a/process/factory/metachain/vmContainerFactory_test.go b/process/factory/metachain/vmContainerFactory_test.go index e4820497315..672e9f18458 100644 --- a/process/factory/metachain/vmContainerFactory_test.go +++ b/process/factory/metachain/vmContainerFactory_test.go @@ -13,6 +13,7 @@ import ( "github.com/multiversx/mx-chain-go/process/factory" "github.com/multiversx/mx-chain-go/process/mock" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/economicsmocks" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" @@ -280,6 +281,7 @@ func TestVmContainerFactory_Create(t *testing.T) { t.Parallel() argsNewEconomicsData := economics.ArgsNewEconomicsData{ + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &config.EconomicsConfig{ GlobalSettings: config.GlobalSettings{ GenesisTotalSupply: "2000000000000000000000", @@ -300,6 +302,10 @@ func TestVmContainerFactory_Create(t *testing.T) { ProtocolSustainabilityAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, }, }, diff --git a/process/interface.go b/process/interface.go index a86f476e147..29ee87a14da 100644 --- a/process/interface.go +++ b/process/interface.go @@ -767,16 +767,20 @@ type rewardsHandler interface { LeaderPercentage() float64 ProtocolSustainabilityPercentage() float64 ProtocolSustainabilityAddress() string - MinInflationRate() float64 - MaxInflationRate(year uint32) float64 + MaxInflationRate(year uint32, epoch uint32) float64 RewardsTopUpGradientPoint() *big.Int RewardsTopUpFactor() float64 LeaderPercentageInEpoch(epoch uint32) float64 DeveloperPercentageInEpoch(epoch uint32) float64 ProtocolSustainabilityPercentageInEpoch(epoch uint32) float64 ProtocolSustainabilityAddressInEpoch(epoch uint32) string + EcosystemGrowthPercentageInEpoch(epoch uint32) float64 + EcosystemGrowthAddressInEpoch(epoch uint32) string + GrowthDividendPercentageInEpoch(epoch uint32) float64 + GrowthDividendAddressInEpoch(epoch uint32) string RewardsTopUpGradientPointInEpoch(epoch uint32) *big.Int RewardsTopUpFactorInEpoch(epoch uint32) float64 + IsTailInflationEnabled(epoch uint32) bool } // RewardsHandler will return information about rewards @@ -1099,13 +1103,13 @@ type RewardsCreator interface { VerifyRewardsMiniBlocks( metaBlock data.MetaHeaderHandler, validatorsInfo state.ShardValidatorsInfoMapHandler, computedEconomics *block.Economics, ) error + GetAcceleratorRewards() *big.Int CreateRewardsMiniBlocksV3( metaBlock data.MetaHeaderHandler, validatorsInfo state.ShardValidatorsInfoMapHandler, computedEconomics *block.Economics, prevBlockExecutionResults data.BaseMetaExecutionResultHandler, ) (block.MiniBlockSlice, error) - GetProtocolSustainabilityRewards() *big.Int GetLocalTxCache() epochStart.TransactionCacher CreateMarshalledData(body *block.Body) map[string][][]byte GetRewardsTxs(body *block.Body) map[string]data.TransactionHandler diff --git a/process/mock/rewardsHandlerMock.go b/process/mock/rewardsHandlerMock.go index e407d2e362d..8a2229d5193 100644 --- a/process/mock/rewardsHandlerMock.go +++ b/process/mock/rewardsHandlerMock.go @@ -5,12 +5,15 @@ import "math/big" // RewardsHandlerMock - type RewardsHandlerMock struct { MaxInflationRateCalled func() float64 - MinInflationRateCalled func() float64 LeaderPercentageCalled func() float64 ProtocolSustainabilityPercentageCalled func() float64 ProtocolSustainabilityAddressCalled func() string RewardsTopUpGradientPointCalled func() *big.Int RewardsTopUpFactorCalled func() float64 + EcosystemGrowthPercentageInEpochCalled func(epoch uint32) float64 + EcosystemGrowthAddressInEpochCalled func(epoch uint32) string + GrowthDividendPercentageInEpochCalled func(epoch uint32) float64 + GrowthDividendAddressInEpochCalled func(epoch uint32) string } // LeaderPercentage - @@ -28,11 +31,6 @@ func (rhm *RewardsHandlerMock) ProtocolSustainabilityAddress() string { return rhm.ProtocolSustainabilityAddressCalled() } -// MinInflationRate - -func (rhm *RewardsHandlerMock) MinInflationRate() float64 { - return rhm.MinInflationRateCalled() -} - // MaxInflationRate - func (rhm *RewardsHandlerMock) MaxInflationRate(uint32) float64 { return rhm.MaxInflationRateCalled() @@ -48,6 +46,38 @@ func (rhm *RewardsHandlerMock) RewardsTopUpFactor() float64 { return rhm.RewardsTopUpFactorCalled() } +// EcosystemGrowthPercentageInEpoch - +func (rhm *RewardsHandlerMock) EcosystemGrowthPercentageInEpoch(epoch uint32) float64 { + if rhm.EcosystemGrowthPercentageInEpochCalled != nil { + return rhm.EcosystemGrowthPercentageInEpochCalled(epoch) + } + return 0 +} + +// EcosystemGrowthAddressInEpoch - +func (rhm *RewardsHandlerMock) EcosystemGrowthAddressInEpoch(epoch uint32) string { + if rhm.EcosystemGrowthAddressInEpochCalled != nil { + return rhm.EcosystemGrowthAddressInEpochCalled(epoch) + } + return "" +} + +// GrowthDividendPercentageInEpoch - +func (rhm *RewardsHandlerMock) GrowthDividendPercentageInEpoch(epoch uint32) float64 { + if rhm.GrowthDividendPercentageInEpochCalled != nil { + return rhm.GrowthDividendPercentageInEpochCalled(epoch) + } + return 0 +} + +// GrowthDividendAddressInEpoch - +func (rhm *RewardsHandlerMock) GrowthDividendAddressInEpoch(epoch uint32) string { + if rhm.GrowthDividendAddressInEpochCalled != nil { + return rhm.GrowthDividendAddressInEpochCalled(epoch) + } + return "" +} + // IsInterfaceNil returns true if there is no value under the interface func (rhm *RewardsHandlerMock) IsInterfaceNil() bool { return rhm == nil diff --git a/process/peer/process_test.go b/process/peer/process_test.go index b66535c1bcd..7a415ed635c 100644 --- a/process/peer/process_test.go +++ b/process/peer/process_test.go @@ -14,10 +14,6 @@ import ( "github.com/multiversx/mx-chain-core-go/core/keyValStorage" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -30,6 +26,7 @@ import ( "github.com/multiversx/mx-chain-go/state/accounts" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" dataRetrieverMock "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" @@ -38,6 +35,9 @@ import ( "github.com/multiversx/mx-chain-go/testscommon/shardingMocks" stateMock "github.com/multiversx/mx-chain-go/testscommon/state" storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -61,7 +61,9 @@ func createMockPubkeyConverter() *testscommon.PubkeyConverterMock { } func createMockArguments() peer.ArgValidatorStatisticsProcessor { + argsNewEconomicsData := economics.ArgsNewEconomicsData{ + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &config.EconomicsConfig{ GlobalSettings: config.GlobalSettings{ GenesisTotalSupply: "2000000000000000000000", @@ -82,6 +84,10 @@ func createMockArguments() peer.ArgValidatorStatisticsProcessor { ProtocolSustainabilityAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, }, }, diff --git a/process/smartContract/process_test.go b/process/smartContract/process_test.go index c21ef326934..54acb623608 100644 --- a/process/smartContract/process_test.go +++ b/process/smartContract/process_test.go @@ -13,14 +13,6 @@ import ( "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" vmData "github.com/multiversx/mx-chain-core-go/data/vm" - "github.com/multiversx/mx-chain-go/txcache" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "github.com/multiversx/mx-chain-vm-common-go/builtInFunctions" - "github.com/multiversx/mx-chain-vm-common-go/parsers" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/enablers" "github.com/multiversx/mx-chain-go/common/forking" @@ -35,6 +27,7 @@ import ( "github.com/multiversx/mx-chain-go/state/accounts" "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/economicsmocks" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" @@ -42,6 +35,13 @@ import ( stateMock "github.com/multiversx/mx-chain-go/testscommon/state" "github.com/multiversx/mx-chain-go/testscommon/trie" "github.com/multiversx/mx-chain-go/testscommon/vmcommonMocks" + "github.com/multiversx/mx-chain-go/txcache" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/multiversx/mx-chain-vm-common-go/builtInFunctions" + "github.com/multiversx/mx-chain-vm-common-go/parsers" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const setGuardianCost = 250000 @@ -4200,7 +4200,9 @@ func TestProcess_createCompletedTxEvent(t *testing.T) { } func createRealEconomicsDataArgs() *economics.ArgsNewEconomicsData { + return &economics.ArgsNewEconomicsData{ + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &config.EconomicsConfig{ GlobalSettings: config.GlobalSettings{ GenesisTotalSupply: "20000000000000000000000000", @@ -4219,6 +4221,10 @@ func createRealEconomicsDataArgs() *economics.ArgsNewEconomicsData { ProtocolSustainabilityAddress: "erd1j25xk97yf820rgdp3mj5scavhjkn6tjyn0t63pmv5qyjj7wxlcfqqe2rw5", TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, }, }, diff --git a/process/smartContract/processorV2/process_test.go b/process/smartContract/processorV2/process_test.go index 46a755af29a..28e644e86a8 100644 --- a/process/smartContract/processorV2/process_test.go +++ b/process/smartContract/processorV2/process_test.go @@ -15,15 +15,6 @@ import ( "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" vmData "github.com/multiversx/mx-chain-core-go/data/vm" - "github.com/multiversx/mx-chain-go/txcache" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "github.com/multiversx/mx-chain-vm-common-go/builtInFunctions" - "github.com/multiversx/mx-chain-vm-common-go/parsers" - "github.com/multiversx/mx-chain-vm-go/vmhost" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/process" @@ -38,6 +29,7 @@ import ( stateFactory "github.com/multiversx/mx-chain-go/state/factory" "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/economicsmocks" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" @@ -46,6 +38,14 @@ import ( stateMock "github.com/multiversx/mx-chain-go/testscommon/state" testsCommonStorage "github.com/multiversx/mx-chain-go/testscommon/storage" "github.com/multiversx/mx-chain-go/testscommon/vmcommonMocks" + "github.com/multiversx/mx-chain-go/txcache" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/multiversx/mx-chain-vm-common-go/builtInFunctions" + "github.com/multiversx/mx-chain-vm-common-go/parsers" + "github.com/multiversx/mx-chain-vm-go/vmhost" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const maxEpoch = math.MaxUint32 @@ -4276,7 +4276,9 @@ func TestProcess_createCompletedTxEvent(t *testing.T) { } func createRealEconomicsDataArgs() *economics.ArgsNewEconomicsData { + return &economics.ArgsNewEconomicsData{ + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &config.EconomicsConfig{ GlobalSettings: config.GlobalSettings{ GenesisTotalSupply: "20000000000000000000000000", @@ -4295,6 +4297,10 @@ func createRealEconomicsDataArgs() *economics.ArgsNewEconomicsData { ProtocolSustainabilityAddress: "erd1j25xk97yf820rgdp3mj5scavhjkn6tjyn0t63pmv5qyjj7wxlcfqqe2rw5", TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, }, }, diff --git a/statusHandler/chainParamsMetricsHandler.go b/statusHandler/chainParamsMetricsHandler.go index 7a599b9aace..857b963af6d 100644 --- a/statusHandler/chainParamsMetricsHandler.go +++ b/statusHandler/chainParamsMetricsHandler.go @@ -29,7 +29,7 @@ func (cpm *chainParamsMetricsHandler) ChainParametersChanged(chainParameters con cpm.appStatusHandler.SetUInt64Value(common.MetricMetaConsensusGroupSize, uint64(chainParameters.MetachainConsensusGroupSize)) cpm.appStatusHandler.SetUInt64Value(common.MetricNumNodesPerShard, uint64(chainParameters.ShardMinNumNodes)) cpm.appStatusHandler.SetUInt64Value(common.MetricNumMetachainNodes, uint64(chainParameters.MetachainMinNumNodes)) - cpm.appStatusHandler.SetUInt64Value(common.MetricRoundDuration, uint64(chainParameters.RoundDuration)) + cpm.appStatusHandler.SetUInt64Value(common.MetricRoundDuration, chainParameters.RoundDuration) } // IsInterfaceNil returns true if there is no value under the interface diff --git a/testscommon/chainParameters/chainParametersHolderMock.go b/testscommon/chainParameters/chainParametersHolderMock.go index 50721aa5716..9781c7b56a2 100644 --- a/testscommon/chainParameters/chainParametersHolderMock.go +++ b/testscommon/chainParameters/chainParametersHolderMock.go @@ -5,6 +5,7 @@ import ( ) var testChainParams = config.ChainParametersByEpochConfig{ + RoundsPerEpoch: 14400, RoundDuration: 6000, Hysteresis: 0, EnableEpoch: 0, diff --git a/testscommon/components/configs.go b/testscommon/components/configs.go index b7b0f7c08b6..0d224203572 100644 --- a/testscommon/components/configs.go +++ b/testscommon/components/configs.go @@ -278,6 +278,10 @@ func CreateDummyEconomicsConfig() config.EconomicsConfig { ProtocolSustainabilityAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", TopUpFactor: 0.25, TopUpGradientPoint: "3000000000000000000000000", + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, }, }, diff --git a/testscommon/economicsConfig.go b/testscommon/economicsConfig.go index aae732d7446..83270b97423 100644 --- a/testscommon/economicsConfig.go +++ b/testscommon/economicsConfig.go @@ -25,6 +25,10 @@ func GetEconomicsConfig() config.EconomicsConfig { DeveloperPercentage: 0.1, ProtocolSustainabilityAddress: "erd1j25xk97yf820rgdp3mj5scavhjkn6tjyn0t63pmv5qyjj7wxlcfqqe2rw5", TopUpGradientPoint: "100000", + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "erd1932eft30w753xyvme8d49qejgkjc09n5e49w4mwdjtm0neld797su0dlxp", }, }, }, diff --git a/testscommon/economicsmocks/economicsHandlerMock.go b/testscommon/economicsmocks/economicsHandlerMock.go index c8c445c605a..84ef11e9d08 100644 --- a/testscommon/economicsmocks/economicsHandlerMock.go +++ b/testscommon/economicsmocks/economicsHandlerMock.go @@ -9,8 +9,7 @@ import ( // EconomicsHandlerMock - type EconomicsHandlerMock struct { - MaxInflationRateCalled func(year uint32) float64 - MinInflationRateCalled func() float64 + MaxInflationRateCalled func(year uint32, epoch uint32) float64 LeaderPercentageCalled func() float64 ProtocolSustainabilityPercentageCalled func() float64 ProtocolSustainabilityAddressCalled func() string @@ -61,6 +60,11 @@ type EconomicsHandlerMock struct { ProtocolSustainabilityAddressInEpochCalled func(epoch uint32) string RewardsTopUpGradientPointInEpochCalled func(epoch uint32) *big.Int RewardsTopUpFactorInEpochCalled func(epoch uint32) float64 + EcosystemGrowthPercentageInEpochCalled func(epoch uint32) float64 + EcosystemGrowthAddressInEpochCalled func(epoch uint32) string + GrowthDividendPercentageInEpochCalled func(epoch uint32) float64 + GrowthDividendAddressInEpochCalled func(epoch uint32) string + IsTailInflationEnabledCalled func(epoch uint32) bool } // ComputeGasUnitsFromRefundValue - @@ -83,14 +87,14 @@ func (ehm *EconomicsHandlerMock) ProtocolSustainabilityAddress() string { return ehm.ProtocolSustainabilityAddressCalled() } -// MinInflationRate - -func (ehm *EconomicsHandlerMock) MinInflationRate() float64 { - return ehm.MinInflationRateCalled() +// MaxInflationRate - +func (ehm *EconomicsHandlerMock) MaxInflationRate(year uint32, epoch uint32) float64 { + return ehm.MaxInflationRateCalled(year, epoch) } -// MaxInflationRate - -func (ehm *EconomicsHandlerMock) MaxInflationRate(year uint32) float64 { - return ehm.MaxInflationRateCalled(year) +// IsTailInflationEnabled - +func (ehm *EconomicsHandlerMock) IsTailInflationEnabled(epoch uint32) bool { + return ehm.IsTailInflationEnabledCalled(epoch) } // MinGasPrice - @@ -462,6 +466,38 @@ func (ehm *EconomicsHandlerMock) MaxGasHigherFactorAccepted() uint64 { return 10 } +// EcosystemGrowthAddressInEpoch - +func (ehm *EconomicsHandlerMock) EcosystemGrowthAddressInEpoch(epoch uint32) string { + if ehm.EcosystemGrowthAddressInEpochCalled != nil { + return ehm.EcosystemGrowthAddressInEpochCalled(epoch) + } + return "" +} + +// GrowthDividendAddressInEpoch - +func (ehm *EconomicsHandlerMock) GrowthDividendAddressInEpoch(epoch uint32) string { + if ehm.GrowthDividendAddressInEpochCalled != nil { + return ehm.GrowthDividendAddressInEpochCalled(epoch) + } + return "" +} + +// EcosystemGrowthPercentageInEpoch - +func (ehm *EconomicsHandlerMock) EcosystemGrowthPercentageInEpoch(epoch uint32) float64 { + if ehm.EcosystemGrowthPercentageInEpochCalled != nil { + return ehm.EcosystemGrowthPercentageInEpochCalled(epoch) + } + return 0 +} + +// GrowthDividendPercentageInEpoch - +func (ehm *EconomicsHandlerMock) GrowthDividendPercentageInEpoch(epoch uint32) float64 { + if ehm.GrowthDividendPercentageInEpochCalled != nil { + return ehm.GrowthDividendPercentageInEpochCalled(epoch) + } + return 0 +} + // IsInterfaceNil returns true if there is no value under the interface func (ehm *EconomicsHandlerMock) IsInterfaceNil() bool { return ehm == nil diff --git a/testscommon/rewardsCreatorStub.go b/testscommon/rewardsCreatorStub.go index a5802fd9652..2080a192132 100644 --- a/testscommon/rewardsCreatorStub.go +++ b/testscommon/rewardsCreatorStub.go @@ -30,7 +30,7 @@ type RewardsCreatorStub struct { computedEconomics *block.Economics, prevBlockExecutionResults data.BaseMetaExecutionResultHandler, ) (block.MiniBlockSlice, error) - GetProtocolSustainabilityRewardsCalled func() *big.Int + GetAcceleratorRewardsCalled func() *big.Int GetLocalTxCacheCalled func() epochStart.TransactionCacher CreateMarshalledDataCalled func(body *block.Body) map[string][][]byte GetRewardsTxsCalled func(body *block.Body) map[string]data.TransactionHandler @@ -77,10 +77,10 @@ func (rcs *RewardsCreatorStub) VerifyRewardsMiniBlocks( return nil } -// GetProtocolSustainabilityRewards - -func (rcs *RewardsCreatorStub) GetProtocolSustainabilityRewards() *big.Int { - if rcs.GetProtocolSustainabilityRewardsCalled != nil { - return rcs.GetProtocolSustainabilityRewardsCalled() +// GetAcceleratorRewards - +func (rcs *RewardsCreatorStub) GetAcceleratorRewards() *big.Int { + if rcs.GetAcceleratorRewardsCalled != nil { + return rcs.GetAcceleratorRewardsCalled() } return big.NewInt(0) diff --git a/testscommon/stakingcommon/stakingCommon.go b/testscommon/stakingcommon/stakingCommon.go index a8b250c0952..61b3f2a3c62 100644 --- a/testscommon/stakingcommon/stakingCommon.go +++ b/testscommon/stakingcommon/stakingCommon.go @@ -11,6 +11,7 @@ import ( economicsHandler "github.com/multiversx/mx-chain-go/process/economics" "github.com/multiversx/mx-chain-go/state" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/multiversx/mx-chain-go/vm" @@ -234,6 +235,7 @@ func CreateEconomicsData() process.EconomicsDataHandler { minGasLimit := strconv.FormatUint(10, 10) argsNewEconomicsData := economicsHandler.ArgsNewEconomicsData{ + ChainParamsHandler: &chainParameters.ChainParametersHolderMock{}, Economics: &config.EconomicsConfig{ GlobalSettings: config.GlobalSettings{ GenesisTotalSupply: "2000000000000000000000", @@ -252,6 +254,10 @@ func CreateEconomicsData() process.EconomicsDataHandler { DeveloperPercentage: 0.1, ProtocolSustainabilityPercentage: 0.1, ProtocolSustainabilityAddress: "protocol", + EcosystemGrowthPercentage: 0.0, + EcosystemGrowthAddress: "protocol", + GrowthDividendPercentage: 0.0, + GrowthDividendAddress: "protocol", TopUpGradientPoint: "300000000000000000000", TopUpFactor: 0.25, }, @@ -283,6 +289,7 @@ func CreateEconomicsData() process.EconomicsDataHandler { ShardCoordinator: &testscommon.ShardsCoordinatorMock{}, } economicsData, _ := economicsHandler.NewEconomicsData(argsNewEconomicsData) + return economicsData }