github.com/klaytn/klaytn@v1.10.2/governance/contract_test.go (about)

     1  // Copyright 2022 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 governance
    18  
    19  import (
    20  	"math/big"
    21  	"testing"
    22  
    23  	"github.com/klaytn/klaytn/accounts/abi/bind"
    24  	"github.com/klaytn/klaytn/accounts/abi/bind/backends"
    25  	"github.com/klaytn/klaytn/blockchain"
    26  	"github.com/klaytn/klaytn/blockchain/types"
    27  	"github.com/klaytn/klaytn/common"
    28  	govcontract "github.com/klaytn/klaytn/contracts/gov"
    29  	"github.com/klaytn/klaytn/crypto"
    30  	"github.com/klaytn/klaytn/params"
    31  	"github.com/klaytn/klaytn/storage/database"
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func prepareSimulatedContract(t *testing.T) ([]*bind.TransactOpts, *backends.SimulatedBackend, common.Address, *govcontract.GovParam) {
    37  	// Create accounts and simulated blockchain
    38  	accounts := []*bind.TransactOpts{}
    39  	alloc := blockchain.GenesisAlloc{}
    40  	for i := 0; i < 1; i++ {
    41  		key, _ := crypto.GenerateKey()
    42  		account := bind.NewKeyedTransactor(key)
    43  		account.GasLimit = 10000000
    44  		accounts = append(accounts, account)
    45  		alloc[account.From] = blockchain.GenesisAccount{Balance: big.NewInt(params.KLAY)}
    46  	}
    47  	config := &params.ChainConfig{}
    48  	config.SetDefaults()
    49  	config.UnitPrice = 25e9
    50  	config.IstanbulCompatibleBlock = common.Big0
    51  	config.LondonCompatibleBlock = common.Big0
    52  	config.EthTxTypeCompatibleBlock = common.Big0
    53  	config.MagmaCompatibleBlock = common.Big0
    54  	config.KoreCompatibleBlock = common.Big0
    55  
    56  	sim := backends.NewSimulatedBackendWithDatabase(database.NewMemoryDBManager(), alloc, config)
    57  
    58  	// Deploy contract
    59  	owner := accounts[0]
    60  	address, tx, contract, err := govcontract.DeployGovParam(owner, sim)
    61  	require.Nil(t, err)
    62  	sim.Commit()
    63  
    64  	receipt, _ := sim.TransactionReceipt(nil, tx.Hash())
    65  	require.NotNil(t, receipt)
    66  	require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
    67  
    68  	return accounts, sim, address, contract
    69  }
    70  
    71  func prepareSimulatedContractWithParams(t *testing.T, params map[string][]byte) ([]*bind.TransactOpts, *backends.SimulatedBackend, common.Address, *govcontract.GovParam) {
    72  	// Create accounts and simulated blockchain
    73  	accounts, sim, address, contract := prepareSimulatedContract(t)
    74  	owner := accounts[0]
    75  
    76  	for name, val := range params {
    77  		tx, err := contract.SetParamIn(owner, name, true, val, big.NewInt(1))
    78  		require.Nil(t, err)
    79  		sim.Commit()
    80  
    81  		// check tx success
    82  		receipt, _ := sim.TransactionReceipt(nil, tx.Hash())
    83  		require.NotNil(t, receipt)
    84  		require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
    85  	}
    86  
    87  	ab := new(big.Int).Set(sim.BlockChain().CurrentHeader().Number)
    88  	ab = ab.Add(ab, big.NewInt(1))
    89  
    90  	// check with govcontract
    91  	names, values, err := contract.GetAllParamsAt(nil, ab)
    92  	require.Nil(t, err)
    93  	require.Equal(t, len(params), len(names))
    94  	require.Equal(t, len(params), len(values))
    95  
    96  	returned := make(map[string][]byte)
    97  	for i := 0; i < len(names); i++ {
    98  		returned[names[i]] = values[i]
    99  	}
   100  
   101  	require.Equal(t, params, returned)
   102  
   103  	return accounts, sim, address, contract
   104  }
   105  
   106  func newTestContractCaller(chain blockChain, addr common.Address) *contractCaller {
   107  	return &contractCaller{
   108  		chain:        chain,
   109  		contractAddr: addr,
   110  	}
   111  }
   112  
   113  func TestContractEngine_contractCaller(t *testing.T) {
   114  	var (
   115  		valueA       = []byte{0xa}
   116  		valueB       = []byte{0xbb, 0xbb}
   117  		name         = "istanbul.committeesize"
   118  		initialParam = map[string][]byte{
   119  			name: valueA,
   120  		}
   121  	)
   122  
   123  	accounts, sim, addr, contract := prepareSimulatedContractWithParams(t, initialParam)
   124  	owner := accounts[0]
   125  
   126  	// Call initial SetParamIn()
   127  	{
   128  		// activation: Now + 1
   129  		tx, err := contract.SetParamIn(owner, name, true, valueA, big.NewInt(1))
   130  		require.Nil(t, err)
   131  		sim.Commit()
   132  
   133  		ab := new(big.Int).Set(sim.BlockChain().CurrentHeader().Number)
   134  		ab = ab.Add(ab, big.NewInt(1))
   135  
   136  		// check tx success
   137  		receipt, _ := sim.TransactionReceipt(nil, tx.Hash())
   138  		require.NotNil(t, receipt)
   139  		require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
   140  
   141  		// check with govcontract
   142  		names, values, err := contract.GetAllParamsAt(nil, ab)
   143  		require.Nil(t, err)
   144  		require.Equal(t, 1, len(names))
   145  		require.Equal(t, 1, len(values))
   146  		require.Equal(t, name, names[0])
   147  		require.Equal(t, valueA, values[0])
   148  
   149  		// check with contractCaller
   150  		cc := newTestContractCaller(sim.BlockChain(), addr)
   151  		pset, err := cc.getAllParamsAt(ab)
   152  		assert.Nil(t, err)
   153  		assert.Equal(t, new(big.Int).SetBytes(valueA).Uint64(), pset.CommitteeSize())
   154  	}
   155  
   156  	// Call SetParam() again
   157  	{
   158  		ab := new(big.Int).Set(sim.BlockChain().CurrentHeader().Number)
   159  		ab = ab.Add(ab, big.NewInt(2))
   160  		tx, err := contract.SetParam(owner, name, true, valueB, ab)
   161  		require.Nil(t, err)
   162  
   163  		// increase block number to reach activation block
   164  		for sim.BlockChain().CurrentHeader().Number.Cmp(ab) < 0 {
   165  			sim.Commit()
   166  		}
   167  
   168  		// check tx success
   169  		receipt, _ := sim.TransactionReceipt(nil, tx.Hash())
   170  		require.NotNil(t, receipt)
   171  		require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
   172  
   173  		// check value changed with govcontract
   174  		names, values, err := contract.GetAllParamsAt(nil, ab)
   175  		require.Nil(t, err)
   176  		require.Equal(t, 1, len(names))
   177  		require.Equal(t, 1, len(values))
   178  		require.Equal(t, name, names[0])
   179  		require.Equal(t, valueB, values[0])
   180  
   181  		// check value changed with contractCaller
   182  		cc := newTestContractCaller(sim.BlockChain(), addr)
   183  		pset, err := cc.getAllParamsAt(ab)
   184  		assert.Nil(t, err)
   185  		assert.Equal(t, new(big.Int).SetBytes(valueB).Uint64(), pset.CommitteeSize())
   186  	}
   187  }
   188  
   189  func prepareContractEngine(t *testing.T, bc *blockchain.BlockChain, addr common.Address) *ContractEngine {
   190  	dbm := database.NewDBManager(&database.DBConfig{DBType: database.MemoryDB})
   191  	dbm.WriteGovernance(map[string]interface{}{
   192  		"governance.govparamcontract": addr,
   193  	}, 0)
   194  	gov := NewGovernance(bc.Config(), dbm)
   195  	pset, err := gov.EffectiveParams(0)
   196  	require.Nil(t, err)
   197  	require.Equal(t, addr, pset.GovParamContract())
   198  
   199  	gov.SetBlockchain(bc)
   200  
   201  	e := NewContractEngine(gov)
   202  	err = e.UpdateParams(bc.CurrentBlock().NumberU64())
   203  	require.Nil(t, err)
   204  
   205  	return e
   206  }
   207  
   208  // TestContractEngine_Params tests if CurrentParams() returns the parameters required
   209  // for generating the next block. That is,
   210  //
   211  //	start          setparam       activation-1       end
   212  //
   213  // Block |---------------|---------------|---------------|
   214  //
   215  // ..............^               ^               ^
   216  // ..............t0              t1              t2
   217  //
   218  // At num = activation - 2, CurrentParams() = prev
   219  // At num = activation - 1, CurrentParams() = next
   220  //
   221  //	because next is for generating "activation" block
   222  func TestContractEngine_Params(t *testing.T) {
   223  	initialParam := map[string][]byte{
   224  		"istanbul.committeesize": {0xa},
   225  		"governance.unitprice":   {0xb},
   226  	}
   227  	accounts, sim, addr, contract := prepareSimulatedContractWithParams(t, initialParam)
   228  
   229  	e := prepareContractEngine(t, sim.BlockChain(), addr)
   230  
   231  	var (
   232  		start      = sim.BlockChain().CurrentBlock().NumberU64()
   233  		setparam   = start + 5
   234  		activation = setparam + 5
   235  		end        = activation + 5
   236  		key        = "governance.unitprice"
   237  		val        = []byte{0xff, 0xff, 0xff, 0xff}
   238  		update, _  = params.NewGovParamSetBytesMap(map[string][]byte{
   239  			key: val,
   240  		})
   241  		psetPrev, _ = params.NewGovParamSetBytesMap(initialParam)   // for t0 & t1
   242  		psetNext    = params.NewGovParamSetMerged(psetPrev, update) // for t2
   243  		owner       = accounts[0]
   244  	)
   245  
   246  	for num := start; num < end; num++ {
   247  		if num == setparam { // setParam
   248  			contract.SetParam(owner, key, true, val, new(big.Int).SetUint64(activation))
   249  		}
   250  
   251  		var expected *params.GovParamSet
   252  
   253  		if num < activation-1 { // t0 & t1
   254  			expected = psetPrev
   255  		} else { // t2
   256  			expected = psetNext
   257  		}
   258  
   259  		assert.Equal(t, expected, e.CurrentParams(), "CurrentParams() on block %d failed", num)
   260  		sim.Commit()
   261  		err := e.UpdateParams(sim.BlockChain().CurrentBlock().NumberU64())
   262  		assert.Nil(t, err)
   263  	}
   264  }
   265  
   266  // TestContractEngine_ParamsAt tests if EffectiveParams(num) returns the parameters
   267  // required for generating the "num" block. That is,
   268  //
   269  //	start          setparam       activation         end
   270  //
   271  // Block |---------------|---------------|---------------|
   272  //
   273  // ..............^               ^               ^
   274  // ..............t0              t1              t2
   275  //
   276  // EffectiveParams(activation - 1) = prev
   277  // EffectiveParams(activation)     = next
   278  func TestContractEngine_ParamsAt(t *testing.T) {
   279  	initialParam := map[string][]byte{
   280  		"istanbul.committeesize": {0xa},
   281  		"governance.unitprice":   {0xbb, 0xbb, 0xbb, 0xbb},
   282  	}
   283  	accounts, sim, addr, contract := prepareSimulatedContractWithParams(t, initialParam)
   284  
   285  	e := prepareContractEngine(t, sim.BlockChain(), addr)
   286  
   287  	var (
   288  		start      = sim.BlockChain().CurrentBlock().NumberU64()
   289  		setparam   = start + 5
   290  		activation = setparam + 5
   291  		end        = activation + 5
   292  		key        = "governance.unitprice"
   293  		val        = []byte{0xff, 0xff, 0xff, 0xff}
   294  		update, _  = params.NewGovParamSetBytesMap(map[string][]byte{
   295  			key: val,
   296  		})
   297  		psetPrev, _ = params.NewGovParamSetBytesMap(initialParam)   // for t0 & t1
   298  		psetNext    = params.NewGovParamSetMerged(psetPrev, update) // for t2
   299  		owner       = accounts[0]
   300  	)
   301  
   302  	for num := start; num < end; num++ {
   303  		if num == setparam { // setParam
   304  			contract.SetParam(owner, key, true, val, new(big.Int).SetUint64(activation))
   305  		}
   306  
   307  		for iter := start + 1; iter <= num; iter++ {
   308  			var expected *params.GovParamSet
   309  
   310  			if iter < activation { // t0 & t1
   311  				expected = psetPrev
   312  			} else { // t2
   313  				expected = psetNext
   314  			}
   315  
   316  			result, _ := e.EffectiveParams(iter)
   317  			assert.Equal(t, expected, result, "EffectiveParams(%d) on block %d failed", iter, num)
   318  		}
   319  
   320  		sim.Commit()
   321  		err := e.UpdateParams(sim.BlockChain().CurrentBlock().NumberU64())
   322  		assert.Nil(t, err)
   323  	}
   324  }