github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/evm/types/journal_test.go (about) 1 package types 2 3 import ( 4 "os" 5 "testing" 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 "github.com/fibonacci-chain/fbc/app/crypto/ethsecp256k1" 11 ethermint "github.com/fibonacci-chain/fbc/app/types" 12 sdkcodec "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 13 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store" 14 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/mpt" 15 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank" 18 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/gov/types" 19 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/supply" 20 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 21 tmlog "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 22 tmdb "github.com/fibonacci-chain/fbc/libs/tm-db" 23 "github.com/fibonacci-chain/fbc/x/params" 24 "github.com/stretchr/testify/suite" 25 ) 26 27 type JournalTestSuite struct { 28 suite.Suite 29 30 address ethcmn.Address 31 journal *journal 32 ctx sdk.Context 33 stateDB *CommitStateDB 34 } 35 36 func newTestCodec() *sdkcodec.Codec { 37 cdc := sdkcodec.New() 38 39 RegisterCodec(cdc) 40 sdk.RegisterCodec(cdc) 41 ethsecp256k1.RegisterCodec(cdc) 42 sdkcodec.RegisterCrypto(cdc) 43 auth.RegisterCodec(cdc) 44 ethermint.RegisterCodec(cdc) 45 46 return cdc 47 } 48 49 func (suite *JournalTestSuite) SetupTest() { 50 suite.setup() 51 52 privkey, err := ethsecp256k1.GenerateKey() 53 suite.Require().NoError(err) 54 55 suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes()) 56 suite.journal = newJournal() 57 58 balance := sdk.NewCoins(ethermint.NewPhotonCoin(sdk.NewInt(100))) 59 acc := ðermint.EthAccount{ 60 BaseAccount: auth.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), balance, nil, 0, 0), 61 CodeHash: ethcrypto.Keccak256(nil), 62 } 63 64 suite.stateDB.accountKeeper.SetAccount(suite.ctx, acc) 65 // suite.stateDB.bankKeeper.SetBalance(suite.ctx, sdk.AccAddress(suite.address.Bytes()), balance) 66 suite.stateDB.SetLogs(ethcmn.BytesToHash([]byte("topic")), []*ethtypes.Log{ 67 { 68 Address: suite.address, 69 Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic_0"))}, 70 Data: []byte("data_0"), 71 BlockNumber: 1, 72 TxHash: ethcmn.BytesToHash([]byte("tx_hash")), 73 TxIndex: 1, 74 BlockHash: ethcmn.BytesToHash([]byte("block_hash")), 75 Index: 1, 76 Removed: false, 77 }, 78 { 79 Address: suite.address, 80 Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic_1"))}, 81 Data: []byte("data_1"), 82 BlockNumber: 10, 83 TxHash: ethcmn.BytesToHash([]byte("tx_hash")), 84 TxIndex: 0, 85 BlockHash: ethcmn.BytesToHash([]byte("block_hash")), 86 Index: 0, 87 Removed: false, 88 }, 89 }) 90 } 91 92 // setup performs a manual setup of the GoLevelDB and mounts the required IAVL stores. We use the manual 93 // setup here instead of the Ethermint app test setup because the journal methods are private and using 94 // the latter would result in a cycle dependency. We also want to avoid declaring the journal methods public 95 // to maintain consistency with the Geth implementation. 96 func (suite *JournalTestSuite) setup() { 97 authKey := sdk.NewKVStoreKey(auth.StoreKey) 98 mptKey := sdk.NewKVStoreKey(mpt.StoreKey) 99 supplyKey := sdk.NewKVStoreKey(supply.StoreKey) 100 paramsKey := sdk.NewKVStoreKey(params.StoreKey) 101 paramsTKey := sdk.NewTransientStoreKey(params.TStoreKey) 102 // bankKey := sdk.NewKVStoreKey(bank.StoreKey) 103 storeKey := sdk.NewKVStoreKey(StoreKey) 104 105 db := tmdb.NewDB("state", tmdb.GoLevelDBBackend, "temp") 106 defer func() { 107 os.RemoveAll("temp") 108 }() 109 110 cms := store.NewCommitMultiStore(db) 111 cms.MountStoreWithDB(authKey, sdk.StoreTypeIAVL, db) 112 cms.MountStoreWithDB(mptKey, sdk.StoreTypeMPT, db) 113 cms.MountStoreWithDB(paramsKey, sdk.StoreTypeIAVL, db) 114 cms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) 115 cms.MountStoreWithDB(paramsTKey, sdk.StoreTypeTransient, db) 116 117 err := cms.LoadLatestVersion() 118 suite.Require().NoError(err) 119 120 cdc := newTestCodec() 121 122 paramsKeeper := params.NewKeeper(cdc, paramsKey, paramsTKey, tmlog.NewNopLogger()) 123 124 authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace) 125 bankSubspace := paramsKeeper.Subspace(bank.DefaultParamspace) 126 evmSubspace := paramsKeeper.Subspace(types.DefaultParamspace).WithKeyTable(ParamKeyTable()) 127 128 ak := auth.NewAccountKeeper(cdc, authKey, mptKey, authSubspace, ethermint.ProtoAccount) 129 bk := bank.NewBaseKeeper(ak, bankSubspace, make(map[string]bool)) 130 sk := supply.NewKeeper(cdc, supplyKey, ak, bank.NewBankKeeperAdapter(bk), make(map[string][]string)) 131 suite.ctx = sdk.NewContext(cms, abci.Header{ChainID: "ethermint-8"}, false, tmlog.NewNopLogger()) 132 csdbParams := CommitStateDBParams{ 133 StoreKey: storeKey, 134 ParamSpace: evmSubspace, 135 AccountKeeper: &ak, 136 SupplyKeeper: sk, 137 BankKeeper: bk, 138 Ada: nil, 139 Cdc: cdc, 140 DB: nil, 141 Trie: nil, 142 } 143 suite.stateDB = NewCommitStateDB(csdbParams).WithContext(suite.ctx) 144 suite.stateDB.SetParams(DefaultParams()) 145 } 146 147 func TestJournalTestSuite(t *testing.T) { 148 suite.Run(t, new(JournalTestSuite)) 149 } 150 151 func (suite *JournalTestSuite) TestJournal_append_revert() { 152 testCases := []struct { 153 name string 154 entry journalEntry 155 }{ 156 { 157 "createObjectChange", 158 createObjectChange{ 159 account: &suite.address, 160 }, 161 }, 162 { 163 "resetObjectChange", 164 resetObjectChange{ 165 prev: &stateObject{ 166 address: suite.address, 167 }, 168 }, 169 }, 170 { 171 "suicideChange", 172 suicideChange{ 173 account: &suite.address, 174 prev: false, 175 prevBalance: sdk.OneDec(), 176 }, 177 }, 178 { 179 "balanceChange", 180 balanceChange{ 181 account: &suite.address, 182 prev: sdk.OneDec(), 183 }, 184 }, 185 { 186 "nonceChange", 187 nonceChange{ 188 account: &suite.address, 189 prev: 1, 190 }, 191 }, 192 { 193 "storageChange", 194 storageChange{ 195 account: &suite.address, 196 key: ethcmn.BytesToHash([]byte("key")), 197 prevValue: ethcmn.BytesToHash([]byte("value")), 198 }, 199 }, 200 { 201 "codeChange", 202 codeChange{ 203 account: &suite.address, 204 prevCode: []byte("code"), 205 prevHash: []byte("hash"), 206 }, 207 }, 208 { 209 "touchChange", 210 touchChange{ 211 account: &suite.address, 212 }, 213 }, 214 { 215 "refundChange", 216 refundChange{ 217 prev: 1, 218 }, 219 }, 220 { 221 "addPreimageChange", 222 addPreimageChange{ 223 hash: ethcmn.BytesToHash([]byte("hash")), 224 }, 225 }, 226 { 227 "addLogChange", 228 addLogChange{ 229 txhash: ethcmn.BytesToHash([]byte("hash")), 230 }, 231 }, 232 { 233 "addLogChange - 2 logs", 234 addLogChange{ 235 txhash: ethcmn.BytesToHash([]byte("txhash")), 236 }, 237 }, 238 { 239 "accessListAddAccountChange", 240 accessListAddAccountChange{ 241 address: &suite.address, 242 }, 243 }, 244 } 245 var dirtyCount int 246 for i, tc := range testCases { 247 suite.journal.append(tc.entry) 248 suite.Require().Equal(suite.journal.length(), i+1, tc.name) 249 if tc.entry.dirtied() != nil { 250 dirtyCount++ 251 252 suite.Require().Equal(dirtyCount, suite.journal.dirties[suite.address], tc.name) 253 } 254 } 255 256 // revert to the initial journal state 257 suite.journal.revert(suite.stateDB, 0) 258 259 // verify the dirty entry has been deleted 260 idx, ok := suite.journal.dirties[suite.address] 261 suite.Require().False(ok) 262 suite.Require().Zero(idx) 263 } 264 265 func (suite *JournalTestSuite) TestJournal_preimage_revert() { 266 suite.stateDB.preimages = map[ethcmn.Hash][]byte{ 267 ethcmn.BytesToHash([]byte("hash")): []byte("preimage0"), 268 ethcmn.BytesToHash([]byte("hash1")): []byte("preimage1"), 269 ethcmn.BytesToHash([]byte("hash2")): []byte("preimage2"), 270 } 271 272 change := addPreimageChange{ 273 hash: ethcmn.BytesToHash([]byte("hash")), 274 } 275 276 // delete first entry 277 change.revert(suite.stateDB) 278 suite.Require().Len(suite.stateDB.preimages, 2) 279 280 for key, value := range suite.stateDB.preimages { 281 suite.Require().NotEqual(len("preimage"), string(value), key.String()) 282 } 283 } 284 285 func (suite *JournalTestSuite) TestJournal_createObjectChange_revert() { 286 addr := ethcmn.BytesToAddress([]byte("addr")) 287 288 suite.stateDB.stateObjects = map[ethcmn.Address]*stateObject{ 289 addr: &stateObject{ 290 address: addr, 291 }, 292 ethcmn.BytesToAddress([]byte("addr1")): &stateObject{ 293 address: ethcmn.BytesToAddress([]byte("addr1")), 294 }, 295 ethcmn.BytesToAddress([]byte("addr2")): &stateObject{ 296 address: ethcmn.BytesToAddress([]byte("addr2")), 297 }, 298 } 299 300 change := createObjectChange{ 301 account: &addr, 302 } 303 304 // delete first entry 305 change.revert(suite.stateDB) 306 suite.Require().Len(suite.stateDB.stateObjects, 2) 307 suite.Require().Equal(len(suite.stateDB.stateObjects), len(suite.stateDB.stateObjects)) 308 309 for k, entry := range suite.stateDB.stateObjects { 310 suite.Require().Equal(k.String(), entry.address.String()) 311 _, found := suite.stateDB.stateObjects[entry.address] 312 suite.Require().True(found) 313 } 314 } 315 316 func (suite *JournalTestSuite) TestJournal_dirty() { 317 // dirty entry hasn't been set 318 idx, ok := suite.journal.dirties[suite.address] 319 suite.Require().False(ok) 320 suite.Require().Zero(idx) 321 322 // update dirty count 323 suite.journal.dirty(suite.address) 324 suite.Require().Equal(1, suite.journal.dirties[suite.address]) 325 }