github.com/klaytn/klaytn@v1.10.2/reward/reward_distributor_test.go (about)

     1  // Copyright 2019 The klaytn Authors
     2  // This file is part of the klaytn library.
     3  //
     4  // The klaytn library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The klaytn library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package reward
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"math/big"
    23  	"testing"
    24  
    25  	"github.com/klaytn/klaytn/blockchain/types"
    26  	"github.com/klaytn/klaytn/common"
    27  	"github.com/klaytn/klaytn/consensus/istanbul"
    28  	"github.com/klaytn/klaytn/params"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func (governance *testGovernance) CurrentParams() *params.GovParamSet {
    34  	return governance.p
    35  }
    36  
    37  func (governance *testGovernance) EffectiveParams(num uint64) (*params.GovParamSet, error) {
    38  	return governance.p, nil
    39  }
    40  
    41  func (governance *testGovernance) setTestGovernance(intMap map[int]interface{}) {
    42  	p, _ := params.NewGovParamSetIntMap(intMap)
    43  	governance.p = p
    44  }
    45  
    46  func assertEqualRewardSpecs(t *testing.T, expected, actual *RewardSpec, msgAndArgs ...interface{}) {
    47  	expectedJson, err := json.MarshalIndent(expected, "", "  ")
    48  	require.Nil(t, err)
    49  
    50  	actualJson, err := json.MarshalIndent(actual, "", "  ")
    51  	require.Nil(t, err)
    52  
    53  	assert.Equal(t, string(expectedJson), string(actualJson), msgAndArgs...)
    54  
    55  	lhs := new(big.Int).Add(actual.Minted, actual.TotalFee)
    56  	lhs = lhs.Sub(lhs, actual.BurntFee)
    57  	rhs := new(big.Int).Add(actual.Proposer, actual.Stakers)
    58  	rhs = rhs.Add(rhs, actual.KFF)
    59  	rhs = rhs.Add(rhs, actual.KCF)
    60  	assert.True(t, lhs.Cmp(rhs) == 0, msgAndArgs...)
    61  }
    62  
    63  var (
    64  	cnBaseAddr     = 500 // Dummy addresses goes like 0x000..5nn
    65  	stakeBaseAddr  = 600
    66  	rewardBaseAddr = 700
    67  	minStaking     = uint64(2000000) // changing this value will not change the governance's min staking
    68  	minted, _      = big.NewInt(0).SetString("9600000000000000000", 10)
    69  	proposerAddr   = common.StringToAddress("0x1552F52D459B713E0C4558e66C8c773a75615FA8")
    70  	kcfAddr        = intToAddress(1000)
    71  	kffAddr        = intToAddress(2000)
    72  )
    73  
    74  // 500 -> 0x00000...0500
    75  func intToAddress(x int) common.Address {
    76  	return common.HexToAddress(fmt.Sprintf("0x%040d", x))
    77  }
    78  
    79  // rewardOverride[i] = j means rewards[i] = rewards[j]
    80  // amountOverride[i] = amt means amount[i] = amt
    81  func genStakingInfo(cnNum int, rewardOverride map[int]int, amountOverride map[int]uint64) *StakingInfo {
    82  	cns := make([]common.Address, 0)
    83  	stakes := make([]common.Address, 0)
    84  	rewards := make([]common.Address, 0)
    85  	amounts := make([]uint64, 0)
    86  
    87  	for i := 0; i < cnNum; i++ {
    88  		cns = append(cns, intToAddress(cnBaseAddr+i))
    89  		stakes = append(stakes, intToAddress(stakeBaseAddr+i))
    90  		rewards = append(rewards, intToAddress(rewardBaseAddr+i))
    91  		amounts = append(amounts, minStaking)
    92  	}
    93  
    94  	for i := range rewardOverride {
    95  		rewards[i] = rewards[rewardOverride[i]]
    96  	}
    97  
    98  	for i := range amountOverride {
    99  		amounts[i] = amountOverride[i]
   100  	}
   101  
   102  	return &StakingInfo{
   103  		BlockNum:              0,
   104  		CouncilNodeAddrs:      cns,
   105  		CouncilStakingAddrs:   stakes,
   106  		CouncilRewardAddrs:    rewards,
   107  		KCFAddr:               kcfAddr,
   108  		KFFAddr:               kffAddr,
   109  		UseGini:               false,
   110  		CouncilStakingAmounts: amounts,
   111  	}
   112  }
   113  
   114  type testBalanceAdder struct {
   115  	accounts map[common.Address]*big.Int
   116  }
   117  
   118  func newTestBalanceAdder() *testBalanceAdder {
   119  	balanceAdder := &testBalanceAdder{}
   120  	balanceAdder.accounts = make(map[common.Address]*big.Int)
   121  	return balanceAdder
   122  }
   123  
   124  func getTestConfig() *params.ChainConfig {
   125  	config := &params.ChainConfig{}
   126  	config.SetDefaults() // To use GovParamSet without having parse errors
   127  
   128  	config.MagmaCompatibleBlock = big.NewInt(0)
   129  	config.KoreCompatibleBlock = big.NewInt(0)
   130  	config.UnitPrice = 1
   131  	config.Governance.Reward.MintingAmount = minted
   132  	config.Governance.Reward.Ratio = "34/54/12"
   133  	config.Governance.Reward.Kip82Ratio = "20/80"
   134  	config.Governance.Reward.DeferredTxFee = true
   135  	config.Governance.Reward.MinimumStake = big.NewInt(0).SetUint64(minStaking)
   136  	config.Istanbul.ProposerPolicy = 2
   137  	return config
   138  }
   139  
   140  func (balanceAdder *testBalanceAdder) AddBalance(addr common.Address, v *big.Int) {
   141  	balance, ok := balanceAdder.accounts[addr]
   142  	if ok {
   143  		balanceAdder.accounts[addr] = big.NewInt(0).Add(balance, v)
   144  	} else {
   145  		balanceAdder.accounts[addr] = v
   146  	}
   147  }
   148  
   149  func noMagma(p *params.ChainConfig) *params.ChainConfig {
   150  	p.MagmaCompatibleBlock = big.NewInt(100000000)
   151  	p.KoreCompatibleBlock = big.NewInt(100000000)
   152  	return p
   153  }
   154  
   155  func noKore(p *params.ChainConfig) *params.ChainConfig {
   156  	p.KoreCompatibleBlock = big.NewInt(100000000)
   157  	return p
   158  }
   159  
   160  func noDeferred(p *params.ChainConfig) *params.ChainConfig {
   161  	p.Governance.Reward.DeferredTxFee = false
   162  	return p
   163  }
   164  
   165  func roundrobin(p *params.ChainConfig) *params.ChainConfig {
   166  	p.Istanbul.ProposerPolicy = 0
   167  	return p
   168  }
   169  
   170  func (balanceAdder *testBalanceAdder) GetBalance(addr common.Address) *big.Int {
   171  	balance, ok := balanceAdder.accounts[addr]
   172  	if ok {
   173  		return balance
   174  	} else {
   175  		return nil
   176  	}
   177  }
   178  
   179  func Test_isEmptyAddress(t *testing.T) {
   180  	testCases := []struct {
   181  		address common.Address
   182  		result  bool
   183  	}{
   184  		{
   185  			common.Address{},
   186  			true,
   187  		},
   188  		{
   189  			common.HexToAddress("0x0000000000000000000000000000000000000000"),
   190  			true,
   191  		},
   192  		{
   193  			common.StringToAddress("0xA75Ed91f789BF9dc121DACB822849955ca3AB6aD"),
   194  			false,
   195  		},
   196  		{
   197  			common.StringToAddress("0x4bCDd8E3F9776d16056815E189EcB5A8bF8E4CBb"),
   198  			false,
   199  		},
   200  	}
   201  	for _, testCase := range testCases {
   202  		assert.Equal(t, testCase.result, common.EmptyAddress(testCase.address))
   203  	}
   204  }
   205  
   206  func TestRewardDistributor_GetTotalTxFee(t *testing.T) {
   207  	testCases := []struct {
   208  		gasUsed            uint64
   209  		unitPrice          uint64
   210  		baseFee            *big.Int
   211  		expectedTotalTxFee *big.Int
   212  	}{
   213  		// before magma hardfork
   214  		// baseFee = nil, expectedTotalTxFee = gasUsed * unitPrice
   215  		{0, 25000000000, nil, big.NewInt(0)},
   216  		{200000, 25000000000, nil, big.NewInt(5000000000000000)},
   217  		{200000, 25000000000, nil, big.NewInt(5000000000000000)},
   218  		{129346, 10000000000, nil, big.NewInt(1293460000000000)},
   219  		{129346, 10000000000, nil, big.NewInt(1293460000000000)},
   220  		{9236192, 50000, nil, big.NewInt(461809600000)},
   221  		{9236192, 50000, nil, big.NewInt(461809600000)},
   222  		{12936418927364923, 0, nil, big.NewInt(0)},
   223  		// after magma hardfork, unitprice ignored
   224  		// baseFee != nil, expectedTotalTxFee = gasUsed * baseFee
   225  		{0, 25000000000, big.NewInt(25000000000), big.NewInt(0)},
   226  		{200000, 25000000000, big.NewInt(25000000000), big.NewInt(5000000000000000)},
   227  		{200000, 25000000000, big.NewInt(25000000000), big.NewInt(5000000000000000)},
   228  		{129346, 25000000000, big.NewInt(10000000000), big.NewInt(1293460000000000)},
   229  		{129346, 250, big.NewInt(10000000000), big.NewInt(1293460000000000)},
   230  		{9236192, 9876, big.NewInt(50000), big.NewInt(461809600000)},
   231  		{9236192, 25000000000, big.NewInt(50000), big.NewInt(461809600000)},
   232  		{12936418927364923, 25000000000, big.NewInt(0), big.NewInt(0)},
   233  	}
   234  
   235  	for _, testCase := range testCases {
   236  		header := &types.Header{
   237  			Number:  big.NewInt(0),
   238  			GasUsed: testCase.gasUsed,
   239  			BaseFee: testCase.baseFee,
   240  		}
   241  		config := &params.ChainConfig{
   242  			UnitPrice: testCase.unitPrice,
   243  		}
   244  		if testCase.baseFee != nil {
   245  			// enable Magma
   246  			config.MagmaCompatibleBlock = big.NewInt(0)
   247  		}
   248  
   249  		rules := config.Rules(header.Number)
   250  		pset, err := params.NewGovParamSetChainConfig(config)
   251  		require.Nil(t, err)
   252  
   253  		result := GetTotalTxFee(header, rules, pset)
   254  		assert.Equal(t, testCase.expectedTotalTxFee.Uint64(), result.Uint64())
   255  	}
   256  }
   257  
   258  func TestRewardDistributor_getBurnAmountMagma(t *testing.T) {
   259  	testCases := []struct {
   260  		gasUsed            uint64
   261  		baseFee            *big.Int
   262  		expectedTotalTxFee *big.Int
   263  	}{
   264  		{0, big.NewInt(25000000000), big.NewInt(0)},
   265  		{200000, big.NewInt(25000000000), big.NewInt(5000000000000000 / 2)},
   266  		{200000, big.NewInt(25000000000), big.NewInt(5000000000000000 / 2)},
   267  		{129346, big.NewInt(10000000000), big.NewInt(1293460000000000 / 2)},
   268  		{129346, big.NewInt(10000000000), big.NewInt(1293460000000000 / 2)},
   269  		{9236192, big.NewInt(50000), big.NewInt(461809600000 / 2)},
   270  		{9236192, big.NewInt(50000), big.NewInt(461809600000 / 2)},
   271  		{12936418927364923, big.NewInt(0), big.NewInt(0)},
   272  	}
   273  
   274  	var (
   275  		header = &types.Header{
   276  			Number: big.NewInt(1),
   277  		}
   278  		rules = params.Rules{
   279  			IsMagma: true,
   280  		}
   281  		pset, _ = params.NewGovParamSetIntMap(map[int]interface{}{
   282  			params.UnitPrice: 0, // unused value because Magma
   283  		})
   284  	)
   285  
   286  	for _, testCase := range testCases {
   287  		header.GasUsed = testCase.gasUsed
   288  		header.BaseFee = testCase.baseFee
   289  		txFee := GetTotalTxFee(header, rules, pset)
   290  		burnedTxFee := getBurnAmountMagma(txFee)
   291  		// expectedTotalTxFee = GetTotalTxFee / 2 = BurnedTxFee
   292  		assert.Equal(t, testCase.expectedTotalTxFee.Uint64(), burnedTxFee.Uint64())
   293  	}
   294  }
   295  
   296  func TestRewardDistributor_GetBlockReward(t *testing.T) {
   297  	oldStakingManager := GetStakingManager()
   298  	defer SetTestStakingManager(oldStakingManager)
   299  
   300  	var (
   301  		header = &types.Header{
   302  			Number:     big.NewInt(1),
   303  			GasUsed:    1000,
   304  			BaseFee:    big.NewInt(1),
   305  			Rewardbase: proposerAddr,
   306  		}
   307  		stakingInfo = genStakingInfo(5, nil, map[int]uint64{
   308  			0: minStaking + 4,
   309  			1: minStaking + 3,
   310  		})
   311  		rules = params.Rules{
   312  			IsMagma: true,
   313  			IsKore:  true,
   314  		}
   315  	)
   316  
   317  	testcases := []struct {
   318  		policy        istanbul.ProposerPolicy
   319  		deferredTxFee bool
   320  		expected      *RewardSpec
   321  	}{
   322  		{
   323  			policy:        istanbul.RoundRobin,
   324  			deferredTxFee: true,
   325  			expected: &RewardSpec{
   326  				Minted:   minted,
   327  				TotalFee: new(big.Int).SetUint64(1000),
   328  				BurntFee: new(big.Int).SetUint64(500),
   329  				Proposer: new(big.Int).SetUint64(9.6e18 + 500),
   330  				Stakers:  new(big.Int).SetUint64(0),
   331  				KFF:      new(big.Int).SetUint64(0),
   332  				KCF:      new(big.Int).SetUint64(0),
   333  				Rewards: map[common.Address]*big.Int{
   334  					proposerAddr: new(big.Int).SetUint64(9.6e18 + 500),
   335  				},
   336  			},
   337  		},
   338  		{
   339  			policy:        istanbul.RoundRobin,
   340  			deferredTxFee: false,
   341  			expected: &RewardSpec{
   342  				Minted:   minted,
   343  				TotalFee: new(big.Int).SetUint64(1000),
   344  				BurntFee: new(big.Int).SetUint64(0),
   345  				Proposer: new(big.Int).SetUint64(9.6e18 + 1000),
   346  				Stakers:  new(big.Int).SetUint64(0),
   347  				KFF:      new(big.Int).SetUint64(0),
   348  				KCF:      new(big.Int).SetUint64(0),
   349  				Rewards: map[common.Address]*big.Int{
   350  					proposerAddr: new(big.Int).SetUint64(9.6e18 + 1000),
   351  				},
   352  			},
   353  		},
   354  		{
   355  			policy:        istanbul.WeightedRandom,
   356  			deferredTxFee: true,
   357  			expected: &RewardSpec{
   358  				Minted:   minted,
   359  				TotalFee: new(big.Int).SetUint64(1000),
   360  				BurntFee: new(big.Int).SetUint64(1000),
   361  				Proposer: new(big.Int).SetUint64(0.6528e18 + 1),
   362  				Stakers:  new(big.Int).SetUint64(2.6112e18 - 1),
   363  				KFF:      new(big.Int).SetUint64(5.184e18),
   364  				KCF:      new(big.Int).SetUint64(1.152e18),
   365  				Rewards: map[common.Address]*big.Int{
   366  					proposerAddr:                     new(big.Int).SetUint64(0.6528e18 + 1),
   367  					kffAddr:                          new(big.Int).SetUint64(5.184e18),
   368  					kcfAddr:                          new(big.Int).SetUint64(1.152e18),
   369  					intToAddress(rewardBaseAddr):     new(big.Int).SetUint64(1492114285714285714),
   370  					intToAddress(rewardBaseAddr + 1): new(big.Int).SetUint64(1119085714285714285),
   371  				},
   372  			},
   373  		},
   374  		{
   375  			policy:        istanbul.WeightedRandom,
   376  			deferredTxFee: false,
   377  			expected: &RewardSpec{
   378  				Minted:   minted,
   379  				TotalFee: new(big.Int).SetUint64(1000),
   380  				BurntFee: new(big.Int).SetUint64(0),
   381  				Proposer: new(big.Int).SetUint64(0.6528e18 + 1000 + 1),
   382  				Stakers:  new(big.Int).SetUint64(2.6112e18 - 1),
   383  				KFF:      new(big.Int).SetUint64(5.184e18),
   384  				KCF:      new(big.Int).SetUint64(1.152e18),
   385  				Rewards: map[common.Address]*big.Int{
   386  					proposerAddr:                     new(big.Int).SetUint64(0.6528e18 + 1000 + 1),
   387  					kffAddr:                          new(big.Int).SetUint64(5.184e18),
   388  					kcfAddr:                          new(big.Int).SetUint64(1.152e18),
   389  					intToAddress(rewardBaseAddr):     new(big.Int).SetUint64(1492114285714285714),
   390  					intToAddress(rewardBaseAddr + 1): new(big.Int).SetUint64(1119085714285714285),
   391  				},
   392  			},
   393  		},
   394  	}
   395  
   396  	SetTestStakingManagerWithStakingInfoCache(stakingInfo)
   397  
   398  	for i, tc := range testcases {
   399  		config := getTestConfig()
   400  		if !tc.deferredTxFee {
   401  			config = noDeferred(config)
   402  		}
   403  		config.Istanbul.ProposerPolicy = uint64(tc.policy)
   404  
   405  		pset, err := params.NewGovParamSetChainConfig(config)
   406  		require.Nil(t, err)
   407  
   408  		spec, err := GetBlockReward(header, rules, pset)
   409  		require.Nil(t, err, "testcases[%d] failed", i)
   410  		assertEqualRewardSpecs(t, tc.expected, spec, "testcases[%d] failed", i)
   411  	}
   412  }
   413  
   414  func TestRewardDistributor_CalcDeferredRewardSimple(t *testing.T) {
   415  	header := &types.Header{
   416  		Number:     big.NewInt(1),
   417  		GasUsed:    1000,
   418  		BaseFee:    big.NewInt(1),
   419  		Rewardbase: proposerAddr,
   420  	}
   421  
   422  	testcases := []struct {
   423  		isMagma  bool
   424  		expected *RewardSpec
   425  	}{
   426  		{
   427  			isMagma: false,
   428  			expected: &RewardSpec{
   429  				Minted:   minted,
   430  				TotalFee: new(big.Int).SetUint64(1000),
   431  				BurntFee: new(big.Int).SetUint64(0),
   432  				Proposer: new(big.Int).SetUint64(9.6e18 + 1000),
   433  				Stakers:  new(big.Int).SetUint64(0),
   434  				KFF:      new(big.Int).SetUint64(0),
   435  				KCF:      new(big.Int).SetUint64(0),
   436  				Rewards: map[common.Address]*big.Int{
   437  					proposerAddr: new(big.Int).SetUint64(9.6e18 + 1000),
   438  				},
   439  			},
   440  		},
   441  		{
   442  			isMagma: true,
   443  			expected: &RewardSpec{
   444  				Minted:   minted,
   445  				TotalFee: new(big.Int).SetUint64(1000),
   446  				BurntFee: new(big.Int).SetUint64(500), // 50% of tx fee burnt
   447  				Proposer: new(big.Int).SetUint64(9.6e18 + 500),
   448  				Stakers:  new(big.Int).SetUint64(0),
   449  				KFF:      new(big.Int).SetUint64(0),
   450  				KCF:      new(big.Int).SetUint64(0),
   451  				Rewards: map[common.Address]*big.Int{
   452  					proposerAddr: new(big.Int).SetUint64(9.6e18 + 500),
   453  				},
   454  			},
   455  		},
   456  	}
   457  
   458  	for i, tc := range testcases {
   459  		config := roundrobin(getTestConfig())
   460  		if !tc.isMagma {
   461  			config = noMagma(config)
   462  		}
   463  
   464  		rules := config.Rules(header.Number)
   465  		pset, err := params.NewGovParamSetChainConfig(config)
   466  		require.Nil(t, err)
   467  
   468  		spec, err := CalcDeferredRewardSimple(header, rules, pset)
   469  		require.Nil(t, err, "testcases[%d] failed", i)
   470  		assertEqualRewardSpecs(t, tc.expected, spec, "testcases[%d] failed", i)
   471  	}
   472  }
   473  
   474  // Before Kore, there was a bug that distributed txFee at the end of
   475  // block processing regardless of `deferredTxFee` flag.
   476  // See https://github.com/klaytn/klaytn/issues/1692.
   477  // To maintain backward compatibility, we only fix the buggy logic after Kore
   478  // and leave the buggy logic before Kore.
   479  func TestRewardDistributor_CalcDeferredRewardSimple_nodeferred(t *testing.T) {
   480  	header := &types.Header{
   481  		Number:     big.NewInt(1),
   482  		GasUsed:    1000,
   483  		BaseFee:    big.NewInt(1),
   484  		Rewardbase: proposerAddr,
   485  	}
   486  
   487  	testcases := []struct {
   488  		isMagma  bool
   489  		isKore   bool
   490  		expected *RewardSpec
   491  	}{
   492  		{ // totalFee should have been 0, but returned due to bug
   493  			isMagma: false,
   494  			isKore:  false,
   495  			expected: &RewardSpec{
   496  				Minted:   minted,
   497  				TotalFee: new(big.Int).SetUint64(1000),
   498  				BurntFee: new(big.Int).SetUint64(0),
   499  				Proposer: new(big.Int).SetUint64(9.6e18 + 1000),
   500  				Stakers:  new(big.Int).SetUint64(0),
   501  				KFF:      new(big.Int).SetUint64(0),
   502  				KCF:      new(big.Int).SetUint64(0),
   503  				Rewards: map[common.Address]*big.Int{
   504  					proposerAddr: new(big.Int).SetUint64(9.6e18 + 1000),
   505  				},
   506  			},
   507  		},
   508  		{ // totalFee should have been 0, but returned due to bug
   509  			isMagma: true,
   510  			isKore:  false,
   511  			expected: &RewardSpec{
   512  				Minted:   minted,
   513  				TotalFee: new(big.Int).SetUint64(1000),
   514  				BurntFee: new(big.Int).SetUint64(500),
   515  				Proposer: new(big.Int).SetUint64(9.6e18 + 500),
   516  				Stakers:  new(big.Int).SetUint64(0),
   517  				KFF:      new(big.Int).SetUint64(0),
   518  				KCF:      new(big.Int).SetUint64(0),
   519  				Rewards: map[common.Address]*big.Int{
   520  					proposerAddr: new(big.Int).SetUint64(9.6e18 + 500),
   521  				},
   522  			},
   523  		},
   524  		{ // totalFee is now 0 because bug is fixed after Kore
   525  			isMagma: true,
   526  			isKore:  true,
   527  			expected: &RewardSpec{
   528  				Minted:   minted,
   529  				TotalFee: new(big.Int).SetUint64(0),
   530  				BurntFee: new(big.Int).SetUint64(0),
   531  				Proposer: new(big.Int).SetUint64(9.6e18),
   532  				Stakers:  new(big.Int).SetUint64(0),
   533  				KFF:      new(big.Int).SetUint64(0),
   534  				KCF:      new(big.Int).SetUint64(0),
   535  				Rewards: map[common.Address]*big.Int{
   536  					proposerAddr: new(big.Int).SetUint64(9.6e18),
   537  				},
   538  			},
   539  		},
   540  	}
   541  
   542  	for i, tc := range testcases {
   543  		config := noDeferred((getTestConfig()))
   544  		if !tc.isMagma {
   545  			config = noMagma(config)
   546  		}
   547  		if !tc.isKore {
   548  			config = noKore(config)
   549  		}
   550  
   551  		rules := config.Rules(header.Number)
   552  		pset, err := params.NewGovParamSetChainConfig(config)
   553  		require.Nil(t, err)
   554  
   555  		spec, err := CalcDeferredRewardSimple(header, rules, pset)
   556  		require.Nil(t, err, "testcases[%d] failed", i)
   557  		assertEqualRewardSpecs(t, tc.expected, spec, "testcases[%d] failed", i)
   558  	}
   559  }
   560  
   561  func TestRewardDistributor_CalcDeferredReward(t *testing.T) {
   562  	oldStakingManager := GetStakingManager()
   563  	defer SetTestStakingManager(oldStakingManager)
   564  
   565  	stakingInfo := genStakingInfo(5, nil, map[int]uint64{
   566  		0: minStaking + 4,
   567  		1: minStaking + 3,
   568  	})
   569  
   570  	testcases := []struct {
   571  		desc     string
   572  		isKore   bool
   573  		isMagma  bool
   574  		fee      uint64
   575  		expected *RewardSpec
   576  	}{
   577  		{
   578  			desc:    "isKore=false, isMagma=false, fee=1000 [000]",
   579  			isKore:  false,
   580  			isMagma: false,
   581  			fee:     1000,
   582  			expected: &RewardSpec{
   583  				Minted:   minted,
   584  				TotalFee: big.NewInt(1000),
   585  				BurntFee: big.NewInt(0),
   586  				Proposer: big.NewInt(0).SetUint64(3.264e18 + 340),
   587  				Stakers:  big.NewInt(0),
   588  				KFF:      big.NewInt(5.184e18 + 540),
   589  				KCF:      big.NewInt(1.152e18 + 120),
   590  				Rewards: map[common.Address]*big.Int{
   591  					proposerAddr: big.NewInt(3.264e18 + 340),
   592  					kffAddr:      big.NewInt(5.184e18 + 540),
   593  					kcfAddr:      big.NewInt(1.152e18 + 120),
   594  				},
   595  			},
   596  		},
   597  		{
   598  			desc:    "isKore=false, isMagma=false, fee=10e18 [001]",
   599  			isKore:  false,
   600  			isMagma: false,
   601  			fee:     10e18,
   602  			expected: &RewardSpec{
   603  				Minted:   minted,
   604  				TotalFee: new(big.Int).SetUint64(10e18),
   605  				BurntFee: new(big.Int).SetUint64(0),
   606  				Proposer: new(big.Int).SetUint64(6.664e18),
   607  				Stakers:  new(big.Int).SetUint64(0),
   608  				KFF:      new(big.Int).SetUint64(10.584e18),
   609  				KCF:      new(big.Int).SetUint64(2.352e18),
   610  				Rewards: map[common.Address]*big.Int{
   611  					proposerAddr: new(big.Int).SetUint64(6.664e18),
   612  					kffAddr:      new(big.Int).SetUint64(10.584e18),
   613  					kcfAddr:      new(big.Int).SetUint64(2.352e18),
   614  				},
   615  			},
   616  		},
   617  		{
   618  			desc:    "isKore=false, isMagma=true, fee=1000 [010]",
   619  			isKore:  false,
   620  			isMagma: true,
   621  			fee:     1000,
   622  			expected: &RewardSpec{
   623  				Minted:   minted,
   624  				TotalFee: new(big.Int).SetUint64(1000),
   625  				BurntFee: new(big.Int).SetUint64(500),
   626  				Proposer: new(big.Int).SetUint64(3.264e18 + 170),
   627  				Stakers:  new(big.Int).SetUint64(0),
   628  				KFF:      new(big.Int).SetUint64(5.184e18 + 270),
   629  				KCF:      new(big.Int).SetUint64(1.152e18 + 60),
   630  				Rewards: map[common.Address]*big.Int{
   631  					proposerAddr: new(big.Int).SetUint64(3.264e18 + 170),
   632  					kcfAddr:      new(big.Int).SetUint64(1.152e18 + 60),
   633  					kffAddr:      new(big.Int).SetUint64(5.184e18 + 270),
   634  				},
   635  			},
   636  		},
   637  		{
   638  			desc:    "isKore=false, isMagma=true, fee=10e18 [011]",
   639  			isKore:  false,
   640  			isMagma: true,
   641  			fee:     10e18,
   642  			expected: &RewardSpec{
   643  				Minted:   minted,
   644  				TotalFee: new(big.Int).SetUint64(10e18),
   645  				BurntFee: new(big.Int).SetUint64(5e18),
   646  				Proposer: new(big.Int).SetUint64(4.964e18),
   647  				Stakers:  new(big.Int).SetUint64(0),
   648  				KFF:      new(big.Int).SetUint64(7.884e18),
   649  				KCF:      new(big.Int).SetUint64(1.752e18),
   650  				Rewards: map[common.Address]*big.Int{
   651  					proposerAddr: new(big.Int).SetUint64(4.964e18),
   652  					kffAddr:      new(big.Int).SetUint64(7.884e18),
   653  					kcfAddr:      new(big.Int).SetUint64(1.752e18),
   654  				},
   655  			},
   656  		},
   657  		{
   658  			desc:    "isKore=true, isMagma=true, fee=1000 [110]",
   659  			isKore:  true,
   660  			isMagma: true,
   661  			fee:     1000,
   662  			expected: &RewardSpec{
   663  				Minted:   minted,
   664  				TotalFee: new(big.Int).SetUint64(1000),
   665  				BurntFee: new(big.Int).SetUint64(1000),
   666  				Proposer: new(big.Int).SetUint64(0.6528e18 + 1),
   667  				Stakers:  new(big.Int).SetUint64(2.6112e18 - 1),
   668  				KFF:      new(big.Int).SetUint64(5.184e18),
   669  				KCF:      new(big.Int).SetUint64(1.152e18),
   670  				Rewards: map[common.Address]*big.Int{
   671  					proposerAddr:                     new(big.Int).SetUint64(0.6528e18 + 1),
   672  					kffAddr:                          new(big.Int).SetUint64(5.184e18),
   673  					kcfAddr:                          new(big.Int).SetUint64(1.152e18),
   674  					intToAddress(rewardBaseAddr):     new(big.Int).SetUint64(1492114285714285714),
   675  					intToAddress(rewardBaseAddr + 1): new(big.Int).SetUint64(1119085714285714285),
   676  				},
   677  			},
   678  		},
   679  		{ // after kore, more-than-default staking, large fee, proposer = rewardbase
   680  			desc:    "isKore=true, isMagma=true, fee=10e18 [111]",
   681  			isKore:  true,
   682  			isMagma: true,
   683  			fee:     10e18,
   684  			expected: &RewardSpec{
   685  				Minted:   minted,
   686  				TotalFee: new(big.Int).SetUint64(10e18),
   687  				BurntFee: new(big.Int).SetUint64(5e18 + 0.6528e18),
   688  				Proposer: new(big.Int).SetUint64(5e18 + 1),
   689  				Stakers:  new(big.Int).SetUint64(2.6112e18 - 1),
   690  				KFF:      new(big.Int).SetUint64(5.184e18),
   691  				KCF:      new(big.Int).SetUint64(1.152e18),
   692  				Rewards: map[common.Address]*big.Int{
   693  					proposerAddr:                     new(big.Int).SetUint64(5e18 + 1),
   694  					kffAddr:                          new(big.Int).SetUint64(5.184e18),
   695  					kcfAddr:                          new(big.Int).SetUint64(1.152e18),
   696  					intToAddress(rewardBaseAddr):     new(big.Int).SetUint64(1492114285714285714),
   697  					intToAddress(rewardBaseAddr + 1): new(big.Int).SetUint64(1119085714285714285),
   698  				},
   699  			},
   700  		},
   701  	}
   702  
   703  	SetTestStakingManagerWithStakingInfoCache(stakingInfo)
   704  
   705  	for _, tc := range testcases {
   706  		header := &types.Header{
   707  			Number:     big.NewInt(1),
   708  			GasUsed:    tc.fee,
   709  			BaseFee:    big.NewInt(1),
   710  			Rewardbase: proposerAddr,
   711  		}
   712  
   713  		config := getTestConfig()
   714  		if !tc.isKore {
   715  			config = noKore(config)
   716  		}
   717  		if !tc.isMagma {
   718  			config = noMagma(config)
   719  		}
   720  
   721  		rules := config.Rules(header.Number)
   722  		pset, err := params.NewGovParamSetChainConfig(config)
   723  		require.Nil(t, err)
   724  
   725  		spec, err := CalcDeferredReward(header, rules, pset)
   726  		require.Nil(t, err, "failed tc: %s", tc.desc)
   727  		assertEqualRewardSpecs(t, tc.expected, spec, "failed tc: %s", tc.desc)
   728  	}
   729  }
   730  
   731  func TestRewardDistributor_CalcDeferredReward_StakingInfos(t *testing.T) {
   732  	oldStakingManager := GetStakingManager()
   733  	defer SetTestStakingManager(oldStakingManager)
   734  
   735  	var (
   736  		header = &types.Header{
   737  			Number:     big.NewInt(1),
   738  			GasUsed:    1000,
   739  			BaseFee:    big.NewInt(1),
   740  			Rewardbase: proposerAddr,
   741  		}
   742  		config  = getTestConfig()
   743  		rules   = config.Rules(header.Number)
   744  		pset, _ = params.NewGovParamSetChainConfig(config)
   745  	)
   746  
   747  	testcases := []struct {
   748  		desc        string
   749  		stakingInfo *StakingInfo
   750  		expected    *RewardSpec
   751  	}{
   752  		{
   753  			desc:        "stakingInfo is nil, its portion goes to proposer",
   754  			stakingInfo: nil,
   755  			expected: &RewardSpec{
   756  				Minted:   minted,
   757  				TotalFee: big.NewInt(1000),
   758  				BurntFee: big.NewInt(1000),
   759  				Proposer: minted,
   760  				Stakers:  big.NewInt(0),
   761  				KFF:      big.NewInt(0),
   762  				KCF:      big.NewInt(0),
   763  				Rewards: map[common.Address]*big.Int{
   764  					proposerAddr: minted,
   765  				},
   766  			},
   767  		},
   768  		{
   769  			desc: "stakingInfo has no kff, its portion goes to proposer",
   770  			stakingInfo: &StakingInfo{
   771  				KCFAddr: kcfAddr,
   772  				KFFAddr: common.Address{},
   773  			},
   774  			expected: &RewardSpec{
   775  				Minted:   minted,
   776  				TotalFee: big.NewInt(1000),
   777  				BurntFee: big.NewInt(1000),
   778  				Proposer: big.NewInt(8.448e18),
   779  				Stakers:  big.NewInt(0),
   780  				KFF:      big.NewInt(0),
   781  				KCF:      big.NewInt(1.152e18), // minted * 0.12
   782  				Rewards: map[common.Address]*big.Int{
   783  					proposerAddr: big.NewInt(8.448e18),
   784  					kcfAddr:      big.NewInt(1.152e18),
   785  				},
   786  			},
   787  		},
   788  		{
   789  			desc: "stakingInfo has no kcf, its portion goes to proposer",
   790  			stakingInfo: &StakingInfo{
   791  				KCFAddr: common.Address{},
   792  				KFFAddr: kffAddr,
   793  			},
   794  			expected: &RewardSpec{
   795  				Minted:   minted,
   796  				TotalFee: big.NewInt(1000),
   797  				BurntFee: big.NewInt(1000),
   798  				Proposer: big.NewInt(4.416e18),
   799  				Stakers:  big.NewInt(0),
   800  				KFF:      big.NewInt(5.184e18), // minted * 0.54
   801  				KCF:      big.NewInt(0),
   802  				Rewards: map[common.Address]*big.Int{
   803  					proposerAddr: big.NewInt(4.416e18),
   804  					kffAddr:      big.NewInt(5.184e18),
   805  				},
   806  			},
   807  		},
   808  		{
   809  			desc: "stakingInfo has the same kff and kcf",
   810  			stakingInfo: &StakingInfo{
   811  				KCFAddr: kffAddr,
   812  				KFFAddr: kffAddr,
   813  			},
   814  			expected: &RewardSpec{
   815  				Minted:   minted,
   816  				TotalFee: big.NewInt(1000),
   817  				BurntFee: big.NewInt(1000),
   818  				Proposer: big.NewInt(3.264e18),
   819  				Stakers:  big.NewInt(0),
   820  				KFF:      big.NewInt(5.184e18),
   821  				KCF:      big.NewInt(1.152e18),
   822  				Rewards: map[common.Address]*big.Int{
   823  					proposerAddr: big.NewInt(3.264e18),
   824  					kffAddr:      big.NewInt(6.336e18),
   825  				},
   826  			},
   827  		},
   828  	}
   829  
   830  	for i, tc := range testcases {
   831  		if tc.stakingInfo == nil {
   832  			SetTestStakingManager(nil)
   833  		} else {
   834  			SetTestStakingManagerWithStakingInfoCache(tc.stakingInfo)
   835  		}
   836  		spec, err := CalcDeferredReward(header, rules, pset)
   837  		require.Nil(t, err, "testcases[%d] failed", i)
   838  		assertEqualRewardSpecs(t, tc.expected, spec, "testcases[%d] failed: %s", i, tc.desc)
   839  	}
   840  }
   841  
   842  func TestRewardDistributor_CalcDeferredReward_Remainings(t *testing.T) {
   843  	oldStakingManager := GetStakingManager()
   844  	defer SetTestStakingManager(oldStakingManager)
   845  
   846  	var (
   847  		header = &types.Header{
   848  			Number:     big.NewInt(1),
   849  			GasUsed:    1000,
   850  			BaseFee:    big.NewInt(1),
   851  			Rewardbase: proposerAddr,
   852  		}
   853  
   854  		stakingInfo = genStakingInfo(5, nil, map[int]uint64{
   855  			0: minStaking + 4,
   856  			1: minStaking + 3,
   857  		})
   858  		splitRemainingConfig = getTestConfig()
   859  	)
   860  	splitRemainingConfig.Governance.Reward.MintingAmount = big.NewInt(333)
   861  
   862  	testcases := []struct {
   863  		desc     string
   864  		config   *params.ChainConfig
   865  		expected *RewardSpec
   866  	}{
   867  		{
   868  			desc:   "split remaining goes to kff",
   869  			config: splitRemainingConfig,
   870  			expected: &RewardSpec{
   871  				Minted:   big.NewInt(333),
   872  				TotalFee: big.NewInt(1000),
   873  				BurntFee: big.NewInt(522),
   874  				Proposer: big.NewInt(501), // proposer=22, rewardFee=478, shareRem=1
   875  				Stakers:  big.NewInt(89),
   876  				KFF:      big.NewInt(182), // splitRem=3
   877  				KCF:      big.NewInt(39),
   878  				Rewards: map[common.Address]*big.Int{
   879  					proposerAddr:                     big.NewInt(501),
   880  					kffAddr:                          big.NewInt(182),
   881  					kcfAddr:                          big.NewInt(39),
   882  					intToAddress(rewardBaseAddr):     big.NewInt(51), // stakers * 4/7
   883  					intToAddress(rewardBaseAddr + 1): big.NewInt(38), // stakers * 3/7
   884  				},
   885  			},
   886  		},
   887  		{
   888  			desc:   "share remaining goes to proposer",
   889  			config: getTestConfig(),
   890  			expected: &RewardSpec{
   891  				Minted:   minted,
   892  				TotalFee: big.NewInt(1000),
   893  				BurntFee: big.NewInt(1000),
   894  				Proposer: big.NewInt(0.6528e18 + 1),
   895  				Stakers:  big.NewInt(2.6112e18 - 1),
   896  				KFF:      big.NewInt(5.184e18),
   897  				KCF:      big.NewInt(1.152e18),
   898  				Rewards: map[common.Address]*big.Int{
   899  					proposerAddr:                     big.NewInt(0.6528e18 + 1),
   900  					kffAddr:                          big.NewInt(5.184e18),
   901  					kcfAddr:                          big.NewInt(1.152e18),
   902  					intToAddress(rewardBaseAddr):     big.NewInt(1492114285714285714), // stakers * 4/7
   903  					intToAddress(rewardBaseAddr + 1): big.NewInt(1119085714285714285), // stakers * 3/7
   904  				},
   905  			},
   906  		},
   907  	}
   908  
   909  	SetTestStakingManagerWithStakingInfoCache(stakingInfo)
   910  
   911  	for _, tc := range testcases {
   912  		rules := tc.config.Rules(header.Number)
   913  		pset, err := params.NewGovParamSetChainConfig(tc.config)
   914  		require.Nil(t, err)
   915  
   916  		spec, err := CalcDeferredReward(header, rules, pset)
   917  		require.Nil(t, err, "failed tc: %s", tc.desc)
   918  		assertEqualRewardSpecs(t, tc.expected, spec, "failed tc: %s", tc.desc)
   919  	}
   920  }
   921  
   922  func TestRewardDistributor_calcDeferredFee(t *testing.T) {
   923  	type Result struct{ total, reward, burnt uint64 }
   924  
   925  	testcases := []struct {
   926  		desc     string
   927  		isKore   bool
   928  		isMagma  bool
   929  		fee      uint64
   930  		expected *Result
   931  	}{
   932  		{
   933  			desc:    "isKore=false, isMagma=false, fee=1000 [000]",
   934  			isKore:  false,
   935  			isMagma: false,
   936  			fee:     1000,
   937  			expected: &Result{
   938  				total:  1000,
   939  				reward: 1000,
   940  				burnt:  0,
   941  			},
   942  		},
   943  		{
   944  			desc:    "isKore=false, isMagma=false, fee=10e18 [001]",
   945  			isKore:  false,
   946  			isMagma: false,
   947  			fee:     10e18,
   948  			expected: &Result{
   949  				total:  10e18,
   950  				reward: 10e18,
   951  				burnt:  0,
   952  			},
   953  		},
   954  		{
   955  			desc:    "isKore=false, isMagma=true, fee=1000 [010]",
   956  			isKore:  false,
   957  			isMagma: true,
   958  			fee:     1000,
   959  			expected: &Result{
   960  				total:  1000,
   961  				reward: 500,
   962  				burnt:  500,
   963  			},
   964  		},
   965  		{
   966  			desc:    "isKore=false, isMagma=true, fee=10e18 [011]",
   967  			isKore:  false,
   968  			isMagma: true,
   969  			fee:     10e18,
   970  			expected: &Result{
   971  				total:  10e18,
   972  				reward: 5e18,
   973  				burnt:  5e18,
   974  			},
   975  		},
   976  		{
   977  			desc:    "isKore=true, isMagma=true, fee=1000 [110]",
   978  			isKore:  true,
   979  			isMagma: true,
   980  			fee:     1000,
   981  			expected: &Result{
   982  				total:  1000,
   983  				reward: 0,
   984  				burnt:  1000,
   985  			},
   986  		},
   987  		{
   988  			desc:    "isKore=true, isMagma=true, fee=10e18 [111]",
   989  			isKore:  true,
   990  			isMagma: true,
   991  			fee:     10e18,
   992  			expected: &Result{
   993  				total:  10e18,
   994  				reward: 4.3472e18, // 5 - minted*0.34*0.2
   995  				burnt:  5.6528e18, // 5 + minted*0.34*0.8
   996  			},
   997  		},
   998  	}
   999  
  1000  	for _, tc := range testcases {
  1001  		header := &types.Header{
  1002  			Number:     big.NewInt(1),
  1003  			GasUsed:    tc.fee,
  1004  			BaseFee:    big.NewInt(1),
  1005  			Rewardbase: proposerAddr,
  1006  		}
  1007  
  1008  		config := getTestConfig()
  1009  		if !tc.isKore {
  1010  			config = noKore(config)
  1011  		}
  1012  		if !tc.isMagma {
  1013  			config = noMagma(config)
  1014  		}
  1015  
  1016  		rules := config.Rules(header.Number)
  1017  		pset, err := params.NewGovParamSetChainConfig(config)
  1018  		require.Nil(t, err)
  1019  
  1020  		rc, err := NewRewardConfig(header, rules, pset)
  1021  		require.Nil(t, err)
  1022  
  1023  		total, reward, burnt := calcDeferredFee(rc)
  1024  		actual := &Result{
  1025  			total:  total.Uint64(),
  1026  			reward: reward.Uint64(),
  1027  			burnt:  burnt.Uint64(),
  1028  		}
  1029  		assert.Equal(t, tc.expected, actual, "failed tc: %s", tc.desc)
  1030  	}
  1031  }
  1032  
  1033  func TestRewardDistributor_calcDeferredFee_nodeferred(t *testing.T) {
  1034  	var (
  1035  		header = &types.Header{
  1036  			Number:     big.NewInt(1),
  1037  			GasUsed:    1000,
  1038  			BaseFee:    big.NewInt(1),
  1039  			Rewardbase: proposerAddr,
  1040  		}
  1041  		rules = params.Rules{
  1042  			IsMagma: true,
  1043  		}
  1044  	)
  1045  
  1046  	pset, err := params.NewGovParamSetChainConfig(noDeferred(getTestConfig()))
  1047  	require.Nil(t, err)
  1048  
  1049  	rc, err := NewRewardConfig(header, rules, pset)
  1050  	require.Nil(t, err)
  1051  
  1052  	total, reward, burnt := calcDeferredFee(rc)
  1053  	assert.Equal(t, uint64(0), total.Uint64())
  1054  	assert.Equal(t, uint64(0), reward.Uint64())
  1055  	assert.Equal(t, uint64(0), burnt.Uint64())
  1056  }
  1057  
  1058  func TestRewardDistributor_calcSplit(t *testing.T) {
  1059  	type Result struct{ proposer, stakers, kff, kcf, remaining uint64 }
  1060  
  1061  	header := &types.Header{
  1062  		Number:  big.NewInt(1),
  1063  		BaseFee: big.NewInt(0), // placeholder
  1064  	}
  1065  
  1066  	testcases := []struct {
  1067  		desc     string
  1068  		isKore   bool
  1069  		fee      uint64
  1070  		expected *Result
  1071  	}{
  1072  		{
  1073  			desc:   "kore=false, fee=0",
  1074  			isKore: false,
  1075  			fee:    0,
  1076  			expected: &Result{
  1077  				proposer:  3.264e18, // minted * 0.34
  1078  				stakers:   0,
  1079  				kff:       5.184e18, // minted * 0.54
  1080  				kcf:       1.152e18, // minted * 0.12
  1081  				remaining: 0,
  1082  			},
  1083  		},
  1084  		{
  1085  			desc:   "kore=false, fee=55555",
  1086  			isKore: false,
  1087  			fee:    55555,
  1088  			expected: &Result{
  1089  				proposer:  3.264e18 + 18888, // (minted + fee) * 0.34
  1090  				stakers:   0,
  1091  				kff:       5.184e18 + 29999, // (minted + fee) * 0.54
  1092  				kcf:       1.152e18 + 6666,  // (minted + fee) * 0.12
  1093  				remaining: 2,
  1094  			},
  1095  		},
  1096  		{
  1097  			desc:   "kore=true, fee=0",
  1098  			isKore: true,
  1099  			fee:    0,
  1100  			expected: &Result{
  1101  				proposer:  0.6528e18, // minted * 0.34 * 0.2
  1102  				stakers:   2.6112e18, // minted * 0.34 * 0.8
  1103  				kff:       5.184e18,  // minted * 0.54
  1104  				kcf:       1.152e18,  // minted * 0.12
  1105  				remaining: 0,
  1106  			},
  1107  		},
  1108  		{
  1109  			desc:   "kore=true, fee=55555",
  1110  			isKore: true,
  1111  			fee:    55555,
  1112  			expected: &Result{
  1113  				proposer:  0.6528e18 + 55555, // minted * 0.34 * 0.2 + fee
  1114  				stakers:   2.6112e18,         // minted * 0.34 * 0.8
  1115  				kff:       5.184e18,          // minted * 0.54
  1116  				kcf:       1.152e18,          // minted * 0.12
  1117  				remaining: 0,
  1118  			},
  1119  		},
  1120  	}
  1121  
  1122  	for _, tc := range testcases {
  1123  		config := getTestConfig()
  1124  		if !tc.isKore {
  1125  			config = noKore(config)
  1126  		}
  1127  
  1128  		rules := config.Rules(header.Number)
  1129  		pset, err := params.NewGovParamSetChainConfig(config)
  1130  		require.Nil(t, err)
  1131  
  1132  		rc, err := NewRewardConfig(header, rules, pset)
  1133  		require.Nil(t, err)
  1134  
  1135  		fee := new(big.Int).SetUint64(tc.fee)
  1136  		proposer, stakers, kff, kcf, remaining := calcSplit(rc, minted, fee)
  1137  		actual := &Result{
  1138  			proposer:  proposer.Uint64(),
  1139  			stakers:   stakers.Uint64(),
  1140  			kff:       kff.Uint64(),
  1141  			kcf:       kcf.Uint64(),
  1142  			remaining: remaining.Uint64(),
  1143  		}
  1144  		assert.Equal(t, tc.expected, actual, "failed tc: %s", tc.desc)
  1145  
  1146  		expectedTotalAmount := big.NewInt(0)
  1147  		expectedTotalAmount = expectedTotalAmount.Add(minted, fee)
  1148  
  1149  		actualTotalAmount := big.NewInt(0)
  1150  		actualTotalAmount = actualTotalAmount.Add(actualTotalAmount, proposer)
  1151  		actualTotalAmount = actualTotalAmount.Add(actualTotalAmount, stakers)
  1152  		actualTotalAmount = actualTotalAmount.Add(actualTotalAmount, kff)
  1153  		actualTotalAmount = actualTotalAmount.Add(actualTotalAmount, kcf)
  1154  		actualTotalAmount = actualTotalAmount.Add(actualTotalAmount, remaining)
  1155  		assert.Equal(t, expectedTotalAmount, actualTotalAmount, "failed tc: %s", tc.desc)
  1156  	}
  1157  }
  1158  
  1159  func TestRewardDistributor_calcShares(t *testing.T) {
  1160  	type Result struct {
  1161  		shares    map[common.Address]*big.Int
  1162  		remaining uint64
  1163  	}
  1164  
  1165  	testcases := []struct {
  1166  		desc        string
  1167  		stakingInfo *StakingInfo
  1168  		stakeReward *big.Int
  1169  		expected    *Result
  1170  	}{
  1171  		{
  1172  			desc:        "all nodes 0%",
  1173  			stakingInfo: genStakingInfo(5, nil, nil),
  1174  			stakeReward: big.NewInt(500),
  1175  			expected: &Result{
  1176  				shares:    map[common.Address]*big.Int{},
  1177  				remaining: 500,
  1178  			},
  1179  		},
  1180  		{
  1181  			desc:        "no staking info",
  1182  			stakingInfo: nil,
  1183  			stakeReward: big.NewInt(500),
  1184  			expected: &Result{
  1185  				shares:    map[common.Address]*big.Int{},
  1186  				remaining: 500,
  1187  			},
  1188  		},
  1189  		{
  1190  			desc:        "CN0: 100%",
  1191  			stakingInfo: genStakingInfo(5, nil, map[int]uint64{0: minStaking + 1}),
  1192  			stakeReward: big.NewInt(500),
  1193  			expected: &Result{
  1194  				shares: map[common.Address]*big.Int{
  1195  					intToAddress(rewardBaseAddr): big.NewInt(500),
  1196  				},
  1197  				remaining: 0,
  1198  			},
  1199  		},
  1200  		{
  1201  			desc: "CN0, CN1: 50%",
  1202  			stakingInfo: genStakingInfo(5, nil, map[int]uint64{
  1203  				0: minStaking + 1,
  1204  				1: minStaking + 1,
  1205  			}),
  1206  			stakeReward: big.NewInt(500),
  1207  			expected: &Result{
  1208  				shares: map[common.Address]*big.Int{
  1209  					intToAddress(rewardBaseAddr):     big.NewInt(250),
  1210  					intToAddress(rewardBaseAddr + 1): big.NewInt(250),
  1211  				},
  1212  				remaining: 0,
  1213  			},
  1214  		},
  1215  		{
  1216  			desc: "CN0: 66%, CN1: 33%",
  1217  			stakingInfo: genStakingInfo(5, nil, map[int]uint64{
  1218  				0: minStaking + 2,
  1219  				1: minStaking + 1,
  1220  			}),
  1221  			stakeReward: big.NewInt(500),
  1222  			expected: &Result{
  1223  				shares: map[common.Address]*big.Int{
  1224  					intToAddress(rewardBaseAddr):     big.NewInt(333),
  1225  					intToAddress(rewardBaseAddr + 1): big.NewInt(166),
  1226  				},
  1227  				remaining: 1,
  1228  			},
  1229  		},
  1230  		{
  1231  			desc: "CN0: 66/97, CN1: 17/97, CN2: 11/97, CN3: 2/97, CN4: 1/97",
  1232  			stakingInfo: genStakingInfo(7, nil, map[int]uint64{
  1233  				0: minStaking + 66,
  1234  				1: minStaking + 17,
  1235  				2: minStaking + 11,
  1236  				3: minStaking + 2,
  1237  				4: minStaking + 1, // total: 97
  1238  			}),
  1239  			stakeReward: big.NewInt(555),
  1240  			expected: &Result{
  1241  				shares: map[common.Address]*big.Int{
  1242  					intToAddress(rewardBaseAddr):     big.NewInt(377),
  1243  					intToAddress(rewardBaseAddr + 1): big.NewInt(97),
  1244  					intToAddress(rewardBaseAddr + 2): big.NewInt(62),
  1245  					intToAddress(rewardBaseAddr + 3): big.NewInt(11),
  1246  					intToAddress(rewardBaseAddr + 4): big.NewInt(5),
  1247  				},
  1248  				remaining: 3,
  1249  			},
  1250  		},
  1251  	}
  1252  
  1253  	for _, tc := range testcases {
  1254  		shares, remaining := calcShares(tc.stakingInfo, tc.stakeReward, minStaking)
  1255  		actual := &Result{
  1256  			shares:    shares,
  1257  			remaining: remaining.Uint64(),
  1258  		}
  1259  		assert.Equal(t, tc.expected, actual, "failed tc: %s", tc.desc)
  1260  	}
  1261  }
  1262  
  1263  func benchSetup() (*types.Header, params.Rules, *params.GovParamSet) {
  1264  	// in the worst case, distribute stake shares among N
  1265  	amounts := make(map[int]uint64)
  1266  	N := 50
  1267  	for i := 0; i < N; i++ {
  1268  		amounts[i] = minStaking + 1
  1269  	}
  1270  
  1271  	stakingInfo := genStakingInfo(N, nil, amounts)
  1272  	SetTestStakingManagerWithStakingInfoCache(stakingInfo)
  1273  
  1274  	config := getTestConfig()
  1275  
  1276  	header := &types.Header{}
  1277  	header.BaseFee = big.NewInt(30000000000)
  1278  	header.Number = big.NewInt(0)
  1279  	header.Rewardbase = intToAddress(rewardBaseAddr)
  1280  
  1281  	rules := config.Rules(header.Number)
  1282  	pset, _ := params.NewGovParamSetChainConfig(config)
  1283  
  1284  	return header, rules, pset
  1285  }
  1286  
  1287  func Benchmark_CalcDeferredReward(b *testing.B) {
  1288  	oldStakingManager := GetStakingManager()
  1289  	defer SetTestStakingManager(oldStakingManager)
  1290  
  1291  	header, rules, pset := benchSetup()
  1292  
  1293  	b.ResetTimer()
  1294  	for i := 0; i < b.N; i++ {
  1295  		CalcDeferredReward(header, rules, pset)
  1296  	}
  1297  }
  1298  
  1299  func TestRewardConfigCache_parseRewardRatio(t *testing.T) {
  1300  	testCases := []struct {
  1301  		s   string
  1302  		cn  int64
  1303  		kff int64
  1304  		kcf int64
  1305  		err error
  1306  	}{
  1307  		{"34/54/12", 34, 54, 12, nil},
  1308  		{"3/3/3", 3, 3, 3, nil},
  1309  		{"10/20/30", 10, 20, 30, nil},
  1310  		{"34,54,12", 0, 0, 0, errInvalidFormat},
  1311  		{"/", 0, 0, 0, errInvalidFormat},
  1312  		{"///", 0, 0, 0, errInvalidFormat},
  1313  		{"1//", 0, 0, 0, errParsingRatio},
  1314  		{"/1/", 0, 0, 0, errParsingRatio},
  1315  		{"//1", 0, 0, 0, errParsingRatio},
  1316  		{"1/2/3/4/", 0, 0, 0, errInvalidFormat},
  1317  		{"3.3/3.3/3.3", 0, 0, 0, errParsingRatio},
  1318  		{"a/b/c", 0, 0, 0, errParsingRatio},
  1319  	}
  1320  
  1321  	for i := 0; i < len(testCases); i++ {
  1322  		cn, kff, kcf, total, err := parseRewardRatio(testCases[i].s)
  1323  
  1324  		assert.Equal(t, testCases[i].cn, cn)
  1325  		assert.Equal(t, testCases[i].kff, kff)
  1326  		assert.Equal(t, testCases[i].kcf, kcf)
  1327  		assert.Equal(t, testCases[i].err, err)
  1328  
  1329  		expectedTotal := testCases[i].cn + testCases[i].kff + testCases[i].kcf
  1330  		assert.Equal(t, expectedTotal, total)
  1331  	}
  1332  }
  1333  
  1334  func TestRewardConfigCache_parseRewardKip82Ratio(t *testing.T) {
  1335  	testCases := []struct {
  1336  		s        string
  1337  		proposer int64
  1338  		staking  int64
  1339  		err      error
  1340  	}{
  1341  		{"34/54", 34, 54, nil},
  1342  		{"20/80", 20, 80, nil},
  1343  		{"0/100", 0, 100, nil},
  1344  		{"34,54", 0, 0, errInvalidFormat},
  1345  		{"", 0, 0, errInvalidFormat},
  1346  		{"//", 0, 0, errInvalidFormat},
  1347  		{"1/", 0, 0, errParsingRatio},
  1348  		{"/1", 0, 0, errParsingRatio},
  1349  		{"1/2/", 0, 0, errInvalidFormat},
  1350  		{"3.3/3.3", 0, 0, errParsingRatio},
  1351  		{"a/b", 0, 0, errParsingRatio},
  1352  	}
  1353  
  1354  	for i := 0; i < len(testCases); i++ {
  1355  		proposer, staking, total, err := parseRewardKip82Ratio(testCases[i].s)
  1356  
  1357  		assert.Equal(t, testCases[i].proposer, proposer)
  1358  		assert.Equal(t, testCases[i].staking, staking)
  1359  		assert.Equal(t, testCases[i].err, err, "tc[%d] failed", i)
  1360  
  1361  		expectedTotal := testCases[i].proposer + testCases[i].staking
  1362  		assert.Equal(t, expectedTotal, total)
  1363  	}
  1364  }