github.com/klaytn/klaytn@v1.12.1/accounts/abi/bind/backends/blockchain_test.go (about) 1 // Copyright 2023 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 backends 18 19 import ( 20 "context" 21 "encoding/hex" 22 "errors" 23 "math/big" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/golang/mock/gomock" 29 "github.com/klaytn/klaytn" 30 "github.com/klaytn/klaytn/accounts/abi" 31 "github.com/klaytn/klaytn/blockchain" 32 "github.com/klaytn/klaytn/blockchain/types" 33 "github.com/klaytn/klaytn/blockchain/vm" 34 "github.com/klaytn/klaytn/common" 35 "github.com/klaytn/klaytn/consensus/gxhash" 36 "github.com/klaytn/klaytn/crypto" 37 "github.com/klaytn/klaytn/event" 38 "github.com/klaytn/klaytn/node/cn/filters" 39 mock_filter "github.com/klaytn/klaytn/node/cn/filters/mock" 40 "github.com/klaytn/klaytn/params" 41 "github.com/klaytn/klaytn/storage/database" 42 "github.com/stretchr/testify/assert" 43 ) 44 45 var ( 46 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 47 code1Addr = common.HexToAddress("0x1111111111111111111111111111111111111111") 48 code2Addr = common.HexToAddress("0x2222222222222222222222222222222222222222") 49 50 parsedAbi1, _ = abi.JSON(strings.NewReader(abiJSON)) 51 parsedAbi2, _ = abi.JSON(strings.NewReader(reverterABI)) 52 code1Bytes = common.FromHex(deployedCode) 53 code2Bytes = common.FromHex(reverterDeployedBin) 54 ) 55 56 func newTestBlockchain() *blockchain.BlockChain { 57 config := params.TestChainConfig.Copy() 58 return newTestBlockchainWithConfig(config) 59 } 60 61 func newTestBlockchainWithConfig(config *params.ChainConfig) *blockchain.BlockChain { 62 alloc := blockchain.GenesisAlloc{ 63 testAddr: {Balance: big.NewInt(10000000000)}, 64 code1Addr: {Balance: big.NewInt(0), Code: code1Bytes}, 65 code2Addr: {Balance: big.NewInt(0), Code: code2Bytes}, 66 } 67 68 db := database.NewMemoryDBManager() 69 genesis := blockchain.Genesis{Config: config, Alloc: alloc} 70 genesis.MustCommit(db) 71 72 bc, _ := blockchain.NewBlockChain(db, nil, genesis.Config, gxhash.NewFaker(), vm.Config{}) 73 74 // Append 10 blocks to test with block numbers other than 0 75 block := bc.CurrentBlock() 76 blocks, _ := blockchain.GenerateChain(config, block, gxhash.NewFaker(), db, 10, func(i int, b *blockchain.BlockGen) {}) 77 bc.InsertChain(blocks) 78 79 return bc 80 } 81 82 func TestBlockchainCodeAt(t *testing.T) { 83 bc := newTestBlockchain() 84 c := NewBlockchainContractBackend(bc, nil, nil) 85 86 // Normal cases 87 code, err := c.CodeAt(context.Background(), code1Addr, nil) 88 assert.Nil(t, err) 89 assert.Equal(t, code1Bytes, code) 90 91 code, err = c.CodeAt(context.Background(), code2Addr, nil) 92 assert.Nil(t, err) 93 assert.Equal(t, code2Bytes, code) 94 95 code, err = c.CodeAt(context.Background(), code1Addr, common.Big0) 96 assert.Nil(t, err) 97 assert.Equal(t, code1Bytes, code) 98 99 code, err = c.CodeAt(context.Background(), code1Addr, common.Big1) 100 assert.Nil(t, err) 101 assert.Equal(t, code1Bytes, code) 102 103 code, err = c.CodeAt(context.Background(), code1Addr, big.NewInt(10)) 104 assert.Nil(t, err) 105 assert.Equal(t, code1Bytes, code) 106 107 // Non-code address 108 code, err = c.CodeAt(context.Background(), testAddr, nil) 109 assert.True(t, code == nil && err == nil) 110 111 // Invalid block number 112 code, err = c.CodeAt(context.Background(), code1Addr, big.NewInt(11)) 113 assert.True(t, code == nil && err == errBlockDoesNotExist) 114 } 115 116 func TestBlockchainCallContract(t *testing.T) { 117 bc := newTestBlockchain() 118 c := NewBlockchainContractBackend(bc, nil, nil) 119 120 data_receive, _ := parsedAbi1.Pack("receive", []byte("X")) 121 data_revertString, _ := parsedAbi2.Pack("revertString") 122 data_revertNoString, _ := parsedAbi2.Pack("revertNoString") 123 124 // Normal case 125 ret, err := c.CallContract(context.Background(), klaytn.CallMsg{ 126 From: testAddr, 127 To: &code1Addr, 128 Gas: 1000000, 129 Data: data_receive, 130 }, nil) 131 assert.Nil(t, err) 132 assert.Equal(t, expectedReturn, ret) 133 134 // Error outside VM - Intrinsic Gas 135 ret, err = c.CallContract(context.Background(), klaytn.CallMsg{ 136 From: testAddr, 137 To: &code1Addr, 138 Gas: 20000, 139 Data: data_receive, 140 }, nil) 141 assert.True(t, errors.Is(err, blockchain.ErrIntrinsicGas)) 142 143 // VM revert error - empty reason 144 ret, err = c.CallContract(context.Background(), klaytn.CallMsg{ 145 From: testAddr, 146 To: &code2Addr, 147 Gas: 100000, 148 Data: data_revertNoString, 149 }, nil) 150 assert.Equal(t, "execution reverted: ", err.Error()) 151 152 // VM revert error - string reason 153 ret, err = c.CallContract(context.Background(), klaytn.CallMsg{ 154 From: testAddr, 155 To: &code2Addr, 156 Gas: 100000, 157 Data: data_revertString, 158 }, nil) 159 assert.Equal(t, "execution reverted: some error", err.Error()) 160 } 161 162 func TestBlockchainPendingCodeAt(t *testing.T) { 163 bc := newTestBlockchain() 164 c := NewBlockchainContractBackend(bc, nil, nil) 165 166 // Normal cases 167 code, err := c.PendingCodeAt(context.Background(), code1Addr) 168 assert.Nil(t, err) 169 assert.Equal(t, code1Bytes, code) 170 171 code, err = c.PendingCodeAt(context.Background(), code2Addr) 172 assert.Nil(t, err) 173 assert.Equal(t, code2Bytes, code) 174 175 // Non-code address 176 code, err = c.PendingCodeAt(context.Background(), testAddr) 177 assert.True(t, code == nil && err == nil) 178 } 179 180 func TestBlockChainSuggestGasPrice(t *testing.T) { 181 bc := newTestBlockchain() 182 c := NewBlockchainContractBackend(bc, nil, nil) 183 184 // Normal case 185 gasPrice, err := c.SuggestGasPrice(context.Background()) 186 assert.Nil(t, err) 187 assert.Equal(t, params.TestChainConfig.UnitPrice, gasPrice.Uint64()) 188 189 config := params.TestChainConfig.Copy() 190 config.IstanbulCompatibleBlock = common.Big0 191 config.LondonCompatibleBlock = common.Big0 192 config.EthTxTypeCompatibleBlock = common.Big0 193 config.MagmaCompatibleBlock = common.Big0 194 config.KoreCompatibleBlock = common.Big0 195 config.Governance = params.GetDefaultGovernanceConfig() 196 config.Governance.KIP71.LowerBoundBaseFee = 0 197 bc = newTestBlockchainWithConfig(config) 198 c = NewBlockchainContractBackend(bc, nil, nil) 199 200 // Normal case 201 gasPrice, err = c.SuggestGasPrice(context.Background()) 202 assert.Nil(t, err) 203 assert.Equal(t, bc.CurrentBlock().Header().BaseFee.Uint64()*2, gasPrice.Uint64()) 204 } 205 206 func TestBlockChainEstimateGas(t *testing.T) { 207 bc := newTestBlockchain() 208 c := NewBlockchainContractBackend(bc, nil, nil) 209 210 // Normal case 211 gas, err := c.EstimateGas(context.Background(), klaytn.CallMsg{ 212 From: testAddr, 213 To: &testAddr, 214 Value: big.NewInt(1000), 215 }) 216 assert.Nil(t, err) 217 assert.Equal(t, uint64(params.TxGas), gas) 218 219 // Error case - simple transfer with insufficient funds with zero gasPrice 220 gas, err = c.EstimateGas(context.Background(), klaytn.CallMsg{ 221 From: code1Addr, 222 To: &code1Addr, 223 Value: big.NewInt(1), 224 }) 225 assert.Contains(t, err.Error(), "insufficient balance for transfer") 226 assert.Zero(t, gas) 227 } 228 229 func TestBlockChainSendTransaction(t *testing.T) { 230 bc := newTestBlockchain() 231 block := bc.CurrentBlock() 232 state, err := bc.State() 233 txPoolConfig := blockchain.DefaultTxPoolConfig 234 txPoolConfig.Journal = "/dev/null" // disable journaling to file 235 txPool := blockchain.NewTxPool(txPoolConfig, bc.Config(), bc) 236 defer txPool.Stop() 237 assert.Nil(t, err) 238 c := NewBlockchainContractBackend(bc, txPool, nil) 239 240 // create a signed transaction to send 241 nonce := state.GetNonce(testAddr) 242 tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil) 243 chainId, err := c.ChainID(context.Background()) 244 if err != nil { 245 t.Errorf("could not get chain ID: %v", err) 246 } 247 signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainId), testKey) 248 if err != nil { 249 t.Errorf("could not sign tx: %v", err) 250 } 251 252 // send tx to simulated backend 253 err = c.SendTransaction(context.Background(), signedTx) 254 if err != nil { 255 t.Errorf("could not add tx to pending block: %v", err) 256 } 257 258 blocks, _ := blockchain.GenerateChain(bc.Config(), block, gxhash.NewFaker(), state.Database().TrieDB().DiskDB(), 1, func(i int, b *blockchain.BlockGen) { 259 txs, err := txPool.Pending() 260 if err != nil { 261 t.Errorf("could not get pending txs: %v", err) 262 } 263 for _, v := range txs { 264 for _, v2 := range v { 265 b.AddTx(v2) 266 } 267 } 268 }) 269 bc.InsertChain(blocks) 270 271 block = bc.GetBlockByNumber(11) 272 if block == nil { 273 t.Errorf("could not get block at height 11") 274 } 275 276 assert.True(t, len(block.Transactions()) != 0) 277 if signedTx.Hash() != block.Transactions()[0].Hash() { 278 t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash()) 279 } 280 assert.False(t, block.Header().EmptyReceipts()) 281 } 282 283 func TestBlockChainChainID(t *testing.T) { 284 bc := newTestBlockchain() 285 c := NewBlockchainContractBackend(bc, nil, nil) 286 287 // Normal case 288 chainId, err := c.ChainID(context.Background()) 289 assert.Nil(t, err) 290 assert.Equal(t, params.TestChainConfig.ChainID, chainId) 291 } 292 293 func initBackendForFiltererTests(t *testing.T, bc *blockchain.BlockChain) *BlockchainContractBackend { 294 block := bc.CurrentBlock() 295 state, _ := bc.State() 296 297 // Add one block with contract execution to generate logs 298 data_receive, _ := parsedAbi1.Pack("receive", []byte("X")) 299 tx := types.NewTransaction(uint64(0), code1Addr, big.NewInt(0), 50000, big.NewInt(1), data_receive) 300 chainId := bc.Config().ChainID 301 302 signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainId), testKey) 303 if err != nil { 304 t.Errorf("could not sign tx: %v", err) 305 } 306 307 blocks, _ := blockchain.GenerateChain(bc.Config(), block, gxhash.NewFaker(), state.Database().TrieDB().DiskDB(), 1, func(i int, b *blockchain.BlockGen) { 308 b.AddTx(signedTx) 309 }) 310 bc.InsertChain(blocks) 311 312 // mock filterer backend 313 mockCtrl := gomock.NewController(t) 314 mockBackend := mock_filter.NewMockBackend(mockCtrl) 315 316 any := gomock.Any() 317 txPoolConfig := blockchain.DefaultTxPoolConfig 318 txPoolConfig.Journal = "/dev/null" // disable journaling to file 319 txPool := blockchain.NewTxPool(txPoolConfig, bc.Config(), bc) 320 subscribeNewTxsEvent := func(ch chan<- blockchain.NewTxsEvent) klaytn.Subscription { 321 return txPool.SubscribeNewTxsEvent(ch) 322 } 323 subscribeLogsEvent := func(ch chan<- []*types.Log) klaytn.Subscription { 324 return bc.SubscribeLogsEvent(ch) 325 } 326 subscribeRemovedLogsEvent := func(ch chan<- blockchain.RemovedLogsEvent) klaytn.Subscription { 327 return bc.SubscribeRemovedLogsEvent(ch) 328 } 329 subscribeChainEvent := func(ch chan<- blockchain.ChainEvent) klaytn.Subscription { 330 return bc.SubscribeChainEvent(ch) 331 } 332 mockBackend.EXPECT().SubscribeNewTxsEvent(any).DoAndReturn(subscribeNewTxsEvent).AnyTimes() 333 mockBackend.EXPECT().SubscribeLogsEvent(any).DoAndReturn(subscribeLogsEvent).AnyTimes() 334 mockBackend.EXPECT().SubscribeRemovedLogsEvent(any).DoAndReturn(subscribeRemovedLogsEvent).AnyTimes() 335 mockBackend.EXPECT().SubscribeChainEvent(any).DoAndReturn(subscribeChainEvent).AnyTimes() 336 337 f := filters.NewEventSystem(&event.TypeMux{}, mockBackend, false) 338 c := NewBlockchainContractBackend(bc, nil, f) 339 340 return c 341 } 342 343 func TestBlockChainFilterLogs(t *testing.T) { 344 bc := newTestBlockchain() 345 c := initBackendForFiltererTests(t, bc) 346 347 // Normal case 348 logs, err := c.FilterLogs(context.Background(), klaytn.FilterQuery{ 349 FromBlock: big.NewInt(10), 350 ToBlock: big.NewInt(11), 351 Addresses: []common.Address{code1Addr}, 352 }) 353 assert.Nil(t, err) 354 assert.Equal(t, 2, len(logs)) 355 356 // No logs exist for code2Addr 357 logs, err = c.FilterLogs(context.Background(), klaytn.FilterQuery{ 358 FromBlock: big.NewInt(0), 359 ToBlock: big.NewInt(11), 360 Addresses: []common.Address{code2Addr}, 361 }) 362 assert.Nil(t, err) 363 assert.Equal(t, 0, len(logs)) 364 } 365 366 func TestBlockChainSubscribeFilterLogs(t *testing.T) { 367 bc := newTestBlockchain() 368 c := initBackendForFiltererTests(t, bc) 369 370 logs := make(chan types.Log) 371 sub, err := c.SubscribeFilterLogs(context.Background(), klaytn.FilterQuery{ 372 FromBlock: big.NewInt(0), 373 ToBlock: big.NewInt(20), 374 Addresses: []common.Address{code1Addr}, 375 }, logs) 376 assert.Nil(t, err) 377 assert.NotNil(t, sub) 378 379 // Insert a block with contract execution to generate logs 380 go func() { 381 state, _ := bc.State() 382 nonce := state.GetNonce(testAddr) 383 data_receive, _ := parsedAbi1.Pack("receive", []byte("X")) 384 tx := types.NewTransaction(nonce, code1Addr, big.NewInt(0), 50000, big.NewInt(1), data_receive) 385 chainId, _ := c.ChainID(context.Background()) 386 387 signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(chainId), testKey) 388 if err != nil { 389 t.Errorf("could not sign tx: %v", err) 390 } 391 392 block := bc.CurrentBlock() 393 394 blocks, _ := blockchain.GenerateChain(c.bc.Config(), block, gxhash.NewFaker(), state.Database().TrieDB().DiskDB(), 1, func(i int, b *blockchain.BlockGen) { 395 b.AddTx(signedTx) 396 }) 397 bc.InsertChain(blocks) 398 }() 399 400 // Wait for 2 logs 401 for i := 0; i < 2; i++ { 402 select { 403 case log := <-logs: 404 assert.Equal(t, code1Addr, log.Address) 405 assert.Contains(t, hex.EncodeToString(log.Data), hex.EncodeToString(testAddr.Bytes())) 406 case <-time.After(3 * time.Second): 407 t.Fatal("timeout while waiting for logs") 408 } 409 } 410 }