github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/evm/keeper/statedb_test.go (about) 1 package keeper_test 2 3 import ( 4 "fmt" 5 "math/big" 6 7 ethcmn "github.com/ethereum/go-ethereum/common" 8 ethtypes "github.com/ethereum/go-ethereum/core/types" 9 ethcrypto "github.com/ethereum/go-ethereum/crypto" 10 11 "github.com/fibonacci-chain/fbc/app/crypto/ethsecp256k1" 12 ethermint "github.com/fibonacci-chain/fbc/app/types" 13 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 14 "github.com/fibonacci-chain/fbc/x/evm/types" 15 ) 16 17 func (suite *KeeperTestSuite) TestBloomFilter() { 18 // Prepare db for logs 19 tHash := ethcmn.BytesToHash([]byte{0x1}) 20 bHash := ethcmn.BytesToHash([]byte{0x1}) 21 suite.stateDB.WithContext(suite.ctx).Prepare(tHash, bHash, 0) 22 contractAddress := ethcmn.BigToAddress(big.NewInt(1)) 23 log := ethtypes.Log{Address: contractAddress} 24 logs := []*ethtypes.Log{&log} 25 26 testCase := []struct { 27 name string 28 malleate func() 29 numLogs int 30 isBloom bool 31 }{ 32 { 33 "no logs", 34 func() {}, 35 0, 36 false, 37 }, 38 { 39 "add log", 40 func() { 41 suite.stateDB.WithContext(suite.ctx).SetLogs(tHash, logs) 42 }, 43 1, 44 false, 45 }, 46 { 47 "bloom", 48 func() {}, 49 0, 50 true, 51 }, 52 } 53 54 for _, tc := range testCase { 55 tc.malleate() 56 logs, err := suite.stateDB.WithContext(suite.ctx).GetLogs(tHash) 57 suite.Require().NoError(err) 58 if !tc.isBloom { 59 suite.Require().Len(logs, tc.numLogs, tc.name) 60 if len(logs) != 0 { 61 suite.Require().Equal(log, *logs[0], tc.name) 62 } 63 } else { 64 // get logs bloom from the log 65 bloomInt := ethtypes.LogsBloom(logs) 66 bloomFilter := ethtypes.BytesToBloom(bloomInt) 67 suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name) 68 suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name) 69 } 70 } 71 } 72 73 func (suite *KeeperTestSuite) TestStateDB_Balance() { 74 testCase := []struct { 75 name string 76 malleate func() 77 balance *big.Int 78 }{ 79 { 80 "set balance", 81 func() { 82 suite.stateDB.WithContext(suite.ctx).SetBalance(suite.address, big.NewInt(100)) 83 }, 84 big.NewInt(100), 85 }, 86 { 87 "sub balance", 88 func() { 89 suite.stateDB.WithContext(suite.ctx).SubBalance(suite.address, big.NewInt(100)) 90 }, 91 big.NewInt(0), 92 }, 93 { 94 "add balance", 95 func() { 96 suite.stateDB.WithContext(suite.ctx).AddBalance(suite.address, big.NewInt(200)) 97 }, 98 big.NewInt(200), 99 }, 100 } 101 102 for _, tc := range testCase { 103 tc.malleate() 104 suite.Require().Equal(tc.balance, suite.stateDB.WithContext(suite.ctx).GetBalance(suite.address), tc.name) 105 } 106 } 107 108 func (suite *KeeperTestSuite) TestStateDBNonce() { 109 nonce := uint64(123) 110 suite.stateDB.WithContext(suite.ctx).SetNonce(suite.address, nonce) 111 suite.Require().Equal(nonce, suite.stateDB.WithContext(suite.ctx).GetNonce(suite.address)) 112 } 113 114 func (suite *KeeperTestSuite) TestStateDB_Error() { 115 nonce := suite.stateDB.WithContext(suite.ctx).GetNonce(ethcmn.Address{}) 116 suite.Require().Equal(0, int(nonce)) 117 suite.Require().Error(suite.stateDB.WithContext(suite.ctx).Error()) 118 } 119 120 func (suite *KeeperTestSuite) TestStateDB_Database() { 121 suite.Require().NotNil(suite.stateDB.WithContext(suite.ctx).Database()) 122 } 123 124 func (suite *KeeperTestSuite) TestStateDB_State() { 125 key := ethcmn.BytesToHash([]byte("foo")) 126 val := ethcmn.BytesToHash([]byte("bar")) 127 suite.stateDB.WithContext(suite.ctx).SetState(suite.address, key, val) 128 129 testCase := []struct { 130 name string 131 address ethcmn.Address 132 key ethcmn.Hash 133 value ethcmn.Hash 134 }{ 135 { 136 "found state", 137 suite.address, 138 ethcmn.BytesToHash([]byte("foo")), 139 ethcmn.BytesToHash([]byte("bar")), 140 }, 141 { 142 "state not found", 143 suite.address, 144 ethcmn.BytesToHash([]byte("key")), 145 ethcmn.Hash{}, 146 }, 147 { 148 "object not found", 149 ethcmn.Address{}, 150 ethcmn.BytesToHash([]byte("foo")), 151 ethcmn.Hash{}, 152 }, 153 } 154 for _, tc := range testCase { 155 value := suite.stateDB.WithContext(suite.ctx).GetState(tc.address, tc.key) 156 suite.Require().Equal(tc.value, value, tc.name) 157 } 158 } 159 160 func (suite *KeeperTestSuite) TestStateDB_Code() { 161 testCase := []struct { 162 name string 163 address ethcmn.Address 164 code []byte 165 codeHash ethcmn.Hash 166 malleate func() 167 }{ 168 { 169 "no stored code for state object", 170 suite.address, 171 nil, 172 ethcmn.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"), 173 func() {}, 174 }, 175 { 176 "existing address", 177 suite.address, 178 []byte("code"), 179 ethcmn.HexToHash("0x2dc081a8d6d4714c79b5abd2e9b08c3a33b4ef1dcf946ef8b8cf6c495014f47b"), 180 func() { 181 suite.stateDB.WithContext(suite.ctx).SetCode(suite.address, []byte("code")) 182 }, 183 }, 184 { 185 "state object not found", 186 ethcmn.Address{}, 187 nil, 188 ethcmn.HexToHash("0"), 189 func() {}, 190 }, 191 } 192 193 for _, tc := range testCase { 194 tc.malleate() 195 196 suite.Require().Equal(tc.code, suite.stateDB.WithContext(suite.ctx).GetCode(tc.address), tc.name) 197 suite.Require().Equal(len(tc.code), suite.stateDB.WithContext(suite.ctx).GetCodeSize(tc.address), tc.name) 198 suite.Require().Equal(tc.codeHash, suite.stateDB.WithContext(suite.ctx).GetCodeHash(tc.address), tc.name) 199 } 200 } 201 202 func (suite *KeeperTestSuite) TestStateDB_Logs() { 203 txHash := ethcmn.BytesToHash([]byte("topic")) 204 testCase := []struct { 205 name string 206 log ethtypes.Log 207 }{ 208 { 209 "state db log", 210 ethtypes.Log{ 211 Address: suite.address, 212 Topics: []ethcmn.Hash{txHash}, 213 Data: []byte("data"), 214 BlockNumber: 1, 215 TxHash: txHash, 216 TxIndex: 1, 217 BlockHash: ethcmn.Hash{}, 218 Index: 1, 219 Removed: false, 220 }, 221 }, 222 } 223 224 for _, tc := range testCase { 225 logs := []*ethtypes.Log{&tc.log} 226 227 suite.stateDB.WithContext(suite.ctx).SetLogs(txHash, logs) 228 dbLogs, err := suite.stateDB.WithContext(suite.ctx).GetLogs(txHash) 229 suite.Require().NoError(err) 230 suite.Require().Equal(logs, dbLogs, tc.name) 231 } 232 } 233 234 func (suite *KeeperTestSuite) TestStateDB_Preimage() { 235 hash := ethcmn.BytesToHash([]byte("hash")) 236 preimage := []byte("preimage") 237 238 suite.stateDB.WithContext(suite.ctx).AddPreimage(hash, preimage) 239 suite.Require().Equal(preimage, suite.stateDB.WithContext(suite.ctx).Preimages()[hash]) 240 } 241 242 func (suite *KeeperTestSuite) TestStateDB_Refund() { 243 testCase := []struct { 244 name string 245 addAmount uint64 246 subAmount uint64 247 expRefund uint64 248 expPanic bool 249 }{ 250 { 251 "refund 0", 252 0, 0, 0, 253 false, 254 }, 255 { 256 "refund positive amount", 257 100, 0, 100, 258 false, 259 }, 260 { 261 "refund panic", 262 100, 200, 100, 263 true, 264 }, 265 } 266 267 for _, tc := range testCase { 268 suite.Run(tc.name, func() { 269 suite.SetupTest() // reset 270 271 suite.stateDB.WithContext(suite.ctx).AddRefund(tc.addAmount) 272 suite.Require().Equal(tc.addAmount, suite.stateDB.WithContext(suite.ctx).GetRefund()) 273 274 if tc.expPanic { 275 suite.Panics(func() { 276 suite.stateDB.WithContext(suite.ctx).SubRefund(tc.subAmount) 277 }) 278 } else { 279 suite.stateDB.WithContext(suite.ctx).SubRefund(tc.subAmount) 280 suite.Require().Equal(tc.expRefund, suite.stateDB.WithContext(suite.ctx).GetRefund()) 281 } 282 }) 283 } 284 } 285 286 func (suite *KeeperTestSuite) TestStateDB_CreateAccount() { 287 prevBalance := big.NewInt(12) 288 289 testCase := []struct { 290 name string 291 address ethcmn.Address 292 malleate func() 293 }{ 294 { 295 "existing account", 296 suite.address, 297 func() { 298 suite.stateDB.WithContext(suite.ctx).AddBalance(suite.address, prevBalance) 299 }, 300 }, 301 { 302 "new account", 303 ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1"), 304 func() { 305 prevBalance = big.NewInt(0) 306 }, 307 }, 308 } 309 310 for _, tc := range testCase { 311 suite.Run(tc.name, func() { 312 suite.SetupTest() // reset 313 tc.malleate() 314 315 suite.stateDB.WithContext(suite.ctx).CreateAccount(tc.address) 316 suite.Require().True(suite.stateDB.WithContext(suite.ctx).Exist(tc.address)) 317 suite.Require().Equal(prevBalance, suite.stateDB.WithContext(suite.ctx).GetBalance(tc.address)) 318 }) 319 } 320 } 321 322 func (suite *KeeperTestSuite) TestStateDB_ClearStateObj() { 323 priv, err := ethsecp256k1.GenerateKey() 324 suite.Require().NoError(err) 325 326 addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) 327 328 suite.stateDB.WithContext(suite.ctx).CreateAccount(addr) 329 suite.Require().True(suite.stateDB.WithContext(suite.ctx).Exist(addr)) 330 331 suite.stateDB.WithContext(suite.ctx).ClearStateObjects() 332 suite.Require().False(suite.stateDB.WithContext(suite.ctx).Exist(addr)) 333 } 334 335 func (suite *KeeperTestSuite) TestStateDB_Reset() { 336 priv, err := ethsecp256k1.GenerateKey() 337 suite.Require().NoError(err) 338 339 addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) 340 341 suite.stateDB.WithContext(suite.ctx).CreateAccount(addr) 342 suite.Require().True(suite.stateDB.WithContext(suite.ctx).Exist(addr)) 343 344 err = suite.stateDB.WithContext(suite.ctx).Reset(ethcmn.BytesToHash(nil)) 345 suite.Require().NoError(err) 346 suite.Require().False(suite.stateDB.WithContext(suite.ctx).Exist(addr)) 347 } 348 349 func (suite *KeeperTestSuite) TestSuiteDB_Prepare() { 350 thash := ethcmn.BytesToHash([]byte("thash")) 351 bhash := ethcmn.BytesToHash([]byte("bhash")) 352 txi := 1 353 354 suite.stateDB.WithContext(suite.ctx).Prepare(thash, bhash, txi) 355 356 suite.Require().Equal(txi, suite.stateDB.WithContext(suite.ctx).TxIndex()) 357 suite.Require().Equal(bhash, suite.stateDB.WithContext(suite.ctx).BlockHash()) 358 } 359 360 func (suite *KeeperTestSuite) TestSuiteDB_Empty() { 361 suite.Require().True(suite.stateDB.WithContext(suite.ctx).Empty(suite.address)) 362 363 suite.stateDB.WithContext(suite.ctx).SetBalance(suite.address, big.NewInt(100)) 364 suite.Require().False(suite.stateDB.WithContext(suite.ctx).Empty(suite.address)) 365 } 366 367 func (suite *KeeperTestSuite) TestSuiteDB_Suicide() { 368 testCase := []struct { 369 name string 370 amount *big.Int 371 expPass bool 372 delete bool 373 }{ 374 { 375 "suicide zero balance", 376 big.NewInt(0), 377 false, false, 378 }, 379 { 380 "suicide with balance", 381 big.NewInt(100), 382 true, false, 383 }, 384 { 385 "delete", 386 big.NewInt(0), 387 true, true, 388 }, 389 } 390 391 for _, tc := range testCase { 392 if tc.delete { 393 _, err := suite.stateDB.WithContext(suite.ctx).Commit(tc.delete) 394 suite.Require().NoError(err, tc.name) 395 suite.Require().False(suite.stateDB.WithContext(suite.ctx).Exist(suite.address), tc.name) 396 continue 397 } 398 399 if tc.expPass { 400 suite.stateDB.WithContext(suite.ctx).SetBalance(suite.address, tc.amount) 401 suicide := suite.stateDB.WithContext(suite.ctx).Suicide(suite.address) 402 suite.Require().True(suicide, tc.name) 403 suite.Require().True(suite.stateDB.WithContext(suite.ctx).HasSuicided(suite.address), tc.name) 404 } else { 405 //Suicide only works for an account with non-zero balance/nonce 406 priv, err := ethsecp256k1.GenerateKey() 407 suite.Require().NoError(err) 408 409 addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) 410 suicide := suite.stateDB.WithContext(suite.ctx).Suicide(addr) 411 suite.Require().False(suicide, tc.name) 412 suite.Require().False(suite.stateDB.WithContext(suite.ctx).HasSuicided(addr), tc.name) 413 } 414 } 415 } 416 417 func (suite *KeeperTestSuite) TestCommitStateDB_Commit() { 418 testCase := []struct { 419 name string 420 malleate func() 421 deleteObjs bool 422 expPass bool 423 }{ 424 { 425 "commit suicided", 426 func() { 427 ok := suite.stateDB.WithContext(suite.ctx).Suicide(suite.address) 428 suite.Require().True(ok) 429 }, 430 true, true, 431 }, 432 { 433 "commit with dirty value", 434 func() { 435 suite.stateDB.WithContext(suite.ctx).SetCode(suite.address, []byte("code")) 436 }, 437 false, true, 438 }, 439 } 440 441 for _, tc := range testCase { 442 tc.malleate() 443 444 hash, err := suite.stateDB.WithContext(suite.ctx).Commit(tc.deleteObjs) 445 suite.Require().Equal(ethcmn.Hash{}, hash) 446 447 if !tc.expPass { 448 suite.Require().Error(err, tc.name) 449 continue 450 } 451 452 suite.Require().NoError(err, tc.name) 453 acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) 454 455 if tc.deleteObjs { 456 suite.Require().Nil(acc, tc.name) 457 continue 458 } 459 460 suite.Require().NotNil(acc, tc.name) 461 ethAcc, ok := acc.(*ethermint.EthAccount) 462 suite.Require().True(ok) 463 suite.Require().Equal(ethcrypto.Keccak256([]byte("code")), ethAcc.CodeHash) 464 } 465 } 466 467 func (suite *KeeperTestSuite) TestCommitStateDB_Finalize() { 468 testCase := []struct { 469 name string 470 malleate func() 471 deleteObjs bool 472 expPass bool 473 }{ 474 { 475 "finalize suicided", 476 func() { 477 ok := suite.stateDB.WithContext(suite.ctx).Suicide(suite.address) 478 suite.Require().True(ok) 479 }, 480 true, true, 481 }, 482 { 483 "finalize, not suicided", 484 func() { 485 suite.stateDB.WithContext(suite.ctx).AddBalance(suite.address, big.NewInt(5)) 486 }, 487 false, true, 488 }, 489 { 490 "finalize, dirty storage", 491 func() { 492 suite.stateDB.WithContext(suite.ctx).SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) 493 }, 494 false, true, 495 }, 496 } 497 498 for _, tc := range testCase { 499 tc.malleate() 500 501 suite.stateDB.WithContext(suite.ctx).IntermediateRoot(tc.deleteObjs) 502 503 if !tc.expPass { 504 hash := suite.stateDB.WithContext(suite.ctx).GetCommittedState(suite.address, ethcmn.BytesToHash([]byte("key"))) 505 suite.Require().NotEqual(ethcmn.Hash{}, hash, tc.name) 506 continue 507 } 508 509 acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) 510 511 if tc.deleteObjs { 512 suite.Require().Nil(acc, tc.name) 513 continue 514 } 515 516 suite.Require().NotNil(acc, tc.name) 517 } 518 } 519 520 func (suite *KeeperTestSuite) TestCommitStateDB_GetCommittedState() { 521 hash := suite.stateDB.WithContext(suite.ctx).GetCommittedState(ethcmn.Address{}, ethcmn.BytesToHash([]byte("key"))) 522 suite.Require().Equal(ethcmn.Hash{}, hash) 523 } 524 525 func (suite *KeeperTestSuite) TestCommitStateDB_Snapshot() { 526 id := suite.stateDB.WithContext(suite.ctx).Snapshot() 527 suite.Require().NotPanics(func() { 528 suite.stateDB.WithContext(suite.ctx).RevertToSnapshot(id) 529 }) 530 531 suite.Require().Panics(func() { 532 suite.stateDB.WithContext(suite.ctx).RevertToSnapshot(-1) 533 }, "invalid revision should panic") 534 } 535 536 func (suite *KeeperTestSuite) TestCommitStateDB_ForEachStorage() { 537 var storage types.Storage 538 539 testCase := []struct { 540 name string 541 malleate func() 542 callback func(key, value ethcmn.Hash) (stop bool) 543 expValues []ethcmn.Hash 544 }{ 545 { 546 "aggregate state", 547 func() { 548 for i := 0; i < 5; i++ { 549 suite.stateDB.WithContext(suite.ctx).SetState(suite.address, ethcmn.BytesToHash([]byte(fmt.Sprintf("key%d", i))), ethcmn.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) 550 } 551 }, 552 func(key, value ethcmn.Hash) bool { 553 storage = append(storage, types.NewState(key, value)) 554 return false 555 }, 556 []ethcmn.Hash{ 557 ethcmn.BytesToHash([]byte("value0")), 558 ethcmn.BytesToHash([]byte("value1")), 559 ethcmn.BytesToHash([]byte("value2")), 560 ethcmn.BytesToHash([]byte("value3")), 561 ethcmn.BytesToHash([]byte("value4")), 562 }, 563 }, 564 { 565 "filter state", 566 func() { 567 suite.stateDB.WithContext(suite.ctx).SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) 568 suite.stateDB.WithContext(suite.ctx).SetState(suite.address, ethcmn.BytesToHash([]byte("filterkey")), ethcmn.BytesToHash([]byte("filtervalue"))) 569 }, 570 func(key, value ethcmn.Hash) bool { 571 if value == ethcmn.BytesToHash([]byte("filtervalue")) { 572 storage = append(storage, types.NewState(key, value)) 573 return true 574 } 575 return false 576 }, 577 []ethcmn.Hash{ 578 ethcmn.BytesToHash([]byte("filtervalue")), 579 }, 580 }, 581 } 582 583 for _, tc := range testCase { 584 suite.Run(tc.name, func() { 585 suite.SetupTest() // reset 586 tc.malleate() 587 suite.stateDB.WithContext(suite.ctx).Commit(false) 588 589 err := suite.stateDB.WithContext(suite.ctx).ForEachStorage(suite.address, tc.callback) 590 suite.Require().NoError(err) 591 suite.Require().Equal(len(tc.expValues), len(storage), fmt.Sprintf("Expected values:\n%v\nStorage Values\n%v", tc.expValues, storage)) 592 593 vals := make([]ethcmn.Hash, len(storage)) 594 for i := range storage { 595 vals[i] = storage[i].Value 596 } 597 598 suite.Require().ElementsMatch(tc.expValues, vals) 599 }) 600 storage = types.Storage{} 601 } 602 } 603 604 func (suite *KeeperTestSuite) TestStorageTrie() { 605 for i := 0; i < 5; i++ { 606 suite.stateDB.WithContext(suite.ctx).SetState(suite.address, ethcmn.BytesToHash([]byte(fmt.Sprintf("key%d", i))), ethcmn.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) 607 } 608 609 trie := suite.stateDB.WithContext(suite.ctx).StorageTrie(suite.address) 610 suite.Require().NotNil(trie, "Ethermint now use a direct storage trie.") 611 }