github.com/klaytn/klaytn@v1.12.1/tests/randao_fork_test.go (about) 1 package tests 2 3 import ( 4 "context" 5 "math/big" 6 "testing" 7 8 "github.com/klaytn/klaytn" 9 "github.com/klaytn/klaytn/accounts/abi/bind" 10 "github.com/klaytn/klaytn/accounts/abi/bind/backends" 11 "github.com/klaytn/klaytn/blockchain" 12 "github.com/klaytn/klaytn/blockchain/system" 13 "github.com/klaytn/klaytn/blockchain/types" 14 "github.com/klaytn/klaytn/common" 15 "github.com/klaytn/klaytn/common/hexutil" 16 "github.com/klaytn/klaytn/consensus/istanbul" 17 "github.com/klaytn/klaytn/contracts/system_contracts" 18 "github.com/klaytn/klaytn/crypto" 19 "github.com/klaytn/klaytn/crypto/bls" 20 "github.com/klaytn/klaytn/log" 21 "github.com/klaytn/klaytn/params" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 ) 25 26 // Test full Randao hardfork scenario under the condition similar to the Cypress network. 27 func TestRandao_Deploy(t *testing.T) { 28 log.EnableLogForTest(log.LvlCrit, log.LvlWarn) 29 30 // Test parameters 31 var ( 32 numNodes = 1 33 forkNum = big.NewInt(15) 34 owner = bind.NewKeyedTransactor(deriveTestAccount(5)) 35 kip113Addr = crypto.CreateAddress(owner.From, uint64(1)) // predict deployed address. 36 randomAddr = common.HexToAddress("0x0000000000000000000000000000000000000404") 37 38 config = testRandao_config(forkNum, owner.From, kip113Addr) 39 alloc = testRandao_allocRandom(randomAddr) 40 ) 41 42 // Start the chain 43 ctx, err := newBlockchainTestContext(&blockchainTestOverrides{ 44 numNodes: numNodes, 45 numAccounts: 8, 46 config: config, 47 alloc: alloc, 48 }) 49 require.Nil(t, err) 50 ctx.Subscribe(t, func(ev *blockchain.ChainEvent) { 51 b := ev.Block 52 t.Logf("block[%3d] txs=%d mixHash=%x", b.NumberU64(), b.Transactions().Len(), b.Header().MixHash) 53 }) 54 ctx.Start() 55 defer ctx.Cleanup() 56 57 // Wait for the chain to start consensus (especially when numNodes > 1) 58 ctx.WaitBlock(t, 1) 59 60 // Deploy KIP113 before hardfork. 61 // Note: this test has a minor difference from Cypress scenario. 62 // In this test, RandaoRegistry[KIP113] is configured in before deployment 63 // but in Cypress RandaoRegistry[KIP113] will be configured after deployment. 64 // following assert ensures the equivalence of this test and Cypress scenario. 65 _, actualKip113Addr := testRandao_deployKip113(t, ctx, owner) 66 assert.Equal(t, kip113Addr, actualKip113Addr) // check the prediced address 67 68 // Pass the hardfork block, give each CN a chance to propose 69 ctx.WaitBlock(t, forkNum.Uint64()+uint64(numNodes)) 70 71 // Inspect the chain 72 testRandao_checkRegistry(t, ctx, owner.From, kip113Addr) 73 testRandao_checkKip113(t, ctx) 74 testRandao_checkKip114(t, ctx, randomAddr) 75 } 76 77 // Test Randao hardfork scenario where it's enabled from the genesis 78 func TestRandao_Genesis(t *testing.T) { 79 log.EnableLogForTest(log.LvlCrit, log.LvlWarn) 80 81 // Test parameters 82 var ( 83 numNodes = 1 84 forkNum = big.NewInt(0) 85 owner = bind.NewKeyedTransactor(deriveTestAccount(5)) 86 kip113Addr = common.HexToAddress("0x0000000000000000000000000000000000000403") 87 randomAddr = common.HexToAddress("0x0000000000000000000000000000000000000404") 88 89 config = testRandao_config(forkNum, owner.From, kip113Addr) 90 alloc = system.MergeGenesisAlloc( 91 testRandao_allocRandom(randomAddr), 92 testRandao_allocRegistry(owner.From, kip113Addr), 93 testRandao_allocKip113(numNodes, owner.From, kip113Addr), 94 ) 95 ) 96 97 // Start the chain 98 ctx, err := newBlockchainTestContext(&blockchainTestOverrides{ 99 numNodes: numNodes, 100 numAccounts: 8, 101 config: config, 102 alloc: alloc, 103 }) 104 require.Nil(t, err) 105 ctx.Subscribe(t, func(ev *blockchain.ChainEvent) { 106 b := ev.Block 107 t.Logf("block[%3d] txs=%d mixHash=%x", b.NumberU64(), b.Transactions().Len(), b.Header().MixHash) 108 }) 109 ctx.Start() 110 defer ctx.Cleanup() 111 112 // Pass the hardfork block, give each CN a chance to propose 113 ctx.WaitBlock(t, forkNum.Uint64()+uint64(numNodes)) 114 115 // Inspect the chain 116 testRandao_checkRegistry(t, ctx, owner.From, kip113Addr) 117 testRandao_checkKip113(t, ctx) 118 testRandao_checkKip114(t, ctx, randomAddr) 119 } 120 121 // Make ChainConfig that hardforks at `forkNum` and the Registry owner be `owner`. 122 func testRandao_config(forkNum *big.Int, owner, kip113Addr common.Address) *params.ChainConfig { 123 config := blockchainTestChainConfig.Copy() 124 config.LondonCompatibleBlock = common.Big0 125 config.IstanbulCompatibleBlock = common.Big0 126 config.EthTxTypeCompatibleBlock = common.Big0 127 config.MagmaCompatibleBlock = common.Big0 128 config.KoreCompatibleBlock = common.Big0 129 config.ShanghaiCompatibleBlock = common.Big0 130 config.CancunCompatibleBlock = forkNum 131 config.RandaoCompatibleBlock = forkNum 132 133 // Use WeightedRandom to test KIP-146 random proposer selection 134 config.Istanbul.ProposerPolicy = uint64(istanbul.WeightedRandom) 135 136 if forkNum.Sign() != 0 { 137 // RandaoRegistry is only effective if forkNum > 0 138 config.RandaoRegistry = ¶ms.RegistryConfig{ 139 Records: map[string]common.Address{ 140 system.Kip113Name: kip113Addr, 141 }, 142 Owner: owner, 143 } 144 } 145 return config 146 } 147 148 // Deploy a small contract to test RANDOM opcode 149 func testRandao_allocRandom(randomAddr common.Address) blockchain.GenesisAlloc { 150 return blockchain.GenesisAlloc{ 151 randomAddr: { 152 // contract Random { function random() external view returns (uint256) { return block.prevrandao; }} // 0x44 opcode is block.prevrandao in solc 0.8.18+ 153 Code: hexutil.MustDecode("0x6080604052348015600f57600080fd5b506004361060285760003560e01c80635ec01e4d14602d575b600080fd5b60336047565b604051603e91906066565b60405180910390f35b600044905090565b6000819050919050565b606081604f565b82525050565b6000602082019050607960008301846059565b9291505056fea2646970667358221220291164179a7b6e34ccb0821e55e26f9202870c95464cde432863dde9ca55426c64736f6c63430008120033"), 154 Balance: common.Big0, 155 }, 156 } 157 } 158 159 // RandaoRegistry must be allocated at Genesis if forkNum == 0 160 func testRandao_allocRegistry(ownerAddr, kip113Addr common.Address) blockchain.GenesisAlloc { 161 return blockchain.GenesisAlloc{ 162 system.RegistryAddr: { 163 Code: system.RegistryCode, 164 Balance: common.Big0, 165 Storage: system.AllocRegistry(¶ms.RegistryConfig{ 166 Records: map[string]common.Address{ 167 system.Kip113Name: kip113Addr, 168 }, 169 Owner: ownerAddr, 170 }), 171 }, 172 } 173 } 174 175 // Allocate the KIP-113 with all node BLS public keys 176 func testRandao_allocKip113(numNodes int, ownerAddr, kip113Addr common.Address) blockchain.GenesisAlloc { 177 infos := make(system.BlsPublicKeyInfos) 178 for i := 0; i < numNodes; i++ { 179 var ( 180 key = deriveTestAccount(i) 181 addr = crypto.PubkeyToAddress(key.PublicKey) 182 sk, _ = bls.DeriveFromECDSA(key) 183 pk = sk.PublicKey().Marshal() 184 pop = bls.PopProve(sk).Marshal() 185 ) 186 infos[addr] = system.BlsPublicKeyInfo{PublicKey: pk, Pop: pop} 187 } 188 189 var ( 190 logicAddr = common.HexToAddress("0x0000000000000000000000000000000000000402") 191 owner = crypto.PubkeyToAddress(deriveTestAccount(5).PublicKey) 192 193 proxyStorage = system.AllocProxy(logicAddr) 194 kip113ProxyStorage = system.AllocKip113Proxy(system.AllocKip113Init{ 195 Infos: infos, 196 Owner: owner, 197 }) 198 kip113LogicStorage = system.AllocKip113Logic() 199 storage = system.MergeStorage(proxyStorage, kip113ProxyStorage) 200 ) 201 202 return blockchain.GenesisAlloc{ 203 logicAddr: { 204 Code: system.Kip113MockCode, 205 Storage: kip113LogicStorage, 206 Balance: common.Big0, 207 }, 208 kip113Addr: { 209 Code: system.ERC1967ProxyCode, 210 Storage: storage, 211 Balance: common.Big0, 212 }, 213 } 214 } 215 216 // Deploy KIP-113 contract 217 func testRandao_deployKip113(t *testing.T, ctx *blockchainTestContext, owner *bind.TransactOpts) (*system_contracts.KIP113Mock, common.Address) { 218 var ( 219 abi, _ = system_contracts.KIP113MockMetaData.GetAbi() 220 initData, _ = abi.Pack("initialize") 221 222 chain = ctx.nodes[0].cn.BlockChain() 223 txpool = ctx.nodes[0].cn.TxPool().(*blockchain.TxPool) 224 backend = backends.NewBlockchainContractBackend(chain, txpool, nil) 225 ) 226 227 // Deploy implementation and proxy 228 implAddr, tx, _, err := system_contracts.DeployKIP113Mock(owner, backend) 229 assert.Nil(t, err) 230 ctx.WaitTx(t, tx.Hash()) 231 232 proxyAddr, tx, _, err := system_contracts.DeployERC1967Proxy(owner, backend, implAddr, initData) 233 assert.Nil(t, err) 234 ctx.WaitTx(t, tx.Hash()) 235 236 t.Logf("Kip113 impl=%s proxy=%s", implAddr.Hex(), proxyAddr.Hex()) 237 kip113, _ := system_contracts.NewKIP113Mock(proxyAddr, backend) 238 239 // Register node BLS public keys 240 var txs []*types.Transaction 241 for i := 0; i < ctx.numNodes; i++ { 242 var ( 243 addr = ctx.accountAddrs[i] 244 sk, _ = bls.DeriveFromECDSA(ctx.accountKeys[i]) 245 pk = sk.PublicKey().Marshal() 246 pop = bls.PopProve(sk).Marshal() 247 ) 248 t.Logf("node[%2d] addr=%x blsPub=%x", i, addr, pk) 249 250 tx, err := kip113.Register(owner, addr, pk, pop) 251 txs = append(txs, tx) 252 assert.Nil(t, err) 253 } 254 for _, tx := range txs { 255 ctx.WaitTx(t, tx.Hash()) 256 } 257 258 infos, _ := system.ReadKip113All(backend, proxyAddr, nil) 259 t.Logf("Kip113 getAllBlsInfo().length=%d", len(infos)) 260 261 return kip113, proxyAddr 262 } 263 264 // Inspect the given chain for Registry contract 265 func testRandao_checkRegistry(t *testing.T, ctx *blockchainTestContext, ownerAddr, kip113Addr common.Address) { 266 var ( 267 forkNum = int64(ctx.config.RandaoCompatibleBlock.Uint64()) 268 bgctx = context.Background() 269 chain = ctx.nodes[0].cn.BlockChain() 270 backend = backends.NewBlockchainContractBackend(chain, nil, nil) 271 registry, _ = system_contracts.NewRegistryCaller(system.RegistryAddr, backend) 272 273 before *big.Int // Largest num without Registry 274 after *big.Int // Smallest num with Registry 275 ) 276 277 if forkNum == 0 { 278 after = common.Big0 279 } else { 280 before = big.NewInt(forkNum - 1) 281 after = big.NewInt(forkNum) 282 } 283 284 // Registry code is installed exactly at forkParentNum 285 if before != nil { 286 code, err := backend.CodeAt(bgctx, system.RegistryAddr, before) 287 assert.Nil(t, err) 288 assert.Empty(t, code) 289 290 addr, err := system.ReadActiveAddressFromRegistry(backend, system.Kip113Name, before) 291 assert.ErrorIs(t, err, system.ErrRegistryNotInstalled) 292 assert.Empty(t, addr) 293 } 294 295 // Inspect code 296 code, err := backend.CodeAt(bgctx, system.RegistryAddr, after) 297 assert.Nil(t, err) 298 assert.NotNil(t, code) 299 300 // Inspect contract contents 301 names, err := registry.GetAllNames(&bind.CallOpts{BlockNumber: after}) 302 t.Logf("Registry.getAllNames()=%v", names) 303 assert.Nil(t, err) 304 assert.Equal(t, []string{system.Kip113Name}, names) 305 306 addr, err := registry.GetActiveAddr(&bind.CallOpts{BlockNumber: after}, system.Kip113Name) 307 t.Logf("Registry.getActiveAddr('KIP113')=%s", addr.Hex()) 308 assert.Nil(t, err) 309 assert.Equal(t, kip113Addr, addr) 310 311 addr, err = registry.Owner(&bind.CallOpts{BlockNumber: after}) 312 t.Logf("Registry.owner()=%s", ownerAddr.Hex()) 313 assert.Nil(t, err) 314 assert.Equal(t, ownerAddr, addr) 315 316 // Inspect via system contract accessors 317 addr, err = system.ReadActiveAddressFromRegistry(backend, system.Kip113Name, after) 318 assert.Nil(t, err) 319 assert.Equal(t, kip113Addr, addr) 320 } 321 322 // Inspect the given chain for KIP-113 contract 323 func testRandao_checkKip113(t *testing.T, ctx *blockchainTestContext) { 324 var ( 325 forkNum = ctx.config.RandaoCompatibleBlock 326 chain = ctx.nodes[0].cn.BlockChain() 327 backend = backends.NewBlockchainContractBackend(chain, nil, nil) 328 ) 329 330 kip113Addr, err := system.ReadActiveAddressFromRegistry(backend, system.Kip113Name, forkNum) 331 assert.Nil(t, err) 332 333 // Inspect via system contract accessors 334 // BLS public keys of every nodes are registered 335 infos, err := system.ReadKip113All(backend, kip113Addr, forkNum) 336 t.Logf("Kip113.getAllBlsInfo()=%v", infos.String()) 337 assert.Nil(t, err) 338 assert.Len(t, infos, ctx.numNodes) 339 for i := 0; i < ctx.numNodes; i++ { 340 addr := ctx.accountAddrs[i] 341 assert.Contains(t, infos, addr) 342 } 343 } 344 345 // Inspect the given chain for KIP-114 header fields and RANDOM opcode 346 func testRandao_checkKip114(t *testing.T, ctx *blockchainTestContext, randomAddr common.Address) { 347 var ( 348 chain = ctx.nodes[0].cn.BlockChain() 349 backend = backends.NewBlockchainContractBackend(chain, nil, nil) 350 351 forkNum = ctx.config.RandaoCompatibleBlock.Uint64() 352 headNum = chain.CurrentBlock().NumberU64() 353 ) 354 355 // Call the contract to check RANDOM opcode result 356 callRandom := func(num uint64) []byte { 357 tx := klaytn.CallMsg{ 358 To: &randomAddr, 359 Data: hexutil.MustDecode("0x5ec01e4d"), // random() 360 } 361 out, err := backend.CallContract(context.Background(), tx, new(big.Int).SetUint64(num)) 362 assert.Nil(t, err) 363 return out 364 } 365 366 for num := uint64(1); num <= headNum; num++ { 367 header := chain.GetHeaderByNumber(num) 368 require.NotNil(t, header) 369 370 random := callRandom(num) 371 t.Logf("block[%3d] opRandom=%x", num, random) 372 373 if num < forkNum { 374 assert.Nil(t, header.RandomReveal, num) 375 assert.Nil(t, header.MixHash, num) 376 assert.Equal(t, header.ParentHash.Bytes(), random, num) 377 } else { 378 assert.NotNil(t, header.RandomReveal, num) 379 assert.NotNil(t, header.MixHash, num) 380 assert.Equal(t, header.MixHash, random, num) 381 } 382 } 383 }