github.com/klaytn/klaytn@v1.12.1/governance/api_test.go (about)

     1  package governance
     2  
     3  import (
     4  	"encoding/json"
     5  	"math/big"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/golang/mock/gomock"
    10  	"github.com/klaytn/klaytn/blockchain/state"
    11  	"github.com/klaytn/klaytn/blockchain/types"
    12  	"github.com/klaytn/klaytn/common"
    13  	"github.com/klaytn/klaytn/consensus"
    14  	"github.com/klaytn/klaytn/networks/rpc"
    15  	"github.com/klaytn/klaytn/params"
    16  	"github.com/klaytn/klaytn/reward"
    17  	"github.com/klaytn/klaytn/storage/database"
    18  	"github.com/klaytn/klaytn/work/mocks"
    19  	"github.com/stretchr/testify/assert"
    20  )
    21  
    22  type testBlockChain struct {
    23  	num    uint64
    24  	config *params.ChainConfig
    25  }
    26  
    27  func newTestBlockchain(config *params.ChainConfig) *testBlockChain {
    28  	return &testBlockChain{
    29  		config: config,
    30  	}
    31  }
    32  
    33  func newTestGovernanceApi() *GovernanceAPI {
    34  	config := params.CypressChainConfig
    35  	config.Governance.KIP71 = params.GetDefaultKIP71Config()
    36  	govApi := NewGovernanceAPI(NewMixedEngine(config, database.NewMemoryDBManager()))
    37  	govApi.governance.SetNodeAddress(common.HexToAddress("0x52d41ca72af615a1ac3301b0a93efa222ecc7541"))
    38  	bc := newTestBlockchain(config)
    39  	govApi.governance.SetBlockchain(bc)
    40  	return govApi
    41  }
    42  
    43  func TestUpperBoundBaseFeeSet(t *testing.T) {
    44  	govApi := newTestGovernanceApi()
    45  
    46  	curLowerBoundBaseFee := govApi.governance.CurrentParams().LowerBoundBaseFee()
    47  	// unexpected case : upperboundbasefee < lowerboundbasefee
    48  	invalidUpperBoundBaseFee := curLowerBoundBaseFee - 100
    49  	_, err := govApi.Vote("kip71.upperboundbasefee", invalidUpperBoundBaseFee)
    50  	assert.Equal(t, err, errInvalidUpperBound)
    51  }
    52  
    53  func TestLowerBoundFeeSet(t *testing.T) {
    54  	govApi := newTestGovernanceApi()
    55  
    56  	curUpperBoundBaseFee := govApi.governance.CurrentParams().UpperBoundBaseFee()
    57  	// unexpected case : upperboundbasefee < lowerboundbasefee
    58  	invalidLowerBoundBaseFee := curUpperBoundBaseFee + 100
    59  	_, err := govApi.Vote("kip71.lowerboundbasefee", invalidLowerBoundBaseFee)
    60  	assert.Equal(t, err, errInvalidLowerBound)
    61  }
    62  
    63  func TestGetRewards(t *testing.T) {
    64  	type expected = map[int]uint64
    65  	type strMap = map[string]interface{}
    66  	type override struct {
    67  		num    int
    68  		strMap strMap
    69  	}
    70  	type testcase struct {
    71  		length   int // total number of blocks to simulate
    72  		override []override
    73  		expected expected
    74  	}
    75  
    76  	var (
    77  		mintAmount = uint64(1)
    78  		koreBlock  = uint64(9)
    79  		epoch      = 3
    80  		latestNum  = rpc.BlockNumber(-1)
    81  		proposer   = common.HexToAddress("0x0000000000000000000000000000000000000000")
    82  		config     = getTestConfig()
    83  	)
    84  
    85  	testcases := []testcase{
    86  		{
    87  			12,
    88  			[]override{
    89  				{
    90  					3,
    91  					strMap{
    92  						"reward.mintingamount": "2",
    93  					},
    94  				},
    95  				{
    96  					6,
    97  					strMap{
    98  						"reward.mintingamount": "3",
    99  					},
   100  				},
   101  			},
   102  			map[int]uint64{
   103  				1:  1,
   104  				2:  1,
   105  				3:  1,
   106  				4:  1,
   107  				5:  1,
   108  				6:  1,
   109  				7:  2, // 2 is minted from now
   110  				8:  2,
   111  				9:  3, // 3 is minted from now
   112  				10: 3,
   113  				11: 3,
   114  				12: 3,
   115  				13: 3,
   116  			},
   117  		},
   118  	}
   119  
   120  	for _, tc := range testcases {
   121  		config.Governance.Reward.MintingAmount = new(big.Int).SetUint64(mintAmount)
   122  		config.Istanbul.Epoch = uint64(epoch)
   123  		config.KoreCompatibleBlock = new(big.Int).SetUint64(koreBlock)
   124  
   125  		bc := newTestBlockchain(config)
   126  
   127  		dbm := database.NewDBManager(&database.DBConfig{DBType: database.MemoryDB})
   128  
   129  		e := NewMixedEngine(config, dbm)
   130  		e.SetBlockchain(bc)
   131  		e.UpdateParams(bc.CurrentBlock().NumberU64())
   132  
   133  		// write initial gov items and overrides to database
   134  		pset, _ := params.NewGovParamSetChainConfig(config)
   135  		gset := NewGovernanceSet()
   136  		gset.Import(pset.StrMap())
   137  		e.headerGov.WriteGovernance(0, NewGovernanceSet(), gset)
   138  		for _, o := range tc.override {
   139  			override := NewGovernanceSet()
   140  			override.Import(o.strMap)
   141  			e.headerGov.WriteGovernance(uint64(o.num), gset, override)
   142  		}
   143  
   144  		govKlayApi := NewGovernanceKlayAPI(e, bc)
   145  
   146  		for num := 1; num <= tc.length; num++ {
   147  			bc.SetBlockNum(uint64(num))
   148  
   149  			rewardSpec, err := govKlayApi.GetRewards(&latestNum)
   150  			assert.Nil(t, err)
   151  
   152  			minted := new(big.Int).SetUint64(tc.expected[num])
   153  			expectedRewardSpec := &reward.RewardSpec{
   154  				Minted:   minted,
   155  				TotalFee: common.Big0,
   156  				BurntFee: common.Big0,
   157  				Proposer: minted,
   158  				Stakers:  common.Big0,
   159  				KFF:      common.Big0,
   160  				KCF:      common.Big0,
   161  				Rewards: map[common.Address]*big.Int{
   162  					proposer: minted,
   163  				},
   164  			}
   165  			assert.Equal(t, expectedRewardSpec, rewardSpec, "wrong at block %d", num)
   166  		}
   167  	}
   168  }
   169  
   170  func TestGetRewardsAccumulated(t *testing.T) {
   171  	mockCtrl := gomock.NewController(t)
   172  	defer mockCtrl.Finish()
   173  
   174  	mockBlockchain := mocks.NewMockBlockChain(mockCtrl)
   175  	mockGovEngine := NewMockEngine(mockCtrl)
   176  	db := database.NewMemoryDBManager()
   177  
   178  	// prepare configurations and data for the test environment
   179  	chainConfig := params.CypressChainConfig.Copy()
   180  	chainConfig.KoreCompatibleBlock = big.NewInt(0)
   181  	chainConfig.Governance.Reward.Ratio = "50/20/30"
   182  	chainConfig.Governance.Reward.Kip82Ratio = params.DefaultKip82Ratio
   183  
   184  	govParamSet, err := params.NewGovParamSetChainConfig(chainConfig)
   185  	if err != nil {
   186  		t.Fatal(err)
   187  	}
   188  
   189  	oldSm := reward.GetStakingManager()
   190  	defer reward.SetTestStakingManager(oldSm)
   191  	reward.SetTestStakingManagerWithChain(mockBlockchain, mockGovEngine, db)
   192  
   193  	testAddrList := []common.Address{
   194  		common.HexToAddress("0x1111111111111111111111111111111111111111"),
   195  		common.HexToAddress("0x2222222222222222222222222222222222222222"),
   196  		common.HexToAddress("0x3333333333333333333333333333333333333333"),
   197  		common.HexToAddress("0x4444444444444444444444444444444444444444"),
   198  	}
   199  
   200  	testStakingAmountList := []uint64{
   201  		uint64(5000000),
   202  		uint64(10000000),
   203  		uint64(15000000),
   204  		uint64(20000000),
   205  	}
   206  
   207  	stInfo := reward.StakingInfo{
   208  		BlockNum:              0,
   209  		CouncilNodeAddrs:      testAddrList,
   210  		CouncilStakingAddrs:   testAddrList,
   211  		CouncilRewardAddrs:    testAddrList,
   212  		KCFAddr:               common.HexToAddress("0xCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"),
   213  		KFFAddr:               common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
   214  		CouncilStakingAmounts: testStakingAmountList,
   215  	}
   216  
   217  	siBytes, _ := json.Marshal(stInfo)
   218  	if err := db.WriteStakingInfo(stInfo.BlockNum, siBytes); err != nil {
   219  		t.Fatal(err)
   220  	}
   221  
   222  	startBlockNum := 0
   223  	endBlockNum := 10
   224  	blocks := make([]*types.Block, endBlockNum-startBlockNum+1)
   225  
   226  	// set testing data for mock instances
   227  	for i := startBlockNum; i <= endBlockNum; i++ {
   228  		blocks[i] = types.NewBlockWithHeader(&types.Header{
   229  			Number:     big.NewInt(int64(i)),
   230  			Rewardbase: stInfo.CouncilRewardAddrs[i%4], // round-robin way
   231  			GasUsed:    uint64(1000),
   232  			BaseFee:    big.NewInt(25 * params.Ston),
   233  			Time:       big.NewInt(int64(1000 + i)),
   234  		})
   235  
   236  		mockBlockchain.EXPECT().GetHeaderByNumber(uint64(i)).Return(blocks[i].Header()).AnyTimes()
   237  	}
   238  
   239  	mockBlockchain.EXPECT().Config().Return(chainConfig).AnyTimes()
   240  	mockBlockchain.EXPECT().CurrentBlock().Return(blocks[endBlockNum]).AnyTimes()
   241  	mockGovEngine.EXPECT().EffectiveParams(gomock.Any()).Return(govParamSet, nil).AnyTimes()
   242  	mockGovEngine.EXPECT().BlockChain().Return(mockBlockchain).AnyTimes()
   243  
   244  	// execute a target function
   245  	govAPI := NewGovernanceAPI(mockGovEngine)
   246  	ret, err := govAPI.GetRewardsAccumulated(rpc.BlockNumber(startBlockNum), rpc.BlockNumber(endBlockNum))
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	assert.NotNil(t, ret)
   251  
   252  	// pre-calculated estimated rewards per a block
   253  	blockMinted, _ := new(big.Int).SetString("9600000000000000000", 10)  // 9.6 KLAY
   254  	blockProposer, _ := new(big.Int).SetString("960000000000000000", 10) // 0.96 KLAY = 9.6 KLAY * 0.5 * 0.2
   255  	blockStaking, _ := new(big.Int).SetString("3840000000000000000", 10) // 3.84 KLAY = 9.6 KLAY * 0.5 * 0.8
   256  	blockTxFee, _ := new(big.Int).SetString("25000000000000", 10)        // 25000 Ston = 1000 * 25 Ston
   257  	blockTxBurnt := blockTxFee
   258  	blockKFF, _ := new(big.Int).SetString("1920000000000000000", 10) //  1.92 KLAY = 9.6 KLAY * 0.2
   259  	blockKCF, _ := new(big.Int).SetString("2880000000000000000", 10) //  2.88 KLAY = 9.6 KLAY * 0.3
   260  
   261  	// check the execution result
   262  	assert.Equal(t, time.Unix(blocks[startBlockNum].Time().Int64(), 0).String(), ret.FirstBlockTime)
   263  	assert.Equal(t, time.Unix(blocks[endBlockNum].Time().Int64(), 0).String(), ret.LastBlockTime)
   264  	assert.Equal(t, uint64(startBlockNum), ret.FirstBlock.Uint64())
   265  	assert.Equal(t, uint64(endBlockNum), ret.LastBlock.Uint64())
   266  
   267  	blockCount := big.NewInt(int64(endBlockNum - startBlockNum + 1))
   268  	assert.Equal(t, new(big.Int).Mul(blockMinted, blockCount), ret.TotalMinted)
   269  	assert.Equal(t, new(big.Int).Mul(blockTxFee, blockCount), ret.TotalTxFee)
   270  	assert.Equal(t, new(big.Int).Mul(blockTxBurnt, blockCount), ret.TotalBurntTxFee)
   271  	assert.Equal(t, new(big.Int).Mul(blockProposer, blockCount), ret.TotalProposerRewards)
   272  	assert.Equal(t, new(big.Int).Mul(blockStaking, blockCount), ret.TotalStakingRewards)
   273  	assert.Equal(t, new(big.Int).Mul(blockKFF, blockCount), ret.TotalKFFRewards)
   274  	assert.Equal(t, new(big.Int).Mul(blockKCF, blockCount), ret.TotalKCFRewards)
   275  
   276  	gcReward := big.NewInt(0)
   277  	for acc, bal := range ret.Rewards {
   278  		if acc != stInfo.KFFAddr && acc != stInfo.KCFAddr {
   279  			gcReward.Add(gcReward, bal)
   280  		}
   281  	}
   282  	assert.Equal(t, gcReward, new(big.Int).Add(ret.TotalStakingRewards, ret.TotalProposerRewards))
   283  }
   284  
   285  func (bc *testBlockChain) Engine() consensus.Engine                    { return nil }
   286  func (bc *testBlockChain) GetHeader(common.Hash, uint64) *types.Header { return nil }
   287  func (bc *testBlockChain) GetHeaderByNumber(val uint64) *types.Header {
   288  	return &types.Header{
   289  		Number: new(big.Int).SetUint64(val),
   290  	}
   291  }
   292  func (bc *testBlockChain) GetBlockByNumber(num uint64) *types.Block         { return nil }
   293  func (bc *testBlockChain) StateAt(root common.Hash) (*state.StateDB, error) { return nil, nil }
   294  func (bc *testBlockChain) State() (*state.StateDB, error)                   { return nil, nil }
   295  func (bc *testBlockChain) Config() *params.ChainConfig {
   296  	return bc.config
   297  }
   298  
   299  func (bc *testBlockChain) CurrentBlock() *types.Block {
   300  	return types.NewBlockWithHeader(bc.CurrentHeader())
   301  }
   302  
   303  func (bc *testBlockChain) CurrentHeader() *types.Header {
   304  	return &types.Header{
   305  		Number: new(big.Int).SetUint64(bc.num),
   306  	}
   307  }
   308  
   309  func (bc *testBlockChain) SetBlockNum(num uint64) {
   310  	bc.num = num
   311  }
   312  
   313  func (bc *testBlockChain) GetBlock(hash common.Hash, num uint64) *types.Block {
   314  	return bc.GetBlockByNumber(num)
   315  }