app, ante, evm: Keeper
StateDB
refactor (#30)
* evm: keeper statedb refactor * keeper: implement stateDB account, balance, nonce and suicide functions * keeper: implement stateDB code and iterator functions * keeper: implement stateDB log and preimage functions * update code to use CommitStateDB * tests updates * journal changes (wip) * cache fields * journal and logs * minor cleanup * evm: remove journal related changes * evm: delete empty account code and storage state * app, evm: transient store * ante, evm: refund gas transient * evm: remove transient keeper state fields * address comments from review * evm: undo revision change
This commit is contained in:
parent
bd89830d64
commit
6c1e7fec01
@ -67,7 +67,7 @@ func NewAnteHandler(
|
||||
// handle as *evmtypes.MsgEthereumTx
|
||||
|
||||
anteHandler = sdk.ChainAnteDecorators(
|
||||
NewEthSetupContextDecorator(), // outermost AnteDecorator. EthSetUpContext must be called first
|
||||
NewEthSetupContextDecorator(evmKeeper), // outermost AnteDecorator. EthSetUpContext must be called first
|
||||
NewEthMempoolFeeDecorator(evmKeeper),
|
||||
NewEthValidateBasicDecorator(),
|
||||
authante.TxTimeoutHeightDecorator{},
|
||||
|
@ -22,6 +22,8 @@ import (
|
||||
type EVMKeeper interface {
|
||||
GetParams(ctx sdk.Context) evmtypes.Params
|
||||
GetChainConfig(ctx sdk.Context) (evmtypes.ChainConfig, bool)
|
||||
WithContext(ctx sdk.Context)
|
||||
ResetRefundTransient(ctx sdk.Context)
|
||||
}
|
||||
|
||||
// EthSetupContextDecorator sets the infinite GasMeter in the Context and wraps
|
||||
@ -30,11 +32,15 @@ type EVMKeeper interface {
|
||||
// on gas provided and gas used.
|
||||
// CONTRACT: Must be first decorator in the chain
|
||||
// CONTRACT: Tx must implement GasTx interface
|
||||
type EthSetupContextDecorator struct{}
|
||||
type EthSetupContextDecorator struct {
|
||||
evmKeeper EVMKeeper
|
||||
}
|
||||
|
||||
// NewEthSetupContextDecorator creates a new EthSetupContextDecorator
|
||||
func NewEthSetupContextDecorator() EthSetupContextDecorator {
|
||||
return EthSetupContextDecorator{}
|
||||
func NewEthSetupContextDecorator(ek EVMKeeper) EthSetupContextDecorator {
|
||||
return EthSetupContextDecorator{
|
||||
evmKeeper: ek,
|
||||
}
|
||||
}
|
||||
|
||||
// AnteHandle sets the infinite gas meter to done to ignore costs in AnteHandler checks.
|
||||
@ -43,6 +49,9 @@ func NewEthSetupContextDecorator() EthSetupContextDecorator {
|
||||
func (escd EthSetupContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
|
||||
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
// reset the refund gas value for the current transaction
|
||||
escd.evmKeeper.ResetRefundTransient(ctx)
|
||||
|
||||
// all transactions must implement GasTx
|
||||
gasTx, ok := tx.(authante.GasTx)
|
||||
if !ok {
|
||||
@ -431,6 +440,8 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
|
||||
|
||||
// Set gas meter after ante handler to ignore gaskv costs
|
||||
newCtx = authante.SetGasMeter(simulate, ctx, gasLimit)
|
||||
egcd.evmKeeper.WithContext(newCtx)
|
||||
|
||||
return next(newCtx, tx, simulate)
|
||||
}
|
||||
|
||||
|
@ -252,7 +252,8 @@ func NewEthermintApp(
|
||||
evmtypes.StoreKey,
|
||||
)
|
||||
|
||||
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
|
||||
// Add the EVM transient store key
|
||||
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey)
|
||||
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
|
||||
|
||||
app := &EthermintApp{
|
||||
@ -310,7 +311,7 @@ func NewEthermintApp(
|
||||
|
||||
// Create Ethermint keepers
|
||||
app.EvmKeeper = evmkeeper.NewKeeper(
|
||||
appCodec, keys[evmtypes.StoreKey], app.GetSubspace(evmtypes.ModuleName), app.AccountKeeper, app.BankKeeper,
|
||||
appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], app.GetSubspace(evmtypes.ModuleName), app.AccountKeeper, app.BankKeeper,
|
||||
)
|
||||
|
||||
// Create IBC Keeper
|
||||
|
@ -105,6 +105,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak authkeeper.
|
||||
genBlock := ethcore.DefaultGenesisBlock()
|
||||
ms := cms.CacheMultiStore()
|
||||
ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger)
|
||||
evmKeeper.CommitStateDB.WithContext(ctx)
|
||||
|
||||
// Set the default Ethermint parameters to the parameter keeper store
|
||||
evmKeeper.SetParams(ctx, evmtypes.DefaultParams())
|
||||
@ -123,23 +124,23 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak authkeeper.
|
||||
addr := ethcmn.HexToAddress(addrStr)
|
||||
acc := genBlock.Alloc[addr]
|
||||
|
||||
evmKeeper.AddBalance(ctx, addr, acc.Balance)
|
||||
evmKeeper.SetCode(ctx, addr, acc.Code)
|
||||
evmKeeper.SetNonce(ctx, addr, acc.Nonce)
|
||||
evmKeeper.CommitStateDB.AddBalance(addr, acc.Balance)
|
||||
evmKeeper.CommitStateDB.SetCode(addr, acc.Code)
|
||||
evmKeeper.CommitStateDB.SetNonce(addr, acc.Nonce)
|
||||
|
||||
for key, value := range acc.Storage {
|
||||
evmKeeper.SetState(ctx, addr, key, value)
|
||||
evmKeeper.CommitStateDB.SetState(addr, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// get balance of one of the genesis account having 400 ETH
|
||||
b := evmKeeper.GetBalance(ctx, genInvestor)
|
||||
b := evmKeeper.CommitStateDB.GetBalance(genInvestor)
|
||||
require.Equal(t, "200000000000000000000", b.String())
|
||||
|
||||
// commit the stateDB with 'false' to delete empty objects
|
||||
//
|
||||
// NOTE: Commit does not yet return the intra merkle root (version)
|
||||
_, err := evmKeeper.Commit(ctx, false)
|
||||
_, err := evmKeeper.CommitStateDB.Commit(false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// persist multi-store cache state
|
||||
@ -257,13 +258,14 @@ func TestImportBlocks(t *testing.T) {
|
||||
ms := cms.CacheMultiStore()
|
||||
ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger)
|
||||
ctx = ctx.WithBlockHeight(int64(block.NumberU64()))
|
||||
evmKeeper.CommitStateDB.WithContext(ctx)
|
||||
|
||||
if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||
applyDAOHardFork(evmKeeper)
|
||||
}
|
||||
|
||||
for i, tx := range block.Transactions() {
|
||||
evmKeeper.Prepare(ctx, tx.Hash(), block.Hash(), i)
|
||||
evmKeeper.CommitStateDB.Prepare(tx.Hash(), block.Hash(), i)
|
||||
// evmKeeper.CommitStateDB.Set(block.Hash())
|
||||
|
||||
receipt, gas, err := applyTransaction(
|
||||
|
@ -21,6 +21,8 @@ func InitGenesis(
|
||||
bankKeeper types.BankKeeper,
|
||||
data types.GenesisState,
|
||||
) []abci.ValidatorUpdate {
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
k.SetParams(ctx, data.Params)
|
||||
evmDenom := data.Params.EvmDenom
|
||||
|
||||
@ -43,18 +45,18 @@ func InitGenesis(
|
||||
}
|
||||
|
||||
evmBalance := bankKeeper.GetBalance(ctx, accAddress, evmDenom)
|
||||
k.SetBalance(ctx, address, evmBalance.Amount.BigInt())
|
||||
k.SetNonce(ctx, address, acc.GetSequence())
|
||||
k.SetCode(ctx, address, ethcmn.Hex2Bytes(account.Code))
|
||||
k.CommitStateDB.SetBalance(address, evmBalance.Amount.BigInt())
|
||||
k.CommitStateDB.SetNonce(address, acc.GetSequence())
|
||||
k.CommitStateDB.SetCode(address, ethcmn.Hex2Bytes(account.Code))
|
||||
|
||||
for _, storage := range account.Storage {
|
||||
k.SetState(ctx, address, ethcmn.HexToHash(storage.Key), ethcmn.HexToHash(storage.Value))
|
||||
k.SetState(address, ethcmn.HexToHash(storage.Key), ethcmn.HexToHash(storage.Value))
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, txLog := range data.TxsLogs {
|
||||
err = k.SetLogs(ctx, ethcmn.HexToHash(txLog.Hash), txLog.EthLogs())
|
||||
err = k.CommitStateDB.SetLogs(ethcmn.HexToHash(txLog.Hash), txLog.EthLogs())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -63,14 +65,14 @@ func InitGenesis(
|
||||
k.SetChainConfig(ctx, data.ChainConfig)
|
||||
|
||||
// set state objects and code to store
|
||||
_, err = k.Commit(ctx, false)
|
||||
_, err = k.CommitStateDB.Commit(false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// set storage to store
|
||||
// NOTE: don't delete empty object to prevent import-export simulation failure
|
||||
err = k.Finalise(ctx, false)
|
||||
err = k.CommitStateDB.Finalise(false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -80,6 +82,8 @@ func InitGenesis(
|
||||
|
||||
// ExportGenesis exports genesis state of the EVM module
|
||||
func ExportGenesis(ctx sdk.Context, k keeper.Keeper, ak types.AccountKeeper) *types.GenesisState {
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
// nolint: prealloc
|
||||
var ethGenAccounts []types.GenesisAccount
|
||||
ak.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
||||
@ -98,7 +102,7 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper, ak types.AccountKeeper) *ty
|
||||
|
||||
genAccount := types.GenesisAccount{
|
||||
Address: addr.String(),
|
||||
Code: ethcmn.Bytes2Hex(k.GetCode(ctx, addr)),
|
||||
Code: ethcmn.Bytes2Hex(k.CommitStateDB.GetCode(addr)),
|
||||
Storage: storage,
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@ func (suite *EvmTestSuite) SetupTest() {
|
||||
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-888", Time: time.Now().UTC()})
|
||||
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||
suite.handler = evm.NewHandler(suite.app.EvmKeeper)
|
||||
suite.codec = suite.app.AppCodec()
|
||||
suite.chainID = suite.chainID
|
||||
@ -80,7 +81,7 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
|
||||
{
|
||||
"passed",
|
||||
func() {
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.from, big.NewInt(100))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.from, big.NewInt(100))
|
||||
to := ethcmn.BytesToAddress(suite.to)
|
||||
tx = types.NewMsgEthereumTx(suite.chainID, 0, &to, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
|
||||
tx.From = suite.from.String()
|
||||
@ -193,10 +194,10 @@ func (suite *EvmTestSuite) TestHandlerLogs() {
|
||||
suite.Require().Equal(len(txResponse.TxLogs.Logs[0].Topics), 2)
|
||||
|
||||
hash := []byte{1}
|
||||
err = suite.app.EvmKeeper.SetLogs(suite.ctx, ethcmn.BytesToHash(hash), txResponse.TxLogs.EthLogs())
|
||||
err = suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash(hash), txResponse.TxLogs.EthLogs())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, ethcmn.BytesToHash(hash))
|
||||
logs, err := suite.app.EvmKeeper.CommitStateDB.GetLogs(ethcmn.BytesToHash(hash))
|
||||
suite.Require().NoError(err, "failed to get logs")
|
||||
|
||||
suite.Require().Equal(logs, txResponse.TxLogs.Logs)
|
||||
@ -227,7 +228,7 @@ func (suite *EvmTestSuite) TestQueryTxLogs() {
|
||||
// get logs by tx hash
|
||||
hash := txResponse.TxLogs.Hash
|
||||
|
||||
logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, ethcmn.HexToHash(hash))
|
||||
logs, err := suite.app.EvmKeeper.CommitStateDB.GetLogs(ethcmn.HexToHash(hash))
|
||||
suite.Require().NoError(err, "failed to get logs")
|
||||
|
||||
suite.Require().Equal(logs, txResponse.TxLogs.EthLogs())
|
||||
@ -345,7 +346,7 @@ func (suite *EvmTestSuite) TestSendTransaction() {
|
||||
gasLimit := uint64(21000)
|
||||
gasPrice := big.NewInt(0x55ae82600)
|
||||
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.from, big.NewInt(100))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.from, big.NewInt(100))
|
||||
|
||||
// send simple value transfer with gasLimit=21000
|
||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, ðcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil, nil)
|
||||
|
@ -33,10 +33,6 @@ func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
|
||||
|
||||
// special setter for csdb
|
||||
k.SetHeightHash(ctx, uint64(req.Header.Height), common.BytesToHash(req.Hash))
|
||||
|
||||
// reset counters that are used on CommitStateDB.Prepare
|
||||
k.Bloom = big.NewInt(0)
|
||||
k.TxCount = 0
|
||||
}
|
||||
|
||||
// EndBlock updates the accounts and commits state objects to the KV Store, while
|
||||
@ -48,11 +44,12 @@ func (k Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.Valid
|
||||
|
||||
// Gas costs are handled within msg handler so costs should be ignored
|
||||
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
// Update account balances before committing other parts of state
|
||||
k.UpdateAccounts(ctx)
|
||||
k.CommitStateDB.UpdateAccounts()
|
||||
|
||||
root, err := k.Commit(ctx, true)
|
||||
root, err := k.CommitStateDB.Commit(true)
|
||||
// Commit state objects to KV store
|
||||
if err != nil {
|
||||
k.Logger(ctx).Error("failed to commit state objects", "error", err, "height", ctx.BlockHeight())
|
||||
@ -60,12 +57,17 @@ func (k Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.Valid
|
||||
}
|
||||
|
||||
// reset all cache after account data has been committed, that make sure node state consistent
|
||||
if err = k.Reset(ctx, root); err != nil {
|
||||
if err = k.CommitStateDB.Reset(root); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// set the block bloom filter bytes to store
|
||||
bloom := ethtypes.BytesToBloom(k.Bloom.Bytes())
|
||||
// get the block bloom bytes from the transient store and set it to the persistent storage
|
||||
bloomBig, found := k.GetBlockBloomTransient()
|
||||
if !found {
|
||||
bloomBig = big.NewInt(0)
|
||||
}
|
||||
|
||||
bloom := ethtypes.BytesToBloom(bloomBig.Bytes())
|
||||
k.SetBlockBloom(ctx, req.Height, bloom)
|
||||
|
||||
return []abci.ValidatorUpdate{}
|
||||
|
@ -32,7 +32,9 @@ func (k Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*typ
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
so := k.GetOrNewStateObject(ctx, ethcmn.HexToAddress(req.Address))
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
so := k.CommitStateDB.GetOrNewStateObject(ethcmn.HexToAddress(req.Address))
|
||||
balance, err := ethermint.MarshalBigInt(so.Balance())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -57,6 +59,7 @@ func (k Keeper) CosmosAccount(c context.Context, req *types.QueryCosmosAccountRe
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
ethAddr := ethcmn.HexToAddress(req.Address)
|
||||
cosmosAddr := sdk.AccAddress(ethAddr.Bytes())
|
||||
@ -88,8 +91,9 @@ func (k Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*typ
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
balanceInt := k.GetBalance(ctx, ethcmn.HexToAddress(req.Address))
|
||||
balanceInt := k.CommitStateDB.GetBalance(ethcmn.HexToAddress(req.Address))
|
||||
balance, err := ethermint.MarshalBigInt(balanceInt)
|
||||
if err != nil {
|
||||
return nil, status.Error(
|
||||
@ -117,11 +121,12 @@ func (k Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*typ
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
address := ethcmn.HexToAddress(req.Address)
|
||||
key := ethcmn.HexToHash(req.Key)
|
||||
|
||||
state := k.GetState(ctx, address, key)
|
||||
state := k.CommitStateDB.GetState(address, key)
|
||||
|
||||
return &types.QueryStorageResponse{
|
||||
Value: state.String(),
|
||||
@ -142,9 +147,10 @@ func (k Keeper) Code(c context.Context, req *types.QueryCodeRequest) (*types.Que
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
address := ethcmn.HexToAddress(req.Address)
|
||||
code := k.GetCode(ctx, address)
|
||||
code := k.CommitStateDB.GetCode(address)
|
||||
|
||||
return &types.QueryCodeResponse{
|
||||
Code: code,
|
||||
@ -165,9 +171,10 @@ func (k Keeper) TxLogs(c context.Context, req *types.QueryTxLogsRequest) (*types
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
hash := ethcmn.HexToHash(req.Hash)
|
||||
logs, err := k.GetLogs(ctx, hash)
|
||||
logs, err := k.CommitStateDB.GetLogs(hash)
|
||||
if err != nil {
|
||||
return nil, status.Error(
|
||||
codes.Internal,
|
||||
@ -296,6 +303,7 @@ func (k Keeper) StaticCall(c context.Context, req *types.QueryStaticCallRequest)
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID())
|
||||
@ -312,7 +320,7 @@ func (k Keeper) StaticCall(c context.Context, req *types.QueryStaticCallRequest)
|
||||
recipient = &addr
|
||||
}
|
||||
|
||||
so := k.GetOrNewStateObject(ctx, *recipient)
|
||||
so := k.CommitStateDB.GetOrNewStateObject(*recipient)
|
||||
sender := ethcmn.HexToAddress("0xaDd00275E3d9d213654Ce5223f0FADE8b106b707")
|
||||
|
||||
msg := types.NewMsgEthereumTx(
|
||||
|
@ -247,7 +247,7 @@ func (suite *KeeperTestSuite) TestQueryStorage() {
|
||||
key := ethcmn.BytesToHash([]byte("key"))
|
||||
value := ethcmn.BytesToHash([]byte("value"))
|
||||
expValue = value.String()
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, key, value)
|
||||
suite.app.EvmKeeper.CommitStateDB.SetState(suite.address, key, value)
|
||||
req = &types.QueryStorageRequest{
|
||||
Address: suite.address.String(),
|
||||
Key: key.String(),
|
||||
@ -302,7 +302,7 @@ func (suite *KeeperTestSuite) TestQueryCode() {
|
||||
"success",
|
||||
func() {
|
||||
expCode = []byte("code")
|
||||
suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, expCode)
|
||||
suite.app.EvmKeeper.CommitStateDB.SetCode(suite.address, expCode)
|
||||
|
||||
req = &types.QueryCodeRequest{
|
||||
Address: suite.address.String(),
|
||||
@ -379,7 +379,7 @@ func (suite *KeeperTestSuite) TestQueryTxLogs() {
|
||||
},
|
||||
}
|
||||
|
||||
suite.app.EvmKeeper.SetLogs(suite.ctx, hash, types.LogsToEthereum(expLogs))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetLogs(hash, types.LogsToEthereum(expLogs))
|
||||
|
||||
req = &types.QueryTxLogsRequest{
|
||||
Hash: hash.String(),
|
||||
@ -488,8 +488,8 @@ func (suite *KeeperTestSuite) TestQueryBlockLogs() {
|
||||
},
|
||||
}
|
||||
|
||||
suite.app.EvmKeeper.SetLogs(suite.ctx, ethcmn.BytesToHash([]byte("tx_hash_0")), types.LogsToEthereum(expLogs[0].Logs))
|
||||
suite.app.EvmKeeper.SetLogs(suite.ctx, ethcmn.BytesToHash([]byte("tx_hash_1")), types.LogsToEthereum(expLogs[1].Logs))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_0")), types.LogsToEthereum(expLogs[0].Logs))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_1")), types.LogsToEthereum(expLogs[1].Logs))
|
||||
|
||||
req = &types.QueryBlockLogsRequest{
|
||||
Hash: hash.String(),
|
||||
|
@ -30,6 +30,8 @@ func (k Keeper) BalanceInvariant() sdk.Invariant {
|
||||
count int
|
||||
)
|
||||
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
||||
ethAccount, ok := account.(*ethermint.EthAccount)
|
||||
if !ok {
|
||||
@ -40,7 +42,7 @@ func (k Keeper) BalanceInvariant() sdk.Invariant {
|
||||
evmDenom := k.GetParams(ctx).EvmDenom
|
||||
|
||||
accountBalance := k.bankKeeper.GetBalance(ctx, ethAccount.GetAddress(), evmDenom)
|
||||
evmBalance := k.GetBalance(ctx, ethAccount.EthAddress())
|
||||
evmBalance := k.CommitStateDB.GetBalance(ethAccount.EthAddress())
|
||||
|
||||
if evmBalance.Cmp(accountBalance.Amount.BigInt()) != 0 {
|
||||
count++
|
||||
@ -71,6 +73,8 @@ func (k Keeper) NonceInvariant() sdk.Invariant {
|
||||
count int
|
||||
)
|
||||
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
||||
ethAccount, ok := account.(*ethermint.EthAccount)
|
||||
if !ok {
|
||||
@ -78,7 +82,7 @@ func (k Keeper) NonceInvariant() sdk.Invariant {
|
||||
return false
|
||||
}
|
||||
|
||||
evmNonce := k.GetNonce(ctx, ethAccount.EthAddress())
|
||||
evmNonce := k.CommitStateDB.GetNonce(ethAccount.EthAddress())
|
||||
|
||||
if evmNonce != ethAccount.Sequence {
|
||||
count++
|
||||
|
@ -29,7 +29,7 @@ func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, address, big.NewInt(1000))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(address, big.NewInt(1000))
|
||||
},
|
||||
true,
|
||||
},
|
||||
@ -42,7 +42,7 @@ func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, address, big.NewInt(1))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(address, big.NewInt(1))
|
||||
},
|
||||
false,
|
||||
},
|
||||
@ -60,6 +60,7 @@ func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset values
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||
tc.malleate()
|
||||
|
||||
_, broken := suite.app.EvmKeeper.BalanceInvariant()(suite.ctx)
|
||||
@ -92,7 +93,7 @@ func (suite *KeeperTestSuite) TestNonceInvariant() {
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.SetNonce(suite.ctx, address, 100)
|
||||
suite.app.EvmKeeper.CommitStateDB.SetNonce(address, 100)
|
||||
},
|
||||
true,
|
||||
},
|
||||
@ -105,7 +106,7 @@ func (suite *KeeperTestSuite) TestNonceInvariant() {
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.SetNonce(suite.ctx, address, 1)
|
||||
suite.app.EvmKeeper.CommitStateDB.SetNonce(address, 1)
|
||||
},
|
||||
false,
|
||||
},
|
||||
@ -123,6 +124,7 @@ func (suite *KeeperTestSuite) TestNonceInvariant() {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset values
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||
tc.malleate()
|
||||
|
||||
_, broken := suite.app.EvmKeeper.NonceInvariant()(suite.ctx)
|
||||
|
@ -1,9 +1,11 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -26,25 +28,28 @@ type Keeper struct {
|
||||
// - storing block hash -> block height map. Needed for the Web3 API. TODO: remove
|
||||
storeKey sdk.StoreKey
|
||||
|
||||
// key to access the transient store, which is reset on every block during Commit
|
||||
transientKey sdk.StoreKey
|
||||
|
||||
paramSpace paramtypes.Subspace
|
||||
accountKeeper types.AccountKeeper
|
||||
bankKeeper types.BankKeeper
|
||||
|
||||
ctx sdk.Context
|
||||
|
||||
// Ethermint concrete implementation on the EVM StateDB interface
|
||||
CommitStateDB *types.CommitStateDB
|
||||
// Transaction counter in a block. Used on StateSB's Prepare function.
|
||||
// It is reset to 0 every block on BeginBlock so there's no point in storing the counter
|
||||
// on the KVStore or adding it as a field on the EVM genesis state.
|
||||
TxCount int
|
||||
Bloom *big.Int
|
||||
|
||||
// LogsCache keeps mapping of contract address -> eth logs emitted
|
||||
// during EVM execution in the current block.
|
||||
LogsCache map[common.Address][]*ethtypes.Log
|
||||
// Per-transaction access list
|
||||
// See EIP-2930 for more info: https://eips.ethereum.org/EIPS/eip-2930
|
||||
// TODO: (@fedekunze) for how long should we persist the entries in the access list?
|
||||
// same block (i.e Transient Store)? 2 or more (KVStore with module Parameter which resets the state after that window)?
|
||||
accessList *types.AccessListMappings
|
||||
}
|
||||
|
||||
// NewKeeper generates new evm module keeper
|
||||
func NewKeeper(
|
||||
cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, paramSpace paramtypes.Subspace,
|
||||
cdc codec.BinaryMarshaler, storeKey, transientKey sdk.StoreKey, paramSpace paramtypes.Subspace,
|
||||
ak types.AccountKeeper, bankKeeper types.BankKeeper,
|
||||
) *Keeper {
|
||||
// set KeyTable if it has not already been set
|
||||
@ -55,13 +60,13 @@ func NewKeeper(
|
||||
// NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations
|
||||
return &Keeper{
|
||||
cdc: cdc,
|
||||
paramSpace: paramSpace,
|
||||
accountKeeper: ak,
|
||||
bankKeeper: bankKeeper,
|
||||
storeKey: storeKey,
|
||||
transientKey: transientKey,
|
||||
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, paramSpace, ak, bankKeeper),
|
||||
TxCount: 0,
|
||||
Bloom: big.NewInt(0),
|
||||
LogsCache: map[common.Address][]*ethtypes.Log{},
|
||||
accessList: types.NewAccessListMappings(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,8 +75,13 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
||||
return ctx.Logger().With("module", types.ModuleName)
|
||||
}
|
||||
|
||||
// WithContext sets an updated SDK context to the keeper
|
||||
func (k *Keeper) WithContext(ctx sdk.Context) {
|
||||
k.ctx = ctx
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Block bloom bits mapping functions
|
||||
// Block Bloom
|
||||
// Required by Web3 API.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@ -94,6 +104,28 @@ func (k Keeper) SetBlockBloom(ctx sdk.Context, height int64, bloom ethtypes.Bloo
|
||||
store.Set(key, bloom.Bytes())
|
||||
}
|
||||
|
||||
// GetBlockBloomTransient returns bloom bytes for the current block height
|
||||
func (k Keeper) GetBlockBloomTransient() (*big.Int, bool) {
|
||||
store := k.ctx.TransientStore(k.transientKey)
|
||||
bz := store.Get(types.KeyPrefixTransientBloom)
|
||||
if len(bz) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return new(big.Int).SetBytes(bz), true
|
||||
}
|
||||
|
||||
// SetBlockBloomTransient sets the given bloom bytes to the transient store. This value is reset on
|
||||
// every block.
|
||||
func (k Keeper) SetBlockBloomTransient(bloom *big.Int) {
|
||||
store := k.ctx.TransientStore(k.transientKey)
|
||||
store.Set(types.KeyPrefixTransientBloom, bloom.Bytes())
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Block
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetBlockHash gets block height from block consensus hash
|
||||
func (k Keeper) GetBlockHashFromHeight(ctx sdk.Context, height int64) (common.Hash, bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
@ -150,11 +182,40 @@ func (k Keeper) SetHeightHash(ctx sdk.Context, height uint64, hash common.Hash)
|
||||
k.CommitStateDB.WithContext(ctx).SetHeightHash(height, hash)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Tx
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetTxIndexTransient returns EVM transaction index on the current block.
|
||||
func (k Keeper) GetTxIndexTransient() uint64 {
|
||||
store := k.ctx.TransientStore(k.transientKey)
|
||||
bz := store.Get(types.KeyPrefixTransientBloom)
|
||||
if len(bz) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return sdk.BigEndianToUint64(bz)
|
||||
}
|
||||
|
||||
// IncreaseTxIndexTransient fetches the current EVM tx index from the transient store, increases its
|
||||
// value by one and then sets the new index back to the transient store.
|
||||
func (k Keeper) IncreaseTxIndexTransient() {
|
||||
txIndex := k.GetTxIndexTransient()
|
||||
store := k.ctx.TransientStore(k.transientKey)
|
||||
store.Set(types.KeyPrefixTransientBloom, sdk.Uint64ToBigEndian(txIndex+1))
|
||||
}
|
||||
|
||||
// ResetRefundTransient resets the refund gas value.
|
||||
func (k Keeper) ResetRefundTransient(ctx sdk.Context) {
|
||||
store := ctx.TransientStore(k.transientKey)
|
||||
store.Delete(types.KeyPrefixTransientRefund)
|
||||
}
|
||||
|
||||
// GetTxReceiptFromHash gets tx receipt by tx hash.
|
||||
func (k Keeper) GetTxReceiptFromHash(ctx sdk.Context, hash common.Hash) (*types.TxReceipt, bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
data := store.Get(types.KeyHashTxReceipt(hash))
|
||||
if data == nil || len(data) == 0 {
|
||||
if len(data) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@ -216,7 +277,7 @@ func (k Keeper) GetTxReceiptsByBlockHeight(ctx sdk.Context, blockHeight int64) [
|
||||
|
||||
for idx, txHash := range txs {
|
||||
data := store.Get(types.KeyHashTxReceipt(txHash))
|
||||
if data == nil || len(data) == 0 {
|
||||
if len(data) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -239,6 +300,10 @@ func (k Keeper) GetTxReceiptsByBlockHash(ctx sdk.Context, hash common.Hash) []*t
|
||||
return k.GetTxReceiptsByBlockHeight(ctx, blockHeight)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Log
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetAllTxLogs return all the transaction logs from the store.
|
||||
func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
@ -256,14 +321,51 @@ func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs {
|
||||
return txsLogs
|
||||
}
|
||||
|
||||
// GetLogs returns the current logs for a given transaction hash from the KVStore.
|
||||
// This function returns an empty, non-nil slice if no logs are found.
|
||||
func (k Keeper) GetTxLogs(txHash common.Hash) []*ethtypes.Log {
|
||||
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.KeyPrefixLogs)
|
||||
|
||||
bz := store.Get(txHash.Bytes())
|
||||
if len(bz) == 0 {
|
||||
return []*ethtypes.Log{}
|
||||
}
|
||||
|
||||
var logs types.TransactionLogs
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &logs)
|
||||
|
||||
return logs.EthLogs()
|
||||
}
|
||||
|
||||
// SetLogs sets the logs for a transaction in the KVStore.
|
||||
func (k Keeper) SetLogs(txHash common.Hash, logs []*ethtypes.Log) {
|
||||
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.KeyPrefixLogs)
|
||||
|
||||
txLogs := types.NewTransactionLogsFromEth(txHash, logs)
|
||||
bz := k.cdc.MustMarshalBinaryBare(&txLogs)
|
||||
|
||||
store.Set(txHash.Bytes(), bz)
|
||||
}
|
||||
|
||||
// DeleteLogs removes the logs from the KVStore. It is used during journal.Revert.
|
||||
func (k Keeper) DeleteTxLogs(ctx sdk.Context, txHash common.Hash) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixLogs)
|
||||
store.Delete(txHash.Bytes())
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Storage
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetAccountStorage return state storage associated with an account
|
||||
func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (types.Storage, error) {
|
||||
storage := types.Storage{}
|
||||
|
||||
err := k.ForEachStorage(ctx, address, func(key, value common.Hash) bool {
|
||||
err := k.ForEachStorage(address, func(key, value common.Hash) bool {
|
||||
storage = append(storage, types.NewState(key, value))
|
||||
return false
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return types.Storage{}, err
|
||||
}
|
||||
@ -271,22 +373,62 @@ func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (type
|
||||
return storage, nil
|
||||
}
|
||||
|
||||
// GetChainConfig gets block height from block consensus hash
|
||||
func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := store.Get(types.KeyPrefixChainConfig)
|
||||
if len(bz) == 0 {
|
||||
return types.ChainConfig{}, false
|
||||
// ----------------------------------------------------------------------------
|
||||
// Account
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
func (k Keeper) DeleteState(addr common.Address, key common.Hash) {
|
||||
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr))
|
||||
key = types.KeyAddressStorage(addr, key)
|
||||
store.Delete(key.Bytes())
|
||||
}
|
||||
|
||||
// DeleteAccountStorage clears all the storage state associated with the given address.
|
||||
func (k Keeper) DeleteAccountStorage(addr common.Address) {
|
||||
_ = k.ForEachStorage(addr, func(key, _ common.Hash) bool {
|
||||
k.DeleteState(addr, key)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteCode removes the contract code byte array from the store associated with
|
||||
// the given address.
|
||||
func (k Keeper) DeleteCode(addr common.Address) {
|
||||
hash := k.GetCodeHash(addr)
|
||||
if bytes.Equal(hash.Bytes(), common.BytesToHash(types.EmptyCodeHash).Bytes()) {
|
||||
return
|
||||
}
|
||||
|
||||
var config types.ChainConfig
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &config)
|
||||
return config, true
|
||||
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.KeyPrefixCode)
|
||||
store.Delete(hash.Bytes())
|
||||
}
|
||||
|
||||
// SetChainConfig sets the mapping from block consensus hash to block height
|
||||
func (k Keeper) SetChainConfig(ctx sdk.Context, config types.ChainConfig) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinaryBare(&config)
|
||||
store.Set(types.KeyPrefixChainConfig, bz)
|
||||
// ClearBalance subtracts the EVM all the balance denomination from the address
|
||||
// balance while also updating the total supply.
|
||||
func (k Keeper) ClearBalance(addr sdk.AccAddress) (prevBalance sdk.Coin, err error) {
|
||||
params := k.GetParams(k.ctx)
|
||||
|
||||
prevBalance = k.bankKeeper.GetBalance(k.ctx, addr, params.EvmDenom)
|
||||
if prevBalance.IsPositive() {
|
||||
err := k.bankKeeper.SubtractCoins(k.ctx, addr, sdk.Coins{prevBalance})
|
||||
if err != nil {
|
||||
return sdk.Coin{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return prevBalance, nil
|
||||
}
|
||||
|
||||
// ResetAccount removes the code, storage state and evm denom balance coins stored
|
||||
// with the given address.
|
||||
func (k Keeper) ResetAccount(addr common.Address) {
|
||||
k.DeleteCode(addr)
|
||||
k.DeleteAccountStorage(addr)
|
||||
_, err := k.ClearBalance(addr.Bytes())
|
||||
if err != nil {
|
||||
k.Logger(k.ctx).Error(
|
||||
"failed to clear balance during account reset",
|
||||
"ethereum-address", addr.Hex(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ func (suite *KeeperTestSuite) SetupTest() {
|
||||
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-3", Time: time.Now().UTC()})
|
||||
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||
|
||||
suite.address = ethcmn.HexToAddress(addrHex)
|
||||
|
||||
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
|
||||
@ -77,18 +79,18 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
||||
}
|
||||
expLogs := []*ethtypes.Log{log}
|
||||
|
||||
err := suite.app.EvmKeeper.SetLogs(suite.ctx, ethHash, expLogs)
|
||||
err := suite.app.EvmKeeper.CommitStateDB.SetLogs(ethHash, expLogs)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, ethHash)
|
||||
logs, err := suite.app.EvmKeeper.CommitStateDB.GetLogs(ethHash)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(expLogs, logs)
|
||||
|
||||
expLogs = []*ethtypes.Log{log2, log}
|
||||
|
||||
// add another log under the zero hash
|
||||
suite.app.EvmKeeper.AddLog(suite.ctx, log2)
|
||||
logs = suite.app.EvmKeeper.AllLogs(suite.ctx)
|
||||
suite.app.EvmKeeper.CommitStateDB.AddLog(log2)
|
||||
logs = suite.app.EvmKeeper.CommitStateDB.AllLogs()
|
||||
suite.Require().Equal(expLogs, logs)
|
||||
|
||||
// add another log under the zero hash
|
||||
@ -97,7 +99,7 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
||||
Data: []byte("log3"),
|
||||
BlockNumber: 10,
|
||||
}
|
||||
suite.app.EvmKeeper.AddLog(suite.ctx, log3)
|
||||
suite.app.EvmKeeper.CommitStateDB.AddLog(log3)
|
||||
|
||||
txLogs := suite.app.EvmKeeper.GetAllTxLogs(suite.ctx)
|
||||
suite.Require().Equal(2, len(txLogs))
|
||||
@ -111,28 +113,28 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
||||
|
||||
func (suite *KeeperTestSuite) TestDBStorage() {
|
||||
// Perform state transitions
|
||||
suite.app.EvmKeeper.CreateAccount(suite.ctx, suite.address)
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(5))
|
||||
suite.app.EvmKeeper.SetNonce(suite.ctx, suite.address, 4)
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
|
||||
suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, []byte{0x1})
|
||||
suite.app.EvmKeeper.CommitStateDB.CreateAccount(suite.address)
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.address, big.NewInt(5))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetNonce(suite.address, 4)
|
||||
suite.app.EvmKeeper.CommitStateDB.SetState(suite.address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetCode(suite.address, []byte{0x1})
|
||||
|
||||
// Test block height mapping functionality
|
||||
testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
|
||||
suite.app.EvmKeeper.SetBlockBloom(suite.ctx, 4, testBloom)
|
||||
|
||||
// Get those state transitions
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address).Cmp(big.NewInt(5)), 0)
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address), uint64(4))
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetState(suite.ctx, suite.address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
|
||||
suite.Require().Equal(suite.app.EvmKeeper.GetCode(suite.ctx, suite.address), []byte{0x1})
|
||||
suite.Require().Equal(suite.app.EvmKeeper.CommitStateDB.GetBalance(suite.address).Cmp(big.NewInt(5)), 0)
|
||||
suite.Require().Equal(suite.app.EvmKeeper.CommitStateDB.GetNonce(suite.address), uint64(4))
|
||||
suite.Require().Equal(suite.app.EvmKeeper.CommitStateDB.GetState(suite.address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3"))
|
||||
suite.Require().Equal(suite.app.EvmKeeper.CommitStateDB.GetCode(suite.address), []byte{0x1})
|
||||
|
||||
bloom, found := suite.app.EvmKeeper.GetBlockBloom(suite.ctx, 4)
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(bloom, testBloom)
|
||||
|
||||
// commit stateDB
|
||||
_, err := suite.app.EvmKeeper.Commit(suite.ctx, false)
|
||||
_, err := suite.app.EvmKeeper.CommitStateDB.Commit(false)
|
||||
suite.Require().NoError(err, "failed to commit StateDB")
|
||||
|
||||
// simulate BaseApp EndBlocker commitment
|
||||
|
@ -3,6 +3,7 @@ package keeper
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
@ -24,6 +25,7 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.TypeMsgEthereumTx)
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
ethMsg, err := msg.AsMessage()
|
||||
if err != nil {
|
||||
@ -67,8 +69,8 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
// other nodes, causing a consensus error
|
||||
if !st.Simulate {
|
||||
// Prepare db for logs
|
||||
k.Prepare(ctx, ethHash, blockHash, k.TxCount)
|
||||
k.TxCount++
|
||||
k.CommitStateDB.Prepare(ethHash, blockHash, int(k.GetTxIndexTransient()))
|
||||
k.IncreaseTxIndexTransient()
|
||||
}
|
||||
|
||||
executionResult, err := st.TransitionDb(ctx, config)
|
||||
@ -102,11 +104,16 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
}
|
||||
|
||||
if !st.Simulate {
|
||||
bloom, found := k.GetBlockBloomTransient()
|
||||
if !found {
|
||||
bloom = big.NewInt(0)
|
||||
}
|
||||
// update block bloom filter
|
||||
k.Bloom.Or(k.Bloom, executionResult.Bloom)
|
||||
bloom = bloom.Or(bloom, executionResult.Bloom)
|
||||
k.SetBlockBloomTransient(bloom)
|
||||
|
||||
// update transaction logs in KVStore
|
||||
err = k.SetLogs(ctx, ethHash, executionResult.Logs)
|
||||
err = k.CommitStateDB.SetLogs(ethHash, executionResult.Logs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -130,10 +137,6 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
})
|
||||
|
||||
k.AddTxHashToBlock(ctx, ctx.BlockHeight(), ethHash)
|
||||
|
||||
for _, ethLog := range executionResult.Logs {
|
||||
k.LogsCache[ethLog.Address] = append(k.LogsCache[ethLog.Address], ethLog)
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
@ -8,10 +8,31 @@ import (
|
||||
|
||||
// GetParams returns the total set of evm parameters.
|
||||
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
|
||||
return k.CommitStateDB.WithContext(ctx).GetParams()
|
||||
k.paramSpace.GetParamSet(ctx, ¶ms)
|
||||
return params
|
||||
}
|
||||
|
||||
// SetParams sets the evm parameters to the param space.
|
||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||
k.CommitStateDB.WithContext(ctx).SetParams(params)
|
||||
k.paramSpace.SetParamSet(ctx, ¶ms)
|
||||
}
|
||||
|
||||
// GetChainConfig gets block height from block consensus hash
|
||||
func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := store.Get(types.KeyPrefixChainConfig)
|
||||
if len(bz) == 0 {
|
||||
return types.ChainConfig{}, false
|
||||
}
|
||||
|
||||
var config types.ChainConfig
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &config)
|
||||
return config, true
|
||||
}
|
||||
|
||||
// SetChainConfig sets the mapping from block consensus hash to block height
|
||||
func (k Keeper) SetChainConfig(ctx sdk.Context, config types.ChainConfig) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinaryBare(&config)
|
||||
store.Set(types.KeyPrefixChainConfig, bz)
|
||||
}
|
||||
|
@ -1,265 +1,589 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethvm "github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
var _ vm.StateDB = &Keeper{}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Setters
|
||||
// Account
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// SetBalance calls CommitStateDB.SetBalance using the passed in context
|
||||
func (k *Keeper) SetBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) {
|
||||
k.CommitStateDB.WithContext(ctx).SetBalance(addr, amount)
|
||||
// CreateAccount creates a new EthAccount instance from the provided address and
|
||||
// sets the value to store.
|
||||
func (k *Keeper) CreateAccount(addr common.Address) {
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
|
||||
account := k.accountKeeper.GetAccount(k.ctx, cosmosAddr)
|
||||
log := ""
|
||||
if account == nil {
|
||||
log = "account created"
|
||||
} else {
|
||||
log = "account overwritten"
|
||||
k.ResetAccount(addr)
|
||||
}
|
||||
|
||||
_ = k.accountKeeper.NewAccountWithAddress(k.ctx, cosmosAddr)
|
||||
|
||||
k.Logger(k.ctx).Debug(
|
||||
log,
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Balance
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// AddBalance calls CommitStateDB.AddBalance using the passed in context
|
||||
func (k *Keeper) AddBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) {
|
||||
k.CommitStateDB.WithContext(ctx).AddBalance(addr, amount)
|
||||
func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
|
||||
params := k.GetParams(k.ctx)
|
||||
coins := sdk.Coins{sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(amount))}
|
||||
|
||||
if err := k.bankKeeper.AddCoins(k.ctx, cosmosAddr, coins); err != nil {
|
||||
k.Logger(k.ctx).Error(
|
||||
"failed to add balance",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
"error", err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
k.Logger(k.ctx).Debug(
|
||||
"balance addition",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// SubBalance calls CommitStateDB.SubBalance using the passed in context
|
||||
func (k *Keeper) SubBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) {
|
||||
k.CommitStateDB.WithContext(ctx).SubBalance(addr, amount)
|
||||
func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) {
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
|
||||
params := k.GetParams(k.ctx)
|
||||
coins := sdk.Coins{sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(amount))}
|
||||
|
||||
if err := k.bankKeeper.SubtractCoins(k.ctx, cosmosAddr, coins); err != nil {
|
||||
k.Logger(k.ctx).Error(
|
||||
"failed to subtract balance",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
"error", err,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
k.Logger(k.ctx).Debug(
|
||||
"balance subtraction",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// GetBalance calls CommitStateDB.GetBalance using the passed in context
|
||||
func (k *Keeper) GetBalance(addr common.Address) *big.Int {
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
params := k.GetParams(k.ctx)
|
||||
balance := k.bankKeeper.GetBalance(k.ctx, cosmosAddr, params.EvmDenom)
|
||||
|
||||
return balance.Amount.BigInt()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Nonce
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetNonce calls CommitStateDB.GetNonce using the passed in context
|
||||
func (k *Keeper) GetNonce(addr common.Address) uint64 {
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
nonce, err := k.accountKeeper.GetSequence(k.ctx, cosmosAddr)
|
||||
if err != nil {
|
||||
k.Logger(k.ctx).Error(
|
||||
"account not found",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
"error", err,
|
||||
)
|
||||
}
|
||||
|
||||
return nonce
|
||||
}
|
||||
|
||||
// SetNonce calls CommitStateDB.SetNonce using the passed in context
|
||||
func (k *Keeper) SetNonce(ctx sdk.Context, addr ethcmn.Address, nonce uint64) {
|
||||
k.CommitStateDB.WithContext(ctx).SetNonce(addr, nonce)
|
||||
}
|
||||
func (k *Keeper) SetNonce(addr common.Address, nonce uint64) {
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
account := k.accountKeeper.GetAccount(k.ctx, cosmosAddr)
|
||||
if account == nil {
|
||||
k.Logger(k.ctx).Debug(
|
||||
"account not found",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
)
|
||||
|
||||
// SetState calls CommitStateDB.SetState using the passed in context
|
||||
func (k *Keeper) SetState(ctx sdk.Context, addr ethcmn.Address, key, value ethcmn.Hash) {
|
||||
k.CommitStateDB.WithContext(ctx).SetState(addr, key, value)
|
||||
}
|
||||
// create address if it doesn't exist
|
||||
account = k.accountKeeper.NewAccountWithAddress(k.ctx, cosmosAddr)
|
||||
}
|
||||
|
||||
// SetCode calls CommitStateDB.SetCode using the passed in context
|
||||
func (k *Keeper) SetCode(ctx sdk.Context, addr ethcmn.Address, code []byte) {
|
||||
k.CommitStateDB.WithContext(ctx).SetCode(addr, code)
|
||||
}
|
||||
if err := account.SetSequence(nonce); err != nil {
|
||||
k.Logger(k.ctx).Error(
|
||||
"failed to set nonce",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
"nonce", nonce,
|
||||
"error", err,
|
||||
)
|
||||
|
||||
// SetLogs calls CommitStateDB.SetLogs using the passed in context
|
||||
func (k *Keeper) SetLogs(ctx sdk.Context, hash ethcmn.Hash, logs []*ethtypes.Log) error {
|
||||
// TODO:@albert
|
||||
// since SetLogs is only called for non-simulation mode, GasEstimation is quite not correct
|
||||
// I suggest using ctx.ConsumeGas(XXX) where XXX is max of gas consume for set logs.
|
||||
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
return
|
||||
}
|
||||
|
||||
return k.CommitStateDB.WithContext(ctx).SetLogs(hash, logs)
|
||||
}
|
||||
k.accountKeeper.SetAccount(k.ctx, account)
|
||||
|
||||
// DeleteLogs calls CommitStateDB.DeleteLogs using the passed in context
|
||||
func (k *Keeper) DeleteLogs(ctx sdk.Context, hash ethcmn.Hash) {
|
||||
k.CommitStateDB.WithContext(ctx).DeleteLogs(hash)
|
||||
}
|
||||
|
||||
// AddLog calls CommitStateDB.AddLog using the passed in context
|
||||
func (k *Keeper) AddLog(ctx sdk.Context, log *ethtypes.Log) {
|
||||
k.CommitStateDB.WithContext(ctx).AddLog(log)
|
||||
}
|
||||
|
||||
// AddPreimage calls CommitStateDB.AddPreimage using the passed in context
|
||||
func (k *Keeper) AddPreimage(ctx sdk.Context, hash ethcmn.Hash, preimage []byte) {
|
||||
k.CommitStateDB.WithContext(ctx).AddPreimage(hash, preimage)
|
||||
}
|
||||
|
||||
// AddRefund calls CommitStateDB.AddRefund using the passed in context
|
||||
func (k *Keeper) AddRefund(ctx sdk.Context, gas uint64) {
|
||||
k.CommitStateDB.WithContext(ctx).AddRefund(gas)
|
||||
}
|
||||
|
||||
// SubRefund calls CommitStateDB.SubRefund using the passed in context
|
||||
func (k *Keeper) SubRefund(ctx sdk.Context, gas uint64) {
|
||||
k.CommitStateDB.WithContext(ctx).SubRefund(gas)
|
||||
k.Logger(k.ctx).Debug(
|
||||
"nonce set",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
"nonce", nonce,
|
||||
)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Getters
|
||||
// Code
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetBalance calls CommitStateDB.GetBalance using the passed in context
|
||||
func (k *Keeper) GetBalance(ctx sdk.Context, addr ethcmn.Address) *big.Int {
|
||||
return k.CommitStateDB.WithContext(ctx).GetBalance(addr)
|
||||
}
|
||||
// GetCodeHash calls CommitStateDB.GetCodeHash using the passed in context
|
||||
func (k *Keeper) GetCodeHash(addr common.Address) common.Hash {
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
account := k.accountKeeper.GetAccount(k.ctx, cosmosAddr)
|
||||
if account == nil {
|
||||
return common.BytesToHash(types.EmptyCodeHash)
|
||||
}
|
||||
|
||||
// GetNonce calls CommitStateDB.GetNonce using the passed in context
|
||||
func (k *Keeper) GetNonce(ctx sdk.Context, addr ethcmn.Address) uint64 {
|
||||
return k.CommitStateDB.WithContext(ctx).GetNonce(addr)
|
||||
}
|
||||
ethAccount, isEthAccount := account.(*ethermint.EthAccount)
|
||||
if !isEthAccount {
|
||||
return common.BytesToHash(types.EmptyCodeHash)
|
||||
}
|
||||
|
||||
// TxIndex calls CommitStateDB.TxIndex using the passed in context
|
||||
func (k *Keeper) TxIndex(ctx sdk.Context) int {
|
||||
return k.CommitStateDB.WithContext(ctx).TxIndex()
|
||||
}
|
||||
|
||||
// BlockHash calls CommitStateDB.BlockHash using the passed in context
|
||||
func (k *Keeper) BlockHash(ctx sdk.Context) ethcmn.Hash {
|
||||
return k.CommitStateDB.WithContext(ctx).BlockHash()
|
||||
return common.BytesToHash(ethAccount.CodeHash)
|
||||
}
|
||||
|
||||
// GetCode calls CommitStateDB.GetCode using the passed in context
|
||||
func (k *Keeper) GetCode(ctx sdk.Context, addr ethcmn.Address) []byte {
|
||||
return k.CommitStateDB.WithContext(ctx).GetCode(addr)
|
||||
func (k *Keeper) GetCode(addr common.Address) []byte {
|
||||
hash := k.GetCodeHash(addr)
|
||||
|
||||
if bytes.Equal(hash.Bytes(), common.BytesToHash(types.EmptyCodeHash).Bytes()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.KeyPrefixCode)
|
||||
code := store.Get(hash.Bytes())
|
||||
|
||||
if len(code) == 0 {
|
||||
k.Logger(k.ctx).Debug(
|
||||
"code not found",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"code-hash", hash.Hex(),
|
||||
)
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
// GetCodeSize calls CommitStateDB.GetCodeSize using the passed in context
|
||||
func (k *Keeper) GetCodeSize(ctx sdk.Context, addr ethcmn.Address) int {
|
||||
return k.CommitStateDB.WithContext(ctx).GetCodeSize(addr)
|
||||
// SetCode calls CommitStateDB.SetCode using the passed in context
|
||||
func (k *Keeper) SetCode(addr common.Address, code []byte) {
|
||||
hash := crypto.Keccak256Hash(code)
|
||||
|
||||
// update account code hash
|
||||
account := k.accountKeeper.GetAccount(k.ctx, addr.Bytes())
|
||||
if account == nil {
|
||||
account = k.accountKeeper.NewAccountWithAddress(k.ctx, addr.Bytes())
|
||||
}
|
||||
|
||||
ethAccount, isEthAccount := account.(*ethermint.EthAccount)
|
||||
if !isEthAccount {
|
||||
k.Logger(k.ctx).Error(
|
||||
"invalid account type",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"code-hash", hash.Hex(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ethAccount.CodeHash = hash.Bytes()
|
||||
k.accountKeeper.SetAccount(k.ctx, ethAccount)
|
||||
|
||||
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.KeyPrefixCode)
|
||||
|
||||
action := "updated"
|
||||
|
||||
// store or delete code
|
||||
if len(code) == 0 {
|
||||
store.Delete(hash.Bytes())
|
||||
action = "deleted"
|
||||
} else {
|
||||
store.Set(hash.Bytes(), code)
|
||||
}
|
||||
|
||||
k.Logger(k.ctx).Debug(
|
||||
fmt.Sprintf("code %s", action),
|
||||
"ethereum-address", addr.Hex(),
|
||||
"code-hash", hash.Hex(),
|
||||
)
|
||||
}
|
||||
|
||||
// GetCodeHash calls CommitStateDB.GetCodeHash using the passed in context
|
||||
func (k *Keeper) GetCodeHash(ctx sdk.Context, addr ethcmn.Address) ethcmn.Hash {
|
||||
return k.CommitStateDB.WithContext(ctx).GetCodeHash(addr)
|
||||
// GetCodeSize returns the code hash stored in the address account.
|
||||
func (k *Keeper) GetCodeSize(addr common.Address) int {
|
||||
return len(k.GetCode(addr))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Refund
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// NOTE: gas refunded needs to be tracked and stored in a separate variable in
|
||||
// order to add it subtract/add it from/to the gas used value after the EVM
|
||||
// execution has finalised. The refund value is cleared on every transaction and
|
||||
// at the end of every block.
|
||||
|
||||
// AddRefund adds the given amount of gas to the refund cached value.
|
||||
func (k *Keeper) AddRefund(gas uint64) {
|
||||
refund := k.GetRefund()
|
||||
|
||||
refund += gas
|
||||
|
||||
store := k.ctx.TransientStore(k.transientKey)
|
||||
store.Set(types.KeyPrefixTransientRefund, sdk.Uint64ToBigEndian(refund))
|
||||
}
|
||||
|
||||
// SubRefund subtracts the given amount of gas from the refund value. This function
|
||||
// will panic if gas amount is greater than the stored refund.
|
||||
func (k *Keeper) SubRefund(gas uint64) {
|
||||
refund := k.GetRefund()
|
||||
|
||||
if gas > refund {
|
||||
// TODO: (@fedekunze) set to 0?? Geth panics here
|
||||
panic("refund counter below zero")
|
||||
}
|
||||
|
||||
refund -= gas
|
||||
|
||||
store := k.ctx.TransientStore(k.transientKey)
|
||||
store.Set(types.KeyPrefixTransientRefund, sdk.Uint64ToBigEndian(refund))
|
||||
}
|
||||
|
||||
// GetRefund returns the amount of gas available for return after the tx execution
|
||||
// finalises. This value is reset to 0 on every transaction.
|
||||
func (k *Keeper) GetRefund() uint64 {
|
||||
store := k.ctx.TransientStore(k.transientKey)
|
||||
|
||||
bz := store.Get(types.KeyPrefixTransientRefund)
|
||||
if len(bz) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return sdk.BigEndianToUint64(bz)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// State
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetCommittedState calls CommitStateDB.GetCommittedState using the passed in context
|
||||
func (k *Keeper) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
||||
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr))
|
||||
|
||||
key := types.KeyAddressStorage(addr, hash)
|
||||
value := store.Get(key.Bytes())
|
||||
if len(value) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
return common.BytesToHash(value)
|
||||
}
|
||||
|
||||
// GetState calls CommitStateDB.GetState using the passed in context
|
||||
func (k *Keeper) GetState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash {
|
||||
return k.CommitStateDB.WithContext(ctx).GetState(addr, hash)
|
||||
func (k *Keeper) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||
// All state is committed directly
|
||||
return k.GetCommittedState(addr, hash)
|
||||
}
|
||||
|
||||
// GetCommittedState calls CommitStateDB.GetCommittedState using the passed in context
|
||||
func (k *Keeper) GetCommittedState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash {
|
||||
return k.CommitStateDB.WithContext(ctx).GetCommittedState(addr, hash)
|
||||
}
|
||||
// SetState calls CommitStateDB.SetState using the passed in context
|
||||
func (k *Keeper) SetState(addr common.Address, key, value common.Hash) {
|
||||
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr))
|
||||
key = types.KeyAddressStorage(addr, key)
|
||||
|
||||
// GetLogs calls CommitStateDB.GetLogs using the passed in context
|
||||
func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) {
|
||||
return k.CommitStateDB.WithContext(ctx).GetLogs(hash)
|
||||
}
|
||||
action := "updated"
|
||||
if ethermint.IsEmptyHash(value.Hex()) {
|
||||
store.Delete(key.Bytes())
|
||||
action = "deleted"
|
||||
} else {
|
||||
store.Set(key.Bytes(), value.Bytes())
|
||||
}
|
||||
|
||||
// AllLogs calls CommitStateDB.AllLogs using the passed in context
|
||||
func (k *Keeper) AllLogs(ctx sdk.Context) []*ethtypes.Log {
|
||||
return k.CommitStateDB.WithContext(ctx).AllLogs()
|
||||
}
|
||||
|
||||
// GetRefund calls CommitStateDB.GetRefund using the passed in context
|
||||
func (k *Keeper) GetRefund(ctx sdk.Context) uint64 {
|
||||
return k.CommitStateDB.WithContext(ctx).GetRefund()
|
||||
}
|
||||
|
||||
// Preimages calls CommitStateDB.Preimages using the passed in context
|
||||
func (k *Keeper) Preimages(ctx sdk.Context) map[ethcmn.Hash][]byte {
|
||||
return k.CommitStateDB.WithContext(ctx).Preimages()
|
||||
}
|
||||
|
||||
// HasSuicided calls CommitStateDB.HasSuicided using the passed in context
|
||||
func (k *Keeper) HasSuicided(ctx sdk.Context, addr ethcmn.Address) bool {
|
||||
return k.CommitStateDB.WithContext(ctx).HasSuicided(addr)
|
||||
}
|
||||
|
||||
// StorageTrie calls CommitStateDB.StorageTrie using the passed in context
|
||||
func (k *Keeper) StorageTrie(ctx sdk.Context, addr ethcmn.Address) ethstate.Trie {
|
||||
return k.CommitStateDB.WithContext(ctx).StorageTrie(addr)
|
||||
k.Logger(k.ctx).Debug(
|
||||
fmt.Sprintf("state %s", action),
|
||||
"ethereum-address", addr.Hex(),
|
||||
"key", key.Hex(),
|
||||
)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Persistence
|
||||
// Suicide
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Commit calls CommitStateDB.Commit using the passed in context
|
||||
func (k *Keeper) Commit(ctx sdk.Context, deleteEmptyObjects bool) (root ethcmn.Hash, err error) {
|
||||
return k.CommitStateDB.WithContext(ctx).Commit(deleteEmptyObjects)
|
||||
// Suicide marks the given account as suicided and clears the account balance of
|
||||
// the EVM tokens.
|
||||
func (k *Keeper) Suicide(addr common.Address) bool {
|
||||
prev := k.HasSuicided(addr)
|
||||
if prev {
|
||||
return true
|
||||
}
|
||||
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
|
||||
_, err := k.ClearBalance(cosmosAddr)
|
||||
if err != nil {
|
||||
k.Logger(k.ctx).Error(
|
||||
"failed to subtract balance on suicide",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
"error", err,
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: (@fedekunze) do we also need to delete the storage state and the code?
|
||||
|
||||
// Set a single byte to the transient store
|
||||
store := prefix.NewStore(k.ctx.TransientStore(k.transientKey), types.KeyPrefixTransientSuicided)
|
||||
store.Set(addr.Bytes(), []byte{1})
|
||||
|
||||
k.Logger(k.ctx).Debug(
|
||||
"account suicided",
|
||||
"ethereum-address", addr.Hex(),
|
||||
"cosmos-address", cosmosAddr.String(),
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Finalise calls CommitStateDB.Finalise using the passed in context
|
||||
func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error {
|
||||
return k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects)
|
||||
// HasSuicided queries the transient store to check if the account has been marked as suicided in the
|
||||
// current block. Accounts that are suicided will be returned as non-nil during queries and "cleared"
|
||||
// after the block has been committed.
|
||||
func (k *Keeper) HasSuicided(addr common.Address) bool {
|
||||
store := prefix.NewStore(k.ctx.TransientStore(k.transientKey), types.KeyPrefixTransientSuicided)
|
||||
return store.Has(addr.Bytes())
|
||||
}
|
||||
|
||||
// IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context
|
||||
func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) error {
|
||||
_, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects)
|
||||
return err
|
||||
// ----------------------------------------------------------------------------
|
||||
// Account Exist / Empty
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Exist returns true if the given account exists in store or if it has been
|
||||
// marked as suicided in the transient store.
|
||||
func (k *Keeper) Exist(addr common.Address) bool {
|
||||
// return true if the account has suicided
|
||||
if k.HasSuicided(addr) {
|
||||
return true
|
||||
}
|
||||
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
account := k.accountKeeper.GetAccount(k.ctx, cosmosAddr)
|
||||
return account != nil
|
||||
}
|
||||
|
||||
// Empty returns true if the address meets the following conditions:
|
||||
// - nonce is 0
|
||||
// - balance amount for evm denom is 0
|
||||
// - account code hash is empty
|
||||
func (k *Keeper) Empty(addr common.Address) bool {
|
||||
nonce := uint64(0)
|
||||
codeHash := types.EmptyCodeHash
|
||||
|
||||
cosmosAddr := sdk.AccAddress(addr.Bytes())
|
||||
account := k.accountKeeper.GetAccount(k.ctx, cosmosAddr)
|
||||
|
||||
if account != nil {
|
||||
nonce = account.GetSequence()
|
||||
ethAccount, isEthAccount := account.(*ethermint.EthAccount)
|
||||
if !isEthAccount {
|
||||
// NOTE: non-ethereum accounts are considered not empty
|
||||
return false
|
||||
}
|
||||
|
||||
codeHash = ethAccount.CodeHash
|
||||
}
|
||||
|
||||
balance := k.GetBalance(addr)
|
||||
hasZeroBalance := balance.Sign() == 0
|
||||
hasEmptyCodeHash := bytes.Equal(codeHash, types.EmptyCodeHash)
|
||||
|
||||
return hasZeroBalance && nonce == 0 && hasEmptyCodeHash
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Access List
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// PrepareAccessList handles the preparatory steps for executing a state transition with
|
||||
// regards to both EIP-2929 and EIP-2930:
|
||||
//
|
||||
// - Add sender to access list (2929)
|
||||
// - Add destination to access list (2929)
|
||||
// - Add precompiles to access list (2929)
|
||||
// - Add the contents of the optional tx access list (2930)
|
||||
//
|
||||
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
|
||||
func (k *Keeper) PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses ethtypes.AccessList) {
|
||||
// NOTE: only update the access list during DeliverTx
|
||||
if k.ctx.IsCheckTx() || k.ctx.IsReCheckTx() {
|
||||
return
|
||||
}
|
||||
|
||||
k.AddAddressToAccessList(sender)
|
||||
if dest != nil {
|
||||
k.AddAddressToAccessList(*dest)
|
||||
// If it's a create-tx, the destination will be added inside evm.create
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
k.AddAddressToAccessList(addr)
|
||||
}
|
||||
for _, tuple := range txAccesses {
|
||||
k.AddAddressToAccessList(tuple.Address)
|
||||
for _, key := range tuple.StorageKeys {
|
||||
k.AddSlotToAccessList(tuple.Address, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddressInAccessList returns true if the address is registered on the access list map.
|
||||
func (k *Keeper) AddressInAccessList(addr common.Address) bool {
|
||||
return k.accessList.ContainsAddress(addr)
|
||||
}
|
||||
|
||||
func (k *Keeper) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
|
||||
return k.accessList.Contains(addr, slot)
|
||||
}
|
||||
|
||||
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
|
||||
// even if the feature/fork is not active yet
|
||||
func (k *Keeper) AddAddressToAccessList(addr common.Address) {
|
||||
// NOTE: only update the access list during DeliverTx
|
||||
if k.ctx.IsCheckTx() || k.ctx.IsReCheckTx() {
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: ignore change return bool because we don't have to keep a journal for state changes
|
||||
_ = k.accessList.AddAddress(addr)
|
||||
}
|
||||
|
||||
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
|
||||
// even if the feature/fork is not active yet
|
||||
func (k *Keeper) AddSlotToAccessList(addr common.Address, slot common.Hash) {
|
||||
// NOTE: only update the access list during DeliverTx
|
||||
if k.ctx.IsCheckTx() || k.ctx.IsReCheckTx() {
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: ignore change return booleans because we don't have to keep a journal for state changes
|
||||
_, _ = k.accessList.AddSlot(addr, slot)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Snapshotting
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Snapshot calls CommitStateDB.Snapshot using the passed in context
|
||||
func (k *Keeper) Snapshot(ctx sdk.Context) int {
|
||||
return k.CommitStateDB.WithContext(ctx).Snapshot()
|
||||
// Snapshot return zero as the state changes won't be committed if the state transition fails. So there
|
||||
// is no need to snapshot before the VM execution.
|
||||
// See Cosmos SDK docs for more info: https://docs.cosmos.network/master/core/baseapp.html#delivertx-state-updates
|
||||
func (k *Keeper) Snapshot() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// RevertToSnapshot calls CommitStateDB.RevertToSnapshot using the passed in context
|
||||
func (k *Keeper) RevertToSnapshot(ctx sdk.Context, revID int) {
|
||||
k.CommitStateDB.WithContext(ctx).RevertToSnapshot(revID)
|
||||
// RevertToSnapshot performs a no-op because when a transaction execution fails on the EVM, the state
|
||||
// won't be persisted during ABCI DeliverTx.
|
||||
func (k *Keeper) RevertToSnapshot(_ int) {}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Log
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// AddLog appends the given ethereum Log to the list of Logs associated with the transaction hash kept in the current
|
||||
// context. This function also fills in the tx hash, block hash, tx index and log index fields before setting the log
|
||||
// to store.
|
||||
func (k *Keeper) AddLog(log *ethtypes.Log) {
|
||||
txHash := common.BytesToHash(tmtypes.Tx(k.ctx.TxBytes()).Hash())
|
||||
blockHash, found := k.GetBlockHashFromHeight(k.ctx, k.ctx.BlockHeight())
|
||||
if found {
|
||||
log.BlockHash = blockHash
|
||||
}
|
||||
|
||||
log.TxHash = txHash
|
||||
log.TxIndex = uint(k.GetTxIndexTransient())
|
||||
|
||||
logs := k.GetTxLogs(txHash)
|
||||
log.Index = uint(len(logs))
|
||||
logs = append(logs, log)
|
||||
k.SetLogs(txHash, logs)
|
||||
|
||||
k.Logger(k.ctx).Debug(
|
||||
"log added",
|
||||
"tx-hash", txHash.Hex(),
|
||||
"log-index", int(log.Index),
|
||||
)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Auxiliary
|
||||
// Trie
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Database calls CommitStateDB.Database using the passed in context
|
||||
func (k *Keeper) Database(ctx sdk.Context) ethstate.Database {
|
||||
return k.CommitStateDB.WithContext(ctx).Database()
|
||||
}
|
||||
// AddPreimage performs a no-op since the EnablePreimageRecording flag is disabled
|
||||
// on the vm.Config during state transitions. No store trie preimages are written
|
||||
// to the database.
|
||||
func (k *Keeper) AddPreimage(_ common.Hash, _ []byte) {}
|
||||
|
||||
// Empty calls CommitStateDB.Empty using the passed in context
|
||||
func (k *Keeper) Empty(ctx sdk.Context, addr ethcmn.Address) bool {
|
||||
return k.CommitStateDB.WithContext(ctx).Empty(addr)
|
||||
}
|
||||
|
||||
// Exist calls CommitStateDB.Exist using the passed in context
|
||||
func (k *Keeper) Exist(ctx sdk.Context, addr ethcmn.Address) bool {
|
||||
return k.CommitStateDB.WithContext(ctx).Exist(addr)
|
||||
}
|
||||
|
||||
// Error calls CommitStateDB.Error using the passed in context
|
||||
func (k *Keeper) Error(ctx sdk.Context) error {
|
||||
return k.CommitStateDB.WithContext(ctx).Error()
|
||||
}
|
||||
|
||||
// Suicide calls CommitStateDB.Suicide using the passed in context
|
||||
func (k *Keeper) Suicide(ctx sdk.Context, addr ethcmn.Address) bool {
|
||||
return k.CommitStateDB.WithContext(ctx).Suicide(addr)
|
||||
}
|
||||
|
||||
// Reset calls CommitStateDB.Reset using the passed in context
|
||||
func (k *Keeper) Reset(ctx sdk.Context, root ethcmn.Hash) error {
|
||||
return k.CommitStateDB.WithContext(ctx).Reset(root)
|
||||
}
|
||||
|
||||
// Prepare calls CommitStateDB.Prepare using the passed in context
|
||||
func (k *Keeper) Prepare(ctx sdk.Context, thash, bhash ethcmn.Hash, txi int) {
|
||||
k.CommitStateDB.WithContext(ctx).Prepare(thash, bhash, txi)
|
||||
}
|
||||
|
||||
// CreateAccount calls CommitStateDB.CreateAccount using the passed in context
|
||||
func (k *Keeper) CreateAccount(ctx sdk.Context, addr ethcmn.Address) {
|
||||
k.CommitStateDB.WithContext(ctx).CreateAccount(addr)
|
||||
}
|
||||
|
||||
// UpdateAccounts calls CommitStateDB.UpdateAccounts using the passed in context
|
||||
func (k *Keeper) UpdateAccounts(ctx sdk.Context) {
|
||||
k.CommitStateDB.WithContext(ctx).UpdateAccounts()
|
||||
}
|
||||
|
||||
// ClearStateObjects calls CommitStateDB.ClearStateObjects using the passed in context
|
||||
func (k *Keeper) ClearStateObjects(ctx sdk.Context) {
|
||||
k.CommitStateDB.WithContext(ctx).ClearStateObjects()
|
||||
}
|
||||
|
||||
// Copy calls CommitStateDB.Copy using the passed in context
|
||||
func (k *Keeper) Copy(ctx sdk.Context) ethvm.StateDB {
|
||||
return k.CommitStateDB.WithContext(ctx).Copy()
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
// Iterator
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ForEachStorage calls CommitStateDB.ForEachStorage using passed in context
|
||||
func (k *Keeper) ForEachStorage(ctx sdk.Context, addr ethcmn.Address, cb func(key, value ethcmn.Hash) bool) error {
|
||||
return k.CommitStateDB.WithContext(ctx).ForEachStorage(addr, cb)
|
||||
}
|
||||
func (k *Keeper) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||
store := k.ctx.KVStore(k.storeKey)
|
||||
prefix := types.AddressStoragePrefix(addr)
|
||||
|
||||
// GetOrNewStateObject calls CommitStateDB.GetOrNetStateObject using the passed in context
|
||||
func (k *Keeper) GetOrNewStateObject(ctx sdk.Context, addr ethcmn.Address) types.StateObject {
|
||||
return k.CommitStateDB.WithContext(ctx).GetOrNewStateObject(addr)
|
||||
iterator := sdk.KVStorePrefixIterator(store, prefix)
|
||||
defer iterator.Close()
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
// TODO: check if the key prefix needs to be trimmed
|
||||
key := common.BytesToHash(iterator.Key())
|
||||
value := common.BytesToHash(iterator.Value())
|
||||
|
||||
// check if iteration stops
|
||||
if cb(key, value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,644 +1 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
|
||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestBloomFilter() {
|
||||
// Prepare db for logs
|
||||
tHash := ethcmn.BytesToHash([]byte{0x1})
|
||||
suite.app.EvmKeeper.Prepare(suite.ctx, tHash, ethcmn.Hash{}, 0)
|
||||
contractAddress := ethcmn.BigToAddress(big.NewInt(1))
|
||||
log := ethtypes.Log{Address: contractAddress, Topics: []ethcmn.Hash{}}
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
numLogs int
|
||||
isBloom bool
|
||||
}{
|
||||
{
|
||||
"no logs",
|
||||
func() {},
|
||||
0,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"add log",
|
||||
func() {
|
||||
suite.app.EvmKeeper.AddLog(suite.ctx, &log)
|
||||
},
|
||||
1,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"bloom",
|
||||
func() {},
|
||||
0,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, tHash)
|
||||
if !tc.isBloom {
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().Len(logs, tc.numLogs, tc.name)
|
||||
if len(logs) != 0 {
|
||||
suite.Require().Equal(log, *logs[0], tc.name)
|
||||
}
|
||||
} else {
|
||||
// get logs bloom from the log
|
||||
bloomInt := ethtypes.LogsBloom(logs)
|
||||
bloomFilter := ethtypes.BytesToBloom(bloomInt)
|
||||
suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name)
|
||||
suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_Balance() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
balance *big.Int
|
||||
}{
|
||||
{
|
||||
"set balance",
|
||||
func() {
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(100))
|
||||
},
|
||||
big.NewInt(100),
|
||||
},
|
||||
{
|
||||
"sub balance",
|
||||
func() {
|
||||
suite.app.EvmKeeper.SubBalance(suite.ctx, suite.address, big.NewInt(100))
|
||||
},
|
||||
big.NewInt(0),
|
||||
},
|
||||
{
|
||||
"add balance",
|
||||
func() {
|
||||
suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, big.NewInt(200))
|
||||
},
|
||||
big.NewInt(200),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
suite.Require().Equal(tc.balance, suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDBNonce() {
|
||||
nonce := uint64(123)
|
||||
suite.app.EvmKeeper.SetNonce(suite.ctx, suite.address, nonce)
|
||||
suite.Require().Equal(nonce, suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_Error() {
|
||||
nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, ethcmn.Address{})
|
||||
suite.Require().Equal(0, int(nonce))
|
||||
suite.Require().Error(suite.app.EvmKeeper.Error(suite.ctx))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_Database() {
|
||||
suite.Require().Nil(suite.app.EvmKeeper.Database(suite.ctx))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_State() {
|
||||
key := ethcmn.BytesToHash([]byte("foo"))
|
||||
val := ethcmn.BytesToHash([]byte("bar"))
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, key, val)
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
address ethcmn.Address
|
||||
key ethcmn.Hash
|
||||
value ethcmn.Hash
|
||||
}{
|
||||
{
|
||||
"found state",
|
||||
suite.address,
|
||||
ethcmn.BytesToHash([]byte("foo")),
|
||||
ethcmn.BytesToHash([]byte("bar")),
|
||||
},
|
||||
{
|
||||
"state not found",
|
||||
suite.address,
|
||||
ethcmn.BytesToHash([]byte("key")),
|
||||
ethcmn.Hash{},
|
||||
},
|
||||
{
|
||||
"object not found",
|
||||
ethcmn.Address{},
|
||||
ethcmn.BytesToHash([]byte("foo")),
|
||||
ethcmn.Hash{},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCase {
|
||||
value := suite.app.EvmKeeper.GetState(suite.ctx, tc.address, tc.key)
|
||||
suite.Require().Equal(tc.value, value, tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_Code() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
address ethcmn.Address
|
||||
code []byte
|
||||
malleate func()
|
||||
}{
|
||||
{
|
||||
"no stored code for state object",
|
||||
suite.address,
|
||||
nil,
|
||||
func() {},
|
||||
},
|
||||
{
|
||||
"existing address",
|
||||
suite.address,
|
||||
[]byte("code"),
|
||||
func() {
|
||||
suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, []byte("code"))
|
||||
},
|
||||
},
|
||||
{
|
||||
"state object not found",
|
||||
ethcmn.Address{},
|
||||
nil,
|
||||
func() {},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
suite.Require().Equal(tc.code, suite.app.EvmKeeper.GetCode(suite.ctx, tc.address), tc.name)
|
||||
suite.Require().Equal(len(tc.code), suite.app.EvmKeeper.GetCodeSize(suite.ctx, tc.address), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_Logs() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
log *ethtypes.Log
|
||||
}{
|
||||
{
|
||||
"state db log",
|
||||
ðtypes.Log{
|
||||
Address: suite.address,
|
||||
Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))},
|
||||
Data: []byte("data"),
|
||||
BlockNumber: 1,
|
||||
TxHash: ethcmn.Hash{},
|
||||
TxIndex: 1,
|
||||
BlockHash: ethcmn.Hash{},
|
||||
Index: 0,
|
||||
Removed: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
hash := ethcmn.BytesToHash([]byte("hash"))
|
||||
logs := []*ethtypes.Log{tc.log}
|
||||
|
||||
err := suite.app.EvmKeeper.SetLogs(suite.ctx, hash, logs)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
dbLogs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, hash)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().Equal(logs, dbLogs, tc.name)
|
||||
|
||||
suite.app.EvmKeeper.DeleteLogs(suite.ctx, hash)
|
||||
dbLogs, err = suite.app.EvmKeeper.GetLogs(suite.ctx, hash)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().Empty(dbLogs, tc.name)
|
||||
|
||||
suite.app.EvmKeeper.AddLog(suite.ctx, tc.log)
|
||||
suite.Require().Equal(logs, suite.app.EvmKeeper.AllLogs(suite.ctx), tc.name)
|
||||
|
||||
//resets state but checking to see if storekey still persists.
|
||||
err = suite.app.EvmKeeper.Reset(suite.ctx, hash)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().Equal(logs, suite.app.EvmKeeper.AllLogs(suite.ctx), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_Preimage() {
|
||||
hash := ethcmn.BytesToHash([]byte("hash"))
|
||||
preimage := []byte("preimage")
|
||||
|
||||
suite.app.EvmKeeper.AddPreimage(suite.ctx, hash, preimage)
|
||||
suite.Require().Equal(preimage, suite.app.EvmKeeper.Preimages(suite.ctx)[hash])
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_Refund() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
addAmount uint64
|
||||
subAmount uint64
|
||||
expRefund uint64
|
||||
expPanic bool
|
||||
}{
|
||||
{
|
||||
"refund 0",
|
||||
0, 0, 0,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"refund positive amount",
|
||||
100, 0, 100,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"refund panic",
|
||||
100, 200, 100,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset
|
||||
|
||||
suite.app.EvmKeeper.AddRefund(suite.ctx, tc.addAmount)
|
||||
suite.Require().Equal(tc.addAmount, suite.app.EvmKeeper.GetRefund(suite.ctx))
|
||||
|
||||
if tc.expPanic {
|
||||
suite.Panics(func() {
|
||||
suite.app.EvmKeeper.SubRefund(suite.ctx, tc.subAmount)
|
||||
})
|
||||
} else {
|
||||
suite.app.EvmKeeper.SubRefund(suite.ctx, tc.subAmount)
|
||||
suite.Require().Equal(tc.expRefund, suite.app.EvmKeeper.GetRefund(suite.ctx))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_CreateAccount() {
|
||||
prevBalance := big.NewInt(12)
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
address ethcmn.Address
|
||||
malleate func()
|
||||
}{
|
||||
{
|
||||
"existing account",
|
||||
suite.address,
|
||||
func() {
|
||||
suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, prevBalance)
|
||||
},
|
||||
},
|
||||
{
|
||||
"new account",
|
||||
ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1"),
|
||||
func() {
|
||||
prevBalance = big.NewInt(0)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset
|
||||
tc.malleate()
|
||||
|
||||
suite.app.EvmKeeper.CreateAccount(suite.ctx, tc.address)
|
||||
suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, tc.address))
|
||||
suite.Require().Equal(prevBalance, suite.app.EvmKeeper.GetBalance(suite.ctx, tc.address))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_ClearStateObj() {
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey)
|
||||
|
||||
suite.app.EvmKeeper.CreateAccount(suite.ctx, addr)
|
||||
suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, addr))
|
||||
|
||||
suite.app.EvmKeeper.ClearStateObjects(suite.ctx)
|
||||
suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, addr))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestStateDB_Reset() {
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey)
|
||||
|
||||
suite.app.EvmKeeper.CreateAccount(suite.ctx, addr)
|
||||
suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, addr))
|
||||
|
||||
err = suite.app.EvmKeeper.Reset(suite.ctx, ethcmn.BytesToHash(nil))
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, addr))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestSuiteDB_Prepare() {
|
||||
thash := ethcmn.BytesToHash([]byte("thash"))
|
||||
bhash := ethcmn.BytesToHash([]byte("bhash"))
|
||||
txi := 1
|
||||
|
||||
suite.app.EvmKeeper.Prepare(suite.ctx, thash, bhash, txi)
|
||||
|
||||
suite.Require().Equal(txi, suite.app.EvmKeeper.TxIndex(suite.ctx))
|
||||
suite.Require().Equal(bhash, suite.app.EvmKeeper.BlockHash(suite.ctx))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestSuiteDB_CopyState() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
log ethtypes.Log
|
||||
}{
|
||||
{
|
||||
"copy state",
|
||||
ethtypes.Log{
|
||||
Address: suite.address,
|
||||
Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))},
|
||||
Data: []byte("data"),
|
||||
BlockNumber: 1,
|
||||
TxHash: ethcmn.Hash{},
|
||||
TxIndex: 1,
|
||||
BlockHash: ethcmn.Hash{},
|
||||
Index: 0,
|
||||
Removed: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
hash := ethcmn.BytesToHash([]byte("hash"))
|
||||
logs := []*ethtypes.Log{&tc.log}
|
||||
|
||||
err := suite.app.EvmKeeper.SetLogs(suite.ctx, hash, logs)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
|
||||
copyDB := suite.app.EvmKeeper.Copy(suite.ctx)
|
||||
suite.Require().Equal(suite.app.EvmKeeper.Exist(suite.ctx, suite.address), copyDB.Exist(suite.address), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestSuiteDB_Empty() {
|
||||
suite.Require().True(suite.app.EvmKeeper.Empty(suite.ctx, suite.address))
|
||||
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(100))
|
||||
suite.Require().False(suite.app.EvmKeeper.Empty(suite.ctx, suite.address))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestSuiteDB_Suicide() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
amount *big.Int
|
||||
expPass bool
|
||||
delete bool
|
||||
}{
|
||||
{
|
||||
"suicide zero balance",
|
||||
big.NewInt(0),
|
||||
false, false,
|
||||
},
|
||||
{
|
||||
"suicide with balance",
|
||||
big.NewInt(100),
|
||||
true, false,
|
||||
},
|
||||
{
|
||||
"delete",
|
||||
big.NewInt(0),
|
||||
true, true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
if tc.delete {
|
||||
_, err := suite.app.EvmKeeper.Commit(suite.ctx, tc.delete)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, suite.address), tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if tc.expPass {
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, tc.amount)
|
||||
suicide := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address)
|
||||
suite.Require().True(suicide, tc.name)
|
||||
suite.Require().True(suite.app.EvmKeeper.HasSuicided(suite.ctx, suite.address), tc.name)
|
||||
} else {
|
||||
//Suicide only works for an account with non-zero balance/nonce
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey)
|
||||
suicide := suite.app.EvmKeeper.Suicide(suite.ctx, addr)
|
||||
suite.Require().False(suicide, tc.name)
|
||||
suite.Require().False(suite.app.EvmKeeper.HasSuicided(suite.ctx, addr), tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestCommitStateDB_Commit() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
deleteObjs bool
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"commit suicided",
|
||||
func() {
|
||||
ok := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address)
|
||||
suite.Require().True(ok)
|
||||
},
|
||||
true, true,
|
||||
},
|
||||
{
|
||||
"commit with dirty value",
|
||||
func() {
|
||||
suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, []byte("code"))
|
||||
},
|
||||
false, true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
hash, err := suite.app.EvmKeeper.Commit(suite.ctx, tc.deleteObjs)
|
||||
suite.Require().Equal(ethcmn.Hash{}, hash)
|
||||
|
||||
if !tc.expPass {
|
||||
suite.Require().Error(err, tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
suite.Require().NoError(err, tc.name)
|
||||
acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes()))
|
||||
|
||||
if tc.deleteObjs {
|
||||
suite.Require().Nil(acc, tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
suite.Require().NotNil(acc, tc.name)
|
||||
ethAcc, ok := acc.(*ethermint.EthAccount)
|
||||
suite.Require().True(ok)
|
||||
suite.Require().Equal(ethcrypto.Keccak256([]byte("code")), ethAcc.CodeHash)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestCommitStateDB_Finalize() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
deleteObjs bool
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"finalize suicided",
|
||||
func() {
|
||||
ok := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address)
|
||||
suite.Require().True(ok)
|
||||
},
|
||||
true, true,
|
||||
},
|
||||
{
|
||||
"finalize, not suicided",
|
||||
func() {
|
||||
suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, big.NewInt(5))
|
||||
},
|
||||
false, true,
|
||||
},
|
||||
{
|
||||
"finalize, dirty storage",
|
||||
func() {
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value")))
|
||||
},
|
||||
false, true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
err := suite.app.EvmKeeper.Finalise(suite.ctx, tc.deleteObjs)
|
||||
|
||||
if !tc.expPass {
|
||||
suite.Require().Error(err, tc.name)
|
||||
hash := suite.app.EvmKeeper.GetCommittedState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("key")))
|
||||
suite.Require().NotEqual(ethcmn.Hash{}, hash, tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
suite.Require().NoError(err, tc.name)
|
||||
acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes()))
|
||||
|
||||
if tc.deleteObjs {
|
||||
suite.Require().Nil(acc, tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
suite.Require().NotNil(acc, tc.name)
|
||||
}
|
||||
}
|
||||
func (suite *KeeperTestSuite) TestCommitStateDB_GetCommittedState() {
|
||||
hash := suite.app.EvmKeeper.GetCommittedState(suite.ctx, ethcmn.Address{}, ethcmn.BytesToHash([]byte("key")))
|
||||
suite.Require().Equal(ethcmn.Hash{}, hash)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestCommitStateDB_Snapshot() {
|
||||
id := suite.app.EvmKeeper.Snapshot(suite.ctx)
|
||||
suite.Require().NotPanics(func() {
|
||||
suite.app.EvmKeeper.RevertToSnapshot(suite.ctx, id)
|
||||
})
|
||||
|
||||
suite.Require().Panics(func() {
|
||||
suite.app.EvmKeeper.RevertToSnapshot(suite.ctx, -1)
|
||||
}, "invalid revision should panic")
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestCommitStateDB_ForEachStorage() {
|
||||
var storage types.Storage
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
callback func(key, value ethcmn.Hash) (stop bool)
|
||||
expValues []string
|
||||
}{
|
||||
{
|
||||
"aggregate state",
|
||||
func() {
|
||||
for i := 0; i < 5; i++ {
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte(fmt.Sprintf("key%d", i))), ethcmn.BytesToHash([]byte(fmt.Sprintf("value%d", i))))
|
||||
}
|
||||
},
|
||||
func(key, value ethcmn.Hash) bool {
|
||||
storage = append(storage, types.NewState(key, value))
|
||||
return false
|
||||
},
|
||||
[]string{
|
||||
ethcmn.BytesToHash([]byte("value0")).String(),
|
||||
ethcmn.BytesToHash([]byte("value1")).String(),
|
||||
ethcmn.BytesToHash([]byte("value2")).String(),
|
||||
ethcmn.BytesToHash([]byte("value3")).String(),
|
||||
ethcmn.BytesToHash([]byte("value4")).String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter state",
|
||||
func() {
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value")))
|
||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("filterkey")), ethcmn.BytesToHash([]byte("filtervalue")))
|
||||
},
|
||||
func(key, value ethcmn.Hash) bool {
|
||||
if value == ethcmn.BytesToHash([]byte("filtervalue")) {
|
||||
storage = append(storage, types.NewState(key, value))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
[]string{
|
||||
ethcmn.BytesToHash([]byte("filtervalue")).String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset
|
||||
tc.malleate()
|
||||
suite.app.EvmKeeper.Finalise(suite.ctx, false)
|
||||
|
||||
err := suite.app.EvmKeeper.ForEachStorage(suite.ctx, suite.address, tc.callback)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(len(tc.expValues), len(storage), fmt.Sprintf("Expected values:\n%v\nStorage Values\n%v", tc.expValues, storage))
|
||||
|
||||
vals := make([]string, len(storage))
|
||||
for i := range storage {
|
||||
vals[i] = storage[i].Value
|
||||
}
|
||||
|
||||
suite.Require().ElementsMatch(tc.expValues, vals)
|
||||
})
|
||||
storage = types.Storage{}
|
||||
}
|
||||
}
|
||||
|
@ -6,22 +6,22 @@ import (
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// accessList is copied from go-ethereum
|
||||
// AccessListMappings is copied from go-ethereum
|
||||
// https://github.com/ethereum/go-ethereum/blob/cf856ea1ad96ac39ea477087822479b63417036a/core/state/access_list.go#L23
|
||||
type accessList struct {
|
||||
type AccessListMappings struct {
|
||||
addresses map[common.Address]int
|
||||
slots []map[common.Hash]struct{}
|
||||
}
|
||||
|
||||
// ContainsAddress returns true if the address is in the access list.
|
||||
func (al *accessList) ContainsAddress(address common.Address) bool {
|
||||
func (al *AccessListMappings) ContainsAddress(address common.Address) bool {
|
||||
_, ok := al.addresses[address]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Contains checks if a slot within an account is present in the access list, returning
|
||||
// separate flags for the presence of the account and the slot respectively.
|
||||
func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||
func (al *AccessListMappings) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
|
||||
idx, ok := al.addresses[address]
|
||||
if !ok {
|
||||
// no such address (and hence zero slots)
|
||||
@ -41,16 +41,16 @@ func (al *accessList) Contains(address common.Address, slot common.Hash) (addres
|
||||
return true, slotPresent
|
||||
}
|
||||
|
||||
// newAccessList creates a new accessList.
|
||||
func newAccessList() *accessList {
|
||||
return &accessList{
|
||||
// newAccessList creates a new AccessListMappings.
|
||||
func NewAccessListMappings() *AccessListMappings {
|
||||
return &AccessListMappings{
|
||||
addresses: make(map[common.Address]int),
|
||||
}
|
||||
}
|
||||
|
||||
// Copy creates an independent copy of an accessList.
|
||||
func (al *accessList) Copy() *accessList {
|
||||
cp := newAccessList()
|
||||
// Copy creates an independent copy of an AccessListMappings.
|
||||
func (al *AccessListMappings) Copy() *AccessListMappings {
|
||||
cp := NewAccessListMappings()
|
||||
for k, v := range al.addresses {
|
||||
cp.addresses[k] = v
|
||||
}
|
||||
@ -67,7 +67,7 @@ func (al *accessList) Copy() *accessList {
|
||||
|
||||
// AddAddress adds an address to the access list, and returns 'true' if the operation
|
||||
// caused a change (addr was not previously in the list).
|
||||
func (al *accessList) AddAddress(address common.Address) bool {
|
||||
func (al *AccessListMappings) AddAddress(address common.Address) bool {
|
||||
if _, present := al.addresses[address]; present {
|
||||
return false
|
||||
}
|
||||
@ -80,7 +80,7 @@ func (al *accessList) AddAddress(address common.Address) bool {
|
||||
// - address added
|
||||
// - slot added
|
||||
// For any 'true' value returned, a corresponding journal entry must be made.
|
||||
func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) {
|
||||
func (al *AccessListMappings) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) {
|
||||
idx, addrPresent := al.addresses[address]
|
||||
if !addrPresent || idx == -1 {
|
||||
// Address not present, or addr present but no slots there
|
||||
@ -99,7 +99,7 @@ func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrCha
|
||||
slotmap := al.slots[idx]
|
||||
if _, ok := slotmap[slot]; !ok {
|
||||
slotmap[slot] = struct{}{}
|
||||
// Journal add slot change
|
||||
// journal add slot change
|
||||
return false, true
|
||||
}
|
||||
// No changes required
|
||||
@ -110,7 +110,7 @@ func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrCha
|
||||
// This operation needs to be performed in the same order as the addition happened.
|
||||
// This method is meant to be used by the journal, which maintains ordering of
|
||||
// operations.
|
||||
func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
|
||||
func (al *AccessListMappings) DeleteSlot(address common.Address, slot common.Hash) {
|
||||
idx, addrOk := al.addresses[address]
|
||||
// There are two ways this can fail
|
||||
if !addrOk {
|
||||
@ -131,7 +131,7 @@ func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) {
|
||||
// needs to be performed in the same order as the addition happened.
|
||||
// This method is meant to be used by the journal, which maintains ordering of
|
||||
// operations.
|
||||
func (al *accessList) DeleteAddress(address common.Address) {
|
||||
func (al *AccessListMappings) DeleteAddress(address common.Address) {
|
||||
delete(al.addresses, address)
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ func NewAccessList(ethAccessList *ethtypes.AccessList) AccessList {
|
||||
return nil
|
||||
}
|
||||
|
||||
var accessList AccessList
|
||||
var AccessListMappings AccessList
|
||||
for _, tuple := range *ethAccessList {
|
||||
storageKeys := make([]string, len(tuple.StorageKeys))
|
||||
|
||||
@ -154,19 +154,19 @@ func NewAccessList(ethAccessList *ethtypes.AccessList) AccessList {
|
||||
storageKeys[i] = tuple.StorageKeys[i].String()
|
||||
}
|
||||
|
||||
accessList = append(accessList, AccessTuple{
|
||||
AccessListMappings = append(AccessListMappings, AccessTuple{
|
||||
Address: tuple.Address.String(),
|
||||
StorageKeys: storageKeys,
|
||||
})
|
||||
}
|
||||
|
||||
return accessList
|
||||
return AccessListMappings
|
||||
}
|
||||
|
||||
// ToEthAccessList is an utility function to convert the protobuf compatible
|
||||
// AccessList to eth core AccessList from go-ethereum
|
||||
func (al AccessList) ToEthAccessList() *ethtypes.AccessList {
|
||||
var accessList ethtypes.AccessList
|
||||
var AccessListMappings ethtypes.AccessList
|
||||
|
||||
for _, tuple := range al {
|
||||
storageKeys := make([]ethcmn.Hash, len(tuple.StorageKeys))
|
||||
@ -175,11 +175,11 @@ func (al AccessList) ToEthAccessList() *ethtypes.AccessList {
|
||||
storageKeys[i] = ethcmn.HexToHash(tuple.StorageKeys[i])
|
||||
}
|
||||
|
||||
accessList = append(accessList, ethtypes.AccessTuple{
|
||||
AccessListMappings = append(AccessListMappings, ethtypes.AccessTuple{
|
||||
Address: ethcmn.HexToAddress(tuple.Address),
|
||||
StorageKeys: storageKeys,
|
||||
})
|
||||
}
|
||||
|
||||
return &accessList
|
||||
return &AccessListMappings
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ type AccessListTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
address ethcmn.Address
|
||||
accessList *accessList
|
||||
accessList *AccessListMappings
|
||||
}
|
||||
|
||||
func (suite *AccessListTestSuite) SetupTest() {
|
||||
@ -23,7 +23,7 @@ func (suite *AccessListTestSuite) SetupTest() {
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes())
|
||||
suite.accessList = newAccessList()
|
||||
suite.accessList = NewAccessListMappings()
|
||||
suite.accessList.addresses[suite.address] = 1
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ func (suite *AccessListTestSuite) TestContains() {
|
||||
}
|
||||
|
||||
func (suite *AccessListTestSuite) TestCopy() {
|
||||
expAccessList := newAccessList()
|
||||
expAccessList := NewAccessListMappings()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@ -96,7 +96,7 @@ func (suite *AccessListTestSuite) TestCopy() {
|
||||
}},
|
||||
{
|
||||
"single address", func() {
|
||||
expAccessList = newAccessList()
|
||||
expAccessList = NewAccessListMappings()
|
||||
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 0)
|
||||
expAccessList.addresses[suite.address] = -1
|
||||
},
|
||||
@ -104,7 +104,7 @@ func (suite *AccessListTestSuite) TestCopy() {
|
||||
{
|
||||
"single address, single slot",
|
||||
func() {
|
||||
expAccessList = newAccessList()
|
||||
expAccessList = NewAccessListMappings()
|
||||
expAccessList.addresses[suite.address] = 0
|
||||
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
|
||||
expAccessList.slots[0] = make(map[ethcmn.Hash]struct{})
|
||||
@ -114,7 +114,7 @@ func (suite *AccessListTestSuite) TestCopy() {
|
||||
{
|
||||
"multiple addresses, single slot each",
|
||||
func() {
|
||||
expAccessList = newAccessList()
|
||||
expAccessList = NewAccessListMappings()
|
||||
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i
|
||||
@ -126,7 +126,7 @@ func (suite *AccessListTestSuite) TestCopy() {
|
||||
{
|
||||
"multiple addresses, multiple slots each",
|
||||
func() {
|
||||
expAccessList = newAccessList()
|
||||
expAccessList = NewAccessListMappings()
|
||||
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i
|
||||
|
@ -10,6 +10,7 @@ type AccountKeeper interface {
|
||||
NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
|
||||
GetAllAccounts(ctx sdk.Context) (accounts []authtypes.AccountI)
|
||||
IterateAccounts(ctx sdk.Context, cb func(account authtypes.AccountI) bool)
|
||||
GetSequence(sdk.Context, sdk.AccAddress) (uint64, error)
|
||||
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
|
||||
SetAccount(ctx sdk.Context, account authtypes.AccountI)
|
||||
RemoveAccount(ctx sdk.Context, account authtypes.AccountI)
|
||||
@ -18,5 +19,7 @@ type AccountKeeper interface {
|
||||
// BankKeeper defines the expected interface needed to retrieve account balances.
|
||||
type BankKeeper interface {
|
||||
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
|
||||
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error
|
||||
SubtractCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error
|
||||
SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -15,21 +16,51 @@ const (
|
||||
// The EVM module should use a prefix store.
|
||||
StoreKey = ModuleName
|
||||
|
||||
// Transient Key is the key to access the EVM transient store, that is reset
|
||||
// during the Commit phase.
|
||||
TransientKey = "transient_" + ModuleName
|
||||
|
||||
// RouterKey uses module name for routing
|
||||
RouterKey = ModuleName
|
||||
)
|
||||
|
||||
const (
|
||||
prefixBlockHash = iota + 1
|
||||
prefixBloom
|
||||
prefixLogs
|
||||
prefixCode
|
||||
prefixStorage
|
||||
prefixChainConfig
|
||||
prefixBlockHeightHash
|
||||
prefixHashTxReceipt
|
||||
prefixBlockHeightTxs
|
||||
)
|
||||
|
||||
const (
|
||||
prefixTransientSuicided = iota + 1
|
||||
prefixTransientBloom
|
||||
prefixTransientTxIndex
|
||||
prefixTransientRefund
|
||||
)
|
||||
|
||||
// KVStore key prefixes
|
||||
var (
|
||||
KeyPrefixBlockHash = []byte{0x01}
|
||||
KeyPrefixBloom = []byte{0x02}
|
||||
KeyPrefixLogs = []byte{0x03}
|
||||
KeyPrefixCode = []byte{0x04}
|
||||
KeyPrefixStorage = []byte{0x05}
|
||||
KeyPrefixChainConfig = []byte{0x06}
|
||||
KeyPrefixBlockHeightHash = []byte{0x07}
|
||||
KeyPrefixHashTxReceipt = []byte{0x08}
|
||||
KeyPrefixBlockHeightTxs = []byte{0x09}
|
||||
KeyPrefixBlockHash = []byte{prefixBlockHash}
|
||||
KeyPrefixBloom = []byte{prefixBloom}
|
||||
KeyPrefixLogs = []byte{prefixLogs}
|
||||
KeyPrefixCode = []byte{prefixCode}
|
||||
KeyPrefixStorage = []byte{prefixStorage}
|
||||
KeyPrefixChainConfig = []byte{prefixChainConfig}
|
||||
KeyPrefixBlockHeightHash = []byte{prefixBlockHeightHash}
|
||||
KeyPrefixHashTxReceipt = []byte{prefixHashTxReceipt}
|
||||
KeyPrefixBlockHeightTxs = []byte{prefixBlockHeightTxs}
|
||||
)
|
||||
|
||||
var (
|
||||
KeyPrefixTransientSuicided = []byte{prefixTransientSuicided}
|
||||
KeyPrefixTransientBloom = []byte{prefixTransientBloom}
|
||||
KeyPrefixTransientTxIndex = []byte{prefixTransientTxIndex}
|
||||
KeyPrefixTransientRefund = []byte{prefixTransientRefund}
|
||||
)
|
||||
|
||||
// BloomKey defines the store key for a block Bloom
|
||||
@ -69,3 +100,17 @@ func KeyBlockHeightTxs(height uint64) []byte {
|
||||
heightBytes := sdk.Uint64ToBigEndian(height)
|
||||
return append(KeyPrefixBlockHeightTxs, heightBytes...)
|
||||
}
|
||||
|
||||
// KeyAddressStorage returns the key hash to access a given account state. The composite key
|
||||
// (address + hash) is hashed using Keccak256.
|
||||
func KeyAddressStorage(address ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash {
|
||||
prefix := address.Bytes()
|
||||
key := hash.Bytes()
|
||||
|
||||
compositeKey := make([]byte, len(prefix)+len(key))
|
||||
|
||||
copy(compositeKey, prefix)
|
||||
copy(compositeKey[len(prefix):], key)
|
||||
|
||||
return ethcrypto.Keccak256Hash(compositeKey)
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
var (
|
||||
_ StateObject = (*stateObject)(nil)
|
||||
|
||||
emptyCodeHash = ethcrypto.Keccak256(nil)
|
||||
EmptyCodeHash = ethcrypto.Keccak256(nil)
|
||||
)
|
||||
|
||||
// StateObject interface for interacting with state object
|
||||
@ -90,7 +90,7 @@ func newStateObject(db *CommitStateDB, accProto authtypes.AccountI, balance sdk.
|
||||
|
||||
// set empty code hash
|
||||
if ethAccount.CodeHash == nil {
|
||||
ethAccount.CodeHash = emptyCodeHash
|
||||
ethAccount.CodeHash = EmptyCodeHash
|
||||
}
|
||||
|
||||
return &stateObject{
|
||||
@ -307,7 +307,7 @@ func (so *stateObject) Balance() *big.Int {
|
||||
// CodeHash returns the state object's code hash.
|
||||
func (so *stateObject) CodeHash() []byte {
|
||||
if so.account == nil || len(so.account.CodeHash) == 0 {
|
||||
return emptyCodeHash
|
||||
return EmptyCodeHash
|
||||
}
|
||||
return so.account.CodeHash
|
||||
}
|
||||
@ -326,7 +326,7 @@ func (so *stateObject) Code(_ ethstate.Database) []byte {
|
||||
return so.code
|
||||
}
|
||||
|
||||
if bytes.Equal(so.CodeHash(), emptyCodeHash) {
|
||||
if bytes.Equal(so.CodeHash(), EmptyCodeHash) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -416,7 +416,7 @@ func (so *stateObject) empty() bool {
|
||||
(so.account != nil &&
|
||||
so.account.Sequence == 0 &&
|
||||
(so.balance.BigInt() == nil || so.balance.IsZero()) &&
|
||||
bytes.Equal(so.account.CodeHash, emptyCodeHash))
|
||||
bytes.Equal(so.account.CodeHash, EmptyCodeHash))
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
|
@ -98,7 +98,8 @@ func (st *StateTransition) newEVM(
|
||||
}
|
||||
|
||||
vmConfig := vm.Config{
|
||||
ExtraEips: eips,
|
||||
EnablePreimageRecording: false, // no need for StateDB.AddPreimage
|
||||
ExtraEips: eips,
|
||||
}
|
||||
|
||||
if st.Debug {
|
||||
|
@ -179,8 +179,8 @@ func (suite *StateDBTestSuite) TestTransitionDb() {
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err, tc.name)
|
||||
fromBalance := suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address)
|
||||
toBalance := suite.app.EvmKeeper.GetBalance(suite.ctx, recipient)
|
||||
fromBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(suite.address)
|
||||
toBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(recipient)
|
||||
suite.Require().Equal(fromBalance, big.NewInt(4950), tc.name)
|
||||
suite.Require().Equal(toBalance, big.NewInt(50), tc.name)
|
||||
} else {
|
||||
|
@ -72,14 +72,14 @@ type CommitStateDB struct {
|
||||
// by StateDB.Commit.
|
||||
dbErr error
|
||||
|
||||
// Journal of state modifications. This is the backbone of
|
||||
// journal of state modifications. This is the backbone of
|
||||
// Snapshot and RevertToSnapshot.
|
||||
journal *journal
|
||||
validRevisions []revision
|
||||
nextRevisionID int
|
||||
|
||||
// Per-transaction access list
|
||||
accessList *accessList
|
||||
accessList *AccessListMappings
|
||||
|
||||
// mutex for state deep copying
|
||||
lock sync.Mutex
|
||||
@ -106,7 +106,7 @@ func NewCommitStateDB(
|
||||
preimages: []preimageEntry{},
|
||||
hashToPreimageIndex: make(map[ethcmn.Hash]int),
|
||||
journal: newJournal(),
|
||||
accessList: newAccessList(),
|
||||
accessList: NewAccessListMappings(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -727,7 +727,7 @@ func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error {
|
||||
csdb.logSize = 0
|
||||
csdb.preimages = []preimageEntry{}
|
||||
csdb.hashToPreimageIndex = make(map[ethcmn.Hash]int)
|
||||
csdb.accessList = newAccessList()
|
||||
csdb.accessList = NewAccessListMappings()
|
||||
|
||||
csdb.clearJournalAndRefund()
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user