evm: remove CommitStateDB
, journal
and stateObject
(#84)
* evm: remove CommitStateDB and stateObject * imported build fixes * lint * rm set nonce * update account response * changelog
This commit is contained in:
parent
6eadc8fdf8
commit
0c6e44d3d3
@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### State Machine Breaking
|
||||
|
||||
* (evm) [tharsis#84](https://github.com/tharsis/ethermint/pull/84) Remove `journal`, `CommitStateDB` and `stateObjects`.
|
||||
* (rpc, evm) [tharsis#81](https://github.com/tharsis/ethermint/pull/81) Remove tx `Receipt` from store and replace it with fields obtained from the Tendermint RPC client.
|
||||
* (evm) [tharsis#72](https://github.com/tharsis/ethermint/issues/72) Update `AccessList` to use `TransientStore` instead of map.
|
||||
* (evm) [tharsis#68](https://github.com/tharsis/ethermint/issues/68) Replace block hash storage map to use staking `HistoricalInfo`.
|
||||
|
@ -385,7 +385,7 @@ func NewEthermintApp(
|
||||
params.NewAppModule(app.ParamsKeeper),
|
||||
transferModule,
|
||||
// Ethermint app modules
|
||||
evm.NewAppModule(app.EvmKeeper, app.AccountKeeper, app.BankKeeper),
|
||||
evm.NewAppModule(app.EvmKeeper, app.AccountKeeper),
|
||||
)
|
||||
|
||||
// During begin block slashing happens after distr.BeginBlocker so that
|
||||
|
@ -427,7 +427,7 @@ QueryAccountResponse is the response type for the Query/Account RPC method.
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `balance` | [string](#string) | | balance is the balance of the EVM denomination. |
|
||||
| `code_hash` | [bytes](#bytes) | | code_hash is the code bytes from the EOA. |
|
||||
| `code_hash` | [string](#string) | | code hash is the hex-formatted code bytes from the EOA. |
|
||||
| `nonce` | [uint64](#uint64) | | nonce is the account's sequence number. |
|
||||
|
||||
|
||||
|
2
go.mod
2
go.mod
@ -45,7 +45,7 @@ require (
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
github.com/xlab/closer v0.0.0-20190328110542-03326addb7c2
|
||||
github.com/xlab/suplog v1.3.0
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
||||
google.golang.org/genproto v0.0.0-20210607140030-00d4fb20b1ae
|
||||
google.golang.org/grpc v1.38.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
|
@ -75,8 +75,8 @@ message QueryAccountRequest {
|
||||
message QueryAccountResponse {
|
||||
// balance is the balance of the EVM denomination.
|
||||
string balance = 1;
|
||||
// code_hash is the code bytes from the EOA.
|
||||
bytes code_hash = 2;
|
||||
// code hash is the hex-formatted code bytes from the EOA.
|
||||
string code_hash = 2;
|
||||
// nonce is the account's sequence number.
|
||||
uint64 nonce = 3;
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak authkeeper.
|
||||
genBlock := ethcore.DefaultGenesisBlock()
|
||||
ms := cms.CacheMultiStore()
|
||||
ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger)
|
||||
evmKeeper.CommitStateDB.WithContext(ctx)
|
||||
evmKeeper.WithContext(ctx)
|
||||
|
||||
// Set the default Ethermint parameters to the parameter keeper store
|
||||
evmKeeper.SetParams(ctx, evmtypes.DefaultParams())
|
||||
@ -126,25 +126,19 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak authkeeper.
|
||||
addr := ethcmn.HexToAddress(addrStr)
|
||||
acc := genBlock.Alloc[addr]
|
||||
|
||||
evmKeeper.CommitStateDB.AddBalance(addr, acc.Balance)
|
||||
evmKeeper.CommitStateDB.SetCode(addr, acc.Code)
|
||||
evmKeeper.CommitStateDB.SetNonce(addr, acc.Nonce)
|
||||
evmKeeper.AddBalance(addr, acc.Balance)
|
||||
evmKeeper.SetCode(addr, acc.Code)
|
||||
evmKeeper.SetNonce(addr, acc.Nonce)
|
||||
|
||||
for key, value := range acc.Storage {
|
||||
evmKeeper.CommitStateDB.SetState(addr, key, value)
|
||||
evmKeeper.SetState(addr, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// get balance of one of the genesis account having 400 ETH
|
||||
b := evmKeeper.CommitStateDB.GetBalance(genInvestor)
|
||||
b := evmKeeper.GetBalance(genInvestor)
|
||||
require.Equal(t, "200000000000000000000", b.String())
|
||||
|
||||
// commit the stateDB with 'false' to delete empty objects
|
||||
//
|
||||
// NOTE: Commit does not yet return the intra merkle root (version)
|
||||
_, err := evmKeeper.CommitStateDB.Commit(false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// persist multi-store cache state
|
||||
ms.Write()
|
||||
|
||||
@ -267,15 +261,13 @@ func TestImportBlocks(t *testing.T) {
|
||||
ms := cms.CacheMultiStore()
|
||||
ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger)
|
||||
ctx = ctx.WithBlockHeight(int64(block.NumberU64()))
|
||||
evmKeeper.CommitStateDB.WithContext(ctx)
|
||||
evmKeeper.WithContext(ctx)
|
||||
|
||||
if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||
applyDAOHardFork(evmKeeper)
|
||||
}
|
||||
|
||||
for i, tx := range block.Transactions() {
|
||||
evmKeeper.CommitStateDB.Prepare(tx.Hash(), block.Hash(), i)
|
||||
// evmKeeper.CommitStateDB.Set(block.Hash())
|
||||
for _, tx := range block.Transactions() {
|
||||
|
||||
receipt, gas, err := applyTransaction(
|
||||
chainConfig, chainContext, nil, gp, evmKeeper, header, tx, usedGas, vmConfig,
|
||||
@ -287,10 +279,6 @@ func TestImportBlocks(t *testing.T) {
|
||||
// apply mining rewards
|
||||
accumulateRewards(chainConfig, evmKeeper, header, block.Uncles())
|
||||
|
||||
// commit stateDB
|
||||
_, err := evmKeeper.CommitStateDB.Commit(chainConfig.IsEIP158(block.Number()))
|
||||
require.NoError(t, err, "failed to commit StateDB")
|
||||
|
||||
// simulate BaseApp EndBlocker commitment
|
||||
ms.Write()
|
||||
cms.Commit()
|
||||
@ -325,12 +313,12 @@ func accumulateRewards(
|
||||
r.Sub(r, header.Number)
|
||||
r.Mul(r, blockReward)
|
||||
r.Div(r, rewardBig8)
|
||||
evmKeeper.CommitStateDB.AddBalance(uncle.Coinbase, r)
|
||||
evmKeeper.AddBalance(uncle.Coinbase, r)
|
||||
r.Div(blockReward, rewardBig32)
|
||||
reward.Add(reward, r)
|
||||
}
|
||||
|
||||
evmKeeper.CommitStateDB.AddBalance(header.Coinbase, reward)
|
||||
evmKeeper.AddBalance(header.Coinbase, reward)
|
||||
}
|
||||
|
||||
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
|
||||
@ -341,14 +329,13 @@ func accumulateRewards(
|
||||
// Ref: https://github.com/ethereum/go-ethereum/blob/52f2461774bcb8cdd310f86b4bc501df5b783852/consensus/misc/dao.go#L74
|
||||
func applyDAOHardFork(evmKeeper *evmkeeper.Keeper) {
|
||||
// Retrieve the contract to refund balances into
|
||||
if !evmKeeper.CommitStateDB.Exist(ethparams.DAORefundContract) {
|
||||
evmKeeper.CommitStateDB.CreateAccount(ethparams.DAORefundContract)
|
||||
if !evmKeeper.Exist(ethparams.DAORefundContract) {
|
||||
evmKeeper.CreateAccount(ethparams.DAORefundContract)
|
||||
}
|
||||
|
||||
// Move every DAO account and extra-balance account funds into the refund contract
|
||||
for _, addr := range ethparams.DAODrainList() {
|
||||
evmKeeper.CommitStateDB.AddBalance(ethparams.DAORefundContract, evmKeeper.CommitStateDB.GetBalance(addr))
|
||||
evmKeeper.CommitStateDB.SetBalance(addr, new(big.Int))
|
||||
evmKeeper.AddBalance(ethparams.DAORefundContract, evmKeeper.GetBalance(addr))
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,7 +361,7 @@ func applyTransaction(
|
||||
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := ethvm.NewEVM(blockCtx, txCtx, evmKeeper.CommitStateDB, config, cfg)
|
||||
vmenv := ethvm.NewEVM(blockCtx, txCtx, evmKeeper, config, cfg)
|
||||
|
||||
// Apply the transaction to the current state (included in the env)
|
||||
execResult, err := ethcore.ApplyMessage(vmenv, msg, gp)
|
||||
@ -383,19 +370,11 @@ func applyTransaction(
|
||||
return ðtypes.Receipt{}, 0, nil
|
||||
}
|
||||
|
||||
// Update the state with pending changes
|
||||
var intRoot ethcmn.Hash
|
||||
if config.IsByzantium(header.Number) {
|
||||
err = evmKeeper.CommitStateDB.Finalise(true)
|
||||
} else {
|
||||
intRoot, err = evmKeeper.CommitStateDB.IntermediateRoot(config.IsEIP158(header.Number))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, execResult.UsedGas, err
|
||||
}
|
||||
|
||||
root := intRoot.Bytes()
|
||||
root := ethcmn.Hash{}.Bytes()
|
||||
*usedGas += execResult.UsedGas
|
||||
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||
@ -410,11 +389,11 @@ func applyTransaction(
|
||||
}
|
||||
|
||||
// Set the receipt logs and create a bloom for filtering
|
||||
receipt.Logs, err = evmKeeper.CommitStateDB.GetLogs(tx.Hash())
|
||||
receipt.Logs = evmKeeper.GetTxLogs(tx.Hash())
|
||||
receipt.Bloom = ethtypes.CreateBloom(ethtypes.Receipts{receipt})
|
||||
receipt.BlockHash = evmKeeper.CommitStateDB.BlockHash()
|
||||
receipt.BlockHash = header.Hash()
|
||||
receipt.BlockNumber = header.Number
|
||||
receipt.TransactionIndex = uint(evmKeeper.CommitStateDB.TxIndex())
|
||||
receipt.TransactionIndex = uint(evmKeeper.GetTxIndexTransient())
|
||||
|
||||
return receipt, execResult.UsedGas, err
|
||||
}
|
||||
|
@ -18,16 +18,12 @@ func InitGenesis(
|
||||
ctx sdk.Context,
|
||||
k *keeper.Keeper,
|
||||
accountKeeper types.AccountKeeper, // nolint: interfacer
|
||||
bankKeeper types.BankKeeper,
|
||||
data types.GenesisState,
|
||||
) []abci.ValidatorUpdate {
|
||||
k.WithContext(ctx)
|
||||
k.WithChainID(ctx)
|
||||
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
k.SetParams(ctx, data.Params)
|
||||
evmDenom := data.Params.EvmDenom
|
||||
|
||||
for _, account := range data.Accounts {
|
||||
address := ethcmn.HexToAddress(account.Address)
|
||||
@ -47,46 +43,25 @@ func InitGenesis(
|
||||
)
|
||||
}
|
||||
|
||||
evmBalance := bankKeeper.GetBalance(ctx, accAddress, evmDenom)
|
||||
k.CommitStateDB.SetBalance(address, evmBalance.Amount.BigInt())
|
||||
k.CommitStateDB.SetNonce(address, acc.GetSequence())
|
||||
k.CommitStateDB.SetCode(address, ethcmn.Hex2Bytes(account.Code))
|
||||
k.SetCode(address, ethcmn.Hex2Bytes(account.Code))
|
||||
|
||||
for _, storage := range account.Storage {
|
||||
k.SetState(address, ethcmn.HexToHash(storage.Key), ethcmn.HexToHash(storage.Value))
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, txLog := range data.TxsLogs {
|
||||
err = k.CommitStateDB.SetLogs(ethcmn.HexToHash(txLog.Hash), txLog.EthLogs())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
k.SetLogs(ethcmn.HexToHash(txLog.Hash), txLog.EthLogs())
|
||||
}
|
||||
|
||||
k.SetChainConfig(ctx, data.ChainConfig)
|
||||
|
||||
// set state objects and code to store
|
||||
_, err = k.CommitStateDB.Commit(false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// set storage to store
|
||||
// NOTE: don't delete empty object to prevent import-export simulation failure
|
||||
err = k.CommitStateDB.Finalise(false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return []abci.ValidatorUpdate{}
|
||||
}
|
||||
|
||||
// ExportGenesis exports genesis state of the EVM module
|
||||
func ExportGenesis(ctx sdk.Context, k *keeper.Keeper, ak types.AccountKeeper) *types.GenesisState {
|
||||
k.WithContext(ctx)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
// nolint: prealloc
|
||||
var ethGenAccounts []types.GenesisAccount
|
||||
@ -106,7 +81,7 @@ func ExportGenesis(ctx sdk.Context, k *keeper.Keeper, ak types.AccountKeeper) *t
|
||||
|
||||
genAccount := types.GenesisAccount{
|
||||
Address: addr.String(),
|
||||
Code: ethcmn.Bytes2Hex(k.CommitStateDB.GetCode(addr)),
|
||||
Code: ethcmn.Bytes2Hex(k.GetCode(addr)),
|
||||
Storage: storage,
|
||||
}
|
||||
|
||||
|
@ -91,13 +91,13 @@ func (suite *EvmTestSuite) TestInitGenesis() {
|
||||
if tc.expPanic {
|
||||
suite.Require().Panics(
|
||||
func() {
|
||||
_ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper, suite.app.BankKeeper, *tc.genState)
|
||||
_ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper, *tc.genState)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
suite.Require().NotPanics(
|
||||
func() {
|
||||
_ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper, suite.app.BankKeeper, *tc.genState)
|
||||
_ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper, *tc.genState)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -8,12 +8,11 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
"github.com/cosmos/ethermint/x/evm/keeper"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
// NewHandler returns a handler for Ethermint type messages.
|
||||
func NewHandler(k *keeper.Keeper) sdk.Handler {
|
||||
func NewHandler(server types.MsgServer) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) (result *sdk.Result, err error) {
|
||||
defer Recover(&err)
|
||||
|
||||
@ -22,7 +21,7 @@ func NewHandler(k *keeper.Keeper) sdk.Handler {
|
||||
switch msg := msg.(type) {
|
||||
case *types.MsgEthereumTx:
|
||||
// execute state transition
|
||||
res, err := k.EthereumTx(sdk.WrapSDKContext(ctx), msg)
|
||||
res, err := server.EthereumTx(sdk.WrapSDKContext(ctx), msg)
|
||||
return sdk.WrapServiceResult(ctx, res, err)
|
||||
|
||||
default:
|
||||
|
@ -49,7 +49,7 @@ func (suite *EvmTestSuite) SetupTest() {
|
||||
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-1", Time: time.Now().UTC()})
|
||||
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||
suite.app.EvmKeeper.WithContext(suite.ctx)
|
||||
suite.handler = evm.NewHandler(suite.app.EvmKeeper)
|
||||
suite.codec = suite.app.AppCodec()
|
||||
suite.chainID = suite.app.EvmKeeper.ChainID()
|
||||
@ -84,7 +84,6 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
|
||||
{
|
||||
"passed",
|
||||
func() {
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.from, big.NewInt(100))
|
||||
to := ethcmn.BytesToAddress(suite.to)
|
||||
tx = types.NewMsgEthereumTx(suite.chainID, 0, &to, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
|
||||
tx.From = suite.from.String()
|
||||
@ -189,11 +188,10 @@ func (suite *EvmTestSuite) TestHandlerLogs() {
|
||||
suite.Require().Equal(len(txResponse.Logs[0].Topics), 2)
|
||||
|
||||
hash := []byte{1}
|
||||
err = suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash(hash), types.LogsToEthereum(txResponse.Logs))
|
||||
suite.app.EvmKeeper.SetLogs(ethcmn.BytesToHash(hash), types.LogsToEthereum(txResponse.Logs))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
logs, err := suite.app.EvmKeeper.CommitStateDB.GetLogs(ethcmn.BytesToHash(hash))
|
||||
suite.Require().NoError(err, "failed to get logs")
|
||||
logs := suite.app.EvmKeeper.GetTxLogs(ethcmn.BytesToHash(hash))
|
||||
|
||||
suite.Require().Equal(logs, txResponse.Logs)
|
||||
}
|
||||
@ -310,8 +308,6 @@ func (suite *EvmTestSuite) TestSendTransaction() {
|
||||
gasLimit := uint64(21000)
|
||||
gasPrice := big.NewInt(0x55ae82600)
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.from, big.NewInt(100))
|
||||
|
||||
// send simple value transfer with gasLimit=21000
|
||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, ðcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil, nil)
|
||||
tx.From = suite.from.String()
|
||||
@ -390,12 +386,12 @@ func (suite *EvmTestSuite) TestOutOfGasWhenDeployContract() {
|
||||
err := tx.Sign(suite.ethSigner, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
||||
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper)
|
||||
suite.Require().Nil(err)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
currentCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
||||
currentCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper)
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().Equal(snapshotCommitStateDBJson, currentCommitStateDBJson)
|
||||
} else {
|
||||
@ -419,13 +415,13 @@ func (suite *EvmTestSuite) TestErrorWhenDeployContract() {
|
||||
err := tx.Sign(suite.ethSigner, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
||||
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper)
|
||||
suite.Require().Nil(err)
|
||||
|
||||
_, sdkErr := suite.handler(suite.ctx, tx)
|
||||
suite.Require().NotNil(sdkErr)
|
||||
|
||||
currentCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
||||
currentCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper)
|
||||
suite.Require().Nil(err)
|
||||
suite.Require().Equal(snapshotCommitStateDBJson, currentCommitStateDBJson)
|
||||
}
|
||||
|
@ -27,29 +27,13 @@ func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
|
||||
// deleting the empty ones. It also sets the bloom filers for the request block to
|
||||
// the store. The EVM end block logic doesn't update the validator set, thus it returns
|
||||
// an empty slice.
|
||||
func (k Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
func (k *Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker)
|
||||
|
||||
// Gas costs are handled within msg handler so costs should be ignored
|
||||
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
infCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
k.WithContext(ctx)
|
||||
|
||||
// Update account balances before committing other parts of state
|
||||
k.CommitStateDB.UpdateAccounts()
|
||||
|
||||
root, err := k.CommitStateDB.Commit(true)
|
||||
// Commit state objects to KV store
|
||||
if err != nil {
|
||||
k.Logger(ctx).Error("failed to commit state objects", "error", err, "height", ctx.BlockHeight())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// reset all cache after account data has been committed, that make sure node state consistent
|
||||
if err = k.CommitStateDB.Reset(root); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// get the block bloom bytes from the transient store and set it to the persistent storage
|
||||
bloomBig, found := k.GetBlockBloomTransient()
|
||||
if !found {
|
||||
@ -57,7 +41,8 @@ func (k Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.Valid
|
||||
}
|
||||
|
||||
bloom := ethtypes.BytesToBloom(bloomBig.Bytes())
|
||||
k.SetBlockBloom(ctx, req.Height, bloom)
|
||||
k.SetBlockBloom(infCtx, req.Height, bloom)
|
||||
k.WithContext(ctx)
|
||||
|
||||
return []abci.ValidatorUpdate{}
|
||||
}
|
||||
|
@ -2,13 +2,10 @@ package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
@ -33,19 +30,15 @@ func (k Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*typ
|
||||
)
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
addr := ethcmn.HexToAddress(req.Address)
|
||||
|
||||
so := k.CommitStateDB.GetOrNewStateObject(ethcmn.HexToAddress(req.Address))
|
||||
balance, err := ethermint.MarshalBigInt(so.Balance())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.WithContext(ctx)
|
||||
|
||||
return &types.QueryAccountResponse{
|
||||
Balance: balance,
|
||||
CodeHash: so.CodeHash(),
|
||||
Nonce: so.Nonce(),
|
||||
Balance: k.GetBalance(addr).String(),
|
||||
CodeHash: k.GetCodeHash(addr).Hex(),
|
||||
Nonce: k.GetNonce(addr),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -61,7 +54,7 @@ func (k Keeper) CosmosAccount(c context.Context, req *types.QueryCosmosAccountRe
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
k.WithContext(ctx)
|
||||
|
||||
ethAddr := ethcmn.HexToAddress(req.Address)
|
||||
cosmosAddr := sdk.AccAddress(ethAddr.Bytes())
|
||||
@ -93,9 +86,9 @@ func (k Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*typ
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
k.WithContext(ctx)
|
||||
|
||||
balanceInt := k.CommitStateDB.GetBalance(ethcmn.HexToAddress(req.Address))
|
||||
balanceInt := k.GetBalance(ethcmn.HexToAddress(req.Address))
|
||||
balance, err := ethermint.MarshalBigInt(balanceInt)
|
||||
if err != nil {
|
||||
return nil, status.Error(
|
||||
@ -130,12 +123,12 @@ func (k Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*typ
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
k.WithContext(ctx)
|
||||
|
||||
address := ethcmn.HexToAddress(req.Address)
|
||||
key := ethcmn.HexToHash(req.Key)
|
||||
|
||||
state := k.CommitStateDB.GetState(address, key)
|
||||
state := k.GetState(address, key)
|
||||
stateHex := state.Hex()
|
||||
|
||||
if ethermint.IsEmptyHash(stateHex) {
|
||||
@ -163,10 +156,10 @@ func (k Keeper) Code(c context.Context, req *types.QueryCodeRequest) (*types.Que
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
k.WithContext(ctx)
|
||||
|
||||
address := ethcmn.HexToAddress(req.Address)
|
||||
code := k.CommitStateDB.GetCode(address)
|
||||
code := k.GetCode(address)
|
||||
|
||||
return &types.QueryCodeResponse{
|
||||
Code: code,
|
||||
@ -187,19 +180,13 @@ func (k Keeper) TxLogs(c context.Context, req *types.QueryTxLogsRequest) (*types
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
k.WithContext(ctx)
|
||||
|
||||
hash := ethcmn.HexToHash(req.Hash)
|
||||
logs, err := k.CommitStateDB.GetLogs(hash)
|
||||
if err != nil {
|
||||
return nil, status.Error(
|
||||
codes.Internal,
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
logs := k.GetTxLogs(hash)
|
||||
|
||||
return &types.QueryTxLogsResponse{
|
||||
Logs: types.NewTransactionLogsFromEth(hash, logs).Logs,
|
||||
Logs: types.NewLogsFromEth(logs),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -277,59 +264,61 @@ func (k Keeper) StaticCall(c context.Context, req *types.QueryStaticCallRequest)
|
||||
return nil, status.Error(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(c)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
// ctx := sdk.UnwrapSDKContext(c)
|
||||
// k.WithContext(ctx)
|
||||
|
||||
// parse the chainID from a string to a base-10 integer
|
||||
chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID())
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
// // parse the chainID from a string to a base-10 integer
|
||||
// chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID())
|
||||
// if err != nil {
|
||||
// return nil, status.Error(codes.Internal, err.Error())
|
||||
// }
|
||||
|
||||
txHash := tmtypes.Tx(ctx.TxBytes()).Hash()
|
||||
ethHash := ethcmn.BytesToHash(txHash)
|
||||
// txHash := tmtypes.Tx(ctx.TxBytes()).Hash()
|
||||
// ethHash := ethcmn.BytesToHash(txHash)
|
||||
|
||||
var recipient *ethcmn.Address
|
||||
if len(req.Address) > 0 {
|
||||
addr := ethcmn.HexToAddress(req.Address)
|
||||
recipient = &addr
|
||||
}
|
||||
// var recipient *ethcmn.Address
|
||||
// if len(req.Address) > 0 {
|
||||
// addr := ethcmn.HexToAddress(req.Address)
|
||||
// recipient = &addr
|
||||
// }
|
||||
|
||||
so := k.CommitStateDB.GetOrNewStateObject(*recipient)
|
||||
sender := ethcmn.HexToAddress("0xaDd00275E3d9d213654Ce5223f0FADE8b106b707")
|
||||
// so := k.GetOrNewStateObject(*recipient)
|
||||
// sender := ethcmn.HexToAddress("0xaDd00275E3d9d213654Ce5223f0FADE8b106b707")
|
||||
|
||||
msg := types.NewMsgEthereumTx(
|
||||
chainIDEpoch, so.Nonce(), recipient, big.NewInt(0), 100000000, big.NewInt(0), req.Input, nil,
|
||||
)
|
||||
msg.From = sender.Hex()
|
||||
// msg := types.NewMsgEthereumTx(
|
||||
// chainIDEpoch, so.Nonce(), recipient, big.NewInt(0), 100000000, big.NewInt(0), req.Input, nil,
|
||||
// )
|
||||
// msg.From = sender.Hex()
|
||||
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
// if err := msg.ValidateBasic(); err != nil {
|
||||
// return nil, status.Error(codes.Internal, err.Error())
|
||||
// }
|
||||
|
||||
ethMsg, err := msg.AsMessage()
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
// ethMsg, err := msg.AsMessage()
|
||||
// if err != nil {
|
||||
// return nil, status.Error(codes.Internal, err.Error())
|
||||
// }
|
||||
|
||||
st := &types.StateTransition{
|
||||
Message: ethMsg,
|
||||
Csdb: k.CommitStateDB.WithContext(ctx),
|
||||
ChainID: chainIDEpoch,
|
||||
TxHash: ðHash,
|
||||
Simulate: ctx.IsCheckTx(),
|
||||
Debug: false,
|
||||
}
|
||||
// st := &types.StateTransition{
|
||||
// Message: ethMsg,
|
||||
// Csdb: k.WithContext(ctx),
|
||||
// ChainID: chainIDEpoch,
|
||||
// TxHash: ðHash,
|
||||
// Simulate: ctx.IsCheckTx(),
|
||||
// Debug: false,
|
||||
// }
|
||||
|
||||
config, found := k.GetChainConfig(ctx)
|
||||
if !found {
|
||||
return nil, status.Error(codes.Internal, types.ErrChainConfigNotFound.Error())
|
||||
}
|
||||
// config, found := k.GetChainConfig(ctx)
|
||||
// if !found {
|
||||
// return nil, status.Error(codes.Internal, types.ErrChainConfigNotFound.Error())
|
||||
// }
|
||||
|
||||
ret, err := st.StaticCall(ctx, config)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
// ret, err := st.StaticCall(ctx, config)
|
||||
// if err != nil {
|
||||
// return nil, status.Error(codes.Internal, err.Error())
|
||||
// }
|
||||
|
||||
return &types.QueryStaticCallResponse{Data: ret}, nil
|
||||
// return &types.QueryStaticCallResponse{Data: ret}, nil
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
@ -33,7 +34,7 @@ func (suite *KeeperTestSuite) TestQueryAccount() {
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(0))
|
||||
expAccount = &types.QueryAccountResponse{
|
||||
Balance: "0",
|
||||
CodeHash: ethcrypto.Keccak256(nil),
|
||||
CodeHash: common.BytesToHash(ethcrypto.Keccak256(nil)).Hex(),
|
||||
Nonce: 0,
|
||||
}
|
||||
req = &types.QueryAccountRequest{
|
||||
@ -48,7 +49,7 @@ func (suite *KeeperTestSuite) TestQueryAccount() {
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(100))
|
||||
expAccount = &types.QueryAccountResponse{
|
||||
Balance: "100",
|
||||
CodeHash: ethcrypto.Keccak256(nil),
|
||||
CodeHash: common.BytesToHash(ethcrypto.Keccak256(nil)).Hex(),
|
||||
Nonce: 0,
|
||||
}
|
||||
req = &types.QueryAccountRequest{
|
||||
@ -245,7 +246,7 @@ func (suite *KeeperTestSuite) TestQueryStorage() {
|
||||
key := ethcmn.BytesToHash([]byte("key"))
|
||||
value := ethcmn.BytesToHash([]byte("value"))
|
||||
expValue = value.String()
|
||||
suite.app.EvmKeeper.CommitStateDB.SetState(suite.address, key, value)
|
||||
suite.app.EvmKeeper.SetState(suite.address, key, value)
|
||||
req = &types.QueryStorageRequest{
|
||||
Address: suite.address.String(),
|
||||
Key: key.String(),
|
||||
@ -300,7 +301,7 @@ func (suite *KeeperTestSuite) TestQueryCode() {
|
||||
"success",
|
||||
func() {
|
||||
expCode = []byte("code")
|
||||
suite.app.EvmKeeper.CommitStateDB.SetCode(suite.address, expCode)
|
||||
suite.app.EvmKeeper.SetCode(suite.address, expCode)
|
||||
|
||||
req = &types.QueryCodeRequest{
|
||||
Address: suite.address.String(),
|
||||
@ -377,7 +378,7 @@ func (suite *KeeperTestSuite) TestQueryTxLogs() {
|
||||
},
|
||||
}
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.SetLogs(hash, types.LogsToEthereum(expLogs))
|
||||
suite.app.EvmKeeper.SetLogs(hash, types.LogsToEthereum(expLogs))
|
||||
|
||||
req = &types.QueryTxLogsRequest{
|
||||
Hash: hash.String(),
|
||||
@ -486,8 +487,8 @@ func (suite *KeeperTestSuite) TestQueryBlockLogs() {
|
||||
},
|
||||
}
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_0")), types.LogsToEthereum(expLogs[0].Logs))
|
||||
suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_1")), types.LogsToEthereum(expLogs[1].Logs))
|
||||
suite.app.EvmKeeper.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_0")), types.LogsToEthereum(expLogs[0].Logs))
|
||||
suite.app.EvmKeeper.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_1")), types.LogsToEthereum(expLogs[1].Logs))
|
||||
|
||||
req = &types.QueryBlockLogsRequest{
|
||||
Hash: hash.String(),
|
||||
|
@ -1,105 +0,0 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
const (
|
||||
balanceInvariant = "balance"
|
||||
nonceInvariant = "nonce"
|
||||
)
|
||||
|
||||
// RegisterInvariants registers the evm module invariants
|
||||
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
|
||||
ir.RegisterRoute(types.ModuleName, balanceInvariant, k.BalanceInvariant())
|
||||
ir.RegisterRoute(types.ModuleName, nonceInvariant, k.NonceInvariant())
|
||||
}
|
||||
|
||||
// BalanceInvariant checks that all auth module's EthAccounts in the application have the same balance
|
||||
// as the EVM one.
|
||||
func (k Keeper) BalanceInvariant() sdk.Invariant {
|
||||
return func(ctx sdk.Context) (string, bool) {
|
||||
var (
|
||||
msg string
|
||||
count int
|
||||
)
|
||||
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
||||
ethAccount, ok := account.(*ethermint.EthAccount)
|
||||
if !ok {
|
||||
// ignore non EthAccounts
|
||||
return false
|
||||
}
|
||||
|
||||
evmDenom := k.GetParams(ctx).EvmDenom
|
||||
|
||||
accountBalance := k.bankKeeper.GetBalance(ctx, ethAccount.GetAddress(), evmDenom)
|
||||
evmBalance := k.CommitStateDB.GetBalance(ethAccount.EthAddress())
|
||||
|
||||
if evmBalance.Cmp(accountBalance.Amount.BigInt()) != 0 {
|
||||
count++
|
||||
msg += fmt.Sprintf(
|
||||
"\tbalance mismatch for address %s: account balance %s, evm balance %s\n",
|
||||
account.GetAddress(), accountBalance.String(), evmBalance.String(),
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
broken := count != 0
|
||||
|
||||
return sdk.FormatInvariant(
|
||||
types.ModuleName, balanceInvariant,
|
||||
fmt.Sprintf("account balances mismatches found %d\n%s", count, msg),
|
||||
), broken
|
||||
}
|
||||
}
|
||||
|
||||
// NonceInvariant checks that all auth module's EthAccounts in the application have the same nonce
|
||||
// sequence as the EVM.
|
||||
func (k Keeper) NonceInvariant() sdk.Invariant {
|
||||
return func(ctx sdk.Context) (string, bool) {
|
||||
var (
|
||||
msg string
|
||||
count int
|
||||
)
|
||||
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool {
|
||||
ethAccount, ok := account.(*ethermint.EthAccount)
|
||||
if !ok {
|
||||
// ignore non EthAccounts
|
||||
return false
|
||||
}
|
||||
|
||||
evmNonce := k.CommitStateDB.GetNonce(ethAccount.EthAddress())
|
||||
|
||||
if evmNonce != ethAccount.Sequence {
|
||||
count++
|
||||
msg += fmt.Sprintf(
|
||||
"\nonce mismatch for address %s: account nonce %d, evm nonce %d\n",
|
||||
account.GetAddress(), ethAccount.Sequence, evmNonce,
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
broken := count != 0
|
||||
|
||||
return sdk.FormatInvariant(
|
||||
types.ModuleName, nonceInvariant,
|
||||
fmt.Sprintf("account nonces mismatches found %d\n%s", count, msg),
|
||||
), broken
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestBalanceInvariant() {
|
||||
privkey, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
address := ethcmn.HexToAddress(privkey.PubKey().Address().String())
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
expBroken bool
|
||||
}{
|
||||
{
|
||||
"balance mismatch",
|
||||
func() {
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes())
|
||||
suite.Require().NotNil(acc)
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), ethermint.NewPhotonCoinInt64(1))
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(address, big.NewInt(1000))
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"balance ok",
|
||||
func() {
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes())
|
||||
suite.Require().NotNil(acc)
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), ethermint.NewPhotonCoinInt64(1))
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.SetBalance(address, big.NewInt(1))
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid account type",
|
||||
func() {
|
||||
acc := authtypes.NewBaseAccountWithAddress(address.Bytes())
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset values
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||
tc.malleate()
|
||||
|
||||
_, broken := suite.app.EvmKeeper.BalanceInvariant()(suite.ctx)
|
||||
if tc.expBroken {
|
||||
suite.Require().True(broken)
|
||||
} else {
|
||||
suite.Require().False(broken)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestNonceInvariant() {
|
||||
privkey, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
address := ethcmn.HexToAddress(privkey.PubKey().Address().String())
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
expBroken bool
|
||||
}{
|
||||
{
|
||||
"nonce mismatch",
|
||||
func() {
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes())
|
||||
suite.Require().NotNil(acc)
|
||||
err := acc.SetSequence(1)
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.SetNonce(address, 100)
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nonce ok",
|
||||
func() {
|
||||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes())
|
||||
suite.Require().NotNil(acc)
|
||||
err := acc.SetSequence(1)
|
||||
suite.Require().NoError(err)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.SetNonce(address, 1)
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid account type",
|
||||
func() {
|
||||
acc := authtypes.NewBaseAccountWithAddress(address.Bytes())
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset values
|
||||
|
||||
suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||
tc.malleate()
|
||||
|
||||
_, broken := suite.app.EvmKeeper.NonceInvariant()(suite.ctx)
|
||||
if tc.expBroken {
|
||||
suite.Require().True(broken)
|
||||
} else {
|
||||
suite.Require().False(broken)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -42,10 +42,6 @@ type Keeper struct {
|
||||
eip155ChainID *big.Int
|
||||
debug bool
|
||||
|
||||
// TODO: deprecate
|
||||
// Ethermint concrete implementation on the EVM StateDB interface
|
||||
CommitStateDB *types.CommitStateDB
|
||||
|
||||
// hash header for the current height. Reset during abci.RequestBeginBlock
|
||||
headerHash common.Hash
|
||||
}
|
||||
@ -69,7 +65,6 @@ func NewKeeper(
|
||||
stakingKeeper: sk,
|
||||
storeKey: storeKey,
|
||||
transientKey: transientKey,
|
||||
CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, transientKey, paramSpace, ak, bankKeeper),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,20 +2,15 @@ package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/telemetry"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
)
|
||||
@ -26,17 +21,7 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.TypeMsgEthereumTx)
|
||||
|
||||
ctx := sdk.UnwrapSDKContext(goCtx)
|
||||
k.CommitStateDB.WithContext(ctx)
|
||||
|
||||
ethMsg, err := msg.AsMessage()
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, err.Error())
|
||||
}
|
||||
|
||||
config, found := k.GetChainConfig(ctx)
|
||||
if !found {
|
||||
return nil, types.ErrChainConfigNotFound
|
||||
}
|
||||
k.WithContext(ctx)
|
||||
|
||||
var labels []metrics.Label
|
||||
if msg.To() == nil {
|
||||
@ -46,62 +31,22 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
} else {
|
||||
labels = []metrics.Label{
|
||||
telemetry.NewLabel("execution", "call"),
|
||||
// add label to the called recipient address (contract or account)
|
||||
telemetry.NewLabel("to", msg.Data.To),
|
||||
telemetry.NewLabel("to", msg.Data.To), // recipient address (contract or account)
|
||||
}
|
||||
}
|
||||
|
||||
sender := ethMsg.From()
|
||||
sender := msg.From
|
||||
tx := msg.AsTransaction()
|
||||
|
||||
txHash := tmtypes.Tx(ctx.TxBytes()).Hash()
|
||||
ethHash := ethcmn.BytesToHash(txHash)
|
||||
|
||||
// Ethereum formatted tx hash
|
||||
etherumTxHash := msg.AsTransaction().Hash()
|
||||
|
||||
st := &types.StateTransition{
|
||||
Message: ethMsg,
|
||||
Csdb: k.CommitStateDB.WithContext(ctx),
|
||||
ChainID: msg.ChainID(),
|
||||
TxHash: ðHash,
|
||||
Simulate: ctx.IsCheckTx(),
|
||||
}
|
||||
|
||||
// since the txCount is used by the stateDB, and a simulated tx is run only on the node it's submitted to,
|
||||
// then this will cause the txCount/stateDB of the node that ran the simulated tx to be different than the
|
||||
// other nodes, causing a consensus error
|
||||
if !st.Simulate {
|
||||
// Prepare db for logs
|
||||
k.CommitStateDB.Prepare(ethHash, k.headerHash, int(k.GetTxIndexTransient()))
|
||||
k.IncreaseTxIndexTransient()
|
||||
}
|
||||
|
||||
executionResult, err := st.TransitionDb(ctx, config)
|
||||
response, err := k.ApplyTransaction(tx)
|
||||
if err != nil {
|
||||
if errors.Is(err, vm.ErrExecutionReverted) && executionResult != nil {
|
||||
// keep the execution result for revert reason
|
||||
executionResult.Response.Reverted = true
|
||||
return executionResult.Response, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !st.Simulate {
|
||||
bloom, found := k.GetBlockBloomTransient()
|
||||
if !found {
|
||||
bloom = big.NewInt(0)
|
||||
}
|
||||
// update block bloom filter
|
||||
logsBloom := ethtypes.LogsBloom(executionResult.Logs)
|
||||
bloom = bloom.Or(bloom, new(big.Int).SetBytes(logsBloom))
|
||||
k.SetBlockBloomTransient(bloom)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if st.Message.Value().IsInt64() {
|
||||
if tx.Value().IsInt64() {
|
||||
telemetry.SetGauge(
|
||||
float32(st.Message.Value().Int64()),
|
||||
float32(tx.Value().Int64()),
|
||||
"tx", "msg", "ethereum_tx",
|
||||
)
|
||||
}
|
||||
@ -114,9 +59,15 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
}()
|
||||
|
||||
attrs := []sdk.Attribute{
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, st.Message.Value().String()),
|
||||
sdk.NewAttribute(types.AttributeKeyTxHash, ethcmn.BytesToHash(txHash).Hex()),
|
||||
sdk.NewAttribute(types.AttributeKeyEthereumTxHash, etherumTxHash.Hex()),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, tx.Value().String()),
|
||||
// add event for ethereum transaction hash format
|
||||
sdk.NewAttribute(types.AttributeKeyEthereumTxHash, response.Hash),
|
||||
}
|
||||
|
||||
if len(ctx.TxBytes()) > 0 {
|
||||
// add event for tendermint transaction hash format
|
||||
hash := tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash())
|
||||
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyTxHash, hash.String()))
|
||||
}
|
||||
|
||||
if len(msg.Data.To) > 0 {
|
||||
@ -132,10 +83,9 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, sender.String()),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, sender),
|
||||
),
|
||||
})
|
||||
|
||||
executionResult.Response.Hash = etherumTxHash.Hex()
|
||||
return executionResult.Response, nil
|
||||
return response, nil
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
@ -91,7 +92,7 @@ func (k Keeper) GetHashFn() vm.GetHashFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// TransitionDb runs and attempts to perform a state transition with the given transaction (i.e Message), that will
|
||||
// ApplyTransaction runs and attempts to perform a state transition with the given transaction (i.e Message), that will
|
||||
// only be persisted to the underlying KVStore if the transaction does not error.
|
||||
//
|
||||
// Gas tracking
|
||||
@ -108,23 +109,39 @@ func (k Keeper) GetHashFn() vm.GetHashFunc {
|
||||
// returning.
|
||||
//
|
||||
// For relevant discussion see: https://github.com/cosmos/cosmos-sdk/discussions/9072
|
||||
func (k *Keeper) TransitionDb(msg core.Message) (*types.ExecutionResult, error) {
|
||||
func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumTxResponse, error) {
|
||||
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.MetricKeyTransitionDB)
|
||||
|
||||
cfg, found := k.GetChainConfig(k.ctx)
|
||||
gasMeter := k.ctx.GasMeter() // tx gas meter
|
||||
infCtx := k.ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
cfg, found := k.GetChainConfig(infCtx)
|
||||
if !found {
|
||||
return nil, types.ErrChainConfigNotFound
|
||||
}
|
||||
ethCfg := cfg.EthereumConfig(k.eip155ChainID)
|
||||
|
||||
evm := k.NewEVM(msg, cfg.EthereumConfig(k.eip155ChainID))
|
||||
|
||||
// create an ethereum StateTransition instance and run TransitionDb
|
||||
result, err := k.ApplyMessage(evm, msg)
|
||||
msg, err := tx.AsMessage(ethtypes.MakeSigner(ethCfg, big.NewInt(k.ctx.BlockHeight())))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
evm := k.NewEVM(msg, ethCfg)
|
||||
|
||||
k.IncreaseTxIndexTransient()
|
||||
|
||||
k.WithContext(k.ctx.WithGasMeter(gasMeter))
|
||||
// create an ethereum StateTransition instance and run TransitionDb
|
||||
res, err := k.ApplyMessage(evm, msg, ethCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txHash := tx.Hash()
|
||||
res.Hash = txHash.Hex()
|
||||
res.Logs = types.NewLogsFromEth(k.GetTxLogs(txHash))
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Gas consumption notes (write doc from this)
|
||||
@ -143,10 +160,10 @@ func (k *Keeper) TransitionDb(msg core.Message) (*types.ExecutionResult, error)
|
||||
// TODO: (@fedekunze) currently we consume the entire gas limit in the ante handler, so if a transaction fails
|
||||
// the amount spent will be grater than the gas spent in an Ethereum tx (i.e here the leftover gas won't be refunded).
|
||||
|
||||
func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message) (*types.ExecutionResult, error) {
|
||||
func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainConfig) (*types.MsgEthereumTxResponse, error) {
|
||||
var (
|
||||
ret []byte // return bytes from evm execution
|
||||
vmErr, err error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
ret []byte // return bytes from evm execution
|
||||
vmErr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
)
|
||||
|
||||
sender := vm.AccountRef(msg.From())
|
||||
@ -166,7 +183,7 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message) (*types.ExecutionRe
|
||||
|
||||
// ensure gas is consistent during CheckTx
|
||||
if k.ctx.IsCheckTx() {
|
||||
if err := k.checkGasConsumption(msg, gasConsumed, contractCreation); err != nil {
|
||||
if err := k.CheckGasConsumption(msg, cfg, gasConsumed, contractCreation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@ -178,8 +195,7 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message) (*types.ExecutionRe
|
||||
}
|
||||
|
||||
// refund gas prior to handling the vm error in order to set the updated gas meter
|
||||
gasConsumed, leftoverGas, err = k.refundGas(msg, leftoverGas)
|
||||
if err != nil {
|
||||
if err := k.RefundGas(msg, leftoverGas); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -193,26 +209,17 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message) (*types.ExecutionRe
|
||||
return nil, sdkerrors.Wrap(types.ErrVMExecution, vmErr.Error())
|
||||
}
|
||||
|
||||
return &types.ExecutionResult{
|
||||
Response: &types.MsgEthereumTxResponse{
|
||||
Ret: ret,
|
||||
},
|
||||
GasInfo: types.GasInfo{
|
||||
GasLimit: k.ctx.GasMeter().Limit(),
|
||||
GasConsumed: gasConsumed,
|
||||
GasRefunded: leftoverGas,
|
||||
},
|
||||
return &types.MsgEthereumTxResponse{
|
||||
Ret: ret,
|
||||
Reverted: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// checkGasConsumption verifies that the amount of gas consumed so far matches the intrinsic gas value.
|
||||
func (k *Keeper) checkGasConsumption(msg core.Message, gasConsumed uint64, isContractCreation bool) error {
|
||||
cfg, _ := k.GetChainConfig(k.ctx)
|
||||
ethCfg := cfg.EthereumConfig(k.eip155ChainID)
|
||||
|
||||
// CheckGasConsumption verifies that the amount of gas consumed so far matches the intrinsic gas value.
|
||||
func (k *Keeper) CheckGasConsumption(msg core.Message, cfg *params.ChainConfig, gasConsumed uint64, isContractCreation bool) error {
|
||||
height := big.NewInt(k.ctx.BlockHeight())
|
||||
homestead := ethCfg.IsHomestead(height)
|
||||
istanbul := ethCfg.IsIstanbul(height)
|
||||
homestead := cfg.IsHomestead(height)
|
||||
istanbul := cfg.IsIstanbul(height)
|
||||
|
||||
intrinsicGas, err := core.IntrinsicGas(msg.Data(), msg.AccessList(), isContractCreation, homestead, istanbul)
|
||||
if err != nil {
|
||||
@ -227,11 +234,11 @@ func (k *Keeper) checkGasConsumption(msg core.Message, gasConsumed uint64, isCon
|
||||
return nil
|
||||
}
|
||||
|
||||
// refundGas transfers the leftover gas to the sender of the message, caped to half of the total gas
|
||||
// RefundGas transfers the leftover gas to the sender of the message, caped to half of the total gas
|
||||
// consumed in the transaction. Additionally, the function sets the total gas consumed to the value
|
||||
// returned by the EVM execution, thus ignoring the previous intrinsic gas inconsumed during in the
|
||||
// AnteHandler.
|
||||
func (k *Keeper) refundGas(msg core.Message, leftoverGas uint64) (consumed, leftover uint64, err error) {
|
||||
func (k *Keeper) RefundGas(msg core.Message, leftoverGas uint64) error {
|
||||
gasConsumed := msg.Gas() - leftoverGas
|
||||
|
||||
// Apply refund counter, capped to half of the used gas.
|
||||
@ -246,18 +253,21 @@ func (k *Keeper) refundGas(msg core.Message, leftoverGas uint64) (consumed, left
|
||||
// Return EVM tokens for remaining gas, exchanged at the original rate.
|
||||
remaining := new(big.Int).Mul(new(big.Int).SetUint64(leftoverGas), msg.GasPrice())
|
||||
|
||||
// ignore gas consumption
|
||||
infCtx := k.ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
|
||||
|
||||
switch remaining.Sign() {
|
||||
case -1:
|
||||
// negative refund errors
|
||||
return 0, 0, fmt.Errorf("refunded amount value cannot be negative %d", remaining.Int64())
|
||||
return fmt.Errorf("refunded amount value cannot be negative %d", remaining.Int64())
|
||||
case 1:
|
||||
// positive amount refund
|
||||
params := k.GetParams(k.ctx)
|
||||
params := k.GetParams(infCtx)
|
||||
refundedCoins := sdk.Coins{sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(remaining))}
|
||||
|
||||
// refund to sender from the fee collector module account, which is the escrow account in charge of collecting tx fees
|
||||
if err := k.bankKeeper.SendCoinsFromModuleToAccount(k.ctx, authtypes.FeeCollectorName, msg.From().Bytes(), refundedCoins); err != nil {
|
||||
return 0, 0, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "fee collector account failed to refund fees: %s", err.Error())
|
||||
if err := k.bankKeeper.SendCoinsFromModuleToAccount(infCtx, authtypes.FeeCollectorName, msg.From().Bytes(), refundedCoins); err != nil {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "fee collector account failed to refund fees: %s", err.Error())
|
||||
}
|
||||
default:
|
||||
// no refund, consume gas and update the tx gas meter
|
||||
@ -270,5 +280,5 @@ func (k *Keeper) refundGas(msg core.Message, leftoverGas uint64) (consumed, left
|
||||
gasMeter.ConsumeGas(gasConsumed, "update gas consumption after refund")
|
||||
k.WithContext(k.ctx.WithGasMeter(gasMeter))
|
||||
|
||||
return gasConsumed, leftoverGas, nil
|
||||
return nil
|
||||
}
|
||||
|
@ -88,16 +88,14 @@ type AppModule struct {
|
||||
AppModuleBasic
|
||||
keeper *keeper.Keeper
|
||||
ak types.AccountKeeper
|
||||
bk types.BankKeeper
|
||||
}
|
||||
|
||||
// NewAppModule creates a new AppModule object
|
||||
func NewAppModule(k *keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper) AppModule {
|
||||
func NewAppModule(k *keeper.Keeper, ak types.AccountKeeper) AppModule {
|
||||
return AppModule{
|
||||
AppModuleBasic: AppModuleBasic{},
|
||||
keeper: k,
|
||||
ak: ak,
|
||||
bk: bk,
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +149,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data j
|
||||
var genesisState types.GenesisState
|
||||
|
||||
cdc.MustUnmarshalJSON(data, &genesisState)
|
||||
InitGenesis(ctx, am.keeper, am.ak, am.bk, genesisState)
|
||||
InitGenesis(ctx, am.keeper, am.ak, genesisState)
|
||||
return []abci.ValidatorUpdate{}
|
||||
}
|
||||
|
||||
|
@ -1,348 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var ripemd = ethcmn.HexToAddress("0000000000000000000000000000000000000003")
|
||||
|
||||
// journalEntry is a modification entry in the state change journal that can be
|
||||
// reverted on demand.
|
||||
type journalEntry interface {
|
||||
// revert undoes the changes introduced by this journal entry.
|
||||
revert(*CommitStateDB)
|
||||
|
||||
// dirtied returns the Ethereum address modified by this journal entry.
|
||||
dirtied() *ethcmn.Address
|
||||
}
|
||||
|
||||
type revision struct {
|
||||
id int
|
||||
journalIndex int
|
||||
}
|
||||
|
||||
// journal contains the list of state modifications applied since the last state
|
||||
// commit. These are tracked to be able to be reverted in case of an execution
|
||||
// exception or revertal request.
|
||||
type journal struct {
|
||||
entries []journalEntry // Current changes tracked by the journal
|
||||
dirties []dirty // Dirty accounts and the number of changes
|
||||
addressToJournalIndex map[ethcmn.Address]int // map from address to the index of the dirties slice
|
||||
}
|
||||
|
||||
// dirty represents a single key value pair of the journal dirties, where the
|
||||
// key correspons to the account address and the value to the number of
|
||||
// changes for that account.
|
||||
type dirty struct {
|
||||
address ethcmn.Address
|
||||
changes int
|
||||
}
|
||||
|
||||
// newJournal create a new initialized journal.
|
||||
func newJournal() *journal {
|
||||
return &journal{
|
||||
dirties: []dirty{},
|
||||
addressToJournalIndex: make(map[ethcmn.Address]int),
|
||||
}
|
||||
}
|
||||
|
||||
// append inserts a new modification entry to the end of the change journal.
|
||||
func (j *journal) append(entry journalEntry) {
|
||||
j.entries = append(j.entries, entry)
|
||||
if addr := entry.dirtied(); addr != nil {
|
||||
j.addDirty(*addr)
|
||||
}
|
||||
}
|
||||
|
||||
// revert undoes a batch of journalled modifications along with any reverted
|
||||
// dirty handling too.
|
||||
func (j *journal) revert(statedb *CommitStateDB, snapshot int) {
|
||||
for i := len(j.entries) - 1; i >= snapshot; i-- {
|
||||
// Undo the changes made by the operation
|
||||
j.entries[i].revert(statedb)
|
||||
|
||||
// Drop any dirty tracking induced by the change
|
||||
if addr := j.entries[i].dirtied(); addr != nil {
|
||||
j.substractDirty(*addr)
|
||||
if j.getDirty(*addr) == 0 {
|
||||
j.deleteDirty(*addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
j.entries = j.entries[:snapshot]
|
||||
}
|
||||
|
||||
// dirty explicitly sets an address to dirty, even if the change entries would
|
||||
// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD
|
||||
// precompile consensus exception.
|
||||
func (j *journal) dirty(addr ethcmn.Address) {
|
||||
j.addDirty(addr)
|
||||
}
|
||||
|
||||
// length returns the current number of entries in the journal.
|
||||
func (j *journal) length() int {
|
||||
return len(j.entries)
|
||||
}
|
||||
|
||||
// getDirty returns the dirty count for a given address. If the address is not
|
||||
// found it returns 0.
|
||||
func (j *journal) getDirty(addr ethcmn.Address) int {
|
||||
idx, found := j.addressToJournalIndex[addr]
|
||||
if !found {
|
||||
return 0
|
||||
}
|
||||
|
||||
return j.dirties[idx].changes
|
||||
}
|
||||
|
||||
// addDirty adds 1 to the dirty count of an address. If the dirty entry is not
|
||||
// found it creates it.
|
||||
func (j *journal) addDirty(addr ethcmn.Address) {
|
||||
idx, found := j.addressToJournalIndex[addr]
|
||||
if !found {
|
||||
j.dirties = append(j.dirties, dirty{address: addr, changes: 0})
|
||||
idx = len(j.dirties) - 1
|
||||
j.addressToJournalIndex[addr] = idx
|
||||
}
|
||||
|
||||
j.dirties[idx].changes++
|
||||
}
|
||||
|
||||
// substractDirty subtracts 1 to the dirty count of an address. It performs a
|
||||
// no-op if the address is not found.
|
||||
func (j *journal) substractDirty(addr ethcmn.Address) {
|
||||
idx, found := j.addressToJournalIndex[addr]
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
if j.dirties[idx].changes == 0 {
|
||||
return
|
||||
}
|
||||
j.dirties[idx].changes--
|
||||
}
|
||||
|
||||
// deleteDirty deletes a dirty entry from the jounal's dirties slice. If the
|
||||
// entry is not found it performs a no-op.
|
||||
func (j *journal) deleteDirty(addr ethcmn.Address) {
|
||||
idx, found := j.addressToJournalIndex[addr]
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
j.dirties = append(j.dirties[:idx], j.dirties[idx+1:]...)
|
||||
delete(j.addressToJournalIndex, addr)
|
||||
}
|
||||
|
||||
type (
|
||||
// Changes to the account trie.
|
||||
createObjectChange struct {
|
||||
account *ethcmn.Address
|
||||
}
|
||||
|
||||
resetObjectChange struct {
|
||||
prev *stateObject
|
||||
}
|
||||
|
||||
suicideChange struct {
|
||||
account *ethcmn.Address
|
||||
prev bool // whether account had already suicided
|
||||
prevBalance sdk.Int
|
||||
}
|
||||
|
||||
// Changes to individual accounts.
|
||||
balanceChange struct {
|
||||
account *ethcmn.Address
|
||||
prev sdk.Int
|
||||
}
|
||||
|
||||
nonceChange struct {
|
||||
account *ethcmn.Address
|
||||
prev uint64
|
||||
}
|
||||
|
||||
storageChange struct {
|
||||
account *ethcmn.Address
|
||||
key, prevValue ethcmn.Hash
|
||||
}
|
||||
|
||||
codeChange struct {
|
||||
account *ethcmn.Address
|
||||
prevCode, prevHash []byte
|
||||
}
|
||||
|
||||
// Changes to other state values.
|
||||
refundChange struct {
|
||||
prev uint64
|
||||
}
|
||||
|
||||
addLogChange struct {
|
||||
txhash ethcmn.Hash
|
||||
}
|
||||
|
||||
addPreimageChange struct {
|
||||
hash ethcmn.Hash
|
||||
}
|
||||
|
||||
touchChange struct {
|
||||
account *ethcmn.Address
|
||||
// prev bool
|
||||
// prevDirty bool
|
||||
}
|
||||
)
|
||||
|
||||
func (ch createObjectChange) revert(s *CommitStateDB) {
|
||||
delete(s.stateObjectsDirty, *ch.account)
|
||||
|
||||
idx, exists := s.addressToObjectIndex[*ch.account]
|
||||
if !exists {
|
||||
// perform no-op
|
||||
return
|
||||
}
|
||||
|
||||
// remove from the slice
|
||||
delete(s.addressToObjectIndex, *ch.account)
|
||||
|
||||
// if the slice contains one element, delete it
|
||||
if len(s.stateObjects) == 1 {
|
||||
s.stateObjects = []stateEntry{}
|
||||
return
|
||||
}
|
||||
|
||||
// move the elements one position left on the array
|
||||
for i := idx + 1; i < len(s.stateObjects); i++ {
|
||||
s.stateObjects[i-1] = s.stateObjects[i]
|
||||
// the new index is i - 1
|
||||
s.addressToObjectIndex[s.stateObjects[i].address] = i - 1
|
||||
}
|
||||
|
||||
// finally, delete the last element of the slice to account for the removed object
|
||||
s.stateObjects = s.stateObjects[:len(s.stateObjects)-1]
|
||||
}
|
||||
|
||||
func (ch createObjectChange) dirtied() *ethcmn.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch resetObjectChange) revert(s *CommitStateDB) {
|
||||
s.setStateObject(ch.prev)
|
||||
}
|
||||
|
||||
func (ch resetObjectChange) dirtied() *ethcmn.Address {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch suicideChange) revert(s *CommitStateDB) {
|
||||
so := s.getStateObject(*ch.account)
|
||||
if so != nil {
|
||||
so.suicided = ch.prev
|
||||
so.setBalance(ch.prevBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func (ch suicideChange) dirtied() *ethcmn.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch touchChange) revert(s *CommitStateDB) {
|
||||
}
|
||||
|
||||
func (ch touchChange) dirtied() *ethcmn.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch balanceChange) revert(s *CommitStateDB) {
|
||||
s.getStateObject(*ch.account).setBalance(ch.prev)
|
||||
}
|
||||
|
||||
func (ch balanceChange) dirtied() *ethcmn.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch nonceChange) revert(s *CommitStateDB) {
|
||||
s.getStateObject(*ch.account).setNonce(ch.prev)
|
||||
}
|
||||
|
||||
func (ch nonceChange) dirtied() *ethcmn.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch codeChange) revert(s *CommitStateDB) {
|
||||
s.getStateObject(*ch.account).setCode(ethcmn.BytesToHash(ch.prevHash), ch.prevCode)
|
||||
}
|
||||
|
||||
func (ch codeChange) dirtied() *ethcmn.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch storageChange) revert(s *CommitStateDB) {
|
||||
s.getStateObject(*ch.account).setState(ch.key, ch.prevValue)
|
||||
}
|
||||
|
||||
func (ch storageChange) dirtied() *ethcmn.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch refundChange) revert(s *CommitStateDB) {
|
||||
s.refund = ch.prev
|
||||
}
|
||||
|
||||
func (ch refundChange) dirtied() *ethcmn.Address {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch addLogChange) revert(s *CommitStateDB) {
|
||||
logs, err := s.GetLogs(ch.txhash)
|
||||
if err != nil {
|
||||
// panic on unmarshal error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// delete logs if entry is empty or has only one item
|
||||
if len(logs) <= 1 {
|
||||
s.DeleteLogs(ch.txhash)
|
||||
} else if err := s.SetLogs(ch.txhash, logs[:len(logs)-1]); err != nil {
|
||||
// panic on marshal error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
s.logSize--
|
||||
}
|
||||
|
||||
func (ch addLogChange) dirtied() *ethcmn.Address {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch addPreimageChange) revert(s *CommitStateDB) {
|
||||
idx, exists := s.hashToPreimageIndex[ch.hash]
|
||||
if !exists {
|
||||
// perform no-op
|
||||
return
|
||||
}
|
||||
|
||||
// remove from the slice
|
||||
delete(s.hashToPreimageIndex, ch.hash)
|
||||
|
||||
// if the slice contains one element, delete it
|
||||
if len(s.preimages) == 1 {
|
||||
s.preimages = []preimageEntry{}
|
||||
return
|
||||
}
|
||||
|
||||
// move the elements one position left on the array
|
||||
for i := idx + 1; i < len(s.preimages); i++ {
|
||||
s.preimages[i-1] = s.preimages[i]
|
||||
// the new index is i - 1
|
||||
s.hashToPreimageIndex[s.preimages[i].hash] = i - 1
|
||||
}
|
||||
|
||||
// finally, delete the last element
|
||||
|
||||
s.preimages = s.preimages[:len(s.preimages)-1]
|
||||
}
|
||||
|
||||
func (ch addPreimageChange) dirtied() *ethcmn.Address {
|
||||
return nil
|
||||
}
|
@ -1,354 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||
enccodec "github.com/cosmos/ethermint/encoding/codec"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
tmdb "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
paramkeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/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/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
)
|
||||
|
||||
func newTestCodec() (codec.BinaryMarshaler, *codec.LegacyAmino) {
|
||||
interfaceRegistry := codectypes.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(interfaceRegistry)
|
||||
amino := codec.NewLegacyAmino()
|
||||
|
||||
sdk.RegisterLegacyAminoCodec(amino)
|
||||
|
||||
enccodec.RegisterInterfaces(interfaceRegistry)
|
||||
|
||||
return cdc, amino
|
||||
}
|
||||
|
||||
type JournalTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
address ethcmn.Address
|
||||
journal *journal
|
||||
ctx sdk.Context
|
||||
stateDB *CommitStateDB
|
||||
}
|
||||
|
||||
func (suite *JournalTestSuite) SetupTest() {
|
||||
suite.setup()
|
||||
|
||||
privkey, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes())
|
||||
suite.journal = newJournal()
|
||||
|
||||
balance := ethermint.NewPhotonCoin(sdk.NewInt(100))
|
||||
acc := ðermint.EthAccount{
|
||||
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0),
|
||||
CodeHash: ethcrypto.Keccak256(nil),
|
||||
}
|
||||
|
||||
suite.stateDB.accountKeeper.SetAccount(suite.ctx, acc)
|
||||
suite.stateDB.bankKeeper.SetBalance(suite.ctx, sdk.AccAddress(suite.address.Bytes()), balance)
|
||||
suite.stateDB.SetLogs(ethcmn.BytesToHash([]byte("txhash")), []*ethtypes.Log{
|
||||
{
|
||||
Address: suite.address,
|
||||
Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic_0"))},
|
||||
Data: []byte("data_0"),
|
||||
BlockNumber: 1,
|
||||
TxHash: ethcmn.BytesToHash([]byte("tx_hash")),
|
||||
TxIndex: 1,
|
||||
BlockHash: ethcmn.BytesToHash([]byte("block_hash")),
|
||||
Index: 1,
|
||||
Removed: false,
|
||||
},
|
||||
{
|
||||
Address: suite.address,
|
||||
Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic_1"))},
|
||||
Data: []byte("data_1"),
|
||||
BlockNumber: 10,
|
||||
TxHash: ethcmn.BytesToHash([]byte("tx_hash")),
|
||||
TxIndex: 0,
|
||||
BlockHash: ethcmn.BytesToHash([]byte("block_hash")),
|
||||
Index: 0,
|
||||
Removed: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// setup performs a manual setup of the GoLevelDB and mounts the required IAVL stores. We use the manual
|
||||
// setup here instead of the Ethermint app test setup because the journal methods are private and using
|
||||
// the latter would result in a cycle dependency. We also want to avoid declaring the journal methods public
|
||||
// to maintain consistency with the Geth implementation.
|
||||
func (suite *JournalTestSuite) setup() {
|
||||
authKey := sdk.NewKVStoreKey(authtypes.StoreKey)
|
||||
paramsKey := sdk.NewKVStoreKey(paramtypes.StoreKey)
|
||||
paramsTKey := sdk.NewTransientStoreKey(paramtypes.TStoreKey)
|
||||
tKey := sdk.NewTransientStoreKey(TransientKey)
|
||||
bankKey := sdk.NewKVStoreKey(banktypes.StoreKey)
|
||||
storeKey := sdk.NewKVStoreKey(StoreKey)
|
||||
|
||||
db, err := tmdb.NewDB("state", tmdb.GoLevelDBBackend, "temp")
|
||||
suite.Require().NoError(err)
|
||||
|
||||
defer func() {
|
||||
os.RemoveAll("temp")
|
||||
}()
|
||||
|
||||
cms := store.NewCommitMultiStore(db)
|
||||
cms.MountStoreWithDB(authKey, sdk.StoreTypeIAVL, db)
|
||||
cms.MountStoreWithDB(bankKey, sdk.StoreTypeIAVL, db)
|
||||
cms.MountStoreWithDB(paramsKey, sdk.StoreTypeIAVL, db)
|
||||
cms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db)
|
||||
cms.MountStoreWithDB(paramsTKey, sdk.StoreTypeTransient, db)
|
||||
cms.MountStoreWithDB(tKey, sdk.StoreTypeTransient, db)
|
||||
|
||||
err = cms.LoadLatestVersion()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
cdc, amino := newTestCodec()
|
||||
|
||||
paramsKeeper := paramkeeper.NewKeeper(cdc, amino, paramsKey, paramsTKey)
|
||||
|
||||
authSubspace := paramsKeeper.Subspace(authtypes.ModuleName)
|
||||
bankSubspace := paramsKeeper.Subspace(banktypes.ModuleName)
|
||||
evmSubspace := paramsKeeper.Subspace(ModuleName).WithKeyTable(ParamKeyTable())
|
||||
|
||||
ak := authkeeper.NewAccountKeeper(cdc, authKey, authSubspace, ethermint.ProtoAccount, nil)
|
||||
bk := bankkeeper.NewBaseKeeper(cdc, bankKey, ak, bankSubspace, nil)
|
||||
suite.ctx = sdk.NewContext(cms, tmproto.Header{ChainID: "ethermint-8"}, false, tmlog.NewNopLogger())
|
||||
suite.stateDB = NewCommitStateDB(suite.ctx, storeKey, tKey, evmSubspace, ak, bk).WithContext(suite.ctx)
|
||||
suite.stateDB.SetParams(DefaultParams())
|
||||
}
|
||||
|
||||
func TestJournalTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(JournalTestSuite))
|
||||
}
|
||||
|
||||
func (suite *JournalTestSuite) TestJournal_append_revert() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
entry journalEntry
|
||||
}{
|
||||
{
|
||||
"createObjectChange",
|
||||
createObjectChange{
|
||||
account: &suite.address,
|
||||
},
|
||||
},
|
||||
{
|
||||
"resetObjectChange",
|
||||
resetObjectChange{
|
||||
prev: &stateObject{
|
||||
address: suite.address,
|
||||
balance: sdk.OneInt(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"suicideChange",
|
||||
suicideChange{
|
||||
account: &suite.address,
|
||||
prev: false,
|
||||
prevBalance: sdk.OneInt(),
|
||||
},
|
||||
},
|
||||
{
|
||||
"balanceChange",
|
||||
balanceChange{
|
||||
account: &suite.address,
|
||||
prev: sdk.OneInt(),
|
||||
},
|
||||
},
|
||||
{
|
||||
"nonceChange",
|
||||
nonceChange{
|
||||
account: &suite.address,
|
||||
prev: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
"storageChange",
|
||||
storageChange{
|
||||
account: &suite.address,
|
||||
key: ethcmn.BytesToHash([]byte("key")),
|
||||
prevValue: ethcmn.BytesToHash([]byte("value")),
|
||||
},
|
||||
},
|
||||
{
|
||||
"codeChange",
|
||||
codeChange{
|
||||
account: &suite.address,
|
||||
prevCode: []byte("code"),
|
||||
prevHash: []byte("hash"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"touchChange",
|
||||
touchChange{
|
||||
account: &suite.address,
|
||||
},
|
||||
},
|
||||
{
|
||||
"refundChange",
|
||||
refundChange{
|
||||
prev: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
"addPreimageChange",
|
||||
addPreimageChange{
|
||||
hash: ethcmn.BytesToHash([]byte("hash")),
|
||||
},
|
||||
},
|
||||
{
|
||||
"addLogChange",
|
||||
addLogChange{
|
||||
txhash: ethcmn.BytesToHash([]byte("hash")),
|
||||
},
|
||||
},
|
||||
{
|
||||
"addLogChange - 2 logs",
|
||||
addLogChange{
|
||||
txhash: ethcmn.BytesToHash([]byte("txhash")),
|
||||
},
|
||||
},
|
||||
{
|
||||
"accessListAddAccountChange",
|
||||
accessListAddAccountChange{
|
||||
address: &suite.address,
|
||||
},
|
||||
},
|
||||
}
|
||||
var dirtyCount int
|
||||
for i, tc := range testCases {
|
||||
suite.journal.append(tc.entry)
|
||||
suite.Require().Equal(suite.journal.length(), i+1, tc.name)
|
||||
if tc.entry.dirtied() != nil {
|
||||
dirtyCount++
|
||||
|
||||
suite.Require().Equal(dirtyCount, suite.journal.getDirty(suite.address), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
// revert to the initial journal state
|
||||
suite.journal.revert(suite.stateDB, 0)
|
||||
|
||||
// verify the dirty entry has been deleted
|
||||
idx, ok := suite.journal.addressToJournalIndex[suite.address]
|
||||
suite.Require().False(ok)
|
||||
suite.Require().Zero(idx)
|
||||
}
|
||||
|
||||
func (suite *JournalTestSuite) TestJournal_preimage_revert() {
|
||||
suite.stateDB.preimages = []preimageEntry{
|
||||
{
|
||||
hash: ethcmn.BytesToHash([]byte("hash")),
|
||||
preimage: []byte("preimage0"),
|
||||
},
|
||||
{
|
||||
hash: ethcmn.BytesToHash([]byte("hash1")),
|
||||
preimage: []byte("preimage1"),
|
||||
},
|
||||
{
|
||||
hash: ethcmn.BytesToHash([]byte("hash2")),
|
||||
preimage: []byte("preimage2"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, preimage := range suite.stateDB.preimages {
|
||||
suite.stateDB.hashToPreimageIndex[preimage.hash] = i
|
||||
}
|
||||
|
||||
change := addPreimageChange{
|
||||
hash: ethcmn.BytesToHash([]byte("hash")),
|
||||
}
|
||||
|
||||
// delete first entry
|
||||
change.revert(suite.stateDB)
|
||||
suite.Require().Len(suite.stateDB.preimages, 2)
|
||||
suite.Require().Equal(len(suite.stateDB.preimages), len(suite.stateDB.hashToPreimageIndex))
|
||||
|
||||
for i, entry := range suite.stateDB.preimages {
|
||||
suite.Require().Equal(fmt.Sprintf("preimage%d", i+1), string(entry.preimage), entry.hash.String())
|
||||
idx, found := suite.stateDB.hashToPreimageIndex[entry.hash]
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(i, idx)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *JournalTestSuite) TestJournal_createObjectChange_revert() {
|
||||
addr := ethcmn.BytesToAddress([]byte("addr"))
|
||||
|
||||
suite.stateDB.stateObjects = []stateEntry{
|
||||
{
|
||||
address: addr,
|
||||
stateObject: &stateObject{
|
||||
address: addr,
|
||||
},
|
||||
},
|
||||
{
|
||||
address: ethcmn.BytesToAddress([]byte("addr1")),
|
||||
stateObject: &stateObject{
|
||||
address: ethcmn.BytesToAddress([]byte("addr1")),
|
||||
},
|
||||
},
|
||||
{
|
||||
address: ethcmn.BytesToAddress([]byte("addr2")),
|
||||
stateObject: &stateObject{
|
||||
address: ethcmn.BytesToAddress([]byte("addr2")),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, so := range suite.stateDB.stateObjects {
|
||||
suite.stateDB.addressToObjectIndex[so.address] = i
|
||||
}
|
||||
|
||||
change := createObjectChange{
|
||||
account: &addr,
|
||||
}
|
||||
|
||||
// delete first entry
|
||||
change.revert(suite.stateDB)
|
||||
suite.Require().Len(suite.stateDB.stateObjects, 2)
|
||||
suite.Require().Equal(len(suite.stateDB.stateObjects), len(suite.stateDB.addressToObjectIndex))
|
||||
|
||||
for i, entry := range suite.stateDB.stateObjects {
|
||||
suite.Require().Equal(ethcmn.BytesToAddress([]byte(fmt.Sprintf("addr%d", i+1))).String(), entry.address.String())
|
||||
idx, found := suite.stateDB.addressToObjectIndex[entry.address]
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(i, idx)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *JournalTestSuite) TestJournal_dirty() {
|
||||
// dirty entry hasn't been set
|
||||
idx, ok := suite.journal.addressToJournalIndex[suite.address]
|
||||
suite.Require().False(ok)
|
||||
suite.Require().Zero(idx)
|
||||
|
||||
// update dirty count
|
||||
suite.journal.dirty(suite.address)
|
||||
suite.Require().Equal(1, suite.journal.getDirty(suite.address))
|
||||
}
|
@ -89,7 +89,7 @@ func (log *Log) ToEthereum() *ethtypes.Log {
|
||||
}
|
||||
|
||||
func NewLogsFromEth(ethlogs []*ethtypes.Log) []*Log {
|
||||
var logs []*Log
|
||||
var logs []*Log // nolint: prealloc
|
||||
for _, ethlog := range ethlogs {
|
||||
logs = append(logs, NewLogFromEth(ethlog))
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
@ -14,7 +13,6 @@ import (
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -191,48 +189,6 @@ func (msg MsgEthereumTx) GetSignBytes() []byte {
|
||||
panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign")
|
||||
}
|
||||
|
||||
// RLPSignBytes returns the RLP hash of an Ethereum transaction message with a
|
||||
// given chainID used for signing.
|
||||
func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
||||
if msg.Data.ChainID != nil {
|
||||
chainID = new(big.Int).SetBytes(msg.Data.ChainID)
|
||||
}
|
||||
|
||||
var accessList *ethtypes.AccessList
|
||||
if msg.Data.Accesses != nil {
|
||||
accessList = msg.Data.Accesses.ToEthAccessList()
|
||||
}
|
||||
|
||||
return rlpHash([]interface{}{
|
||||
chainID,
|
||||
msg.Data.Nonce,
|
||||
new(big.Int).SetBytes(msg.Data.GasPrice),
|
||||
msg.Data.GasLimit,
|
||||
msg.To(),
|
||||
new(big.Int).SetBytes(msg.Data.Amount),
|
||||
new(big.Int).SetBytes(msg.Data.Input),
|
||||
accessList,
|
||||
})
|
||||
}
|
||||
|
||||
// EncodeRLP implements the rlp.Encoder interface.
|
||||
func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error {
|
||||
tx := msg.AsTransaction()
|
||||
return tx.EncodeRLP(w)
|
||||
}
|
||||
|
||||
// DecodeRLP implements the rlp.Decoder interface.
|
||||
func (msg *MsgEthereumTx) DecodeRLP(stream *rlp.Stream) error {
|
||||
tx := ðtypes.Transaction{}
|
||||
if err := tx.DecodeRLP(stream); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.FromEthereumTx(tx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sign calculates a secp256k1 ECDSA signature and signs the transaction. It
|
||||
// takes a keyring signer and the chainID to sign an Ethereum transaction according to
|
||||
// EIP155 standard.
|
||||
|
@ -1,7 +1,6 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
@ -15,7 +14,6 @@ import (
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
type MsgsTestSuite struct {
|
||||
@ -91,23 +89,6 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *MsgsTestSuite) TestMsgEthereumTx_EncodeRLP() {
|
||||
expMsg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||
|
||||
raw, err := rlp.EncodeToBytes(&expMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
msg := &MsgEthereumTx{}
|
||||
err = rlp.Decode(bytes.NewReader(raw), &msg)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(expMsg.Data, msg.Data)
|
||||
}
|
||||
|
||||
func (suite *MsgsTestSuite) TestMsgEthereumTx_RLPSignBytes() {
|
||||
msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||
suite.NotPanics(func() { _ = msg.RLPSignBytes(suite.chainID) })
|
||||
}
|
||||
|
||||
func (suite *MsgsTestSuite) TestMsgEthereumTx_Sign() {
|
||||
msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||
|
||||
|
@ -73,8 +73,8 @@ var xxx_messageInfo_QueryAccountRequest proto.InternalMessageInfo
|
||||
type QueryAccountResponse struct {
|
||||
// balance is the balance of the EVM denomination.
|
||||
Balance string `protobuf:"bytes,1,opt,name=balance,proto3" json:"balance,omitempty"`
|
||||
// code_hash is the code bytes from the EOA.
|
||||
CodeHash []byte `protobuf:"bytes,2,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"`
|
||||
// code hash is the hex-formatted code bytes from the EOA.
|
||||
CodeHash string `protobuf:"bytes,2,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"`
|
||||
// nonce is the account's sequence number.
|
||||
Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"`
|
||||
}
|
||||
@ -119,11 +119,11 @@ func (m *QueryAccountResponse) GetBalance() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *QueryAccountResponse) GetCodeHash() []byte {
|
||||
func (m *QueryAccountResponse) GetCodeHash() string {
|
||||
if m != nil {
|
||||
return m.CodeHash
|
||||
}
|
||||
return nil
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *QueryAccountResponse) GetNonce() uint64 {
|
||||
@ -972,72 +972,72 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_8bbc79ec2b6c5cb2 = []byte{
|
||||
// 1028 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x96, 0xcf, 0x6f, 0x1b, 0x45,
|
||||
0x14, 0xc7, 0xbd, 0x8d, 0x63, 0x27, 0x2f, 0x09, 0x2a, 0x83, 0x29, 0x61, 0x8b, 0x1c, 0x6b, 0x51,
|
||||
0x63, 0xe7, 0x47, 0x77, 0x6b, 0x23, 0xf1, 0x4b, 0x48, 0x28, 0xa9, 0x14, 0x2a, 0x51, 0xa1, 0xe2,
|
||||
0xf4, 0xc4, 0xc5, 0x1a, 0xaf, 0x47, 0x6b, 0x2b, 0xeb, 0x1d, 0xd7, 0xb3, 0xb6, 0x12, 0x45, 0xb9,
|
||||
0x70, 0x40, 0x20, 0x71, 0x00, 0x71, 0xa0, 0x42, 0x42, 0xea, 0x95, 0x1b, 0x7f, 0x46, 0x8f, 0x95,
|
||||
0xb8, 0x70, 0x42, 0x28, 0xe1, 0xc0, 0x9f, 0x81, 0x66, 0xe6, 0xad, 0xbd, 0x9b, 0x78, 0xb3, 0x0e,
|
||||
0xb7, 0x9d, 0xf1, 0xfb, 0xf1, 0x79, 0xef, 0xcd, 0x7c, 0xc7, 0x60, 0xb1, 0xb0, 0xcb, 0x86, 0xfd,
|
||||
0x5e, 0x10, 0x3a, 0x6c, 0xdc, 0x77, 0xc6, 0x75, 0xea, 0x0f, 0xba, 0xb4, 0xee, 0x3c, 0x1b, 0xb1,
|
||||
0xe1, 0x89, 0x3d, 0x18, 0xf2, 0x90, 0x93, 0x3b, 0x13, 0x1b, 0x9b, 0x8d, 0xfb, 0x76, 0x64, 0x63,
|
||||
0x96, 0x3c, 0xee, 0x71, 0x65, 0xe2, 0xc8, 0x2f, 0x6d, 0x6d, 0x6e, 0xbb, 0x5c, 0xf4, 0xb9, 0x70,
|
||||
0xda, 0x54, 0x30, 0x1d, 0xc6, 0x19, 0xd7, 0xdb, 0x2c, 0xa4, 0x75, 0x67, 0x40, 0xbd, 0x5e, 0x40,
|
||||
0xc3, 0x1e, 0x0f, 0xd0, 0xf6, 0x1d, 0x8f, 0x73, 0xcf, 0x67, 0x0e, 0x1d, 0xf4, 0x1c, 0x1a, 0x04,
|
||||
0x3c, 0x54, 0x3f, 0x0a, 0xfc, 0xb5, 0x92, 0xc2, 0x26, 0x21, 0x94, 0x85, 0xf5, 0x11, 0xbc, 0xf1,
|
||||
0xa5, 0xcc, 0xb0, 0xe7, 0xba, 0x7c, 0x14, 0x84, 0x4d, 0xf6, 0x6c, 0xc4, 0x44, 0x48, 0xd6, 0xa1,
|
||||
0x48, 0x3b, 0x9d, 0x21, 0x13, 0x62, 0xdd, 0xa8, 0x18, 0xb5, 0xe5, 0x66, 0xb4, 0xfc, 0x78, 0xe9,
|
||||
0xdb, 0x17, 0x1b, 0xb9, 0x7f, 0x5f, 0x6c, 0xe4, 0x2c, 0x17, 0x4a, 0x49, 0x57, 0x31, 0xe0, 0x81,
|
||||
0x60, 0xd2, 0xb7, 0x4d, 0x7d, 0x1a, 0xb8, 0x2c, 0xf2, 0xc5, 0x25, 0xb9, 0x0b, 0xcb, 0x2e, 0xef,
|
||||
0xb0, 0x56, 0x97, 0x8a, 0xee, 0xfa, 0xad, 0x8a, 0x51, 0x5b, 0x6d, 0x2e, 0xc9, 0x8d, 0x47, 0x54,
|
||||
0x74, 0x49, 0x09, 0x16, 0x03, 0x2e, 0x9d, 0x16, 0x2a, 0x46, 0x2d, 0xdf, 0xd4, 0x0b, 0xeb, 0x53,
|
||||
0x78, 0x5b, 0x25, 0x79, 0xa8, 0x5a, 0xf2, 0x3f, 0x28, 0xbf, 0x31, 0xc0, 0x9c, 0x15, 0x01, 0x61,
|
||||
0xef, 0xc1, 0x6b, 0xba, 0xdb, 0xad, 0x64, 0xa4, 0x35, 0xbd, 0xbb, 0xa7, 0x37, 0x89, 0x09, 0x4b,
|
||||
0x42, 0x26, 0x95, 0x7c, 0xb7, 0x14, 0xdf, 0x64, 0x2d, 0x43, 0x50, 0x1d, 0xb5, 0x15, 0x8c, 0xfa,
|
||||
0x6d, 0x36, 0xc4, 0x0a, 0xd6, 0x70, 0xf7, 0x0b, 0xb5, 0x39, 0xe9, 0xf4, 0xbe, 0x6e, 0xc6, 0x4d,
|
||||
0x6a, 0x78, 0x80, 0x9d, 0x9e, 0xb8, 0x66, 0x75, 0xda, 0xfa, 0x1c, 0x93, 0x1d, 0x86, 0x7c, 0x48,
|
||||
0xbd, 0xec, 0x64, 0xe4, 0x36, 0x2c, 0x1c, 0xb1, 0x13, 0x55, 0xdb, 0x72, 0x53, 0x7e, 0xc6, 0xd2,
|
||||
0xef, 0x62, 0xfa, 0x49, 0x30, 0x4c, 0x5f, 0x82, 0xc5, 0x31, 0xf5, 0x47, 0x51, 0x72, 0xbd, 0xb0,
|
||||
0xde, 0x87, 0xdb, 0xd8, 0xef, 0xce, 0x8d, 0x8a, 0xac, 0xc2, 0xeb, 0x31, 0x3f, 0x4c, 0x41, 0x20,
|
||||
0x2f, 0x0f, 0x88, 0xf2, 0x5a, 0x6d, 0xaa, 0x6f, 0xab, 0x01, 0x44, 0x19, 0x3e, 0x3d, 0x7e, 0xcc,
|
||||
0x3d, 0x11, 0xa5, 0x20, 0x90, 0x57, 0xc7, 0x4a, 0xc7, 0x57, 0xdf, 0xb1, 0xe0, 0x07, 0xd8, 0x8f,
|
||||
0xc8, 0x07, 0xc3, 0x3b, 0x90, 0xf7, 0xb9, 0x27, 0xa1, 0x16, 0x6a, 0x2b, 0x8d, 0xbb, 0xf6, 0xec,
|
||||
0x6b, 0x6a, 0x3f, 0xe6, 0x5e, 0x53, 0x19, 0x5a, 0x67, 0xf0, 0xa6, 0x9e, 0x84, 0xcf, 0xdd, 0xa3,
|
||||
0x8c, 0xf4, 0xe4, 0x00, 0x60, 0x7a, 0x5f, 0x55, 0x6b, 0x57, 0x1a, 0x9b, 0xb6, 0x3e, 0x58, 0xb6,
|
||||
0xbc, 0xdc, 0xb6, 0xd6, 0x08, 0xbc, 0xdc, 0xf6, 0x93, 0xe9, 0xa4, 0x9a, 0x31, 0xcf, 0x58, 0x19,
|
||||
0xbf, 0x19, 0x70, 0xe7, 0x72, 0x7e, 0x2c, 0xe5, 0x00, 0x8a, 0xe1, 0x71, 0x2b, 0x56, 0x4d, 0x35,
|
||||
0xad, 0x9a, 0xa7, 0x43, 0x1a, 0x08, 0xea, 0xca, 0xd0, 0x32, 0xc2, 0x7e, 0xfe, 0xe5, 0x5f, 0x1b,
|
||||
0xb9, 0x66, 0x21, 0x54, 0xad, 0x21, 0x9f, 0xcd, 0x80, 0xae, 0x66, 0x42, 0x6b, 0x88, 0x38, 0xb5,
|
||||
0xb5, 0x1e, 0x47, 0xdd, 0xf7, 0x39, 0xef, 0x63, 0x6d, 0x96, 0x03, 0x6f, 0x5d, 0xf9, 0x65, 0x7a,
|
||||
0xa4, 0xda, 0x72, 0x03, 0x07, 0xae, 0x17, 0x56, 0x09, 0x27, 0xfe, 0x84, 0x0e, 0x69, 0x3f, 0x6a,
|
||||
0xb9, 0x75, 0x88, 0x33, 0x8d, 0x76, 0x31, 0xc4, 0x27, 0x50, 0x18, 0xa8, 0x1d, 0x15, 0x63, 0xa5,
|
||||
0x51, 0x4e, 0xeb, 0x83, 0xf6, 0x8b, 0xca, 0xd7, 0x3e, 0xd6, 0x23, 0xa4, 0x3e, 0x94, 0x42, 0xea,
|
||||
0x3e, 0xa4, 0xbe, 0x9f, 0x7d, 0x77, 0x4a, 0xb0, 0xd8, 0x0b, 0x06, 0xa3, 0x10, 0x25, 0x4d, 0x2f,
|
||||
0xac, 0xfb, 0x58, 0x65, 0x3c, 0xd2, 0xf4, 0x54, 0x77, 0x68, 0x48, 0xa3, 0x53, 0x2d, 0xbf, 0x1b,
|
||||
0xcf, 0x57, 0x61, 0x51, 0xd9, 0x93, 0x9f, 0x0d, 0x28, 0xa2, 0x4c, 0x91, 0x9d, 0x34, 0xf8, 0x19,
|
||||
0xa2, 0x6d, 0xee, 0xce, 0x67, 0xac, 0x21, 0xac, 0xfa, 0xd7, 0x7f, 0xfc, 0xf3, 0xd3, 0xad, 0x1d,
|
||||
0xb2, 0xe5, 0xa4, 0x3c, 0x12, 0x28, 0x5f, 0xce, 0x29, 0xd6, 0x79, 0x46, 0x7e, 0x37, 0x60, 0x2d,
|
||||
0x21, 0xa3, 0xa4, 0x7e, 0x6d, 0xca, 0x59, 0xa2, 0x6d, 0x36, 0x6e, 0xe2, 0x82, 0xac, 0x1f, 0x2a,
|
||||
0xd6, 0x06, 0x79, 0x90, 0xc6, 0x1a, 0x69, 0xf8, 0x15, 0xe4, 0xe7, 0x06, 0x14, 0x51, 0x36, 0x33,
|
||||
0x9a, 0x99, 0xd4, 0xe5, 0x8c, 0x66, 0x5e, 0x52, 0x62, 0xab, 0xa1, 0x00, 0x77, 0xc9, 0x76, 0x1a,
|
||||
0x20, 0x0a, 0xb3, 0x88, 0xa1, 0xfd, 0x6a, 0x40, 0x11, 0x25, 0x35, 0x03, 0x2d, 0xa9, 0xe2, 0x19,
|
||||
0x68, 0x97, 0x54, 0xda, 0xfa, 0x40, 0xa1, 0xd5, 0x89, 0x93, 0x86, 0x26, 0xb4, 0xc3, 0x94, 0xcc,
|
||||
0x39, 0x3d, 0x62, 0x27, 0x67, 0xe4, 0x7b, 0x03, 0xf2, 0x52, 0x8c, 0x49, 0x2d, 0x63, 0x62, 0x13,
|
||||
0x9d, 0x37, 0xb7, 0xe6, 0xb0, 0x44, 0x2c, 0x47, 0x61, 0x6d, 0x91, 0x6a, 0xfa, 0x48, 0x3b, 0x89,
|
||||
0x76, 0xfd, 0x68, 0x40, 0x41, 0xcb, 0x37, 0xd9, 0xbe, 0x36, 0x4d, 0xe2, 0x5d, 0x30, 0x77, 0xe6,
|
||||
0xb2, 0x45, 0x28, 0x5b, 0x41, 0xd5, 0xc8, 0x66, 0x1a, 0x14, 0x4a, 0xac, 0x73, 0x2a, 0x05, 0x5e,
|
||||
0x8d, 0x70, 0x79, 0x22, 0xc5, 0xe4, 0xfe, 0xf5, 0x47, 0xe6, 0xd2, 0x93, 0x61, 0xda, 0xf3, 0x9a,
|
||||
0xcf, 0x7b, 0x61, 0xdb, 0xd2, 0x25, 0xc1, 0xf7, 0x8b, 0x01, 0x30, 0x55, 0x59, 0x32, 0x47, 0xc6,
|
||||
0xb8, 0x50, 0x9b, 0xce, 0xdc, 0xf6, 0x88, 0xb8, 0xa3, 0x10, 0xef, 0x91, 0x77, 0xaf, 0x47, 0x54,
|
||||
0xaa, 0x4e, 0xbe, 0x33, 0xa0, 0xa0, 0x35, 0x38, 0x63, 0xa0, 0x09, 0xd9, 0xcf, 0x18, 0x68, 0xf2,
|
||||
0x31, 0xb0, 0x36, 0x15, 0x50, 0x85, 0x94, 0xd3, 0x80, 0xb4, 0xec, 0xab, 0x46, 0x4d, 0x85, 0x3a,
|
||||
0xa3, 0x51, 0x57, 0xde, 0x86, 0x8c, 0x46, 0x5d, 0x7d, 0x01, 0xb2, 0x1b, 0x25, 0x94, 0x4f, 0xcb,
|
||||
0xa5, 0xbe, 0xbf, 0xbf, 0xf7, 0xf2, 0xbc, 0x6c, 0xbc, 0x3a, 0x2f, 0x1b, 0x7f, 0x9f, 0x97, 0x8d,
|
||||
0x1f, 0x2e, 0xca, 0xb9, 0x57, 0x17, 0xe5, 0xdc, 0x9f, 0x17, 0xe5, 0xdc, 0x57, 0x55, 0xaf, 0x17,
|
||||
0x76, 0x47, 0x6d, 0xdb, 0xe5, 0x7d, 0x94, 0xc0, 0x58, 0xbc, 0x63, 0x15, 0x31, 0x3c, 0x19, 0x30,
|
||||
0xd1, 0x2e, 0xa8, 0x7f, 0xfb, 0xef, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0xc1, 0xa4, 0x09, 0x94,
|
||||
0xad, 0x0c, 0x00, 0x00,
|
||||
// 1026 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x96, 0xcf, 0x6f, 0xe3, 0x44,
|
||||
0x14, 0xc7, 0xe3, 0x36, 0x4d, 0xda, 0xd7, 0x16, 0x2d, 0x43, 0x58, 0x8a, 0x17, 0xa5, 0x95, 0xd1,
|
||||
0x36, 0xe9, 0x8f, 0xb5, 0x37, 0x41, 0xe2, 0x97, 0x90, 0x50, 0xbb, 0x52, 0x59, 0x89, 0x15, 0x5a,
|
||||
0xd2, 0x3d, 0x71, 0x89, 0x26, 0xce, 0xc8, 0x89, 0xea, 0x78, 0xb2, 0x19, 0x27, 0x6a, 0x55, 0xf5,
|
||||
0xc2, 0x01, 0x81, 0xc4, 0x01, 0xc4, 0x81, 0x15, 0x12, 0xd2, 0x5e, 0xb9, 0xf1, 0x67, 0xec, 0xb1,
|
||||
0x12, 0x17, 0x4e, 0x08, 0xb5, 0x1c, 0xf8, 0x33, 0xd0, 0xcc, 0x3c, 0x27, 0x76, 0x1b, 0xd7, 0x29,
|
||||
0x37, 0xcf, 0xe4, 0xfd, 0xf8, 0xbc, 0xf7, 0x66, 0xbe, 0x13, 0xb0, 0x58, 0xd8, 0x61, 0x83, 0x5e,
|
||||
0x37, 0x08, 0x1d, 0x36, 0xea, 0x39, 0xa3, 0x1a, 0xf5, 0xfb, 0x1d, 0x5a, 0x73, 0x9e, 0x0f, 0xd9,
|
||||
0xe0, 0xc4, 0xee, 0x0f, 0x78, 0xc8, 0xc9, 0xdd, 0xb1, 0x8d, 0xcd, 0x46, 0x3d, 0x3b, 0xb2, 0x31,
|
||||
0x4b, 0x1e, 0xf7, 0xb8, 0x32, 0x71, 0xe4, 0x97, 0xb6, 0x36, 0xb7, 0x5d, 0x2e, 0x7a, 0x5c, 0x38,
|
||||
0x2d, 0x2a, 0x98, 0x0e, 0xe3, 0x8c, 0x6a, 0x2d, 0x16, 0xd2, 0x9a, 0xd3, 0xa7, 0x5e, 0x37, 0xa0,
|
||||
0x61, 0x97, 0x07, 0x68, 0xfb, 0x8e, 0xc7, 0xb9, 0xe7, 0x33, 0x87, 0xf6, 0xbb, 0x0e, 0x0d, 0x02,
|
||||
0x1e, 0xaa, 0x1f, 0x05, 0xfe, 0xba, 0x91, 0xc2, 0x26, 0x21, 0x94, 0x85, 0xf5, 0x11, 0xbc, 0xf1,
|
||||
0xa5, 0xcc, 0xb0, 0xe7, 0xba, 0x7c, 0x18, 0x84, 0x0d, 0xf6, 0x7c, 0xc8, 0x44, 0x48, 0xd6, 0xa0,
|
||||
0x48, 0xdb, 0xed, 0x01, 0x13, 0x62, 0xcd, 0xd8, 0x30, 0xaa, 0x4b, 0x8d, 0x68, 0xf9, 0xf1, 0xe2,
|
||||
0xb7, 0x2f, 0xd7, 0x73, 0xff, 0xbe, 0x5c, 0xcf, 0x59, 0x2e, 0x94, 0x92, 0xae, 0xa2, 0xcf, 0x03,
|
||||
0xc1, 0xa4, 0x6f, 0x8b, 0xfa, 0x34, 0x70, 0x59, 0xe4, 0x8b, 0x4b, 0x72, 0x0f, 0x96, 0x5c, 0xde,
|
||||
0x66, 0xcd, 0x0e, 0x15, 0x9d, 0xb5, 0x39, 0xf5, 0xdb, 0xa2, 0xdc, 0x78, 0x4c, 0x45, 0x87, 0x94,
|
||||
0x60, 0x21, 0xe0, 0xd2, 0x69, 0x7e, 0xc3, 0xa8, 0xe6, 0x1b, 0x7a, 0x61, 0x7d, 0x0a, 0x6f, 0xab,
|
||||
0x24, 0x8f, 0x54, 0x4b, 0xfe, 0x07, 0xe5, 0x37, 0x06, 0x98, 0xd3, 0x22, 0x20, 0xec, 0x7d, 0x78,
|
||||
0x4d, 0x77, 0xbb, 0x99, 0x8c, 0xb4, 0xaa, 0x77, 0xf7, 0xf4, 0x26, 0x31, 0x61, 0x51, 0xc8, 0xa4,
|
||||
0x92, 0x6f, 0x4e, 0xf1, 0x8d, 0xd7, 0x32, 0x04, 0xd5, 0x51, 0x9b, 0xc1, 0xb0, 0xd7, 0x62, 0x03,
|
||||
0xac, 0x60, 0x15, 0x77, 0xbf, 0x50, 0x9b, 0xe3, 0x4e, 0xef, 0xeb, 0x66, 0xdc, 0xa6, 0x86, 0x87,
|
||||
0xd8, 0xe9, 0xb1, 0x6b, 0x56, 0xa7, 0xad, 0xcf, 0x31, 0xd9, 0x61, 0xc8, 0x07, 0xd4, 0xcb, 0x4e,
|
||||
0x46, 0xee, 0xc0, 0xfc, 0x11, 0x3b, 0xc1, 0xa1, 0xc8, 0xcf, 0x58, 0xfa, 0x5d, 0x4c, 0x3f, 0x0e,
|
||||
0x86, 0xe9, 0x4b, 0xb0, 0x30, 0xa2, 0xfe, 0x30, 0x4a, 0xae, 0x17, 0xd6, 0xfb, 0x70, 0x07, 0xfb,
|
||||
0xdd, 0xbe, 0x55, 0x91, 0x15, 0x78, 0x3d, 0xe6, 0x87, 0x29, 0x08, 0xe4, 0xe5, 0x01, 0x51, 0x5e,
|
||||
0x2b, 0x0d, 0xf5, 0x6d, 0xd5, 0x81, 0x28, 0xc3, 0x67, 0xc7, 0x4f, 0xb8, 0x27, 0xa2, 0x14, 0x04,
|
||||
0xf2, 0xea, 0x58, 0xe9, 0xf8, 0xea, 0x3b, 0x16, 0xfc, 0x00, 0xfb, 0x11, 0xf9, 0x60, 0x78, 0x07,
|
||||
0xf2, 0x3e, 0xf7, 0x24, 0xd4, 0x7c, 0x75, 0xb9, 0x7e, 0xcf, 0x9e, 0x7e, 0x4d, 0xed, 0x27, 0xdc,
|
||||
0x6b, 0x28, 0x43, 0xeb, 0x0c, 0xde, 0xd4, 0x93, 0xf0, 0xb9, 0x7b, 0x94, 0x91, 0x9e, 0x1c, 0x00,
|
||||
0x4c, 0xee, 0xab, 0x6a, 0xed, 0x72, 0x7d, 0xd3, 0xd6, 0x07, 0xcb, 0x96, 0x97, 0xdb, 0xd6, 0x1a,
|
||||
0x81, 0x97, 0xdb, 0x7e, 0x3a, 0x99, 0x54, 0x23, 0xe6, 0x19, 0x2b, 0xe3, 0x37, 0x03, 0xee, 0x5e,
|
||||
0xcd, 0x8f, 0xa5, 0x1c, 0x40, 0x31, 0x3c, 0x6e, 0xc6, 0xaa, 0xa9, 0xa4, 0x55, 0xf3, 0x6c, 0x40,
|
||||
0x03, 0x41, 0x5d, 0x19, 0x5a, 0x46, 0xd8, 0xcf, 0xbf, 0xfa, 0x6b, 0x3d, 0xd7, 0x28, 0x84, 0xaa,
|
||||
0x35, 0xe4, 0xb3, 0x29, 0xd0, 0x95, 0x4c, 0x68, 0x0d, 0x11, 0xa7, 0xb6, 0xd6, 0xe2, 0xa8, 0xfb,
|
||||
0x3e, 0xe7, 0x3d, 0xac, 0xcd, 0x72, 0xe0, 0xad, 0x6b, 0xbf, 0x4c, 0x8e, 0x54, 0x4b, 0x6e, 0xe0,
|
||||
0xc0, 0xf5, 0xc2, 0x2a, 0xe1, 0xc4, 0x9f, 0xd2, 0x01, 0xed, 0x45, 0x2d, 0xb7, 0x0e, 0x71, 0xa6,
|
||||
0xd1, 0x2e, 0x86, 0xf8, 0x04, 0x0a, 0x7d, 0xb5, 0xa3, 0x62, 0x2c, 0xd7, 0xcb, 0x69, 0x7d, 0xd0,
|
||||
0x7e, 0x51, 0xf9, 0xda, 0xc7, 0x7a, 0x8c, 0xd4, 0x87, 0x52, 0x48, 0xdd, 0x47, 0xd4, 0xf7, 0xb3,
|
||||
0xef, 0x4e, 0x09, 0x16, 0xba, 0x41, 0x7f, 0x18, 0xaa, 0x6e, 0xad, 0x34, 0xf4, 0xc2, 0x7a, 0x80,
|
||||
0x55, 0xc6, 0x23, 0x4d, 0x4e, 0x75, 0x9b, 0x86, 0x34, 0x3a, 0xd5, 0xf2, 0xbb, 0xfe, 0x62, 0x05,
|
||||
0x16, 0x94, 0x3d, 0xf9, 0xd9, 0x80, 0x22, 0xca, 0x14, 0xd9, 0x49, 0x83, 0x9f, 0x22, 0xda, 0xe6,
|
||||
0xee, 0x6c, 0xc6, 0x1a, 0xc2, 0xaa, 0x7d, 0xfd, 0xc7, 0x3f, 0x3f, 0xcd, 0xed, 0x90, 0x2d, 0x27,
|
||||
0xe5, 0x91, 0x40, 0xf9, 0x72, 0x4e, 0xb1, 0xce, 0x33, 0xf2, 0xbb, 0x01, 0xab, 0x09, 0x19, 0x25,
|
||||
0xb5, 0x1b, 0x53, 0x4e, 0x13, 0x6d, 0xb3, 0x7e, 0x1b, 0x17, 0x64, 0xfd, 0x50, 0xb1, 0xd6, 0xc9,
|
||||
0xc3, 0x34, 0xd6, 0x48, 0xc3, 0xaf, 0x21, 0xbf, 0x30, 0xa0, 0x88, 0xb2, 0x99, 0xd1, 0xcc, 0xa4,
|
||||
0x2e, 0x67, 0x34, 0xf3, 0x8a, 0x12, 0x5b, 0x75, 0x05, 0xb8, 0x4b, 0xb6, 0xd3, 0x00, 0x51, 0x98,
|
||||
0x45, 0x0c, 0xed, 0x57, 0x03, 0x8a, 0x28, 0xa9, 0x19, 0x68, 0x49, 0x15, 0xcf, 0x40, 0xbb, 0xa2,
|
||||
0xd2, 0xd6, 0x07, 0x0a, 0xad, 0x46, 0x9c, 0x34, 0x34, 0xa1, 0x1d, 0x26, 0x64, 0xce, 0xe9, 0x11,
|
||||
0x3b, 0x39, 0x23, 0xdf, 0x1b, 0x90, 0x97, 0x62, 0x4c, 0xaa, 0x19, 0x13, 0x1b, 0xeb, 0xbc, 0xb9,
|
||||
0x35, 0x83, 0x25, 0x62, 0x39, 0x0a, 0x6b, 0x8b, 0x54, 0xd2, 0x47, 0xda, 0x4e, 0xb4, 0xeb, 0x47,
|
||||
0x03, 0x0a, 0x5a, 0xbe, 0xc9, 0xf6, 0x8d, 0x69, 0x12, 0xef, 0x82, 0xb9, 0x33, 0x93, 0x2d, 0x42,
|
||||
0xd9, 0x0a, 0xaa, 0x4a, 0x36, 0xd3, 0xa0, 0x50, 0x62, 0x9d, 0x53, 0x29, 0xf0, 0x6a, 0x84, 0x4b,
|
||||
0x63, 0x29, 0x26, 0x0f, 0x6e, 0x3e, 0x32, 0x57, 0x9e, 0x0c, 0xd3, 0x9e, 0xd5, 0x7c, 0xd6, 0x0b,
|
||||
0xdb, 0x92, 0x2e, 0x09, 0xbe, 0x5f, 0x0c, 0x80, 0x89, 0xca, 0x92, 0x19, 0x32, 0xc6, 0x85, 0xda,
|
||||
0x74, 0x66, 0xb6, 0x47, 0xc4, 0x1d, 0x85, 0x78, 0x9f, 0xbc, 0x7b, 0x33, 0xa2, 0x52, 0x75, 0xf2,
|
||||
0x9d, 0x01, 0x05, 0xad, 0xc1, 0x19, 0x03, 0x4d, 0xc8, 0x7e, 0xc6, 0x40, 0x93, 0x8f, 0x81, 0xb5,
|
||||
0xa9, 0x80, 0x36, 0x48, 0x39, 0x0d, 0x48, 0xcb, 0xbe, 0x6a, 0xd4, 0x44, 0xa8, 0x33, 0x1a, 0x75,
|
||||
0xed, 0x6d, 0xc8, 0x68, 0xd4, 0xf5, 0x17, 0x20, 0xbb, 0x51, 0x42, 0xf9, 0x34, 0x5d, 0xea, 0xfb,
|
||||
0xfb, 0x7b, 0xaf, 0x2e, 0xca, 0xc6, 0xf9, 0x45, 0xd9, 0xf8, 0xfb, 0xa2, 0x6c, 0xfc, 0x70, 0x59,
|
||||
0xce, 0x9d, 0x5f, 0x96, 0x73, 0x7f, 0x5e, 0x96, 0x73, 0x5f, 0x55, 0xbc, 0x6e, 0xd8, 0x19, 0xb6,
|
||||
0x6c, 0x97, 0xf7, 0x50, 0x02, 0x63, 0xf1, 0x8e, 0x55, 0xc4, 0xf0, 0xa4, 0xcf, 0x44, 0xab, 0xa0,
|
||||
0xfe, 0xed, 0xbf, 0xf7, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4b, 0xf0, 0x51, 0x91, 0xad, 0x0c,
|
||||
0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@ -2579,7 +2579,7 @@ func (m *QueryAccountResponse) Unmarshal(dAtA []byte) error {
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field CodeHash", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowQuery
|
||||
@ -2589,25 +2589,23 @@ func (m *QueryAccountResponse) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthQuery
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthQuery
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.CodeHash = append(m.CodeHash[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.CodeHash == nil {
|
||||
m.CodeHash = []byte{}
|
||||
}
|
||||
m.CodeHash = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 0 {
|
||||
|
@ -1,457 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var (
|
||||
_ StateObject = (*stateObject)(nil)
|
||||
|
||||
EmptyCodeHash = ethcrypto.Keccak256(nil)
|
||||
)
|
||||
|
||||
// StateObject interface for interacting with state object
|
||||
type StateObject interface {
|
||||
GetCommittedState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash
|
||||
GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash
|
||||
SetState(db ethstate.Database, key, value ethcmn.Hash)
|
||||
|
||||
Code(db ethstate.Database) []byte
|
||||
SetCode(codeHash ethcmn.Hash, code []byte)
|
||||
CodeHash() []byte
|
||||
|
||||
AddBalance(amount *big.Int)
|
||||
SubBalance(amount *big.Int)
|
||||
SetBalance(amount *big.Int)
|
||||
|
||||
Balance() *big.Int
|
||||
ReturnGas(gas *big.Int)
|
||||
Address() ethcmn.Address
|
||||
|
||||
SetNonce(nonce uint64)
|
||||
Nonce() uint64
|
||||
}
|
||||
|
||||
// stateObject represents an Ethereum account which is being modified.
|
||||
//
|
||||
// The usage pattern is as follows:
|
||||
// First you need to obtain a state object.
|
||||
// Account values can be accessed and modified through the object.
|
||||
// Finally, call CommitTrie to write the modified storage trie into a database.
|
||||
type stateObject struct {
|
||||
code ethermint.Code // contract bytecode, which gets set when code is loaded
|
||||
// State objects are used by the consensus core and VM which are
|
||||
// unable to deal with database-level errors. Any error that occurs
|
||||
// during a database read is memoized here and will eventually be returned
|
||||
// by StateDB.Commit.
|
||||
originStorage Storage // Storage cache of original entries to dedup rewrites
|
||||
dirtyStorage Storage // Storage entries that need to be flushed to disk
|
||||
|
||||
// DB error
|
||||
dbErr error
|
||||
stateDB *CommitStateDB
|
||||
account *ethermint.EthAccount
|
||||
// balance represents the amount of the EVM denom token that an account holds
|
||||
balance sdk.Int
|
||||
|
||||
keyToOriginStorageIndex map[ethcmn.Hash]int
|
||||
keyToDirtyStorageIndex map[ethcmn.Hash]int
|
||||
|
||||
address ethcmn.Address
|
||||
|
||||
// cache flags
|
||||
//
|
||||
// When an object is marked suicided it will be delete from the trie during
|
||||
// the "update" phase of the state transition.
|
||||
dirtyCode bool // true if the code was updated
|
||||
suicided bool
|
||||
deleted bool
|
||||
}
|
||||
|
||||
func newStateObject(db *CommitStateDB, accProto authtypes.AccountI, balance sdk.Int) *stateObject {
|
||||
ethAccount, ok := accProto.(*ethermint.EthAccount)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid account type for state object: %T", accProto))
|
||||
}
|
||||
|
||||
// set empty code hash
|
||||
if ethAccount.CodeHash == nil {
|
||||
ethAccount.CodeHash = EmptyCodeHash
|
||||
}
|
||||
|
||||
return &stateObject{
|
||||
stateDB: db,
|
||||
account: ethAccount,
|
||||
balance: balance,
|
||||
address: ethAccount.EthAddress(),
|
||||
originStorage: Storage{},
|
||||
dirtyStorage: Storage{},
|
||||
keyToOriginStorageIndex: make(map[ethcmn.Hash]int),
|
||||
keyToDirtyStorageIndex: make(map[ethcmn.Hash]int),
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Setters
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// SetState updates a value in account storage. Note, the key will be prefixed
|
||||
// with the address of the state object.
|
||||
func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) {
|
||||
// if the new value is the same as old, don't set
|
||||
prev := so.GetState(db, key)
|
||||
if prev == value {
|
||||
return
|
||||
}
|
||||
|
||||
prefixKey := so.GetStorageByAddressKey(key.Bytes())
|
||||
|
||||
// since the new value is different, update and journal the change
|
||||
so.stateDB.journal.append(storageChange{
|
||||
account: &so.address,
|
||||
key: prefixKey,
|
||||
prevValue: prev,
|
||||
})
|
||||
|
||||
so.setState(prefixKey, value)
|
||||
}
|
||||
|
||||
// setState sets a state with a prefixed key and value to the dirty storage.
|
||||
func (so *stateObject) setState(key, value ethcmn.Hash) {
|
||||
idx, ok := so.keyToDirtyStorageIndex[key]
|
||||
if ok {
|
||||
so.dirtyStorage[idx].Value = value.String()
|
||||
return
|
||||
}
|
||||
|
||||
// create new entry
|
||||
so.dirtyStorage = append(so.dirtyStorage, NewState(key, value))
|
||||
idx = len(so.dirtyStorage) - 1
|
||||
so.keyToDirtyStorageIndex[key] = idx
|
||||
}
|
||||
|
||||
// SetCode sets the state object's code.
|
||||
func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) {
|
||||
prevCode := so.Code(nil)
|
||||
|
||||
so.stateDB.journal.append(codeChange{
|
||||
account: &so.address,
|
||||
prevHash: so.CodeHash(),
|
||||
prevCode: prevCode,
|
||||
})
|
||||
|
||||
so.setCode(codeHash, code)
|
||||
}
|
||||
|
||||
func (so *stateObject) setCode(codeHash ethcmn.Hash, code []byte) {
|
||||
so.code = code
|
||||
so.account.CodeHash = codeHash.Bytes()
|
||||
so.dirtyCode = true
|
||||
}
|
||||
|
||||
// AddBalance adds an amount to a state object's balance. It is used to add
|
||||
// funds to the destination account of a transfer.
|
||||
func (so *stateObject) AddBalance(amount *big.Int) {
|
||||
amt := sdk.NewIntFromBigInt(amount)
|
||||
// EIP158: We must check emptiness for the objects such that the account
|
||||
// clearing (0,0,0 objects) can take effect.
|
||||
|
||||
// NOTE: this will panic if amount is nil
|
||||
if amt.IsZero() {
|
||||
if so.empty() {
|
||||
so.touch()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
newBalance := so.balance.Add(amt)
|
||||
so.SetBalance(newBalance.BigInt())
|
||||
}
|
||||
|
||||
// SubBalance removes an amount from the stateObject's balance. It is used to
|
||||
// remove funds from the origin account of a transfer.
|
||||
func (so *stateObject) SubBalance(amount *big.Int) {
|
||||
amt := sdk.NewIntFromBigInt(amount)
|
||||
if amt.IsZero() {
|
||||
return
|
||||
}
|
||||
|
||||
newBalance := so.balance.Sub(amt)
|
||||
so.SetBalance(newBalance.BigInt())
|
||||
}
|
||||
|
||||
// SetBalance sets the state object's balance. It doesn't perform any validation
|
||||
// on the amount value.
|
||||
func (so *stateObject) SetBalance(amount *big.Int) {
|
||||
amt := sdk.NewIntFromBigInt(amount)
|
||||
|
||||
so.stateDB.journal.append(balanceChange{
|
||||
account: &so.address,
|
||||
prev: so.balance,
|
||||
})
|
||||
|
||||
so.setBalance(amt)
|
||||
}
|
||||
|
||||
func (so *stateObject) setBalance(amount sdk.Int) {
|
||||
so.balance = amount
|
||||
}
|
||||
|
||||
// SetNonce sets the state object's nonce (i.e sequence number of the account).
|
||||
func (so *stateObject) SetNonce(nonce uint64) {
|
||||
so.stateDB.journal.append(nonceChange{
|
||||
account: &so.address,
|
||||
prev: so.account.Sequence,
|
||||
})
|
||||
|
||||
so.setNonce(nonce)
|
||||
}
|
||||
|
||||
func (so *stateObject) setNonce(nonce uint64) {
|
||||
if so.account == nil {
|
||||
panic("state object account is empty")
|
||||
}
|
||||
so.account.Sequence = nonce
|
||||
}
|
||||
|
||||
// setError remembers the first non-nil error it is called with.
|
||||
func (so *stateObject) setError(err error) {
|
||||
if so.dbErr == nil {
|
||||
so.dbErr = err
|
||||
}
|
||||
}
|
||||
|
||||
func (so *stateObject) markSuicided() {
|
||||
so.suicided = true
|
||||
}
|
||||
|
||||
// commitState commits all dirty storage to a KVStore and resets
|
||||
// the dirty storage slice to the empty state.
|
||||
func (so *stateObject) commitState() {
|
||||
ctx := so.stateDB.ctx
|
||||
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address()))
|
||||
|
||||
for _, state := range so.dirtyStorage {
|
||||
// NOTE: key is already prefixed from GetStorageByAddressKey
|
||||
|
||||
key := ethcmn.HexToHash(state.Key)
|
||||
value := ethcmn.HexToHash(state.Value)
|
||||
// delete empty values from the store
|
||||
if ethermint.IsEmptyHash(state.Value) {
|
||||
store.Delete(key.Bytes())
|
||||
}
|
||||
|
||||
delete(so.keyToDirtyStorageIndex, key)
|
||||
|
||||
// skip no-op changes, persist actual changes
|
||||
idx, ok := so.keyToOriginStorageIndex[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if ethermint.IsEmptyHash(state.Value) {
|
||||
delete(so.keyToOriginStorageIndex, key)
|
||||
continue
|
||||
}
|
||||
|
||||
if state.Value == so.originStorage[idx].Value {
|
||||
continue
|
||||
}
|
||||
|
||||
so.originStorage[idx].Value = state.Value
|
||||
store.Set(key.Bytes(), value.Bytes())
|
||||
}
|
||||
// clean storage as all entries are dirty
|
||||
so.dirtyStorage = Storage{}
|
||||
}
|
||||
|
||||
// commitCode persists the state object's code to the KVStore.
|
||||
func (so *stateObject) commitCode() {
|
||||
ctx := so.stateDB.ctx
|
||||
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), KeyPrefixCode)
|
||||
store.Set(so.CodeHash(), so.code)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Address returns the address of the state object.
|
||||
func (so stateObject) Address() ethcmn.Address {
|
||||
return so.address
|
||||
}
|
||||
|
||||
// Balance returns the state object's current balance.
|
||||
func (so *stateObject) Balance() *big.Int {
|
||||
balance := so.balance.BigInt()
|
||||
if balance == nil {
|
||||
return zeroBalance
|
||||
}
|
||||
return balance
|
||||
}
|
||||
|
||||
// CodeHash returns the state object's code hash.
|
||||
func (so *stateObject) CodeHash() []byte {
|
||||
if so.account == nil || len(so.account.CodeHash) == 0 {
|
||||
return EmptyCodeHash
|
||||
}
|
||||
return so.account.CodeHash
|
||||
}
|
||||
|
||||
// Nonce returns the state object's current nonce (sequence number).
|
||||
func (so *stateObject) Nonce() uint64 {
|
||||
if so.account == nil {
|
||||
return 0
|
||||
}
|
||||
return so.account.Sequence
|
||||
}
|
||||
|
||||
// Code returns the contract code associated with this object, if any.
|
||||
func (so *stateObject) Code(_ ethstate.Database) []byte {
|
||||
if len(so.code) > 0 {
|
||||
return so.code
|
||||
}
|
||||
|
||||
if bytes.Equal(so.CodeHash(), EmptyCodeHash) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx := so.stateDB.ctx
|
||||
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), KeyPrefixCode)
|
||||
code := store.Get(so.CodeHash())
|
||||
|
||||
if len(code) == 0 {
|
||||
so.setError(fmt.Errorf("failed to get code hash %x for address %s", so.CodeHash(), so.Address().String()))
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
// GetState retrieves a value from the account storage trie. Note, the key will
|
||||
// be prefixed with the address of the state object.
|
||||
func (so *stateObject) GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash {
|
||||
prefixKey := so.GetStorageByAddressKey(key.Bytes())
|
||||
|
||||
// if we have a dirty value for this state entry, return it
|
||||
idx, dirty := so.keyToDirtyStorageIndex[prefixKey]
|
||||
if dirty {
|
||||
value := ethcmn.HexToHash(so.dirtyStorage[idx].Value)
|
||||
return value
|
||||
}
|
||||
|
||||
// otherwise return the entry's original value
|
||||
value := so.GetCommittedState(db, key)
|
||||
return value
|
||||
}
|
||||
|
||||
// GetCommittedState retrieves a value from the committed account storage trie.
|
||||
//
|
||||
// NOTE: the key will be prefixed with the address of the state object.
|
||||
func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash {
|
||||
prefixKey := so.GetStorageByAddressKey(key.Bytes())
|
||||
|
||||
// if we have the original value cached, return that
|
||||
idx, cached := so.keyToOriginStorageIndex[prefixKey]
|
||||
if cached {
|
||||
value := ethcmn.HexToHash(so.originStorage[idx].Value)
|
||||
return value
|
||||
}
|
||||
|
||||
// otherwise load the value from the KVStore
|
||||
state := NewState(prefixKey, ethcmn.Hash{})
|
||||
value := ethcmn.Hash{}
|
||||
|
||||
ctx := so.stateDB.ctx
|
||||
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address()))
|
||||
rawValue := store.Get(prefixKey.Bytes())
|
||||
|
||||
if len(rawValue) > 0 {
|
||||
value.SetBytes(rawValue)
|
||||
state.Value = value.String()
|
||||
}
|
||||
|
||||
so.originStorage = append(so.originStorage, state)
|
||||
so.keyToOriginStorageIndex[prefixKey] = len(so.originStorage) - 1
|
||||
return value
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Auxiliary
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// ReturnGas returns the gas back to the origin. Used by the Virtual machine or
|
||||
// Closures. It performs a no-op.
|
||||
func (so *stateObject) ReturnGas(gas *big.Int) {}
|
||||
|
||||
func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject {
|
||||
newStateObj := newStateObject(db, so.account, so.balance)
|
||||
|
||||
newStateObj.code = so.code
|
||||
newStateObj.dirtyStorage = so.dirtyStorage.Copy()
|
||||
newStateObj.originStorage = so.originStorage.Copy()
|
||||
newStateObj.suicided = so.suicided
|
||||
newStateObj.dirtyCode = so.dirtyCode
|
||||
newStateObj.deleted = so.deleted
|
||||
|
||||
return newStateObj
|
||||
}
|
||||
|
||||
// empty returns whether the account is considered empty.
|
||||
func (so *stateObject) empty() bool {
|
||||
return so.account == nil ||
|
||||
(so.account != nil &&
|
||||
so.account.Sequence == 0 &&
|
||||
(so.balance.BigInt() == nil || so.balance.IsZero()) &&
|
||||
bytes.Equal(so.account.CodeHash, EmptyCodeHash))
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (so *stateObject) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, so.account)
|
||||
}
|
||||
|
||||
func (so *stateObject) touch() {
|
||||
so.stateDB.journal.append(touchChange{
|
||||
account: &so.address,
|
||||
})
|
||||
|
||||
if so.address == ripemd {
|
||||
// Explicitly put it in the dirty-cache, which is otherwise generated from
|
||||
// flattened journals.
|
||||
so.stateDB.journal.dirty(so.address)
|
||||
}
|
||||
}
|
||||
|
||||
// GetStorageByAddressKey returns a hash of the composite key for a state
|
||||
// object's storage prefixed with it's address.
|
||||
func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash {
|
||||
prefix := so.Address().Bytes()
|
||||
compositeKey := make([]byte, len(prefix)+len(key))
|
||||
|
||||
copy(compositeKey, prefix)
|
||||
copy(compositeKey[len(prefix):], key)
|
||||
|
||||
return ethcrypto.Keccak256Hash(compositeKey)
|
||||
}
|
||||
|
||||
// stateEntry represents a single key value pair from the StateDB's stateObject mappindg.
|
||||
// This is to prevent non determinism at genesis initialization or export.
|
||||
type stateEntry struct {
|
||||
// address key of the state object
|
||||
address ethcmn.Address
|
||||
stateObject *stateObject
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateObject_State() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
key ethcmn.Hash
|
||||
expValue ethcmn.Hash
|
||||
malleate func()
|
||||
}{
|
||||
{
|
||||
"no set value, load from KVStore",
|
||||
ethcmn.BytesToHash([]byte("key")),
|
||||
ethcmn.Hash{},
|
||||
func() {},
|
||||
},
|
||||
{
|
||||
"no-op SetState",
|
||||
ethcmn.BytesToHash([]byte("key")),
|
||||
ethcmn.Hash{},
|
||||
func() {
|
||||
suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key")), ethcmn.Hash{})
|
||||
},
|
||||
},
|
||||
{
|
||||
"cached value",
|
||||
ethcmn.BytesToHash([]byte("key1")),
|
||||
ethcmn.BytesToHash([]byte("value1")),
|
||||
func() {
|
||||
suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value1")))
|
||||
},
|
||||
},
|
||||
{
|
||||
"update value",
|
||||
ethcmn.BytesToHash([]byte("key1")),
|
||||
ethcmn.BytesToHash([]byte("value2")),
|
||||
func() {
|
||||
suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value2")))
|
||||
},
|
||||
},
|
||||
{
|
||||
"update various keys",
|
||||
ethcmn.BytesToHash([]byte("key1")),
|
||||
ethcmn.BytesToHash([]byte("value1")),
|
||||
func() {
|
||||
suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value1")))
|
||||
suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key2")), ethcmn.BytesToHash([]byte("value2")))
|
||||
suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key3")), ethcmn.BytesToHash([]byte("value3")))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
value := suite.stateObject.GetState(nil, tc.key)
|
||||
suite.Require().Equal(tc.expValue, value, tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateObject_AddBalance() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
amount *big.Int
|
||||
expBalance *big.Int
|
||||
}{
|
||||
{"zero amount", big.NewInt(0), big.NewInt(0)},
|
||||
{"positive amount", big.NewInt(10), big.NewInt(10)},
|
||||
{"negative amount", big.NewInt(-1), big.NewInt(9)},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
suite.stateObject.AddBalance(tc.amount)
|
||||
suite.Require().Equal(tc.expBalance, suite.stateObject.Balance(), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateObject_SubBalance() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
amount *big.Int
|
||||
expBalance *big.Int
|
||||
}{
|
||||
{"zero amount", big.NewInt(0), big.NewInt(0)},
|
||||
{"negative amount", big.NewInt(-10), big.NewInt(10)},
|
||||
{"positive amount", big.NewInt(1), big.NewInt(9)},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
suite.stateObject.SubBalance(tc.amount)
|
||||
suite.Require().Equal(tc.expBalance, suite.stateObject.Balance(), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateObject_Code() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
expCode []byte
|
||||
malleate func()
|
||||
}{
|
||||
{
|
||||
"cached code",
|
||||
[]byte("code"),
|
||||
func() {
|
||||
suite.stateObject.SetCode(ethcmn.BytesToHash([]byte("code_hash")), []byte("code"))
|
||||
},
|
||||
},
|
||||
{
|
||||
"empty code hash",
|
||||
nil,
|
||||
func() {
|
||||
suite.stateObject.SetCode(ethcmn.Hash{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
"empty code",
|
||||
nil,
|
||||
func() {
|
||||
suite.stateObject.SetCode(ethcmn.BytesToHash([]byte("code_hash")), nil)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
code := suite.stateObject.Code(nil)
|
||||
suite.Require().Equal(tc.expCode, code, tc.name)
|
||||
}
|
||||
}
|
@ -1,340 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/xlab/suplog"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/telemetry"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// StateTransition defines data to transitionDB in evm
|
||||
type StateTransition struct {
|
||||
// TxData fields
|
||||
Message core.Message
|
||||
|
||||
ChainID *big.Int
|
||||
Csdb *CommitStateDB
|
||||
TxHash *common.Hash
|
||||
Simulate bool // i.e CheckTx execution
|
||||
Debug bool // enable EVM debugging
|
||||
}
|
||||
|
||||
// GasInfo returns the gas limit, gas consumed and gas refunded from the EVM transition
|
||||
// execution
|
||||
type GasInfo struct {
|
||||
GasLimit uint64
|
||||
GasConsumed uint64
|
||||
GasRefunded uint64
|
||||
}
|
||||
|
||||
// ExecutionResult represents what's returned from a transition
|
||||
type ExecutionResult struct {
|
||||
Logs []*ethtypes.Log
|
||||
Bloom *big.Int
|
||||
Response *MsgEthereumTxResponse
|
||||
GasInfo GasInfo
|
||||
}
|
||||
|
||||
// GetHashFn implements vm.GetHashFunc for Ethermint. It handles 3 cases:
|
||||
// 1. The requested height matches the current height from context (and thus same epoch number)
|
||||
// 2. The requested height is from an previous height from the same chain epoch
|
||||
// 3. The requested height is from a height greater than the latest one
|
||||
func GetHashFn(ctx sdk.Context, csdb *CommitStateDB) vm.GetHashFunc {
|
||||
return func(height uint64) common.Hash {
|
||||
switch {
|
||||
case ctx.BlockHeight() == int64(height):
|
||||
// Case 1: The requested height matches the one from the context so we can retrieve the header
|
||||
// hash directly from the context.
|
||||
return csdb.bhash
|
||||
|
||||
case ctx.BlockHeight() > int64(height):
|
||||
// Case 2: if the chain is not the current height we need to retrieve the hash from the store for the
|
||||
// current chain epoch. This only applies if the current height is greater than the requested height.
|
||||
return csdb.WithContext(ctx).GetHeightHash(height)
|
||||
|
||||
default:
|
||||
// Case 3: heights greater than the current one returns an empty hash.
|
||||
return common.Hash{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (st *StateTransition) newEVM(
|
||||
ctx sdk.Context,
|
||||
csdb *CommitStateDB,
|
||||
gasLimit uint64,
|
||||
config ChainConfig,
|
||||
extraEIPs []int64,
|
||||
) *vm.EVM {
|
||||
// Create context for evm
|
||||
blockCtx := vm.BlockContext{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
GetHash: GetHashFn(ctx, csdb),
|
||||
Coinbase: common.Address{}, // there's no beneficiary since we're not mining
|
||||
BlockNumber: big.NewInt(ctx.BlockHeight()),
|
||||
Time: big.NewInt(ctx.BlockHeader().Time.Unix()),
|
||||
Difficulty: big.NewInt(0), // unused. Only required in PoW context
|
||||
GasLimit: gasLimit,
|
||||
}
|
||||
|
||||
txCtx := core.NewEVMTxContext(st.Message)
|
||||
|
||||
eips := make([]int, len(extraEIPs))
|
||||
for i, eip := range extraEIPs {
|
||||
eips[i] = int(eip)
|
||||
}
|
||||
|
||||
vmConfig := vm.Config{
|
||||
EnablePreimageRecording: false, // no need for StateDB.AddPreimage
|
||||
ExtraEips: eips,
|
||||
}
|
||||
|
||||
if st.Debug {
|
||||
vmConfig.Tracer = vm.NewJSONLogger(&vm.LogConfig{
|
||||
Debug: true,
|
||||
}, os.Stderr)
|
||||
|
||||
vmConfig.Debug = true
|
||||
}
|
||||
|
||||
return vm.NewEVM(blockCtx, txCtx, csdb, config.EthereumConfig(st.ChainID), vmConfig)
|
||||
}
|
||||
|
||||
// TransitionDb will transition the state by applying the current transaction and
|
||||
// returning the evm execution result.
|
||||
// NOTE: State transition checks are run during AnteHandler execution.
|
||||
func (st *StateTransition) TransitionDb(ctx sdk.Context, config ChainConfig) (resp *ExecutionResult, err error) {
|
||||
defer telemetry.ModuleMeasureSince(ModuleName, time.Now(), MetricKeyTransitionDB)
|
||||
|
||||
contractCreation := st.Message.To() == nil
|
||||
|
||||
cost, err := core.IntrinsicGas(st.Message.Data(), st.Message.AccessList(), true, false, true)
|
||||
if err != nil {
|
||||
err = sdkerrors.Wrap(err, "invalid intrinsic gas for transaction")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This gas limit the the transaction gas limit with intrinsic gas subtracted
|
||||
gasLimit := st.Message.Gas() - ctx.GasMeter().GasConsumed()
|
||||
|
||||
csdb := st.Csdb.WithContext(ctx)
|
||||
if st.Simulate {
|
||||
// gasLimit is set here because stdTxs incur gaskv charges in the ante handler, but for eth_call
|
||||
// the cost needs to be the same as an Ethereum transaction sent through the web3 API
|
||||
consumedGas := ctx.GasMeter().GasConsumed()
|
||||
gasLimit = st.Message.Gas() - cost
|
||||
if consumedGas < cost {
|
||||
// If Cosmos standard tx ante handler cost is less than EVM intrinsic cost
|
||||
// gas must be consumed to match to accurately simulate an Ethereum transaction
|
||||
ctx.GasMeter().ConsumeGas(cost-consumedGas, "Intrinsic gas match")
|
||||
}
|
||||
|
||||
csdb = st.Csdb.Copy()
|
||||
}
|
||||
|
||||
// This gas meter is set up to consume gas from gaskv during evm execution and be ignored
|
||||
currentGasMeter := ctx.GasMeter()
|
||||
evmGasMeter := sdk.NewInfiniteGasMeter()
|
||||
csdb.WithContext(ctx.WithGasMeter(evmGasMeter))
|
||||
|
||||
// Clear cache of accounts to handle changes outside of the EVM
|
||||
csdb.UpdateAccounts()
|
||||
|
||||
params := csdb.GetParams()
|
||||
|
||||
gasPrice := ctx.MinGasPrices().AmountOf(params.EvmDenom)
|
||||
//gasPrice := sdk.ZeroDec()
|
||||
if gasPrice.IsNil() {
|
||||
return nil, errors.New("min gas price cannot be nil")
|
||||
}
|
||||
|
||||
evm := st.newEVM(ctx, csdb, gasLimit, config, params.ExtraEIPs)
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
leftOverGas uint64
|
||||
contractAddress common.Address
|
||||
senderRef = vm.AccountRef(st.Message.From())
|
||||
)
|
||||
|
||||
// Get nonce of account outside of the EVM
|
||||
currentNonce := csdb.GetNonce(st.Message.From())
|
||||
// Set nonce of sender account before evm state transition for usage in generating Create address
|
||||
csdb.SetNonce(st.Message.From(), st.Message.Nonce())
|
||||
|
||||
// create contract or execute call
|
||||
switch contractCreation {
|
||||
case true:
|
||||
if !params.EnableCreate {
|
||||
return nil, ErrCreateDisabled
|
||||
}
|
||||
|
||||
ret, contractAddress, leftOverGas, err = evm.Create(senderRef, st.Message.Data(), gasLimit, st.Message.Value())
|
||||
|
||||
if err != nil {
|
||||
log.WithField("simulate", st.Simulate).
|
||||
WithField("nonce", st.Message.Nonce()).
|
||||
WithField("contract", contractAddress.String()).
|
||||
WithError(err).Warningln("evm contract creation failed")
|
||||
}
|
||||
|
||||
gasConsumed := gasLimit - leftOverGas
|
||||
resp = &ExecutionResult{
|
||||
Response: &MsgEthereumTxResponse{
|
||||
Ret: ret,
|
||||
},
|
||||
GasInfo: GasInfo{
|
||||
GasConsumed: gasConsumed,
|
||||
GasLimit: gasLimit,
|
||||
GasRefunded: leftOverGas,
|
||||
},
|
||||
}
|
||||
default:
|
||||
if !params.EnableCall {
|
||||
return nil, ErrCallDisabled
|
||||
}
|
||||
|
||||
// Increment the nonce for the next transaction (just for evm state transition)
|
||||
csdb.SetNonce(st.Message.From(), csdb.GetNonce(st.Message.From())+1)
|
||||
|
||||
ret, leftOverGas, err = evm.Call(senderRef, *st.Message.To(), st.Message.Data(), gasLimit, st.Message.Value())
|
||||
|
||||
// fmt.Println("EVM CALL!!!", senderRef.Address().Hex(), (*st.Message.To()).Hex(), gasLimit)
|
||||
// fmt.Println("EVM CALL RESULT", common.ToHex(ret), leftOverGas, err)
|
||||
|
||||
if err != nil {
|
||||
log.WithField("recipient", st.Message.To().String()).
|
||||
WithError(err).Debugln("evm call failed")
|
||||
}
|
||||
|
||||
gasConsumed := gasLimit - leftOverGas
|
||||
resp = &ExecutionResult{
|
||||
Response: &MsgEthereumTxResponse{
|
||||
Ret: ret,
|
||||
},
|
||||
GasInfo: GasInfo{
|
||||
GasConsumed: gasConsumed,
|
||||
GasLimit: gasLimit,
|
||||
GasRefunded: leftOverGas,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// Consume gas before returning
|
||||
ctx.GasMeter().ConsumeGas(resp.GasInfo.GasConsumed, "evm execution consumption")
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Resets nonce to value pre state transition
|
||||
csdb.SetNonce(st.Message.From(), currentNonce)
|
||||
|
||||
var (
|
||||
logs []*ethtypes.Log
|
||||
)
|
||||
|
||||
if st.TxHash != nil && !st.Simulate {
|
||||
logs, err = csdb.GetLogs(*st.TxHash)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "failed to get logs")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !st.Simulate {
|
||||
// Finalise state if not a simulated transaction
|
||||
// TODO: change to depend on config
|
||||
if err := csdb.Finalise(true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp.Logs = logs
|
||||
resp.Response = &MsgEthereumTxResponse{
|
||||
Logs: NewLogsFromEth(logs),
|
||||
Ret: ret,
|
||||
}
|
||||
|
||||
// TODO: Refund unused gas here, if intended in future
|
||||
|
||||
// Consume gas from evm execution
|
||||
// Out of gas check does not need to be done here since it is done within the EVM execution
|
||||
|
||||
// TODO: @albert, @maxim, decide if can take this out, since InternalEthereumTx may want to continue execution afterwards
|
||||
// which will use gas.
|
||||
_ = currentGasMeter
|
||||
//ctx.WithGasMeter(currentGasMeter).GasMeter().ConsumeGas(resp.GasInfo.GasConsumed, "EVM execution consumption")
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// StaticCall executes the contract associated with the addr with the given input
|
||||
// as parameters while disallowing any modifications to the state during the call.
|
||||
// Opcodes that attempt to perform such modifications will result in exceptions
|
||||
// instead of performing the modifications.
|
||||
func (st *StateTransition) StaticCall(ctx sdk.Context, config ChainConfig) ([]byte, error) {
|
||||
defer telemetry.ModuleMeasureSince(ModuleName, time.Now(), MetricKeyStaticCall)
|
||||
|
||||
// This gas limit the the transaction gas limit with intrinsic gas subtracted
|
||||
gasLimit := st.Message.Gas() - ctx.GasMeter().GasConsumed()
|
||||
csdb := st.Csdb.WithContext(ctx)
|
||||
|
||||
// This gas meter is set up to consume gas from gaskv during evm execution and be ignored
|
||||
evmGasMeter := sdk.NewInfiniteGasMeter()
|
||||
csdb.WithContext(ctx.WithGasMeter(evmGasMeter))
|
||||
|
||||
// Clear cache of accounts to handle changes outside of the EVM
|
||||
csdb.UpdateAccounts()
|
||||
|
||||
params := csdb.GetParams()
|
||||
|
||||
gasPrice := ctx.MinGasPrices().AmountOf(params.EvmDenom)
|
||||
if gasPrice.IsNil() {
|
||||
return []byte{}, errors.New("min gas price cannot be nil")
|
||||
}
|
||||
|
||||
evm := st.newEVM(ctx, csdb, gasLimit, config, params.ExtraEIPs)
|
||||
senderRef := vm.AccountRef(st.Message.From())
|
||||
|
||||
ret, _, err := evm.StaticCall(senderRef, *st.Message.To(), st.Message.Data(), gasLimit)
|
||||
|
||||
// fmt.Println("EVM STATIC CALL!!!", senderRef.Address().Hex(), (*st.Message.To()).Hex(), st.Message.Data(), gasLimit)
|
||||
// fmt.Println("EVM STATIC CALL RESULT", common.ToHex(ret), leftOverGas, err)
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// HashFromContext returns the Ethereum Header hash from the context's Tendermint
|
||||
// block header.
|
||||
func HashFromContext(ctx sdk.Context) common.Hash {
|
||||
// cast the ABCI header to tendermint Header type
|
||||
protoHeader := ctx.BlockHeader()
|
||||
tmHeader, err := tmtypes.HeaderFromProto(&protoHeader)
|
||||
if err != nil {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
// get the Tendermint block hash from the current header
|
||||
tmBlockHash := tmHeader.Hash()
|
||||
|
||||
// NOTE: if the validator set hash is missing the hash will be returned as nil,
|
||||
// so we need to check for this case to prevent a panic when calling Bytes()
|
||||
if tmBlockHash == nil {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
return common.BytesToHash(tmBlockHash.Bytes())
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
func (suite *StateDBTestSuite) TestTransitionDb() {
|
||||
suite.stateDB.SetNonce(suite.address, 123)
|
||||
|
||||
addr := sdk.AccAddress(suite.address.Bytes())
|
||||
balance := ethermint.NewPhotonCoin(sdk.NewInt(5000))
|
||||
acc := suite.app.AccountKeeper.GetAccount(suite.ctx, addr)
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
suite.app.BankKeeper.SetBalance(suite.ctx, addr, balance)
|
||||
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
recipient := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey)
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
state types.StateTransition
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"passing state transition",
|
||||
func() {},
|
||||
types.StateTransition{
|
||||
Message: ethtypes.NewMessage(
|
||||
suite.address,
|
||||
&recipient,
|
||||
123,
|
||||
big.NewInt(50),
|
||||
11,
|
||||
big.NewInt(10),
|
||||
[]byte("data"),
|
||||
nil,
|
||||
true,
|
||||
),
|
||||
ChainID: big.NewInt(1),
|
||||
Csdb: suite.stateDB,
|
||||
TxHash: ðcmn.Hash{},
|
||||
Simulate: suite.ctx.IsCheckTx(),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"contract creation",
|
||||
func() {},
|
||||
types.StateTransition{
|
||||
Message: ethtypes.NewMessage(
|
||||
suite.address,
|
||||
nil,
|
||||
123,
|
||||
big.NewInt(50),
|
||||
11,
|
||||
big.NewInt(10),
|
||||
[]byte("data"),
|
||||
nil,
|
||||
true,
|
||||
),
|
||||
ChainID: big.NewInt(1),
|
||||
Csdb: suite.stateDB,
|
||||
TxHash: ðcmn.Hash{},
|
||||
Simulate: true,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"state transition simulation",
|
||||
func() {},
|
||||
types.StateTransition{
|
||||
Message: ethtypes.NewMessage(
|
||||
suite.address,
|
||||
&recipient,
|
||||
123,
|
||||
big.NewInt(50),
|
||||
11,
|
||||
big.NewInt(10),
|
||||
[]byte("data"),
|
||||
nil,
|
||||
true,
|
||||
),
|
||||
ChainID: big.NewInt(1),
|
||||
Csdb: suite.stateDB,
|
||||
TxHash: ðcmn.Hash{},
|
||||
Simulate: true,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"fail by sending more than balance",
|
||||
func() {},
|
||||
types.StateTransition{
|
||||
Message: ethtypes.NewMessage(
|
||||
suite.address,
|
||||
&recipient,
|
||||
123,
|
||||
big.NewInt(50000000),
|
||||
11,
|
||||
big.NewInt(10),
|
||||
[]byte("data"),
|
||||
nil,
|
||||
true,
|
||||
),
|
||||
ChainID: big.NewInt(1),
|
||||
Csdb: suite.stateDB,
|
||||
TxHash: ðcmn.Hash{},
|
||||
Simulate: suite.ctx.IsCheckTx(),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"failed to Finalize",
|
||||
func() {},
|
||||
types.StateTransition{
|
||||
Message: ethtypes.NewMessage(
|
||||
suite.address,
|
||||
&recipient,
|
||||
123,
|
||||
big.NewInt(-5000),
|
||||
11,
|
||||
big.NewInt(10),
|
||||
[]byte("data"),
|
||||
nil,
|
||||
true,
|
||||
),
|
||||
ChainID: big.NewInt(1),
|
||||
Csdb: suite.stateDB,
|
||||
TxHash: ðcmn.Hash{},
|
||||
Simulate: false,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil gas price",
|
||||
func() {
|
||||
invalidGas := sdk.DecCoins{
|
||||
{Denom: ethermint.AttoPhoton},
|
||||
}
|
||||
suite.ctx = suite.ctx.WithMinGasPrices(invalidGas)
|
||||
},
|
||||
types.StateTransition{
|
||||
Message: ethtypes.NewMessage(
|
||||
suite.address,
|
||||
&recipient,
|
||||
123,
|
||||
big.NewInt(50),
|
||||
11,
|
||||
nil,
|
||||
[]byte("data"),
|
||||
nil,
|
||||
true,
|
||||
),
|
||||
ChainID: big.NewInt(1),
|
||||
Csdb: suite.stateDB,
|
||||
TxHash: ðcmn.Hash{},
|
||||
Simulate: suite.ctx.IsCheckTx(),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
_, err = tc.state.TransitionDb(suite.ctx, types.DefaultChainConfig())
|
||||
|
||||
if tc.expPass {
|
||||
suite.Require().NoError(err, tc.name)
|
||||
fromBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(suite.address)
|
||||
toBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(recipient)
|
||||
suite.Require().Equal(fromBalance, big.NewInt(4950), tc.name)
|
||||
suite.Require().Equal(toBalance, big.NewInt(50), tc.name)
|
||||
} else {
|
||||
suite.Require().Error(err, tc.name)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,990 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
|
||||
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethstate "github.com/ethereum/go-ethereum/core/state"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethvm "github.com/ethereum/go-ethereum/core/vm"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
_ ethvm.StateDB = (*CommitStateDB)(nil)
|
||||
|
||||
zeroBalance = sdk.ZeroInt().BigInt()
|
||||
)
|
||||
|
||||
// CommitStateDB implements the Geth state.StateDB interface. Instead of using
|
||||
// a trie and database for querying and persistence, the Keeper uses KVStores
|
||||
// and an AccountKeeper to facilitate state transitions.
|
||||
//
|
||||
// TODO: This implementation is subject to change in regards to its statefull
|
||||
// manner. In otherwords, how this relates to the keeper in this module.
|
||||
type CommitStateDB struct {
|
||||
// TODO: We need to store the context as part of the structure itself opposed
|
||||
// to being passed as a parameter (as it should be) in order to implement the
|
||||
// StateDB interface. Perhaps there is a better way.
|
||||
ctx sdk.Context
|
||||
|
||||
storeKey sdk.StoreKey
|
||||
transientKey sdk.StoreKey
|
||||
paramSpace paramtypes.Subspace
|
||||
accountKeeper AccountKeeper
|
||||
bankKeeper BankKeeper
|
||||
|
||||
// array that hold 'live' objects, which will get modified while processing a
|
||||
// state transition
|
||||
stateObjects []stateEntry
|
||||
addressToObjectIndex map[ethcmn.Address]int // map from address to the index of the state objects slice
|
||||
stateObjectsDirty map[ethcmn.Address]struct{}
|
||||
|
||||
// The refund counter, also used by state transitioning.
|
||||
refund uint64
|
||||
|
||||
thash, bhash ethcmn.Hash
|
||||
txIndex int
|
||||
logSize uint
|
||||
|
||||
// TODO: Determine if we actually need this as we do not need preimages in
|
||||
// the SDK, but it seems to be used elsewhere in Geth.
|
||||
preimages []preimageEntry
|
||||
hashToPreimageIndex map[ethcmn.Hash]int // map from hash to the index of the preimages slice
|
||||
|
||||
// DB error.
|
||||
// State objects are used by the consensus core and VM which are
|
||||
// unable to deal with database-level errors. Any error that occurs
|
||||
// during a database read is memo-ized here and will eventually be returned
|
||||
// by StateDB.Commit.
|
||||
dbErr error
|
||||
|
||||
// journal of state modifications. This is the backbone of
|
||||
// Snapshot and RevertToSnapshot.
|
||||
journal *journal
|
||||
validRevisions []revision
|
||||
nextRevisionID int
|
||||
|
||||
// mutex for state deep copying
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// NewCommitStateDB returns a reference to a newly initialized CommitStateDB
|
||||
// which implements Geth's state.StateDB interface.
|
||||
//
|
||||
// CONTRACT: Stores used for state must be cache-wrapped as the ordering of the
|
||||
// key/value space matters in determining the merkle root.
|
||||
func NewCommitStateDB(
|
||||
ctx sdk.Context, storeKey, tKey sdk.StoreKey, paramSpace paramtypes.Subspace,
|
||||
ak AccountKeeper, bankKeeper BankKeeper,
|
||||
) *CommitStateDB {
|
||||
return &CommitStateDB{
|
||||
ctx: ctx,
|
||||
storeKey: storeKey,
|
||||
transientKey: tKey,
|
||||
paramSpace: paramSpace,
|
||||
accountKeeper: ak,
|
||||
bankKeeper: bankKeeper,
|
||||
stateObjects: []stateEntry{},
|
||||
addressToObjectIndex: make(map[ethcmn.Address]int),
|
||||
stateObjectsDirty: make(map[ethcmn.Address]struct{}),
|
||||
preimages: []preimageEntry{},
|
||||
hashToPreimageIndex: make(map[ethcmn.Hash]int),
|
||||
journal: newJournal(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithContext returns a Database with an updated SDK context
|
||||
func (csdb *CommitStateDB) WithContext(ctx sdk.Context) *CommitStateDB {
|
||||
csdb.ctx = ctx
|
||||
return csdb
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Setters
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// SetHeightHash sets the block header hash associated with a given height.
|
||||
func (csdb *CommitStateDB) SetHeightHash(height uint64, hash ethcmn.Hash) {
|
||||
store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixHeightToHeaderHash)
|
||||
store.Set(sdk.Uint64ToBigEndian(height), hash.Bytes())
|
||||
}
|
||||
|
||||
// SetParams sets the evm parameters to the param space.
|
||||
func (csdb *CommitStateDB) SetParams(params Params) {
|
||||
csdb.paramSpace.SetParamSet(csdb.ctx, ¶ms)
|
||||
}
|
||||
|
||||
// SetBalance sets the balance of an account.
|
||||
func (csdb *CommitStateDB) SetBalance(addr ethcmn.Address, amount *big.Int) {
|
||||
so := csdb.GetOrNewStateObject(addr)
|
||||
so.SetBalance(amount)
|
||||
}
|
||||
|
||||
// AddBalance adds amount to the account associated with addr.
|
||||
func (csdb *CommitStateDB) AddBalance(addr ethcmn.Address, amount *big.Int) {
|
||||
so := csdb.GetOrNewStateObject(addr)
|
||||
so.AddBalance(amount)
|
||||
}
|
||||
|
||||
// SubBalance subtracts amount from the account associated with addr.
|
||||
func (csdb *CommitStateDB) SubBalance(addr ethcmn.Address, amount *big.Int) {
|
||||
so := csdb.GetOrNewStateObject(addr)
|
||||
so.SubBalance(amount)
|
||||
}
|
||||
|
||||
// SetNonce sets the nonce (sequence number) of an account.
|
||||
func (csdb *CommitStateDB) SetNonce(addr ethcmn.Address, nonce uint64) {
|
||||
so := csdb.GetOrNewStateObject(addr)
|
||||
so.SetNonce(nonce)
|
||||
}
|
||||
|
||||
// SetState sets the storage state with a key, value pair for an account.
|
||||
func (csdb *CommitStateDB) SetState(addr ethcmn.Address, key, value ethcmn.Hash) {
|
||||
so := csdb.GetOrNewStateObject(addr)
|
||||
so.SetState(nil, key, value)
|
||||
}
|
||||
|
||||
// SetCode sets the code for a given account.
|
||||
func (csdb *CommitStateDB) SetCode(addr ethcmn.Address, code []byte) {
|
||||
so := csdb.GetOrNewStateObject(addr)
|
||||
so.SetCode(ethcrypto.Keccak256Hash(code), code)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Transaction logs
|
||||
// Required for upgrade logic or ease of querying.
|
||||
// NOTE: we use BinaryLengthPrefixed since the tx logs are also included on Result data,
|
||||
// which can't use BinaryBare.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// SetLogs sets the logs for a transaction in the KVStore.
|
||||
func (csdb *CommitStateDB) SetLogs(hash ethcmn.Hash, logs []*ethtypes.Log) error {
|
||||
store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs)
|
||||
|
||||
txLogs := NewTransactionLogsFromEth(hash, logs)
|
||||
bz, err := ModuleCdc.MarshalBinaryBare(&txLogs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store.Set(hash.Bytes(), bz)
|
||||
csdb.logSize = uint(len(logs))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteLogs removes the logs from the KVStore. It is used during journal.Revert.
|
||||
func (csdb *CommitStateDB) DeleteLogs(hash ethcmn.Hash) {
|
||||
store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs)
|
||||
store.Delete(hash.Bytes())
|
||||
}
|
||||
|
||||
// AddLog adds a new log to the state and sets the log metadata from the state.
|
||||
func (csdb *CommitStateDB) AddLog(log *ethtypes.Log) {
|
||||
csdb.journal.append(addLogChange{txhash: csdb.thash})
|
||||
|
||||
log.TxHash = csdb.thash
|
||||
log.BlockHash = csdb.bhash
|
||||
log.TxIndex = uint(csdb.txIndex)
|
||||
log.Index = csdb.logSize
|
||||
|
||||
logs, err := csdb.GetLogs(csdb.thash)
|
||||
if err != nil {
|
||||
// panic on unmarshal error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err = csdb.SetLogs(csdb.thash, append(logs, log)); err != nil {
|
||||
// panic on marshal error
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// AddPreimage records a SHA3 preimage seen by the VM.
|
||||
func (csdb *CommitStateDB) AddPreimage(hash ethcmn.Hash, preimage []byte) {
|
||||
if _, ok := csdb.hashToPreimageIndex[hash]; !ok {
|
||||
csdb.journal.append(addPreimageChange{hash: hash})
|
||||
|
||||
pi := make([]byte, len(preimage))
|
||||
copy(pi, preimage)
|
||||
|
||||
csdb.preimages = append(csdb.preimages, preimageEntry{hash: hash, preimage: pi})
|
||||
csdb.hashToPreimageIndex[hash] = len(csdb.preimages) - 1
|
||||
}
|
||||
}
|
||||
|
||||
// AddRefund adds gas to the refund counter.
|
||||
func (csdb *CommitStateDB) AddRefund(gas uint64) {
|
||||
csdb.journal.append(refundChange{prev: csdb.refund})
|
||||
csdb.refund += gas
|
||||
}
|
||||
|
||||
// SubRefund removes gas from the refund counter. It will panic if the refund
|
||||
// counter goes below zero.
|
||||
func (csdb *CommitStateDB) SubRefund(gas uint64) {
|
||||
csdb.journal.append(refundChange{prev: csdb.refund})
|
||||
if gas > csdb.refund {
|
||||
panic("refund counter below zero")
|
||||
}
|
||||
|
||||
csdb.refund -= gas
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Access List // TODO: deprecate
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// 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 (csdb *CommitStateDB) PrepareAccessList(sender ethcmn.Address, dest *ethcmn.Address, precompiles []ethcmn.Address, txAccesses ethtypes.AccessList) {
|
||||
csdb.AddAddressToAccessList(sender)
|
||||
if dest != nil {
|
||||
csdb.AddAddressToAccessList(*dest)
|
||||
// If it's a create-tx, the destination will be added inside evm.create
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
csdb.AddAddressToAccessList(addr)
|
||||
}
|
||||
for _, tuple := range txAccesses {
|
||||
csdb.AddAddressToAccessList(tuple.Address)
|
||||
for _, key := range tuple.StorageKeys {
|
||||
csdb.AddSlotToAccessList(tuple.Address, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddressInAccessList returns true if the address is registered on the access list map.
|
||||
func (csdb *CommitStateDB) AddressInAccessList(addr ethcmn.Address) bool {
|
||||
ts := prefix.NewStore(csdb.ctx.TransientStore(csdb.transientKey), KeyPrefixTransientAccessListAddress)
|
||||
return ts.Has(addr.Bytes())
|
||||
}
|
||||
|
||||
func (csdb *CommitStateDB) SlotInAccessList(addr ethcmn.Address, slot ethcmn.Hash) (addressOk bool, slotOk bool) {
|
||||
addressOk = csdb.AddressInAccessList(addr)
|
||||
slotOk = csdb.addressSlotInAccessList(addr, slot)
|
||||
return addressOk, slotOk
|
||||
}
|
||||
|
||||
func (csdb *CommitStateDB) addressSlotInAccessList(addr ethcmn.Address, slot ethcmn.Hash) bool {
|
||||
ts := prefix.NewStore(csdb.ctx.TransientStore(csdb.transientKey), KeyPrefixTransientAccessListSlot)
|
||||
key := append(addr.Bytes(), slot.Bytes()...)
|
||||
return ts.Has(key)
|
||||
}
|
||||
|
||||
// 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 (csdb *CommitStateDB) AddAddressToAccessList(addr ethcmn.Address) {
|
||||
if csdb.AddressInAccessList(addr) {
|
||||
return
|
||||
}
|
||||
|
||||
ts := prefix.NewStore(csdb.ctx.TransientStore(csdb.transientKey), KeyPrefixTransientAccessListAddress)
|
||||
ts.Set(addr.Bytes(), []byte{0x1})
|
||||
}
|
||||
|
||||
// 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 (csdb *CommitStateDB) AddSlotToAccessList(addr ethcmn.Address, slot ethcmn.Hash) {
|
||||
csdb.AddAddressToAccessList(addr)
|
||||
if csdb.addressSlotInAccessList(addr, slot) {
|
||||
return
|
||||
}
|
||||
|
||||
ts := prefix.NewStore(csdb.ctx.TransientStore(csdb.transientKey), KeyPrefixTransientAccessListSlot)
|
||||
key := append(addr.Bytes(), slot.Bytes()...)
|
||||
ts.Set(key, []byte{0x1})
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// GetHeightHash returns the block header hash associated with a given block height and chain epoch number.
|
||||
func (csdb *CommitStateDB) GetHeightHash(height uint64) ethcmn.Hash {
|
||||
store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixHeightToHeaderHash)
|
||||
key := sdk.Uint64ToBigEndian(height)
|
||||
bz := store.Get(key)
|
||||
if len(bz) == 0 {
|
||||
return ethcmn.Hash{}
|
||||
}
|
||||
|
||||
return ethcmn.BytesToHash(bz)
|
||||
}
|
||||
|
||||
// GetParams returns the total set of evm parameters.
|
||||
func (csdb *CommitStateDB) GetParams() (params Params) {
|
||||
csdb.paramSpace.GetParamSet(csdb.ctx, ¶ms)
|
||||
return params
|
||||
}
|
||||
|
||||
// GetBalance retrieves the balance from the given address or 0 if object not
|
||||
// found.
|
||||
func (csdb *CommitStateDB) GetBalance(addr ethcmn.Address) *big.Int {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so != nil {
|
||||
return so.Balance()
|
||||
}
|
||||
|
||||
return zeroBalance
|
||||
}
|
||||
|
||||
// GetNonce returns the nonce (sequence number) for a given account.
|
||||
func (csdb *CommitStateDB) GetNonce(addr ethcmn.Address) uint64 {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so != nil {
|
||||
return so.Nonce()
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// TxIndex returns the current transaction index set by Prepare.
|
||||
func (csdb *CommitStateDB) TxIndex() int {
|
||||
return csdb.txIndex
|
||||
}
|
||||
|
||||
// BlockHash returns the current block hash set by Prepare.
|
||||
func (csdb *CommitStateDB) BlockHash() ethcmn.Hash {
|
||||
return csdb.bhash
|
||||
}
|
||||
|
||||
// GetCode returns the code for a given account.
|
||||
func (csdb *CommitStateDB) GetCode(addr ethcmn.Address) []byte {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so != nil {
|
||||
return so.Code(nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCodeSize returns the code size for a given account.
|
||||
func (csdb *CommitStateDB) GetCodeSize(addr ethcmn.Address) int {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if so.code != nil {
|
||||
return len(so.code)
|
||||
}
|
||||
|
||||
return len(so.Code(nil))
|
||||
}
|
||||
|
||||
// GetCodeHash returns the code hash for a given account.
|
||||
func (csdb *CommitStateDB) GetCodeHash(addr ethcmn.Address) ethcmn.Hash {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so == nil {
|
||||
return ethcmn.Hash{}
|
||||
}
|
||||
|
||||
return ethcmn.BytesToHash(so.CodeHash())
|
||||
}
|
||||
|
||||
// GetState retrieves a value from the given account's storage store.
|
||||
func (csdb *CommitStateDB) GetState(addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so != nil {
|
||||
return so.GetState(nil, hash)
|
||||
}
|
||||
|
||||
return ethcmn.Hash{}
|
||||
}
|
||||
|
||||
// GetCommittedState retrieves a value from the given account's committed
|
||||
// storage.
|
||||
func (csdb *CommitStateDB) GetCommittedState(addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so != nil {
|
||||
return so.GetCommittedState(nil, hash)
|
||||
}
|
||||
|
||||
return ethcmn.Hash{}
|
||||
}
|
||||
|
||||
// GetLogs returns the current logs for a given transaction hash from the KVStore.
|
||||
func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) ([]*ethtypes.Log, error) {
|
||||
store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs)
|
||||
bz := store.Get(hash.Bytes())
|
||||
if len(bz) == 0 {
|
||||
// return nil error if logs are not found
|
||||
return []*ethtypes.Log{}, nil
|
||||
}
|
||||
|
||||
var txLogs TransactionLogs
|
||||
if err := ModuleCdc.UnmarshalBinaryBare(bz, &txLogs); err != nil {
|
||||
return []*ethtypes.Log{}, err
|
||||
}
|
||||
|
||||
allLogs := []*ethtypes.Log{}
|
||||
for _, txLog := range txLogs.Logs {
|
||||
allLogs = append(allLogs, txLog.ToEthereum())
|
||||
}
|
||||
|
||||
return allLogs, nil
|
||||
}
|
||||
|
||||
// AllLogs returns all the current logs in the state.
|
||||
func (csdb *CommitStateDB) AllLogs() []*ethtypes.Log {
|
||||
store := csdb.ctx.KVStore(csdb.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, KeyPrefixLogs)
|
||||
defer iterator.Close()
|
||||
|
||||
allLogs := []*ethtypes.Log{}
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var txLogs TransactionLogs
|
||||
ModuleCdc.MustUnmarshalBinaryBare(iterator.Value(), &txLogs)
|
||||
|
||||
for _, txLog := range txLogs.Logs {
|
||||
allLogs = append(allLogs, txLog.ToEthereum())
|
||||
}
|
||||
}
|
||||
|
||||
return allLogs
|
||||
}
|
||||
|
||||
// GetRefund returns the current value of the refund counter.
|
||||
func (csdb *CommitStateDB) GetRefund() uint64 {
|
||||
return csdb.refund
|
||||
}
|
||||
|
||||
// Preimages returns a list of SHA3 preimages that have been submitted.
|
||||
func (csdb *CommitStateDB) Preimages() map[ethcmn.Hash][]byte {
|
||||
preimages := map[ethcmn.Hash][]byte{}
|
||||
|
||||
for _, pe := range csdb.preimages {
|
||||
preimages[pe.hash] = pe.preimage
|
||||
}
|
||||
return preimages
|
||||
}
|
||||
|
||||
// HasSuicided returns if the given account for the specified address has been
|
||||
// killed.
|
||||
func (csdb *CommitStateDB) HasSuicided(addr ethcmn.Address) bool {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so != nil {
|
||||
return so.suicided
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// StorageTrie returns nil as the state in Ethermint does not use a direct
|
||||
// storage trie.
|
||||
func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Persistence
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Commit writes the state to the appropriate KVStores. For each state object
|
||||
// in the cache, it will either be removed, or have it's code set and/or it's
|
||||
// state (storage) updated. In addition, the state object (account) itself will
|
||||
// be written. Finally, the root hash (version) will be returned.
|
||||
func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error) {
|
||||
defer csdb.clearJournalAndRefund()
|
||||
|
||||
// remove dirty state object entries based on the journal
|
||||
for _, dirty := range csdb.journal.dirties {
|
||||
csdb.stateObjectsDirty[dirty.address] = struct{}{}
|
||||
}
|
||||
|
||||
// set the state objects
|
||||
for _, stateEntry := range csdb.stateObjects {
|
||||
_, isDirty := csdb.stateObjectsDirty[stateEntry.address]
|
||||
|
||||
switch {
|
||||
case stateEntry.stateObject.suicided || (isDirty && deleteEmptyObjects && stateEntry.stateObject.empty()):
|
||||
// If the state object has been removed, don't bother syncing it and just
|
||||
// remove it from the store.
|
||||
csdb.deleteStateObject(stateEntry.stateObject)
|
||||
|
||||
case isDirty:
|
||||
// write any contract code associated with the state object
|
||||
if stateEntry.stateObject.code != nil && stateEntry.stateObject.dirtyCode {
|
||||
stateEntry.stateObject.commitCode()
|
||||
stateEntry.stateObject.dirtyCode = false
|
||||
}
|
||||
|
||||
// update the object in the KVStore
|
||||
if err := csdb.updateStateObject(stateEntry.stateObject); err != nil {
|
||||
return ethcmn.Hash{}, err
|
||||
}
|
||||
}
|
||||
|
||||
delete(csdb.stateObjectsDirty, stateEntry.address)
|
||||
}
|
||||
|
||||
// NOTE: Ethereum returns the trie merkle root here, but as commitment
|
||||
// actually happens in the BaseApp at EndBlocker, we do not know the root at
|
||||
// this time.
|
||||
return ethcmn.Hash{}, nil
|
||||
}
|
||||
|
||||
// Finalise finalizes the state objects (accounts) state by setting their state,
|
||||
// removing the csdb destructed objects and clearing the journal as well as the
|
||||
// refunds.
|
||||
func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error {
|
||||
for _, dirty := range csdb.journal.dirties {
|
||||
idx, exist := csdb.addressToObjectIndex[dirty.address]
|
||||
if !exist {
|
||||
// ripeMD is 'touched' at block 1714175, in tx:
|
||||
// 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
|
||||
//
|
||||
// That tx goes out of gas, and although the notion of 'touched' does not
|
||||
// exist there, the touch-event will still be recorded in the journal.
|
||||
// Since ripeMD is a special snowflake, it will persist in the journal even
|
||||
// though the journal is reverted. In this special circumstance, it may
|
||||
// exist in journal.dirties but not in stateObjects. Thus, we can safely
|
||||
// ignore it here.
|
||||
continue
|
||||
}
|
||||
|
||||
stateEntry := csdb.stateObjects[idx]
|
||||
if stateEntry.stateObject.suicided || (deleteEmptyObjects && stateEntry.stateObject.empty()) {
|
||||
csdb.deleteStateObject(stateEntry.stateObject)
|
||||
} else {
|
||||
// Set all the dirty state storage items for the state object in the
|
||||
// KVStore and finally set the account in the account mapper.
|
||||
stateEntry.stateObject.commitState()
|
||||
if err := csdb.updateStateObject(stateEntry.stateObject); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
csdb.stateObjectsDirty[dirty.address] = struct{}{}
|
||||
}
|
||||
|
||||
// invalidate journal because reverting across transactions is not allowed
|
||||
csdb.clearJournalAndRefund()
|
||||
return nil
|
||||
}
|
||||
|
||||
// IntermediateRoot returns the current root hash of the state. It is called in
|
||||
// between transactions to get the root hash that goes into transaction
|
||||
// receipts.
|
||||
//
|
||||
// NOTE: The SDK has not concept or method of getting any intermediate merkle
|
||||
// root as commitment of the merkle-ized tree doesn't happen until the
|
||||
// BaseApps' EndBlocker.
|
||||
func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) (ethcmn.Hash, error) {
|
||||
if err := csdb.Finalise(deleteEmptyObjects); err != nil {
|
||||
return ethcmn.Hash{}, err
|
||||
}
|
||||
|
||||
return ethcmn.Hash{}, nil
|
||||
}
|
||||
|
||||
// updateStateObject writes the given state object to the store.
|
||||
func (csdb *CommitStateDB) updateStateObject(so *stateObject) error {
|
||||
evmDenom := csdb.GetParams().EvmDenom
|
||||
// NOTE: we don't use sdk.NewCoin here to avoid panic on test importer's genesis
|
||||
newBalance := sdk.Coin{Denom: evmDenom, Amount: sdk.NewIntFromBigInt(so.Balance())}
|
||||
if !newBalance.IsValid() {
|
||||
return fmt.Errorf("invalid balance %s", newBalance)
|
||||
}
|
||||
|
||||
err := csdb.bankKeeper.SetBalance(csdb.ctx, so.account.GetAddress(), newBalance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
csdb.accountKeeper.SetAccount(csdb.ctx, so.account)
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteStateObject removes the given state object from the state store.
|
||||
func (csdb *CommitStateDB) deleteStateObject(so *stateObject) {
|
||||
so.deleted = true
|
||||
csdb.accountKeeper.RemoveAccount(csdb.ctx, so.account)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Snapshotting
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Snapshot returns an identifier for the current revision of the state.
|
||||
func (csdb *CommitStateDB) Snapshot() int {
|
||||
id := csdb.nextRevisionID
|
||||
csdb.nextRevisionID++
|
||||
|
||||
csdb.validRevisions = append(
|
||||
csdb.validRevisions,
|
||||
revision{
|
||||
id: id,
|
||||
journalIndex: csdb.journal.length(),
|
||||
},
|
||||
)
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
// RevertToSnapshot reverts all state changes made since the given revision.
|
||||
func (csdb *CommitStateDB) RevertToSnapshot(revID int) {
|
||||
// find the snapshot in the stack of valid snapshots
|
||||
idx := sort.Search(len(csdb.validRevisions), func(i int) bool {
|
||||
return csdb.validRevisions[i].id >= revID
|
||||
})
|
||||
|
||||
if idx == len(csdb.validRevisions) || csdb.validRevisions[idx].id != revID {
|
||||
panic(fmt.Errorf("revision ID %v cannot be reverted", revID))
|
||||
}
|
||||
|
||||
snapshot := csdb.validRevisions[idx].journalIndex
|
||||
|
||||
// replay the journal to undo changes and remove invalidated snapshots
|
||||
csdb.journal.revert(csdb, snapshot)
|
||||
csdb.validRevisions = csdb.validRevisions[:idx]
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Auxiliary
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Database retrieves the low level database supporting the lower level trie
|
||||
// ops. It is not used in Ethermint, so it returns nil.
|
||||
func (csdb *CommitStateDB) Database() ethstate.Database {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Empty returns whether the state object is either non-existent or empty
|
||||
// according to the EIP161 specification (balance = nonce = code = 0).
|
||||
func (csdb *CommitStateDB) Empty(addr ethcmn.Address) bool {
|
||||
so := csdb.getStateObject(addr)
|
||||
return so == nil || so.empty()
|
||||
}
|
||||
|
||||
// Exist reports whether the given account address exists in the state. Notably,
|
||||
// this also returns true for suicided accounts.
|
||||
func (csdb *CommitStateDB) Exist(addr ethcmn.Address) bool {
|
||||
return csdb.getStateObject(addr) != nil
|
||||
}
|
||||
|
||||
// Error returns the first non-nil error the StateDB encountered.
|
||||
func (csdb *CommitStateDB) Error() error {
|
||||
return csdb.dbErr
|
||||
}
|
||||
|
||||
// Suicide marks the given account as suicided and clears the account balance.
|
||||
//
|
||||
// The account's state object is still available until the state is committed,
|
||||
// getStateObject will return a non-nil account after Suicide.
|
||||
func (csdb *CommitStateDB) Suicide(addr ethcmn.Address) bool {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
csdb.journal.append(suicideChange{
|
||||
account: &addr,
|
||||
prev: so.suicided,
|
||||
prevBalance: sdk.NewIntFromBigInt(so.Balance()),
|
||||
})
|
||||
|
||||
so.markSuicided()
|
||||
so.SetBalance(new(big.Int))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Reset clears out all ephemeral state objects from the state db, but keeps
|
||||
// the underlying account mapper and store keys to avoid reloading data for the
|
||||
// next operations.
|
||||
func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error {
|
||||
csdb.stateObjects = []stateEntry{}
|
||||
csdb.addressToObjectIndex = make(map[ethcmn.Address]int)
|
||||
csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{})
|
||||
csdb.thash = ethcmn.Hash{}
|
||||
csdb.bhash = ethcmn.Hash{}
|
||||
csdb.txIndex = 0
|
||||
csdb.logSize = 0
|
||||
csdb.preimages = []preimageEntry{}
|
||||
csdb.hashToPreimageIndex = make(map[ethcmn.Hash]int)
|
||||
|
||||
csdb.clearJournalAndRefund()
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAccounts updates the nonce and coin balances of accounts
|
||||
func (csdb *CommitStateDB) UpdateAccounts() {
|
||||
for _, stateEntry := range csdb.stateObjects {
|
||||
address := sdk.AccAddress(stateEntry.address.Bytes())
|
||||
currAccount := csdb.accountKeeper.GetAccount(csdb.ctx, address)
|
||||
ethAcc, ok := currAccount.(*ethermint.EthAccount)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
evmDenom := csdb.GetParams().EvmDenom
|
||||
balance := csdb.bankKeeper.GetBalance(csdb.ctx, address, evmDenom)
|
||||
|
||||
if stateEntry.stateObject.Balance() != balance.Amount.BigInt() && balance.IsValid() {
|
||||
stateEntry.stateObject.balance = balance.Amount
|
||||
}
|
||||
|
||||
if stateEntry.stateObject.Nonce() != ethAcc.GetSequence() {
|
||||
stateEntry.stateObject.account = ethAcc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ClearStateObjects clears cache of state objects to handle account changes outside of the EVM
|
||||
func (csdb *CommitStateDB) ClearStateObjects() {
|
||||
csdb.stateObjects = []stateEntry{}
|
||||
csdb.addressToObjectIndex = make(map[ethcmn.Address]int)
|
||||
csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{})
|
||||
}
|
||||
|
||||
func (csdb *CommitStateDB) clearJournalAndRefund() {
|
||||
csdb.journal = newJournal()
|
||||
csdb.validRevisions = csdb.validRevisions[:0]
|
||||
csdb.refund = 0
|
||||
}
|
||||
|
||||
// Prepare sets the current transaction hash and index and block hash which is
|
||||
// used when the EVM emits new state logs.
|
||||
func (csdb *CommitStateDB) Prepare(thash, bhash ethcmn.Hash, txi int) {
|
||||
csdb.thash = thash
|
||||
csdb.bhash = bhash
|
||||
csdb.txIndex = txi
|
||||
}
|
||||
|
||||
// CreateAccount explicitly creates a state object. If a state object with the
|
||||
// address already exists the balance is carried over to the new account.
|
||||
//
|
||||
// CreateAccount is called during the EVM CREATE operation. The situation might
|
||||
// arise that a contract does the following:
|
||||
//
|
||||
// 1. sends funds to sha(account ++ (nonce + 1))
|
||||
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
||||
//
|
||||
// Carrying over the balance ensures that Ether doesn't disappear.
|
||||
func (csdb *CommitStateDB) CreateAccount(addr ethcmn.Address) {
|
||||
newobj, prevobj := csdb.createObject(addr)
|
||||
if prevobj != nil {
|
||||
newobj.setBalance(sdk.NewIntFromBigInt(prevobj.Balance()))
|
||||
}
|
||||
}
|
||||
|
||||
// Copy creates a deep, independent copy of the state.
|
||||
//
|
||||
// NOTE: Snapshots of the copied state cannot be applied to the copy.
|
||||
func (csdb *CommitStateDB) Copy() *CommitStateDB {
|
||||
|
||||
// copy all the basic fields, initialize the memory ones
|
||||
state := &CommitStateDB{}
|
||||
CopyCommitStateDB(csdb, state)
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
func CopyCommitStateDB(from, to *CommitStateDB) {
|
||||
from.lock.Lock()
|
||||
defer from.lock.Unlock()
|
||||
|
||||
to.ctx = from.ctx
|
||||
to.storeKey = from.storeKey
|
||||
to.paramSpace = from.paramSpace
|
||||
to.accountKeeper = from.accountKeeper
|
||||
to.bankKeeper = from.bankKeeper
|
||||
to.stateObjects = []stateEntry{}
|
||||
to.addressToObjectIndex = make(map[ethcmn.Address]int)
|
||||
to.stateObjectsDirty = make(map[ethcmn.Address]struct{})
|
||||
to.refund = from.refund
|
||||
to.logSize = from.logSize
|
||||
to.preimages = make([]preimageEntry, len(from.preimages))
|
||||
to.hashToPreimageIndex = make(map[ethcmn.Hash]int, len(from.hashToPreimageIndex))
|
||||
to.journal = newJournal()
|
||||
to.thash = from.thash
|
||||
to.bhash = from.bhash
|
||||
to.txIndex = from.txIndex
|
||||
validRevisions := make([]revision, len(from.validRevisions))
|
||||
copy(validRevisions, from.validRevisions)
|
||||
to.validRevisions = validRevisions
|
||||
to.nextRevisionID = from.nextRevisionID
|
||||
|
||||
// copy the dirty states, logs, and preimages
|
||||
for _, dirty := range from.journal.dirties {
|
||||
// There is a case where an object is in the journal but not in the
|
||||
// stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we
|
||||
// need to check for nil.
|
||||
//
|
||||
// Ref: https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527
|
||||
if idx, exist := from.addressToObjectIndex[dirty.address]; exist {
|
||||
to.stateObjects = append(to.stateObjects, stateEntry{
|
||||
address: dirty.address,
|
||||
stateObject: from.stateObjects[idx].stateObject.deepCopy(to),
|
||||
})
|
||||
to.addressToObjectIndex[dirty.address] = len(to.stateObjects) - 1
|
||||
to.stateObjectsDirty[dirty.address] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Above, we don't copy the actual journal. This means that if the copy is
|
||||
// copied, the loop above will be a no-op, since the copy's journal is empty.
|
||||
// Thus, here we iterate over stateObjects, to enable copies of copies.
|
||||
for addr := range from.stateObjectsDirty {
|
||||
if idx, exist := to.addressToObjectIndex[addr]; !exist {
|
||||
to.setStateObject(from.stateObjects[idx].stateObject.deepCopy(to))
|
||||
to.stateObjectsDirty[addr] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// copy pre-images
|
||||
for i, preimageEntry := range from.preimages {
|
||||
to.preimages[i] = preimageEntry
|
||||
to.hashToPreimageIndex[preimageEntry.hash] = i
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachStorage iterates over each storage items, all invoke the provided
|
||||
// callback on each key, value pair.
|
||||
func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, value ethcmn.Hash) (stop bool)) error {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
store := csdb.ctx.KVStore(csdb.storeKey)
|
||||
prefix := AddressStoragePrefix(so.Address())
|
||||
iterator := sdk.KVStorePrefixIterator(store, prefix)
|
||||
defer iterator.Close()
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
key := ethcmn.BytesToHash(iterator.Key())
|
||||
value := ethcmn.BytesToHash(iterator.Value())
|
||||
|
||||
if idx, dirty := so.keyToDirtyStorageIndex[key]; dirty {
|
||||
// check if iteration stops
|
||||
if cb(key, ethcmn.HexToHash(so.dirtyStorage[idx].Value)) {
|
||||
break
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// check if iteration stops
|
||||
if cb(key, value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOrNewStateObject retrieves a state object or create a new state object if
|
||||
// nil.
|
||||
func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) StateObject {
|
||||
so := csdb.getStateObject(addr)
|
||||
if so == nil || so.deleted {
|
||||
so, _ = csdb.createObject(addr)
|
||||
}
|
||||
|
||||
return so
|
||||
}
|
||||
|
||||
// createObject creates a new state object. If there is an existing account with
|
||||
// the given address, it is overwritten and returned as the second return value.
|
||||
func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) {
|
||||
prevObj = csdb.getStateObject(addr)
|
||||
|
||||
acc := csdb.accountKeeper.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes()))
|
||||
|
||||
newObj = newStateObject(csdb, acc, sdk.ZeroInt())
|
||||
newObj.setNonce(0) // sets the object to dirty
|
||||
|
||||
if prevObj == nil {
|
||||
csdb.journal.append(createObjectChange{account: &addr})
|
||||
} else {
|
||||
csdb.journal.append(resetObjectChange{prev: prevObj})
|
||||
}
|
||||
|
||||
csdb.setStateObject(newObj)
|
||||
return newObj, prevObj
|
||||
}
|
||||
|
||||
// setError remembers the first non-nil error it is called with.
|
||||
func (csdb *CommitStateDB) setError(err error) {
|
||||
if csdb.dbErr == nil {
|
||||
csdb.dbErr = err
|
||||
}
|
||||
}
|
||||
|
||||
// getStateObject attempts to retrieve a state object given by the address.
|
||||
// Returns nil and sets an error if not found.
|
||||
func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *stateObject) {
|
||||
if idx, found := csdb.addressToObjectIndex[addr]; found {
|
||||
// prefer 'live' (cached) objects
|
||||
if so := csdb.stateObjects[idx].stateObject; so != nil {
|
||||
if so.deleted {
|
||||
return nil
|
||||
}
|
||||
|
||||
return so
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, attempt to fetch the account from the account mapper
|
||||
acc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes()))
|
||||
if acc == nil {
|
||||
csdb.setError(fmt.Errorf("no account found for address: %s", addr.String()))
|
||||
return nil
|
||||
}
|
||||
|
||||
evmDenom := csdb.GetParams().EvmDenom
|
||||
balance := csdb.bankKeeper.GetBalance(csdb.ctx, acc.GetAddress(), evmDenom)
|
||||
|
||||
// insert the state object into the live set
|
||||
so := newStateObject(csdb, acc, balance.Amount)
|
||||
csdb.setStateObject(so)
|
||||
|
||||
return so
|
||||
}
|
||||
|
||||
func (csdb *CommitStateDB) setStateObject(so *stateObject) {
|
||||
if idx, found := csdb.addressToObjectIndex[so.Address()]; found {
|
||||
// update the existing object
|
||||
csdb.stateObjects[idx].stateObject = so
|
||||
return
|
||||
}
|
||||
|
||||
// append the new state object to the stateObjects slice
|
||||
se := stateEntry{
|
||||
address: so.Address(),
|
||||
stateObject: so,
|
||||
}
|
||||
|
||||
csdb.stateObjects = append(csdb.stateObjects, se)
|
||||
csdb.addressToObjectIndex[se.address] = len(csdb.stateObjects) - 1
|
||||
}
|
||||
|
||||
// RawDump returns a raw state dump.
|
||||
//
|
||||
// TODO: Implement if we need it, especially for the RPC API.
|
||||
func (csdb *CommitStateDB) RawDump() ethstate.Dump {
|
||||
return ethstate.Dump{}
|
||||
}
|
||||
|
||||
type preimageEntry struct {
|
||||
// hash key of the preimage entry
|
||||
hash ethcmn.Hash
|
||||
preimage []byte
|
||||
}
|
@ -1,739 +0,0 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/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/app"
|
||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
type StateDBTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
ctx sdk.Context
|
||||
app *app.EthermintApp
|
||||
stateDB *types.CommitStateDB
|
||||
address ethcmn.Address
|
||||
stateObject types.StateObject
|
||||
}
|
||||
|
||||
func TestStateDBTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(StateDBTestSuite))
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) SetupTest() {
|
||||
checkTx := false
|
||||
|
||||
suite.app = app.Setup(checkTx)
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1})
|
||||
suite.stateDB = suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx)
|
||||
|
||||
privkey, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes())
|
||||
|
||||
balance := ethermint.NewPhotonCoin(sdk.ZeroInt())
|
||||
acc := ðermint.EthAccount{
|
||||
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0),
|
||||
CodeHash: ethcrypto.Keccak256(nil),
|
||||
}
|
||||
|
||||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
|
||||
err = suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), balance)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.stateObject = suite.stateDB.GetOrNewStateObject(suite.address)
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestParams() {
|
||||
params := suite.stateDB.GetParams()
|
||||
suite.Require().Equal(types.DefaultParams(), params)
|
||||
params.EvmDenom = "inj"
|
||||
suite.stateDB.SetParams(params)
|
||||
newParams := suite.stateDB.GetParams()
|
||||
suite.Require().Equal(newParams, params)
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestBloomFilter() {
|
||||
// Prepare db for logs
|
||||
tHash := ethcmn.BytesToHash([]byte{0x1})
|
||||
suite.stateDB.Prepare(tHash, ethcmn.Hash{}, 0)
|
||||
contractAddress := ethcmn.BigToAddress(big.NewInt(1))
|
||||
log := ethtypes.Log{Address: contractAddress}
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
numLogs int
|
||||
isBloom bool
|
||||
}{
|
||||
{
|
||||
"no logs",
|
||||
func() {},
|
||||
0,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"add log",
|
||||
func() {
|
||||
suite.stateDB.AddLog(&log)
|
||||
},
|
||||
1,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"bloom",
|
||||
func() {},
|
||||
0,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
logs, err := suite.stateDB.GetLogs(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 *StateDBTestSuite) TestStateDB_Balance() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
balance *big.Int
|
||||
}{
|
||||
{
|
||||
"set balance",
|
||||
func() {
|
||||
suite.stateDB.SetBalance(suite.address, big.NewInt(100))
|
||||
},
|
||||
big.NewInt(100),
|
||||
},
|
||||
{
|
||||
"sub balance",
|
||||
func() {
|
||||
suite.stateDB.SubBalance(suite.address, big.NewInt(100))
|
||||
},
|
||||
big.NewInt(0),
|
||||
},
|
||||
{
|
||||
"add balance",
|
||||
func() {
|
||||
suite.stateDB.AddBalance(suite.address, big.NewInt(200))
|
||||
},
|
||||
big.NewInt(200),
|
||||
},
|
||||
{
|
||||
"sub more than balance",
|
||||
func() {
|
||||
suite.stateDB.SubBalance(suite.address, big.NewInt(300))
|
||||
},
|
||||
big.NewInt(-100),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
suite.Require().Equal(tc.balance, suite.stateDB.GetBalance(suite.address), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDBNonce() {
|
||||
nonce := uint64(123)
|
||||
suite.stateDB.SetNonce(suite.address, nonce)
|
||||
suite.Require().Equal(nonce, suite.stateDB.GetNonce(suite.address))
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDB_Error() {
|
||||
nonce := suite.stateDB.GetNonce(ethcmn.Address{})
|
||||
suite.Require().Equal(0, int(nonce))
|
||||
suite.Require().Error(suite.stateDB.Error())
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDB_Database() {
|
||||
suite.Require().Nil(suite.stateDB.Database())
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDB_State() {
|
||||
key := ethcmn.BytesToHash([]byte("foo"))
|
||||
val := ethcmn.BytesToHash([]byte("bar"))
|
||||
suite.stateDB.SetState(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.stateDB.GetState(tc.address, tc.key)
|
||||
suite.Require().Equal(tc.value, value, tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) 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.stateDB.SetCode(suite.address, []byte("code"))
|
||||
},
|
||||
},
|
||||
{
|
||||
"state object not found",
|
||||
ethcmn.Address{},
|
||||
nil,
|
||||
func() {},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
suite.Require().Equal(tc.code, suite.stateDB.GetCode(tc.address), tc.name)
|
||||
suite.Require().Equal(len(tc.code), suite.stateDB.GetCodeSize(tc.address), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDB_Logs() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
log ethtypes.Log
|
||||
}{
|
||||
{
|
||||
"state db log",
|
||||
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.stateDB.SetLogs(hash, logs)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
dbLogs, err := suite.stateDB.GetLogs(hash)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().Equal(logs, dbLogs, tc.name)
|
||||
|
||||
suite.stateDB.DeleteLogs(hash)
|
||||
dbLogs, err = suite.stateDB.GetLogs(hash)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().Empty(dbLogs, tc.name)
|
||||
|
||||
suite.stateDB.AddLog(&tc.log)
|
||||
suite.Require().Equal(logs, suite.stateDB.AllLogs(), tc.name)
|
||||
|
||||
//resets state but checking to see if storekey still persists.
|
||||
err = suite.stateDB.Reset(hash)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().Equal(logs, suite.stateDB.AllLogs(), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDB_Preimage() {
|
||||
hash := ethcmn.BytesToHash([]byte("hash"))
|
||||
preimage := []byte("preimage")
|
||||
|
||||
suite.stateDB.AddPreimage(hash, preimage)
|
||||
suite.Require().Equal(preimage, suite.stateDB.Preimages()[hash])
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) 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.stateDB.AddRefund(tc.addAmount)
|
||||
suite.Require().Equal(tc.addAmount, suite.stateDB.GetRefund())
|
||||
|
||||
if tc.expPanic {
|
||||
suite.Panics(func() {
|
||||
suite.stateDB.SubRefund(tc.subAmount)
|
||||
})
|
||||
} else {
|
||||
suite.stateDB.SubRefund(tc.subAmount)
|
||||
suite.Require().Equal(tc.expRefund, suite.stateDB.GetRefund())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDB_CreateAccount() {
|
||||
prevBalance := big.NewInt(12)
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
address ethcmn.Address
|
||||
malleate func()
|
||||
}{
|
||||
{
|
||||
"existing account",
|
||||
suite.address,
|
||||
func() {
|
||||
suite.stateDB.AddBalance(suite.address, prevBalance)
|
||||
},
|
||||
},
|
||||
{
|
||||
"new account",
|
||||
ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1"),
|
||||
func() {
|
||||
prevBalance = big.NewInt(0)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
suite.stateDB.CreateAccount(tc.address)
|
||||
suite.Require().True(suite.stateDB.Exist(tc.address), tc.name)
|
||||
suite.Require().Equal(prevBalance, suite.stateDB.GetBalance(tc.address), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDB_ClearStateObj() {
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey)
|
||||
|
||||
suite.stateDB.CreateAccount(addr)
|
||||
suite.Require().True(suite.stateDB.Exist(addr))
|
||||
|
||||
suite.stateDB.ClearStateObjects()
|
||||
suite.Require().False(suite.stateDB.Exist(addr))
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestStateDB_Reset() {
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey)
|
||||
|
||||
suite.stateDB.CreateAccount(addr)
|
||||
suite.Require().True(suite.stateDB.Exist(addr))
|
||||
|
||||
err = suite.stateDB.Reset(ethcmn.BytesToHash(nil))
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().False(suite.stateDB.Exist(addr))
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestSuiteDB_Prepare() {
|
||||
thash := ethcmn.BytesToHash([]byte("thash"))
|
||||
bhash := ethcmn.BytesToHash([]byte("bhash"))
|
||||
txi := 1
|
||||
|
||||
suite.stateDB.Prepare(thash, bhash, txi)
|
||||
|
||||
suite.Require().Equal(txi, suite.stateDB.TxIndex())
|
||||
suite.Require().Equal(bhash, suite.stateDB.BlockHash())
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) 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.stateDB.SetLogs(hash, logs)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
|
||||
copyDB := suite.stateDB.Copy()
|
||||
|
||||
copiedDBLogs, err := copyDB.GetLogs(hash)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().Equal(logs, copiedDBLogs, tc.name)
|
||||
suite.Require().Equal(suite.stateDB.Exist(suite.address), copyDB.Exist(suite.address), tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestSuiteDB_Empty() {
|
||||
suite.Require().True(suite.stateDB.Empty(suite.address))
|
||||
|
||||
suite.stateDB.SetBalance(suite.address, big.NewInt(100))
|
||||
suite.Require().False(suite.stateDB.Empty(suite.address))
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) 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.stateDB.Commit(tc.delete)
|
||||
suite.Require().NoError(err, tc.name)
|
||||
suite.Require().False(suite.stateDB.Exist(suite.address), tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if tc.expPass {
|
||||
suite.stateDB.SetBalance(suite.address, tc.amount)
|
||||
suicide := suite.stateDB.Suicide(suite.address)
|
||||
suite.Require().True(suicide, tc.name)
|
||||
suite.Require().True(suite.stateDB.HasSuicided(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.stateDB.Suicide(addr)
|
||||
suite.Require().False(suicide, tc.name)
|
||||
suite.Require().False(suite.stateDB.HasSuicided(addr), tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestCommitStateDB_Commit() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
deleteObjs bool
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"commit suicided",
|
||||
func() {
|
||||
ok := suite.stateDB.Suicide(suite.address)
|
||||
suite.Require().True(ok)
|
||||
},
|
||||
true, true,
|
||||
},
|
||||
{
|
||||
"commit with dirty value",
|
||||
func() {
|
||||
suite.stateDB.SetCode(suite.address, []byte("code"))
|
||||
},
|
||||
false, true,
|
||||
},
|
||||
{
|
||||
"faled to update state object",
|
||||
func() {
|
||||
suite.stateDB.SubBalance(suite.address, big.NewInt(10))
|
||||
},
|
||||
false, false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
hash, err := suite.stateDB.Commit(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 *StateDBTestSuite) TestCommitStateDB_Finalize() {
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
deleteObjs bool
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"finalize suicided",
|
||||
func() {
|
||||
ok := suite.stateDB.Suicide(suite.address)
|
||||
suite.Require().True(ok)
|
||||
},
|
||||
true, true,
|
||||
},
|
||||
{
|
||||
"finalize, not suicided",
|
||||
func() {
|
||||
suite.stateDB.AddBalance(suite.address, big.NewInt(5))
|
||||
},
|
||||
false, true,
|
||||
},
|
||||
{
|
||||
"finalize, dirty storage",
|
||||
func() {
|
||||
suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value")))
|
||||
},
|
||||
false, true,
|
||||
},
|
||||
{
|
||||
"faled to update state object",
|
||||
func() {
|
||||
suite.stateDB.SubBalance(suite.address, big.NewInt(10))
|
||||
},
|
||||
false, false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
tc.malleate()
|
||||
|
||||
err := suite.stateDB.Finalise(tc.deleteObjs)
|
||||
|
||||
if !tc.expPass {
|
||||
suite.Require().Error(err, tc.name)
|
||||
hash := suite.stateDB.GetCommittedState(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 *StateDBTestSuite) TestCommitStateDB_GetCommittedState() {
|
||||
hash := suite.stateDB.GetCommittedState(ethcmn.Address{}, ethcmn.BytesToHash([]byte("key")))
|
||||
suite.Require().Equal(ethcmn.Hash{}, hash)
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestCommitStateDB_Snapshot() {
|
||||
id := suite.stateDB.Snapshot()
|
||||
suite.Require().NotPanics(func() {
|
||||
suite.stateDB.RevertToSnapshot(id)
|
||||
})
|
||||
|
||||
suite.Require().Panics(func() {
|
||||
suite.stateDB.RevertToSnapshot(-1)
|
||||
}, "invalid revision should panic")
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() {
|
||||
var storage types.Storage
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
malleate func()
|
||||
callback func(key, value ethcmn.Hash) (stop bool)
|
||||
expValues []ethcmn.Hash
|
||||
}{
|
||||
{
|
||||
"aggregate state",
|
||||
func() {
|
||||
for i := 0; i < 5; i++ {
|
||||
suite.stateDB.SetState(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
|
||||
},
|
||||
[]ethcmn.Hash{
|
||||
ethcmn.BytesToHash([]byte("value0")),
|
||||
ethcmn.BytesToHash([]byte("value1")),
|
||||
ethcmn.BytesToHash([]byte("value2")),
|
||||
ethcmn.BytesToHash([]byte("value3")),
|
||||
ethcmn.BytesToHash([]byte("value4")),
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter state",
|
||||
func() {
|
||||
suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value")))
|
||||
suite.stateDB.SetState(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
|
||||
},
|
||||
[]ethcmn.Hash{
|
||||
ethcmn.BytesToHash([]byte("filtervalue")),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCase {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupTest() // reset
|
||||
tc.malleate()
|
||||
suite.stateDB.Finalise(false)
|
||||
|
||||
err := suite.stateDB.ForEachStorage(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{}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StateDBTestSuite) TestCommitStateDB_AccessList() {
|
||||
addr := ethcmn.Address([20]byte{77})
|
||||
hash := ethcmn.Hash([32]byte{99})
|
||||
|
||||
suite.Require().False(suite.stateDB.AddressInAccessList(addr))
|
||||
|
||||
suite.stateDB.AddAddressToAccessList(addr)
|
||||
suite.Require().True(suite.stateDB.AddressInAccessList(addr))
|
||||
addrIn, slotIn := suite.stateDB.SlotInAccessList(addr, hash)
|
||||
suite.Require().True(addrIn)
|
||||
suite.Require().False(slotIn)
|
||||
|
||||
suite.stateDB.AddSlotToAccessList(addr, hash)
|
||||
addrIn, slotIn = suite.stateDB.SlotInAccessList(addr, hash)
|
||||
suite.Require().True(addrIn)
|
||||
suite.Require().True(slotIn)
|
||||
}
|
@ -5,20 +5,13 @@ import (
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
func rlpHash(x interface{}) (hash ethcmn.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
_ = rlp.Encode(hasher, x)
|
||||
_ = hasher.Sum(hash[:0])
|
||||
|
||||
return hash
|
||||
}
|
||||
var EmptyCodeHash = crypto.Keccak256(nil)
|
||||
|
||||
// EncodeTxResponse takes all of the necessary data from the EVM execution
|
||||
// and returns the data as a byte slice encoded with protobuf.
|
||||
|
Loading…
Reference in New Issue
Block a user