github.com/klaytn/klaytn@v1.12.1/tests/gov_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 tests 18 19 import ( 20 "encoding/hex" 21 "math/big" 22 "os" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/klaytn/klaytn/accounts/abi" 28 "github.com/klaytn/klaytn/blockchain" 29 "github.com/klaytn/klaytn/blockchain/types" 30 "github.com/klaytn/klaytn/common" 31 "github.com/klaytn/klaytn/consensus/istanbul" 32 govcontract "github.com/klaytn/klaytn/contracts/gov" 33 "github.com/klaytn/klaytn/crypto" 34 "github.com/klaytn/klaytn/log" 35 "github.com/klaytn/klaytn/node/cn" 36 "github.com/klaytn/klaytn/params" 37 "github.com/stretchr/testify/assert" 38 "github.com/stretchr/testify/require" 39 ) 40 41 // TestGovernance_Engines tests MixedEngine, ContractEngine, and their 42 // (1) CurrentParams() and (2) EffectiveParams() results. 43 func TestGovernance_Engines(t *testing.T) { 44 log.EnableLogForTest(log.LvlCrit, log.LvlDebug) 45 46 config := params.CypressChainConfig.Copy() 47 config.IstanbulCompatibleBlock = new(big.Int).SetUint64(0) 48 config.LondonCompatibleBlock = new(big.Int).SetUint64(0) 49 config.EthTxTypeCompatibleBlock = new(big.Int).SetUint64(0) 50 config.MagmaCompatibleBlock = new(big.Int).SetUint64(0) 51 config.KoreCompatibleBlock = new(big.Int).SetUint64(0) 52 53 config.Istanbul.Epoch = 2 54 config.Istanbul.SubGroupSize = 1 55 config.Istanbul.ProposerPolicy = uint64(istanbul.RoundRobin) 56 config.Governance.Reward.MintingAmount = new(big.Int).Mul(big.NewInt(9000000000000000000), big.NewInt(params.KLAY)) 57 config.Governance.Reward.Kip82Ratio = params.DefaultKip82Ratio 58 59 config.Governance.GovParamContract = common.Address{} 60 config.Governance.GovernanceMode = "none" 61 62 fullNode, node, validator, chainId, workspace := newBlockchain(t, config, nil) 63 defer os.RemoveAll(workspace) 64 defer fullNode.Stop() 65 66 var ( 67 chain = node.BlockChain().(*blockchain.BlockChain) 68 owner = validator 69 contractAddr = crypto.CreateAddress(owner.Addr, owner.Nonce) 70 71 paramName = "istanbul.committeesize" 72 oldVal = config.Istanbul.SubGroupSize 73 newVal = uint64(22) 74 paramBytes = []byte{22} 75 76 govBlock uint64 // Before vote: 0, After vote: the governance block 77 stopBlock uint64 // Before govBlock is set: 0, After: the block to stop receiving new blocks 78 ) 79 80 // Here we are running (tx sender) and (param reader) in parallel. 81 // This is to check that param reader (mixed engine) works in such situations: 82 // (a) contract engine disabled 83 // (b) contract engine enabled (via vote) 84 85 // Run tx sender thread 86 go func() { 87 deployGovParamTx_constructor(t, node, owner, chainId) 88 89 // Give some time for txpool to recognize the contract, because otherwise 90 // the txpool may reject the setParam tx with 'not a program account' 91 time.Sleep(2 * time.Second) 92 93 deployGovParamTx_setParamIn(t, node, owner, chainId, contractAddr, paramName, paramBytes) 94 95 node.Governance().AddVote("governance.govparamcontract", contractAddr) 96 }() 97 98 // Run param reader thread 99 mixedEngine := node.Governance() 100 contractEngine := node.Governance().ContractGov() 101 102 // Validate current params from mixedEngine.CurrentParams() & contractEngine.CurrentParams(), 103 // alongside block processing. 104 // At block #N, CurrentParams() returns the parameters to be used when building 105 // block #N+1 (i.e. pending block). 106 chainEventCh := make(chan blockchain.ChainEvent) 107 subscription := chain.SubscribeChainEvent(chainEventCh) 108 defer subscription.Unsubscribe() 109 110 // 1. test CurrentParams() while subscribing new blocks 111 for { 112 ev := <-chainEventCh 113 time.Sleep(100 * time.Millisecond) // wait for tx sender thread to set deployBlock, etc. 114 115 num := ev.Block.Number().Uint64() 116 mixedEngine.UpdateParams(num) 117 118 mixedVal, _ := mixedEngine.CurrentParams().Get(params.CommitteeSize) 119 contractVal, _ := contractEngine.CurrentParams().Get(params.CommitteeSize) 120 121 if len(ev.Block.Header().Governance) > 0 { 122 govBlock = num 123 // stopBlock is the epoch block, so we stop when receiving it 124 // otherwise, EffectiveParams(stopBlock) may fail 125 stopBlock = govBlock + 5 126 stopBlock = stopBlock - (stopBlock % config.Istanbul.Epoch) 127 t.Logf("Governance at block=%2d, stopBlock=%2d", num, stopBlock) 128 } 129 130 if govBlock == 0 || num <= govBlock { // ContractEngine disabled 131 assert.Equal(t, oldVal, mixedVal) 132 assert.Equal(t, nil, contractVal) 133 } else { // ContractEngine enabled 134 assert.Equal(t, newVal, mixedVal) 135 assert.Equal(t, newVal, contractVal) 136 } 137 138 if stopBlock != 0 && num >= stopBlock { 139 break 140 } 141 142 if num >= 60 { 143 t.Fatal("test taking too long; something must be wrong") 144 } 145 } 146 147 // 2. test EffectiveParams(): Validate historic params from both Engines 148 for num := uint64(0); num < stopBlock; num++ { 149 mixedpset, err := mixedEngine.EffectiveParams(num) 150 assert.Nil(t, err) 151 mixedVal, _ := mixedpset.Get(params.CommitteeSize) 152 153 contractpset, err := contractEngine.EffectiveParams(num) 154 assert.Nil(t, err) 155 156 if num <= govBlock+1 { // ContractEngine disabled 157 assert.Equal(t, oldVal, mixedVal) 158 assert.Equal(t, params.NewGovParamSet(), contractpset) 159 } else { // ContractEngine enabled 160 assert.Equal(t, newVal, mixedVal) 161 contractVal, _ := contractpset.Get(params.CommitteeSize) 162 assert.Equal(t, newVal, contractVal) 163 } 164 } 165 } 166 167 func deployGovParamTx_constructor(t *testing.T, node *cn.CN, owner *TestAccountType, chainId *big.Int, 168 ) (uint64, common.Address, *types.Transaction) { 169 var ( 170 // Deploy contract: constructor(address _owner) 171 contractAbi, _ = abi.JSON(strings.NewReader(govcontract.GovParamABI)) 172 contractBin = govcontract.GovParamBin 173 ctorArgs, _ = contractAbi.Pack("") 174 code = contractBin + hex.EncodeToString(ctorArgs) 175 ) 176 177 // Deploy contract 178 tx, addr := deployContractDeployTx(t, node.TxPool(), chainId, owner, code) 179 180 chain := node.BlockChain().(*blockchain.BlockChain) 181 receipt := waitReceipt(chain, tx.Hash()) 182 require.NotNil(t, receipt) 183 require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) 184 185 _, _, num, _ := chain.GetTxAndLookupInfo(tx.Hash()) 186 t.Logf("GovParam deployed at block=%2d, addr=%s", num, addr.Hex()) 187 188 return num, addr, tx 189 } 190 191 func deployGovParamTx_setParamIn(t *testing.T, node *cn.CN, owner *TestAccountType, chainId *big.Int, 192 contractAddr common.Address, name string, value []byte, 193 ) (uint64, *types.Transaction) { 194 var ( 195 contractAbi, _ = abi.JSON(strings.NewReader(govcontract.GovParamABI)) 196 callArgs, _ = contractAbi.Pack("setParamIn", name, true, value, big.NewInt(1)) 197 data = common.ToHex(callArgs) 198 ) 199 200 tx := deployContractExecutionTx(t, node.TxPool(), chainId, owner, contractAddr, data) 201 202 chain := node.BlockChain().(*blockchain.BlockChain) 203 receipt := waitReceipt(chain, tx.Hash()) 204 require.NotNil(t, receipt) 205 require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "setParamIn failed") 206 207 _, _, num, _ := chain.GetTxAndLookupInfo(tx.Hash()) 208 t.Logf("GovParam.setParamIn executed at block=%2d", num) 209 return num, tx 210 } 211 212 func deployGovParamTx_batchSetParamIn(t *testing.T, node *cn.CN, owner *TestAccountType, chainId *big.Int, 213 contractAddr common.Address, bytesMap map[string][]byte, 214 ) []*types.Transaction { 215 var ( 216 chain = node.BlockChain().(*blockchain.BlockChain) 217 beginBlock = chain.CurrentHeader().Number.Uint64() 218 contractAbi, _ = abi.JSON(strings.NewReader(govcontract.GovParamABI)) 219 txs = []*types.Transaction{} 220 ) 221 222 // Send all setParamIn() calls at once 223 for name, value := range bytesMap { 224 callArgs, _ := contractAbi.Pack("setParamIn", name, true, value, big.NewInt(1)) 225 data := common.ToHex(callArgs) 226 tx := deployContractExecutionTx(t, node.TxPool(), chainId, owner, contractAddr, data) 227 txs = append(txs, tx) 228 } 229 230 // Wait for all txs 231 for _, tx := range txs { 232 receipt := waitReceipt(chain, tx.Hash()) 233 require.NotNil(t, receipt) 234 require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "batchSetParamIn failed") 235 } 236 num := chain.CurrentHeader().Number.Uint64() 237 t.Logf("GovParam.setParamIn executed %d times between blocks=%2d,%2d", len(txs), beginBlock, num) 238 return txs 239 } 240 241 // Klaytn node only decodes the byte-array param values (refer to params/governance_paramset.go). 242 // Encoding is the job of transaction senders (i.e. clients and dApps). 243 // This is a reference implementation of such encoder. 244 func chainConfigToBytesMap(t *testing.T, config *params.ChainConfig) map[string][]byte { 245 pset, err := params.NewGovParamSetChainConfig(config) 246 require.Nil(t, err) 247 strMap := pset.StrMap() 248 249 bytesMap := map[string][]byte{} 250 for name, value := range strMap { 251 switch value.(type) { 252 case string: 253 bytesMap[name] = []byte(value.(string)) 254 case common.Address: 255 bytesMap[name] = value.(common.Address).Bytes() 256 case uint64: 257 bytesMap[name] = new(big.Int).SetUint64(value.(uint64)).Bytes() 258 case bool: 259 if value.(bool) == true { 260 bytesMap[name] = []byte{0x01} 261 } else { 262 bytesMap[name] = []byte{0x00} 263 } 264 } 265 } 266 267 // Check that bytesMap is correct just in case 268 qset, err := params.NewGovParamSetBytesMap(bytesMap) 269 require.Nil(t, err) 270 require.Equal(t, pset.StrMap(), qset.StrMap()) 271 return bytesMap 272 }