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 := &ethermint.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  }