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 := ¶ms.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 }