forked from cerc-io/laconicd-deprecated
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
|
// handle as *evmtypes.MsgEthereumTx
|
||||||
|
|
||||||
anteHandler = sdk.ChainAnteDecorators(
|
anteHandler = sdk.ChainAnteDecorators(
|
||||||
NewEthSetupContextDecorator(), // outermost AnteDecorator. EthSetUpContext must be called first
|
NewEthSetupContextDecorator(evmKeeper), // outermost AnteDecorator. EthSetUpContext must be called first
|
||||||
NewEthMempoolFeeDecorator(evmKeeper),
|
NewEthMempoolFeeDecorator(evmKeeper),
|
||||||
NewEthValidateBasicDecorator(),
|
NewEthValidateBasicDecorator(),
|
||||||
authante.TxTimeoutHeightDecorator{},
|
authante.TxTimeoutHeightDecorator{},
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
type EVMKeeper interface {
|
type EVMKeeper interface {
|
||||||
GetParams(ctx sdk.Context) evmtypes.Params
|
GetParams(ctx sdk.Context) evmtypes.Params
|
||||||
GetChainConfig(ctx sdk.Context) (evmtypes.ChainConfig, bool)
|
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
|
// EthSetupContextDecorator sets the infinite GasMeter in the Context and wraps
|
||||||
@ -30,11 +32,15 @@ type EVMKeeper interface {
|
|||||||
// on gas provided and gas used.
|
// on gas provided and gas used.
|
||||||
// CONTRACT: Must be first decorator in the chain
|
// CONTRACT: Must be first decorator in the chain
|
||||||
// CONTRACT: Tx must implement GasTx interface
|
// CONTRACT: Tx must implement GasTx interface
|
||||||
type EthSetupContextDecorator struct{}
|
type EthSetupContextDecorator struct {
|
||||||
|
evmKeeper EVMKeeper
|
||||||
|
}
|
||||||
|
|
||||||
// NewEthSetupContextDecorator creates a new EthSetupContextDecorator
|
// NewEthSetupContextDecorator creates a new EthSetupContextDecorator
|
||||||
func NewEthSetupContextDecorator() EthSetupContextDecorator {
|
func NewEthSetupContextDecorator(ek EVMKeeper) EthSetupContextDecorator {
|
||||||
return EthSetupContextDecorator{}
|
return EthSetupContextDecorator{
|
||||||
|
evmKeeper: ek,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnteHandle sets the infinite gas meter to done to ignore costs in AnteHandler checks.
|
// 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) {
|
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())
|
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||||
|
|
||||||
|
// reset the refund gas value for the current transaction
|
||||||
|
escd.evmKeeper.ResetRefundTransient(ctx)
|
||||||
|
|
||||||
// all transactions must implement GasTx
|
// all transactions must implement GasTx
|
||||||
gasTx, ok := tx.(authante.GasTx)
|
gasTx, ok := tx.(authante.GasTx)
|
||||||
if !ok {
|
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
|
// Set gas meter after ante handler to ignore gaskv costs
|
||||||
newCtx = authante.SetGasMeter(simulate, ctx, gasLimit)
|
newCtx = authante.SetGasMeter(simulate, ctx, gasLimit)
|
||||||
|
egcd.evmKeeper.WithContext(newCtx)
|
||||||
|
|
||||||
return next(newCtx, tx, simulate)
|
return next(newCtx, tx, simulate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +252,8 @@ func NewEthermintApp(
|
|||||||
evmtypes.StoreKey,
|
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)
|
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
|
||||||
|
|
||||||
app := &EthermintApp{
|
app := &EthermintApp{
|
||||||
@ -310,7 +311,7 @@ func NewEthermintApp(
|
|||||||
|
|
||||||
// Create Ethermint keepers
|
// Create Ethermint keepers
|
||||||
app.EvmKeeper = evmkeeper.NewKeeper(
|
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
|
// Create IBC Keeper
|
||||||
|
@ -105,6 +105,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak authkeeper.
|
|||||||
genBlock := ethcore.DefaultGenesisBlock()
|
genBlock := ethcore.DefaultGenesisBlock()
|
||||||
ms := cms.CacheMultiStore()
|
ms := cms.CacheMultiStore()
|
||||||
ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger)
|
ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger)
|
||||||
|
evmKeeper.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
// Set the default Ethermint parameters to the parameter keeper store
|
// Set the default Ethermint parameters to the parameter keeper store
|
||||||
evmKeeper.SetParams(ctx, evmtypes.DefaultParams())
|
evmKeeper.SetParams(ctx, evmtypes.DefaultParams())
|
||||||
@ -123,23 +124,23 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak authkeeper.
|
|||||||
addr := ethcmn.HexToAddress(addrStr)
|
addr := ethcmn.HexToAddress(addrStr)
|
||||||
acc := genBlock.Alloc[addr]
|
acc := genBlock.Alloc[addr]
|
||||||
|
|
||||||
evmKeeper.AddBalance(ctx, addr, acc.Balance)
|
evmKeeper.CommitStateDB.AddBalance(addr, acc.Balance)
|
||||||
evmKeeper.SetCode(ctx, addr, acc.Code)
|
evmKeeper.CommitStateDB.SetCode(addr, acc.Code)
|
||||||
evmKeeper.SetNonce(ctx, addr, acc.Nonce)
|
evmKeeper.CommitStateDB.SetNonce(addr, acc.Nonce)
|
||||||
|
|
||||||
for key, value := range acc.Storage {
|
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
|
// 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())
|
require.Equal(t, "200000000000000000000", b.String())
|
||||||
|
|
||||||
// commit the stateDB with 'false' to delete empty objects
|
// commit the stateDB with 'false' to delete empty objects
|
||||||
//
|
//
|
||||||
// NOTE: Commit does not yet return the intra merkle root (version)
|
// 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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// persist multi-store cache state
|
// persist multi-store cache state
|
||||||
@ -257,13 +258,14 @@ func TestImportBlocks(t *testing.T) {
|
|||||||
ms := cms.CacheMultiStore()
|
ms := cms.CacheMultiStore()
|
||||||
ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger)
|
ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger)
|
||||||
ctx = ctx.WithBlockHeight(int64(block.NumberU64()))
|
ctx = ctx.WithBlockHeight(int64(block.NumberU64()))
|
||||||
|
evmKeeper.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
|
if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||||
applyDAOHardFork(evmKeeper)
|
applyDAOHardFork(evmKeeper)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tx := range block.Transactions() {
|
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())
|
// evmKeeper.CommitStateDB.Set(block.Hash())
|
||||||
|
|
||||||
receipt, gas, err := applyTransaction(
|
receipt, gas, err := applyTransaction(
|
||||||
|
@ -21,6 +21,8 @@ func InitGenesis(
|
|||||||
bankKeeper types.BankKeeper,
|
bankKeeper types.BankKeeper,
|
||||||
data types.GenesisState,
|
data types.GenesisState,
|
||||||
) []abci.ValidatorUpdate {
|
) []abci.ValidatorUpdate {
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
k.SetParams(ctx, data.Params)
|
k.SetParams(ctx, data.Params)
|
||||||
evmDenom := data.Params.EvmDenom
|
evmDenom := data.Params.EvmDenom
|
||||||
|
|
||||||
@ -43,18 +45,18 @@ func InitGenesis(
|
|||||||
}
|
}
|
||||||
|
|
||||||
evmBalance := bankKeeper.GetBalance(ctx, accAddress, evmDenom)
|
evmBalance := bankKeeper.GetBalance(ctx, accAddress, evmDenom)
|
||||||
k.SetBalance(ctx, address, evmBalance.Amount.BigInt())
|
k.CommitStateDB.SetBalance(address, evmBalance.Amount.BigInt())
|
||||||
k.SetNonce(ctx, address, acc.GetSequence())
|
k.CommitStateDB.SetNonce(address, acc.GetSequence())
|
||||||
k.SetCode(ctx, address, ethcmn.Hex2Bytes(account.Code))
|
k.CommitStateDB.SetCode(address, ethcmn.Hex2Bytes(account.Code))
|
||||||
|
|
||||||
for _, storage := range account.Storage {
|
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
|
var err error
|
||||||
for _, txLog := range data.TxsLogs {
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -63,14 +65,14 @@ func InitGenesis(
|
|||||||
k.SetChainConfig(ctx, data.ChainConfig)
|
k.SetChainConfig(ctx, data.ChainConfig)
|
||||||
|
|
||||||
// set state objects and code to store
|
// set state objects and code to store
|
||||||
_, err = k.Commit(ctx, false)
|
_, err = k.CommitStateDB.Commit(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set storage to store
|
// set storage to store
|
||||||
// NOTE: don't delete empty object to prevent import-export simulation failure
|
// 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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -80,6 +82,8 @@ func InitGenesis(
|
|||||||
|
|
||||||
// ExportGenesis exports genesis state of the EVM module
|
// ExportGenesis exports genesis state of the EVM module
|
||||||
func ExportGenesis(ctx sdk.Context, k keeper.Keeper, ak types.AccountKeeper) *types.GenesisState {
|
func ExportGenesis(ctx sdk.Context, k keeper.Keeper, ak types.AccountKeeper) *types.GenesisState {
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
// nolint: prealloc
|
// nolint: prealloc
|
||||||
var ethGenAccounts []types.GenesisAccount
|
var ethGenAccounts []types.GenesisAccount
|
||||||
ak.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
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{
|
genAccount := types.GenesisAccount{
|
||||||
Address: addr.String(),
|
Address: addr.String(),
|
||||||
Code: ethcmn.Bytes2Hex(k.GetCode(ctx, addr)),
|
Code: ethcmn.Bytes2Hex(k.CommitStateDB.GetCode(addr)),
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ func (suite *EvmTestSuite) SetupTest() {
|
|||||||
|
|
||||||
suite.app = app.Setup(checkTx)
|
suite.app = app.Setup(checkTx)
|
||||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-888", Time: time.Now().UTC()})
|
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.handler = evm.NewHandler(suite.app.EvmKeeper)
|
||||||
suite.codec = suite.app.AppCodec()
|
suite.codec = suite.app.AppCodec()
|
||||||
suite.chainID = suite.chainID
|
suite.chainID = suite.chainID
|
||||||
@ -80,7 +81,7 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
|
|||||||
{
|
{
|
||||||
"passed",
|
"passed",
|
||||||
func() {
|
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)
|
to := ethcmn.BytesToAddress(suite.to)
|
||||||
tx = types.NewMsgEthereumTx(suite.chainID, 0, &to, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
|
tx = types.NewMsgEthereumTx(suite.chainID, 0, &to, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
|
||||||
tx.From = suite.from.String()
|
tx.From = suite.from.String()
|
||||||
@ -193,10 +194,10 @@ func (suite *EvmTestSuite) TestHandlerLogs() {
|
|||||||
suite.Require().Equal(len(txResponse.TxLogs.Logs[0].Topics), 2)
|
suite.Require().Equal(len(txResponse.TxLogs.Logs[0].Topics), 2)
|
||||||
|
|
||||||
hash := []byte{1}
|
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)
|
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().NoError(err, "failed to get logs")
|
||||||
|
|
||||||
suite.Require().Equal(logs, txResponse.TxLogs.Logs)
|
suite.Require().Equal(logs, txResponse.TxLogs.Logs)
|
||||||
@ -227,7 +228,7 @@ func (suite *EvmTestSuite) TestQueryTxLogs() {
|
|||||||
// get logs by tx hash
|
// get logs by tx hash
|
||||||
hash := txResponse.TxLogs.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().NoError(err, "failed to get logs")
|
||||||
|
|
||||||
suite.Require().Equal(logs, txResponse.TxLogs.EthLogs())
|
suite.Require().Equal(logs, txResponse.TxLogs.EthLogs())
|
||||||
@ -345,7 +346,7 @@ func (suite *EvmTestSuite) TestSendTransaction() {
|
|||||||
gasLimit := uint64(21000)
|
gasLimit := uint64(21000)
|
||||||
gasPrice := big.NewInt(0x55ae82600)
|
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
|
// send simple value transfer with gasLimit=21000
|
||||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, ðcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil, nil)
|
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
|
// special setter for csdb
|
||||||
k.SetHeightHash(ctx, uint64(req.Header.Height), common.BytesToHash(req.Hash))
|
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
|
// 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
|
// Gas costs are handled within msg handler so costs should be ignored
|
||||||
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
// Update account balances before committing other parts of state
|
// 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
|
// Commit state objects to KV store
|
||||||
if err != nil {
|
if err != nil {
|
||||||
k.Logger(ctx).Error("failed to commit state objects", "error", err, "height", ctx.BlockHeight())
|
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
|
// 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)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the block bloom filter bytes to store
|
// get the block bloom bytes from the transient store and set it to the persistent storage
|
||||||
bloom := ethtypes.BytesToBloom(k.Bloom.Bytes())
|
bloomBig, found := k.GetBlockBloomTransient()
|
||||||
|
if !found {
|
||||||
|
bloomBig = big.NewInt(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
bloom := ethtypes.BytesToBloom(bloomBig.Bytes())
|
||||||
k.SetBlockBloom(ctx, req.Height, bloom)
|
k.SetBlockBloom(ctx, req.Height, bloom)
|
||||||
|
|
||||||
return []abci.ValidatorUpdate{}
|
return []abci.ValidatorUpdate{}
|
||||||
|
@ -32,7 +32,9 @@ func (k Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := sdk.UnwrapSDKContext(c)
|
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())
|
balance, err := ethermint.MarshalBigInt(so.Balance())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -57,6 +59,7 @@ func (k Keeper) CosmosAccount(c context.Context, req *types.QueryCosmosAccountRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := sdk.UnwrapSDKContext(c)
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
ethAddr := ethcmn.HexToAddress(req.Address)
|
ethAddr := ethcmn.HexToAddress(req.Address)
|
||||||
cosmosAddr := sdk.AccAddress(ethAddr.Bytes())
|
cosmosAddr := sdk.AccAddress(ethAddr.Bytes())
|
||||||
@ -88,8 +91,9 @@ func (k Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := sdk.UnwrapSDKContext(c)
|
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)
|
balance, err := ethermint.MarshalBigInt(balanceInt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Error(
|
return nil, status.Error(
|
||||||
@ -117,11 +121,12 @@ func (k Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := sdk.UnwrapSDKContext(c)
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
address := ethcmn.HexToAddress(req.Address)
|
address := ethcmn.HexToAddress(req.Address)
|
||||||
key := ethcmn.HexToHash(req.Key)
|
key := ethcmn.HexToHash(req.Key)
|
||||||
|
|
||||||
state := k.GetState(ctx, address, key)
|
state := k.CommitStateDB.GetState(address, key)
|
||||||
|
|
||||||
return &types.QueryStorageResponse{
|
return &types.QueryStorageResponse{
|
||||||
Value: state.String(),
|
Value: state.String(),
|
||||||
@ -142,9 +147,10 @@ func (k Keeper) Code(c context.Context, req *types.QueryCodeRequest) (*types.Que
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := sdk.UnwrapSDKContext(c)
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
address := ethcmn.HexToAddress(req.Address)
|
address := ethcmn.HexToAddress(req.Address)
|
||||||
code := k.GetCode(ctx, address)
|
code := k.CommitStateDB.GetCode(address)
|
||||||
|
|
||||||
return &types.QueryCodeResponse{
|
return &types.QueryCodeResponse{
|
||||||
Code: code,
|
Code: code,
|
||||||
@ -165,9 +171,10 @@ func (k Keeper) TxLogs(c context.Context, req *types.QueryTxLogsRequest) (*types
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := sdk.UnwrapSDKContext(c)
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
hash := ethcmn.HexToHash(req.Hash)
|
hash := ethcmn.HexToHash(req.Hash)
|
||||||
logs, err := k.GetLogs(ctx, hash)
|
logs, err := k.CommitStateDB.GetLogs(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Error(
|
return nil, status.Error(
|
||||||
codes.Internal,
|
codes.Internal,
|
||||||
@ -296,6 +303,7 @@ func (k Keeper) StaticCall(c context.Context, req *types.QueryStaticCallRequest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := sdk.UnwrapSDKContext(c)
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
// parse the chainID from a string to a base-10 integer
|
// parse the chainID from a string to a base-10 integer
|
||||||
chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID())
|
chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID())
|
||||||
@ -312,7 +320,7 @@ func (k Keeper) StaticCall(c context.Context, req *types.QueryStaticCallRequest)
|
|||||||
recipient = &addr
|
recipient = &addr
|
||||||
}
|
}
|
||||||
|
|
||||||
so := k.GetOrNewStateObject(ctx, *recipient)
|
so := k.CommitStateDB.GetOrNewStateObject(*recipient)
|
||||||
sender := ethcmn.HexToAddress("0xaDd00275E3d9d213654Ce5223f0FADE8b106b707")
|
sender := ethcmn.HexToAddress("0xaDd00275E3d9d213654Ce5223f0FADE8b106b707")
|
||||||
|
|
||||||
msg := types.NewMsgEthereumTx(
|
msg := types.NewMsgEthereumTx(
|
||||||
|
@ -247,7 +247,7 @@ func (suite *KeeperTestSuite) TestQueryStorage() {
|
|||||||
key := ethcmn.BytesToHash([]byte("key"))
|
key := ethcmn.BytesToHash([]byte("key"))
|
||||||
value := ethcmn.BytesToHash([]byte("value"))
|
value := ethcmn.BytesToHash([]byte("value"))
|
||||||
expValue = value.String()
|
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{
|
req = &types.QueryStorageRequest{
|
||||||
Address: suite.address.String(),
|
Address: suite.address.String(),
|
||||||
Key: key.String(),
|
Key: key.String(),
|
||||||
@ -302,7 +302,7 @@ func (suite *KeeperTestSuite) TestQueryCode() {
|
|||||||
"success",
|
"success",
|
||||||
func() {
|
func() {
|
||||||
expCode = []byte("code")
|
expCode = []byte("code")
|
||||||
suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, expCode)
|
suite.app.EvmKeeper.CommitStateDB.SetCode(suite.address, expCode)
|
||||||
|
|
||||||
req = &types.QueryCodeRequest{
|
req = &types.QueryCodeRequest{
|
||||||
Address: suite.address.String(),
|
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{
|
req = &types.QueryTxLogsRequest{
|
||||||
Hash: hash.String(),
|
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.CommitStateDB.SetLogs(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_1")), types.LogsToEthereum(expLogs[1].Logs))
|
||||||
|
|
||||||
req = &types.QueryBlockLogsRequest{
|
req = &types.QueryBlockLogsRequest{
|
||||||
Hash: hash.String(),
|
Hash: hash.String(),
|
||||||
|
@ -30,6 +30,8 @@ func (k Keeper) BalanceInvariant() sdk.Invariant {
|
|||||||
count int
|
count int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
||||||
ethAccount, ok := account.(*ethermint.EthAccount)
|
ethAccount, ok := account.(*ethermint.EthAccount)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -40,7 +42,7 @@ func (k Keeper) BalanceInvariant() sdk.Invariant {
|
|||||||
evmDenom := k.GetParams(ctx).EvmDenom
|
evmDenom := k.GetParams(ctx).EvmDenom
|
||||||
|
|
||||||
accountBalance := k.bankKeeper.GetBalance(ctx, ethAccount.GetAddress(), 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 {
|
if evmBalance.Cmp(accountBalance.Amount.BigInt()) != 0 {
|
||||||
count++
|
count++
|
||||||
@ -71,6 +73,8 @@ func (k Keeper) NonceInvariant() sdk.Invariant {
|
|||||||
count int
|
count int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
||||||
ethAccount, ok := account.(*ethermint.EthAccount)
|
ethAccount, ok := account.(*ethermint.EthAccount)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -78,7 +82,7 @@ func (k Keeper) NonceInvariant() sdk.Invariant {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
evmNonce := k.GetNonce(ctx, ethAccount.EthAddress())
|
evmNonce := k.CommitStateDB.GetNonce(ethAccount.EthAddress())
|
||||||
|
|
||||||
if evmNonce != ethAccount.Sequence {
|
if evmNonce != ethAccount.Sequence {
|
||||||
count++
|
count++
|
||||||
|
@ -29,7 +29,7 @@ func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
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,
|
true,
|
||||||
},
|
},
|
||||||
@ -42,7 +42,7 @@ func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
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,
|
false,
|
||||||
},
|
},
|
||||||
@ -60,6 +60,7 @@ func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
|||||||
suite.Run(tc.name, func() {
|
suite.Run(tc.name, func() {
|
||||||
suite.SetupTest() // reset values
|
suite.SetupTest() // reset values
|
||||||
|
|
||||||
|
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||||
tc.malleate()
|
tc.malleate()
|
||||||
|
|
||||||
_, broken := suite.app.EvmKeeper.BalanceInvariant()(suite.ctx)
|
_, broken := suite.app.EvmKeeper.BalanceInvariant()(suite.ctx)
|
||||||
@ -92,7 +93,7 @@ func (suite *KeeperTestSuite) TestNonceInvariant() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||||
|
|
||||||
suite.app.EvmKeeper.SetNonce(suite.ctx, address, 100)
|
suite.app.EvmKeeper.CommitStateDB.SetNonce(address, 100)
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
@ -105,7 +106,7 @@ func (suite *KeeperTestSuite) TestNonceInvariant() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||||
|
|
||||||
suite.app.EvmKeeper.SetNonce(suite.ctx, address, 1)
|
suite.app.EvmKeeper.CommitStateDB.SetNonce(address, 1)
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@ -123,6 +124,7 @@ func (suite *KeeperTestSuite) TestNonceInvariant() {
|
|||||||
suite.Run(tc.name, func() {
|
suite.Run(tc.name, func() {
|
||||||
suite.SetupTest() // reset values
|
suite.SetupTest() // reset values
|
||||||
|
|
||||||
|
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||||
tc.malleate()
|
tc.malleate()
|
||||||
|
|
||||||
_, broken := suite.app.EvmKeeper.NonceInvariant()(suite.ctx)
|
_, broken := suite.app.EvmKeeper.NonceInvariant()(suite.ctx)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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
|
// - storing block hash -> block height map. Needed for the Web3 API. TODO: remove
|
||||||
storeKey sdk.StoreKey
|
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
|
accountKeeper types.AccountKeeper
|
||||||
bankKeeper types.BankKeeper
|
bankKeeper types.BankKeeper
|
||||||
|
|
||||||
|
ctx sdk.Context
|
||||||
|
|
||||||
// Ethermint concrete implementation on the EVM StateDB interface
|
// Ethermint concrete implementation on the EVM StateDB interface
|
||||||
CommitStateDB *types.CommitStateDB
|
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
|
// Per-transaction access list
|
||||||
// during EVM execution in the current block.
|
// See EIP-2930 for more info: https://eips.ethereum.org/EIPS/eip-2930
|
||||||
LogsCache map[common.Address][]*ethtypes.Log
|
// 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
|
// NewKeeper generates new evm module keeper
|
||||||
func NewKeeper(
|
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,
|
ak types.AccountKeeper, bankKeeper types.BankKeeper,
|
||||||
) *Keeper {
|
) *Keeper {
|
||||||
// set KeyTable if it has not already been set
|
// 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
|
// NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations
|
||||||
return &Keeper{
|
return &Keeper{
|
||||||
cdc: cdc,
|
cdc: cdc,
|
||||||
|
paramSpace: paramSpace,
|
||||||
accountKeeper: ak,
|
accountKeeper: ak,
|
||||||
bankKeeper: bankKeeper,
|
bankKeeper: bankKeeper,
|
||||||
storeKey: storeKey,
|
storeKey: storeKey,
|
||||||
|
transientKey: transientKey,
|
||||||
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, paramSpace, ak, bankKeeper),
|
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, paramSpace, ak, bankKeeper),
|
||||||
TxCount: 0,
|
accessList: types.NewAccessListMappings(),
|
||||||
Bloom: big.NewInt(0),
|
|
||||||
LogsCache: map[common.Address][]*ethtypes.Log{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,8 +75,13 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
|||||||
return ctx.Logger().With("module", types.ModuleName)
|
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.
|
// 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())
|
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
|
// GetBlockHash gets block height from block consensus hash
|
||||||
func (k Keeper) GetBlockHashFromHeight(ctx sdk.Context, height int64) (common.Hash, bool) {
|
func (k Keeper) GetBlockHashFromHeight(ctx sdk.Context, height int64) (common.Hash, bool) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
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)
|
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.
|
// GetTxReceiptFromHash gets tx receipt by tx hash.
|
||||||
func (k Keeper) GetTxReceiptFromHash(ctx sdk.Context, hash common.Hash) (*types.TxReceipt, bool) {
|
func (k Keeper) GetTxReceiptFromHash(ctx sdk.Context, hash common.Hash) (*types.TxReceipt, bool) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
data := store.Get(types.KeyHashTxReceipt(hash))
|
data := store.Get(types.KeyHashTxReceipt(hash))
|
||||||
if data == nil || len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +277,7 @@ func (k Keeper) GetTxReceiptsByBlockHeight(ctx sdk.Context, blockHeight int64) [
|
|||||||
|
|
||||||
for idx, txHash := range txs {
|
for idx, txHash := range txs {
|
||||||
data := store.Get(types.KeyHashTxReceipt(txHash))
|
data := store.Get(types.KeyHashTxReceipt(txHash))
|
||||||
if data == nil || len(data) == 0 {
|
if len(data) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +300,10 @@ func (k Keeper) GetTxReceiptsByBlockHash(ctx sdk.Context, hash common.Hash) []*t
|
|||||||
return k.GetTxReceiptsByBlockHeight(ctx, blockHeight)
|
return k.GetTxReceiptsByBlockHeight(ctx, blockHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Log
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// GetAllTxLogs return all the transaction logs from the store.
|
// GetAllTxLogs return all the transaction logs from the store.
|
||||||
func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs {
|
func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
@ -256,14 +321,51 @@ func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs {
|
|||||||
return txsLogs
|
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
|
// GetAccountStorage return state storage associated with an account
|
||||||
func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (types.Storage, error) {
|
func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (types.Storage, error) {
|
||||||
storage := types.Storage{}
|
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))
|
storage = append(storage, types.NewState(key, value))
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.Storage{}, err
|
return types.Storage{}, err
|
||||||
}
|
}
|
||||||
@ -271,22 +373,62 @@ func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (type
|
|||||||
return storage, nil
|
return storage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChainConfig gets block height from block consensus hash
|
// ----------------------------------------------------------------------------
|
||||||
func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) {
|
// Account
|
||||||
store := ctx.KVStore(k.storeKey)
|
// ----------------------------------------------------------------------------
|
||||||
bz := store.Get(types.KeyPrefixChainConfig)
|
|
||||||
if len(bz) == 0 {
|
func (k Keeper) DeleteState(addr common.Address, key common.Hash) {
|
||||||
return types.ChainConfig{}, false
|
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
|
store := prefix.NewStore(k.ctx.KVStore(k.storeKey), types.KeyPrefixCode)
|
||||||
k.cdc.MustUnmarshalBinaryBare(bz, &config)
|
store.Delete(hash.Bytes())
|
||||||
return config, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetChainConfig sets the mapping from block consensus hash to block height
|
// ClearBalance subtracts the EVM all the balance denomination from the address
|
||||||
func (k Keeper) SetChainConfig(ctx sdk.Context, config types.ChainConfig) {
|
// balance while also updating the total supply.
|
||||||
store := ctx.KVStore(k.storeKey)
|
func (k Keeper) ClearBalance(addr sdk.AccAddress) (prevBalance sdk.Coin, err error) {
|
||||||
bz := k.cdc.MustMarshalBinaryBare(&config)
|
params := k.GetParams(k.ctx)
|
||||||
store.Set(types.KeyPrefixChainConfig, bz)
|
|
||||||
|
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.app = app.Setup(checkTx)
|
||||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-3", Time: time.Now().UTC()})
|
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)
|
suite.address = ethcmn.HexToAddress(addrHex)
|
||||||
|
|
||||||
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
|
queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry())
|
||||||
@ -77,18 +79,18 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
|||||||
}
|
}
|
||||||
expLogs := []*ethtypes.Log{log}
|
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)
|
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().NoError(err)
|
||||||
suite.Require().Equal(expLogs, logs)
|
suite.Require().Equal(expLogs, logs)
|
||||||
|
|
||||||
expLogs = []*ethtypes.Log{log2, log}
|
expLogs = []*ethtypes.Log{log2, log}
|
||||||
|
|
||||||
// add another log under the zero hash
|
// add another log under the zero hash
|
||||||
suite.app.EvmKeeper.AddLog(suite.ctx, log2)
|
suite.app.EvmKeeper.CommitStateDB.AddLog(log2)
|
||||||
logs = suite.app.EvmKeeper.AllLogs(suite.ctx)
|
logs = suite.app.EvmKeeper.CommitStateDB.AllLogs()
|
||||||
suite.Require().Equal(expLogs, logs)
|
suite.Require().Equal(expLogs, logs)
|
||||||
|
|
||||||
// add another log under the zero hash
|
// add another log under the zero hash
|
||||||
@ -97,7 +99,7 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
|||||||
Data: []byte("log3"),
|
Data: []byte("log3"),
|
||||||
BlockNumber: 10,
|
BlockNumber: 10,
|
||||||
}
|
}
|
||||||
suite.app.EvmKeeper.AddLog(suite.ctx, log3)
|
suite.app.EvmKeeper.CommitStateDB.AddLog(log3)
|
||||||
|
|
||||||
txLogs := suite.app.EvmKeeper.GetAllTxLogs(suite.ctx)
|
txLogs := suite.app.EvmKeeper.GetAllTxLogs(suite.ctx)
|
||||||
suite.Require().Equal(2, len(txLogs))
|
suite.Require().Equal(2, len(txLogs))
|
||||||
@ -111,28 +113,28 @@ func (suite *KeeperTestSuite) TestTransactionLogs() {
|
|||||||
|
|
||||||
func (suite *KeeperTestSuite) TestDBStorage() {
|
func (suite *KeeperTestSuite) TestDBStorage() {
|
||||||
// Perform state transitions
|
// Perform state transitions
|
||||||
suite.app.EvmKeeper.CreateAccount(suite.ctx, suite.address)
|
suite.app.EvmKeeper.CommitStateDB.CreateAccount(suite.address)
|
||||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(5))
|
suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.address, big.NewInt(5))
|
||||||
suite.app.EvmKeeper.SetNonce(suite.ctx, suite.address, 4)
|
suite.app.EvmKeeper.CommitStateDB.SetNonce(suite.address, 4)
|
||||||
suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
|
suite.app.EvmKeeper.CommitStateDB.SetState(suite.address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3"))
|
||||||
suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, []byte{0x1})
|
suite.app.EvmKeeper.CommitStateDB.SetCode(suite.address, []byte{0x1})
|
||||||
|
|
||||||
// Test block height mapping functionality
|
// Test block height mapping functionality
|
||||||
testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
|
testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3})
|
||||||
suite.app.EvmKeeper.SetBlockBloom(suite.ctx, 4, testBloom)
|
suite.app.EvmKeeper.SetBlockBloom(suite.ctx, 4, testBloom)
|
||||||
|
|
||||||
// Get those state transitions
|
// 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.CommitStateDB.GetBalance(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.CommitStateDB.GetNonce(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.CommitStateDB.GetState(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.GetCode(suite.address), []byte{0x1})
|
||||||
|
|
||||||
bloom, found := suite.app.EvmKeeper.GetBlockBloom(suite.ctx, 4)
|
bloom, found := suite.app.EvmKeeper.GetBlockBloom(suite.ctx, 4)
|
||||||
suite.Require().True(found)
|
suite.Require().True(found)
|
||||||
suite.Require().Equal(bloom, testBloom)
|
suite.Require().Equal(bloom, testBloom)
|
||||||
|
|
||||||
// commit stateDB
|
// 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")
|
suite.Require().NoError(err, "failed to commit StateDB")
|
||||||
|
|
||||||
// simulate BaseApp EndBlocker commitment
|
// simulate BaseApp EndBlocker commitment
|
||||||
|
@ -3,6 +3,7 @@ package keeper
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/armon/go-metrics"
|
"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)
|
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.TypeMsgEthereumTx)
|
||||||
|
|
||||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||||
|
k.CommitStateDB.WithContext(ctx)
|
||||||
|
|
||||||
ethMsg, err := msg.AsMessage()
|
ethMsg, err := msg.AsMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -67,8 +69,8 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
|||||||
// other nodes, causing a consensus error
|
// other nodes, causing a consensus error
|
||||||
if !st.Simulate {
|
if !st.Simulate {
|
||||||
// Prepare db for logs
|
// Prepare db for logs
|
||||||
k.Prepare(ctx, ethHash, blockHash, k.TxCount)
|
k.CommitStateDB.Prepare(ethHash, blockHash, int(k.GetTxIndexTransient()))
|
||||||
k.TxCount++
|
k.IncreaseTxIndexTransient()
|
||||||
}
|
}
|
||||||
|
|
||||||
executionResult, err := st.TransitionDb(ctx, config)
|
executionResult, err := st.TransitionDb(ctx, config)
|
||||||
@ -102,11 +104,16 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !st.Simulate {
|
if !st.Simulate {
|
||||||
|
bloom, found := k.GetBlockBloomTransient()
|
||||||
|
if !found {
|
||||||
|
bloom = big.NewInt(0)
|
||||||
|
}
|
||||||
// update block bloom filter
|
// 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
|
// update transaction logs in KVStore
|
||||||
err = k.SetLogs(ctx, ethHash, executionResult.Logs)
|
err = k.CommitStateDB.SetLogs(ethHash, executionResult.Logs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -130,10 +137,6 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
|||||||
})
|
})
|
||||||
|
|
||||||
k.AddTxHashToBlock(ctx, ctx.BlockHeight(), ethHash)
|
k.AddTxHashToBlock(ctx, ctx.BlockHeight(), ethHash)
|
||||||
|
|
||||||
for _, ethLog := range executionResult.Logs {
|
|
||||||
k.LogsCache[ethLog.Address] = append(k.LogsCache[ethLog.Address], ethLog)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -8,10 +8,31 @@ import (
|
|||||||
|
|
||||||
// GetParams returns the total set of evm parameters.
|
// GetParams returns the total set of evm parameters.
|
||||||
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
|
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.
|
// SetParams sets the evm parameters to the param space.
|
||||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
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
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/cosmos/ethermint/x/evm/types"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
|
||||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
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
|
// CreateAccount creates a new EthAccount instance from the provided address and
|
||||||
func (k *Keeper) SetBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) {
|
// sets the value to store.
|
||||||
k.CommitStateDB.WithContext(ctx).SetBalance(addr, amount)
|
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
|
// AddBalance calls CommitStateDB.AddBalance using the passed in context
|
||||||
func (k *Keeper) AddBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) {
|
func (k *Keeper) AddBalance(addr common.Address, amount *big.Int) {
|
||||||
k.CommitStateDB.WithContext(ctx).AddBalance(addr, amount)
|
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
|
// SubBalance calls CommitStateDB.SubBalance using the passed in context
|
||||||
func (k *Keeper) SubBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) {
|
func (k *Keeper) SubBalance(addr common.Address, amount *big.Int) {
|
||||||
k.CommitStateDB.WithContext(ctx).SubBalance(addr, amount)
|
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
|
// SetNonce calls CommitStateDB.SetNonce using the passed in context
|
||||||
func (k *Keeper) SetNonce(ctx sdk.Context, addr ethcmn.Address, nonce uint64) {
|
func (k *Keeper) SetNonce(addr common.Address, nonce uint64) {
|
||||||
k.CommitStateDB.WithContext(ctx).SetNonce(addr, nonce)
|
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
|
// create address if it doesn't exist
|
||||||
func (k *Keeper) SetState(ctx sdk.Context, addr ethcmn.Address, key, value ethcmn.Hash) {
|
account = k.accountKeeper.NewAccountWithAddress(k.ctx, cosmosAddr)
|
||||||
k.CommitStateDB.WithContext(ctx).SetState(addr, key, value)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// SetCode calls CommitStateDB.SetCode using the passed in context
|
if err := account.SetSequence(nonce); err != nil {
|
||||||
func (k *Keeper) SetCode(ctx sdk.Context, addr ethcmn.Address, code []byte) {
|
k.Logger(k.ctx).Error(
|
||||||
k.CommitStateDB.WithContext(ctx).SetCode(addr, code)
|
"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
|
return
|
||||||
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 k.CommitStateDB.WithContext(ctx).SetLogs(hash, logs)
|
k.accountKeeper.SetAccount(k.ctx, account)
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteLogs calls CommitStateDB.DeleteLogs using the passed in context
|
k.Logger(k.ctx).Debug(
|
||||||
func (k *Keeper) DeleteLogs(ctx sdk.Context, hash ethcmn.Hash) {
|
"nonce set",
|
||||||
k.CommitStateDB.WithContext(ctx).DeleteLogs(hash)
|
"ethereum-address", addr.Hex(),
|
||||||
}
|
"cosmos-address", cosmosAddr.String(),
|
||||||
|
"nonce", nonce,
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Getters
|
// Code
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// GetBalance calls CommitStateDB.GetBalance using the passed in context
|
// GetCodeHash calls CommitStateDB.GetCodeHash using the passed in context
|
||||||
func (k *Keeper) GetBalance(ctx sdk.Context, addr ethcmn.Address) *big.Int {
|
func (k *Keeper) GetCodeHash(addr common.Address) common.Hash {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetBalance(addr)
|
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
|
ethAccount, isEthAccount := account.(*ethermint.EthAccount)
|
||||||
func (k *Keeper) GetNonce(ctx sdk.Context, addr ethcmn.Address) uint64 {
|
if !isEthAccount {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetNonce(addr)
|
return common.BytesToHash(types.EmptyCodeHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxIndex calls CommitStateDB.TxIndex using the passed in context
|
return common.BytesToHash(ethAccount.CodeHash)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCode calls CommitStateDB.GetCode using the passed in context
|
// GetCode calls CommitStateDB.GetCode using the passed in context
|
||||||
func (k *Keeper) GetCode(ctx sdk.Context, addr ethcmn.Address) []byte {
|
func (k *Keeper) GetCode(addr common.Address) []byte {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetCode(addr)
|
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
|
// SetCode calls CommitStateDB.SetCode using the passed in context
|
||||||
func (k *Keeper) GetCodeSize(ctx sdk.Context, addr ethcmn.Address) int {
|
func (k *Keeper) SetCode(addr common.Address, code []byte) {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetCodeSize(addr)
|
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
|
// GetCodeSize returns the code hash stored in the address account.
|
||||||
func (k *Keeper) GetCodeHash(ctx sdk.Context, addr ethcmn.Address) ethcmn.Hash {
|
func (k *Keeper) GetCodeSize(addr common.Address) int {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetCodeHash(addr)
|
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
|
// GetState calls CommitStateDB.GetState using the passed in context
|
||||||
func (k *Keeper) GetState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash {
|
func (k *Keeper) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetState(addr, hash)
|
// All state is committed directly
|
||||||
|
return k.GetCommittedState(addr, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommittedState calls CommitStateDB.GetCommittedState using the passed in context
|
// SetState calls CommitStateDB.SetState using the passed in context
|
||||||
func (k *Keeper) GetCommittedState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash {
|
func (k *Keeper) SetState(addr common.Address, key, value common.Hash) {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetCommittedState(addr, 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
|
action := "updated"
|
||||||
func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) {
|
if ethermint.IsEmptyHash(value.Hex()) {
|
||||||
return k.CommitStateDB.WithContext(ctx).GetLogs(hash)
|
store.Delete(key.Bytes())
|
||||||
}
|
action = "deleted"
|
||||||
|
} else {
|
||||||
|
store.Set(key.Bytes(), value.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
// AllLogs calls CommitStateDB.AllLogs using the passed in context
|
k.Logger(k.ctx).Debug(
|
||||||
func (k *Keeper) AllLogs(ctx sdk.Context) []*ethtypes.Log {
|
fmt.Sprintf("state %s", action),
|
||||||
return k.CommitStateDB.WithContext(ctx).AllLogs()
|
"ethereum-address", addr.Hex(),
|
||||||
}
|
"key", key.Hex(),
|
||||||
|
)
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Persistence
|
// Suicide
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Commit calls CommitStateDB.Commit using the passed in context
|
// Suicide marks the given account as suicided and clears the account balance of
|
||||||
func (k *Keeper) Commit(ctx sdk.Context, deleteEmptyObjects bool) (root ethcmn.Hash, err error) {
|
// the EVM tokens.
|
||||||
return k.CommitStateDB.WithContext(ctx).Commit(deleteEmptyObjects)
|
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
|
// HasSuicided queries the transient store to check if the account has been marked as suicided in the
|
||||||
func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error {
|
// current block. Accounts that are suicided will be returned as non-nil during queries and "cleared"
|
||||||
return k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects)
|
// 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 {
|
// Account Exist / Empty
|
||||||
_, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects)
|
// ----------------------------------------------------------------------------
|
||||||
return err
|
|
||||||
|
// 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
|
// Snapshotting
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Snapshot calls CommitStateDB.Snapshot using the passed in context
|
// Snapshot return zero as the state changes won't be committed if the state transition fails. So there
|
||||||
func (k *Keeper) Snapshot(ctx sdk.Context) int {
|
// is no need to snapshot before the VM execution.
|
||||||
return k.CommitStateDB.WithContext(ctx).Snapshot()
|
// 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
|
// RevertToSnapshot performs a no-op because when a transaction execution fails on the EVM, the state
|
||||||
func (k *Keeper) RevertToSnapshot(ctx sdk.Context, revID int) {
|
// won't be persisted during ABCI DeliverTx.
|
||||||
k.CommitStateDB.WithContext(ctx).RevertToSnapshot(revID)
|
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
|
// AddPreimage performs a no-op since the EnablePreimageRecording flag is disabled
|
||||||
func (k *Keeper) Database(ctx sdk.Context) ethstate.Database {
|
// on the vm.Config during state transitions. No store trie preimages are written
|
||||||
return k.CommitStateDB.WithContext(ctx).Database()
|
// 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 {
|
// Iterator
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForEachStorage calls CommitStateDB.ForEachStorage using passed in context
|
// 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 {
|
func (k *Keeper) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
|
||||||
return k.CommitStateDB.WithContext(ctx).ForEachStorage(addr, cb)
|
store := k.ctx.KVStore(k.storeKey)
|
||||||
}
|
prefix := types.AddressStoragePrefix(addr)
|
||||||
|
|
||||||
// GetOrNewStateObject calls CommitStateDB.GetOrNetStateObject using the passed in context
|
iterator := sdk.KVStorePrefixIterator(store, prefix)
|
||||||
func (k *Keeper) GetOrNewStateObject(ctx sdk.Context, addr ethcmn.Address) types.StateObject {
|
defer iterator.Close()
|
||||||
return k.CommitStateDB.WithContext(ctx).GetOrNewStateObject(addr)
|
|
||||||
|
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
|
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"
|
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
|
// 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
|
addresses map[common.Address]int
|
||||||
slots []map[common.Hash]struct{}
|
slots []map[common.Hash]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainsAddress returns true if the address is in the access list.
|
// 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]
|
_, ok := al.addresses[address]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains checks if a slot within an account is present in the access list, returning
|
// 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.
|
// 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]
|
idx, ok := al.addresses[address]
|
||||||
if !ok {
|
if !ok {
|
||||||
// no such address (and hence zero slots)
|
// 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
|
return true, slotPresent
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAccessList creates a new accessList.
|
// newAccessList creates a new AccessListMappings.
|
||||||
func newAccessList() *accessList {
|
func NewAccessListMappings() *AccessListMappings {
|
||||||
return &accessList{
|
return &AccessListMappings{
|
||||||
addresses: make(map[common.Address]int),
|
addresses: make(map[common.Address]int),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy creates an independent copy of an accessList.
|
// Copy creates an independent copy of an AccessListMappings.
|
||||||
func (al *accessList) Copy() *accessList {
|
func (al *AccessListMappings) Copy() *AccessListMappings {
|
||||||
cp := newAccessList()
|
cp := NewAccessListMappings()
|
||||||
for k, v := range al.addresses {
|
for k, v := range al.addresses {
|
||||||
cp.addresses[k] = v
|
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
|
// AddAddress adds an address to the access list, and returns 'true' if the operation
|
||||||
// caused a change (addr was not previously in the list).
|
// 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 {
|
if _, present := al.addresses[address]; present {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ func (al *accessList) AddAddress(address common.Address) bool {
|
|||||||
// - address added
|
// - address added
|
||||||
// - slot added
|
// - slot added
|
||||||
// For any 'true' value returned, a corresponding journal entry must be made.
|
// 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]
|
idx, addrPresent := al.addresses[address]
|
||||||
if !addrPresent || idx == -1 {
|
if !addrPresent || idx == -1 {
|
||||||
// Address not present, or addr present but no slots there
|
// 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]
|
slotmap := al.slots[idx]
|
||||||
if _, ok := slotmap[slot]; !ok {
|
if _, ok := slotmap[slot]; !ok {
|
||||||
slotmap[slot] = struct{}{}
|
slotmap[slot] = struct{}{}
|
||||||
// Journal add slot change
|
// journal add slot change
|
||||||
return false, true
|
return false, true
|
||||||
}
|
}
|
||||||
// No changes required
|
// 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 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
|
// This method is meant to be used by the journal, which maintains ordering of
|
||||||
// operations.
|
// 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]
|
idx, addrOk := al.addresses[address]
|
||||||
// There are two ways this can fail
|
// There are two ways this can fail
|
||||||
if !addrOk {
|
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.
|
// 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
|
// This method is meant to be used by the journal, which maintains ordering of
|
||||||
// operations.
|
// operations.
|
||||||
func (al *accessList) DeleteAddress(address common.Address) {
|
func (al *AccessListMappings) DeleteAddress(address common.Address) {
|
||||||
delete(al.addresses, address)
|
delete(al.addresses, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ func NewAccessList(ethAccessList *ethtypes.AccessList) AccessList {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var accessList AccessList
|
var AccessListMappings AccessList
|
||||||
for _, tuple := range *ethAccessList {
|
for _, tuple := range *ethAccessList {
|
||||||
storageKeys := make([]string, len(tuple.StorageKeys))
|
storageKeys := make([]string, len(tuple.StorageKeys))
|
||||||
|
|
||||||
@ -154,19 +154,19 @@ func NewAccessList(ethAccessList *ethtypes.AccessList) AccessList {
|
|||||||
storageKeys[i] = tuple.StorageKeys[i].String()
|
storageKeys[i] = tuple.StorageKeys[i].String()
|
||||||
}
|
}
|
||||||
|
|
||||||
accessList = append(accessList, AccessTuple{
|
AccessListMappings = append(AccessListMappings, AccessTuple{
|
||||||
Address: tuple.Address.String(),
|
Address: tuple.Address.String(),
|
||||||
StorageKeys: storageKeys,
|
StorageKeys: storageKeys,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessList
|
return AccessListMappings
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToEthAccessList is an utility function to convert the protobuf compatible
|
// ToEthAccessList is an utility function to convert the protobuf compatible
|
||||||
// AccessList to eth core AccessList from go-ethereum
|
// AccessList to eth core AccessList from go-ethereum
|
||||||
func (al AccessList) ToEthAccessList() *ethtypes.AccessList {
|
func (al AccessList) ToEthAccessList() *ethtypes.AccessList {
|
||||||
var accessList ethtypes.AccessList
|
var AccessListMappings ethtypes.AccessList
|
||||||
|
|
||||||
for _, tuple := range al {
|
for _, tuple := range al {
|
||||||
storageKeys := make([]ethcmn.Hash, len(tuple.StorageKeys))
|
storageKeys := make([]ethcmn.Hash, len(tuple.StorageKeys))
|
||||||
@ -175,11 +175,11 @@ func (al AccessList) ToEthAccessList() *ethtypes.AccessList {
|
|||||||
storageKeys[i] = ethcmn.HexToHash(tuple.StorageKeys[i])
|
storageKeys[i] = ethcmn.HexToHash(tuple.StorageKeys[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
accessList = append(accessList, ethtypes.AccessTuple{
|
AccessListMappings = append(AccessListMappings, ethtypes.AccessTuple{
|
||||||
Address: ethcmn.HexToAddress(tuple.Address),
|
Address: ethcmn.HexToAddress(tuple.Address),
|
||||||
StorageKeys: storageKeys,
|
StorageKeys: storageKeys,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return &accessList
|
return &AccessListMappings
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ type AccessListTestSuite struct {
|
|||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
address ethcmn.Address
|
address ethcmn.Address
|
||||||
accessList *accessList
|
accessList *AccessListMappings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AccessListTestSuite) SetupTest() {
|
func (suite *AccessListTestSuite) SetupTest() {
|
||||||
@ -23,7 +23,7 @@ func (suite *AccessListTestSuite) SetupTest() {
|
|||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes())
|
suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes())
|
||||||
suite.accessList = newAccessList()
|
suite.accessList = NewAccessListMappings()
|
||||||
suite.accessList.addresses[suite.address] = 1
|
suite.accessList.addresses[suite.address] = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ func (suite *AccessListTestSuite) TestContains() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AccessListTestSuite) TestCopy() {
|
func (suite *AccessListTestSuite) TestCopy() {
|
||||||
expAccessList := newAccessList()
|
expAccessList := NewAccessListMappings()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
@ -96,7 +96,7 @@ func (suite *AccessListTestSuite) TestCopy() {
|
|||||||
}},
|
}},
|
||||||
{
|
{
|
||||||
"single address", func() {
|
"single address", func() {
|
||||||
expAccessList = newAccessList()
|
expAccessList = NewAccessListMappings()
|
||||||
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 0)
|
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 0)
|
||||||
expAccessList.addresses[suite.address] = -1
|
expAccessList.addresses[suite.address] = -1
|
||||||
},
|
},
|
||||||
@ -104,7 +104,7 @@ func (suite *AccessListTestSuite) TestCopy() {
|
|||||||
{
|
{
|
||||||
"single address, single slot",
|
"single address, single slot",
|
||||||
func() {
|
func() {
|
||||||
expAccessList = newAccessList()
|
expAccessList = NewAccessListMappings()
|
||||||
expAccessList.addresses[suite.address] = 0
|
expAccessList.addresses[suite.address] = 0
|
||||||
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
|
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 1)
|
||||||
expAccessList.slots[0] = make(map[ethcmn.Hash]struct{})
|
expAccessList.slots[0] = make(map[ethcmn.Hash]struct{})
|
||||||
@ -114,7 +114,7 @@ func (suite *AccessListTestSuite) TestCopy() {
|
|||||||
{
|
{
|
||||||
"multiple addresses, single slot each",
|
"multiple addresses, single slot each",
|
||||||
func() {
|
func() {
|
||||||
expAccessList = newAccessList()
|
expAccessList = NewAccessListMappings()
|
||||||
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
|
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i
|
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = i
|
||||||
@ -126,7 +126,7 @@ func (suite *AccessListTestSuite) TestCopy() {
|
|||||||
{
|
{
|
||||||
"multiple addresses, multiple slots each",
|
"multiple addresses, multiple slots each",
|
||||||
func() {
|
func() {
|
||||||
expAccessList = newAccessList()
|
expAccessList = NewAccessListMappings()
|
||||||
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
|
expAccessList.slots = make([]map[ethcmn.Hash]struct{}, 10)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
expAccessList.addresses[ethcmn.BytesToAddress([]byte(fmt.Sprintf("%d", i)))] = 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
|
NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
|
||||||
GetAllAccounts(ctx sdk.Context) (accounts []authtypes.AccountI)
|
GetAllAccounts(ctx sdk.Context) (accounts []authtypes.AccountI)
|
||||||
IterateAccounts(ctx sdk.Context, cb func(account authtypes.AccountI) bool)
|
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
|
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
|
||||||
SetAccount(ctx sdk.Context, account authtypes.AccountI)
|
SetAccount(ctx sdk.Context, account authtypes.AccountI)
|
||||||
RemoveAccount(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.
|
// BankKeeper defines the expected interface needed to retrieve account balances.
|
||||||
type BankKeeper interface {
|
type BankKeeper interface {
|
||||||
GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
|
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
|
SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance sdk.Coin) error
|
||||||
}
|
}
|
@ -4,6 +4,7 @@ import (
|
|||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
|
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -15,21 +16,51 @@ const (
|
|||||||
// The EVM module should use a prefix store.
|
// The EVM module should use a prefix store.
|
||||||
StoreKey = ModuleName
|
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 uses module name for routing
|
||||||
RouterKey = ModuleName
|
RouterKey = ModuleName
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prefixBlockHash = iota + 1
|
||||||
|
prefixBloom
|
||||||
|
prefixLogs
|
||||||
|
prefixCode
|
||||||
|
prefixStorage
|
||||||
|
prefixChainConfig
|
||||||
|
prefixBlockHeightHash
|
||||||
|
prefixHashTxReceipt
|
||||||
|
prefixBlockHeightTxs
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prefixTransientSuicided = iota + 1
|
||||||
|
prefixTransientBloom
|
||||||
|
prefixTransientTxIndex
|
||||||
|
prefixTransientRefund
|
||||||
|
)
|
||||||
|
|
||||||
// KVStore key prefixes
|
// KVStore key prefixes
|
||||||
var (
|
var (
|
||||||
KeyPrefixBlockHash = []byte{0x01}
|
KeyPrefixBlockHash = []byte{prefixBlockHash}
|
||||||
KeyPrefixBloom = []byte{0x02}
|
KeyPrefixBloom = []byte{prefixBloom}
|
||||||
KeyPrefixLogs = []byte{0x03}
|
KeyPrefixLogs = []byte{prefixLogs}
|
||||||
KeyPrefixCode = []byte{0x04}
|
KeyPrefixCode = []byte{prefixCode}
|
||||||
KeyPrefixStorage = []byte{0x05}
|
KeyPrefixStorage = []byte{prefixStorage}
|
||||||
KeyPrefixChainConfig = []byte{0x06}
|
KeyPrefixChainConfig = []byte{prefixChainConfig}
|
||||||
KeyPrefixBlockHeightHash = []byte{0x07}
|
KeyPrefixBlockHeightHash = []byte{prefixBlockHeightHash}
|
||||||
KeyPrefixHashTxReceipt = []byte{0x08}
|
KeyPrefixHashTxReceipt = []byte{prefixHashTxReceipt}
|
||||||
KeyPrefixBlockHeightTxs = []byte{0x09}
|
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
|
// BloomKey defines the store key for a block Bloom
|
||||||
@ -69,3 +100,17 @@ func KeyBlockHeightTxs(height uint64) []byte {
|
|||||||
heightBytes := sdk.Uint64ToBigEndian(height)
|
heightBytes := sdk.Uint64ToBigEndian(height)
|
||||||
return append(KeyPrefixBlockHeightTxs, heightBytes...)
|
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 (
|
var (
|
||||||
_ StateObject = (*stateObject)(nil)
|
_ StateObject = (*stateObject)(nil)
|
||||||
|
|
||||||
emptyCodeHash = ethcrypto.Keccak256(nil)
|
EmptyCodeHash = ethcrypto.Keccak256(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
// StateObject interface for interacting with state object
|
// StateObject interface for interacting with state object
|
||||||
@ -90,7 +90,7 @@ func newStateObject(db *CommitStateDB, accProto authtypes.AccountI, balance sdk.
|
|||||||
|
|
||||||
// set empty code hash
|
// set empty code hash
|
||||||
if ethAccount.CodeHash == nil {
|
if ethAccount.CodeHash == nil {
|
||||||
ethAccount.CodeHash = emptyCodeHash
|
ethAccount.CodeHash = EmptyCodeHash
|
||||||
}
|
}
|
||||||
|
|
||||||
return &stateObject{
|
return &stateObject{
|
||||||
@ -307,7 +307,7 @@ func (so *stateObject) Balance() *big.Int {
|
|||||||
// CodeHash returns the state object's code hash.
|
// CodeHash returns the state object's code hash.
|
||||||
func (so *stateObject) CodeHash() []byte {
|
func (so *stateObject) CodeHash() []byte {
|
||||||
if so.account == nil || len(so.account.CodeHash) == 0 {
|
if so.account == nil || len(so.account.CodeHash) == 0 {
|
||||||
return emptyCodeHash
|
return EmptyCodeHash
|
||||||
}
|
}
|
||||||
return so.account.CodeHash
|
return so.account.CodeHash
|
||||||
}
|
}
|
||||||
@ -326,7 +326,7 @@ func (so *stateObject) Code(_ ethstate.Database) []byte {
|
|||||||
return so.code
|
return so.code
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Equal(so.CodeHash(), emptyCodeHash) {
|
if bytes.Equal(so.CodeHash(), EmptyCodeHash) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +416,7 @@ func (so *stateObject) empty() bool {
|
|||||||
(so.account != nil &&
|
(so.account != nil &&
|
||||||
so.account.Sequence == 0 &&
|
so.account.Sequence == 0 &&
|
||||||
(so.balance.BigInt() == nil || so.balance.IsZero()) &&
|
(so.balance.BigInt() == nil || so.balance.IsZero()) &&
|
||||||
bytes.Equal(so.account.CodeHash, emptyCodeHash))
|
bytes.Equal(so.account.CodeHash, EmptyCodeHash))
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRLP implements rlp.Encoder.
|
// EncodeRLP implements rlp.Encoder.
|
||||||
|
@ -98,6 +98,7 @@ func (st *StateTransition) newEVM(
|
|||||||
}
|
}
|
||||||
|
|
||||||
vmConfig := vm.Config{
|
vmConfig := vm.Config{
|
||||||
|
EnablePreimageRecording: false, // no need for StateDB.AddPreimage
|
||||||
ExtraEips: eips,
|
ExtraEips: eips,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,8 +179,8 @@ func (suite *StateDBTestSuite) TestTransitionDb() {
|
|||||||
|
|
||||||
if tc.expPass {
|
if tc.expPass {
|
||||||
suite.Require().NoError(err, tc.name)
|
suite.Require().NoError(err, tc.name)
|
||||||
fromBalance := suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address)
|
fromBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(suite.address)
|
||||||
toBalance := suite.app.EvmKeeper.GetBalance(suite.ctx, recipient)
|
toBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(recipient)
|
||||||
suite.Require().Equal(fromBalance, big.NewInt(4950), tc.name)
|
suite.Require().Equal(fromBalance, big.NewInt(4950), tc.name)
|
||||||
suite.Require().Equal(toBalance, big.NewInt(50), tc.name)
|
suite.Require().Equal(toBalance, big.NewInt(50), tc.name)
|
||||||
} else {
|
} else {
|
||||||
|
@ -72,14 +72,14 @@ type CommitStateDB struct {
|
|||||||
// by StateDB.Commit.
|
// by StateDB.Commit.
|
||||||
dbErr error
|
dbErr error
|
||||||
|
|
||||||
// Journal of state modifications. This is the backbone of
|
// journal of state modifications. This is the backbone of
|
||||||
// Snapshot and RevertToSnapshot.
|
// Snapshot and RevertToSnapshot.
|
||||||
journal *journal
|
journal *journal
|
||||||
validRevisions []revision
|
validRevisions []revision
|
||||||
nextRevisionID int
|
nextRevisionID int
|
||||||
|
|
||||||
// Per-transaction access list
|
// Per-transaction access list
|
||||||
accessList *accessList
|
accessList *AccessListMappings
|
||||||
|
|
||||||
// mutex for state deep copying
|
// mutex for state deep copying
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
@ -106,7 +106,7 @@ func NewCommitStateDB(
|
|||||||
preimages: []preimageEntry{},
|
preimages: []preimageEntry{},
|
||||||
hashToPreimageIndex: make(map[ethcmn.Hash]int),
|
hashToPreimageIndex: make(map[ethcmn.Hash]int),
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
accessList: newAccessList(),
|
accessList: NewAccessListMappings(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,7 +727,7 @@ func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error {
|
|||||||
csdb.logSize = 0
|
csdb.logSize = 0
|
||||||
csdb.preimages = []preimageEntry{}
|
csdb.preimages = []preimageEntry{}
|
||||||
csdb.hashToPreimageIndex = make(map[ethcmn.Hash]int)
|
csdb.hashToPreimageIndex = make(map[ethcmn.Hash]int)
|
||||||
csdb.accessList = newAccessList()
|
csdb.accessList = NewAccessListMappings()
|
||||||
|
|
||||||
csdb.clearJournalAndRefund()
|
csdb.clearJournalAndRefund()
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user