Skip to content

Commit

Permalink
Initialize precompiles in UpdateParams
Browse files Browse the repository at this point in the history
  • Loading branch information
evgeniy-scherbina committed May 8, 2024
1 parent 2ada74f commit 357e30a
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 6 deletions.
74 changes: 69 additions & 5 deletions x/evm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,34 @@
package keeper

import (
"bytes"
"context"
"encoding/json"
"fmt"
"math/big"
"strconv"

govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"

tmbytes "github.com/cometbft/cometbft/libs/bytes"
tmtypes "github.com/cometbft/cometbft/types"

errorsmod "cosmossdk.io/errors"
"github.com/armon/go-metrics"
tmbytes "github.com/cometbft/cometbft/libs/bytes"
tmtypes "github.com/cometbft/cometbft/types"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"

"github.com/evmos/ethermint/x/evm/statedb"
"github.com/evmos/ethermint/x/evm/types"
)

const PrecompileNonce uint64 = 1

var (
PrecompileCode = []byte{0x1}
PrecompileCodeHash = crypto.Keccak256Hash(PrecompileCode)
)

var _ types.MsgServer = &Keeper{}

// EthereumTx implements the gRPC MsgServer interface. It receives a transaction which is then
Expand Down Expand Up @@ -153,9 +163,63 @@ func (k *Keeper) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams)
}

ctx := sdk.UnwrapSDKContext(goCtx)

if err := k.updatePrecompiles(ctx, req.Params.EnabledPrecompiles); err != nil {
return nil, err
}

if err := k.SetParams(ctx, req.Params); err != nil {
return nil, err
}

return &types.MsgUpdateParamsResponse{}, nil
}

func (k *Keeper) updatePrecompiles(ctx sdk.Context, newEnabledPrecompiles []string) error {
oldParams := k.GetParams(ctx)
oldEnabledPrecompiles := oldParams.GetEnabledPrecompiles()

newEnabledPrecompilesMap := make(map[common.Address]struct{}, len(newEnabledPrecompiles))

// initialize newly enabled precompiles
for _, hexAddr := range newEnabledPrecompiles {
addr := common.HexToAddress(hexAddr)
newEnabledPrecompilesMap[addr] = struct{}{}

account := k.GetAccount(ctx, addr)
if account != nil && account.Nonce == PrecompileNonce && bytes.Equal(account.CodeHash, PrecompileCodeHash[:]) {
// skip initialization if account is already initialized
continue
}

// Set the nonce of the precompile's address (as is done when a contract is created) to ensure
// that it is marked as non-empty and will not be cleaned up when the statedb is finalized.
err := k.SetAccount(ctx, addr, statedb.Account{
Nonce: PrecompileNonce,
Balance: big.NewInt(0),
CodeHash: PrecompileCodeHash[:],
})
if err != nil {
return err
}

// Set the code of the precompile's address to a non-zero length byte slice to ensure that the precompile
// can be called from within Solidity contracts. Solidity adds a check before invoking a contract to ensure
// that it does not attempt to invoke a non-existent contract.
k.SetCode(ctx, PrecompileCodeHash[:], PrecompileCode)
}

// uninitialize disabled precompiles
for _, hexAddr := range oldEnabledPrecompiles {
addr := common.HexToAddress(hexAddr)
if _, ok := newEnabledPrecompilesMap[addr]; ok {
continue
}

if err := k.DeleteAccount(ctx, addr); err != nil {
return err
}
}

return nil
}
86 changes: 85 additions & 1 deletion x/evm/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ package keeper_test
import (
"math/big"

sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"

"github.com/evmos/ethermint/x/evm/statedb"
"github.com/evmos/ethermint/x/evm/types"
)

const PrecompileNonce uint64 = 1

var PrecompileCode = []byte{0x1}

func (suite *KeeperTestSuite) TestEthereumTx() {
var (
err error
Expand Down Expand Up @@ -115,3 +121,81 @@ func (suite *KeeperTestSuite) TestUpdateParams() {
})
}
}

func (suite *KeeperTestSuite) TestUpdatePrecompiles() {
addr1 := "0x1000000000000000000000000000000000000000"
addr2 := "0x2000000000000000000000000000000000000000"
addr3 := "0x3000000000000000000000000000000000000000"

testCases := []struct {
name string
enabledPrecompiles []string
// precompiles which must be uninitialized after corresponding test case
uninitialized []string
}{
{
name: "enable addr1 and addr2",
enabledPrecompiles: []string{addr1, addr2},
uninitialized: []string{addr3},
},
{
name: "enable addr3, and disable the rest",
enabledPrecompiles: []string{addr3},
uninitialized: []string{addr1, addr2},
},
{
name: "no changes",
enabledPrecompiles: []string{addr3},
uninitialized: []string{addr1, addr2},
},
{
name: "enable all precompiles",
enabledPrecompiles: []string{addr1, addr2, addr3},
uninitialized: []string{},
},
{
name: "disable all precompiles",
enabledPrecompiles: []string{},
uninitialized: []string{addr1, addr2, addr3},
},
}

for _, tc := range testCases {
suite.Run(tc.name, func() {
params := suite.app.EvmKeeper.GetParams(suite.ctx)
params.EnabledPrecompiles = tc.enabledPrecompiles

_, err := suite.app.EvmKeeper.UpdateParams(sdk.WrapSDKContext(suite.ctx), &types.MsgUpdateParams{
Authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(),
Params: params,
})
suite.Require().NoError(err)

vmdb := suite.StateDB()

// check that precompiles are initialized
for _, hexAddr := range tc.enabledPrecompiles {
addr := common.HexToAddress(hexAddr)

// A precompile address must exist and be non-empty
suite.True(vmdb.Exist(addr), "expected enabled precompile %s to exist", hexAddr)
suite.False(vmdb.Empty(addr), "expected enabled precompile %s to not be empty", hexAddr)

// A precompile address must have nonce 1, code set to 0x01, and have a byte length of 1
suite.Equal(PrecompileNonce, vmdb.GetNonce(addr), "expected enabled precompile %s to have nonce set in state", hexAddr)
suite.Equal(PrecompileCode, vmdb.GetCode(addr), "expected enabled precompile %s to have code set in state", hexAddr)
suite.Equal(1, vmdb.GetCodeSize(addr), "expected enabled precompile %s to have code size of 1 byte", hexAddr)
}

// check that precompiles are uninitialized
for _, hexAddr := range tc.uninitialized {
addr := common.HexToAddress(hexAddr)

// A precompile address must not exist
suite.False(vmdb.Exist(addr), "expected uninitialized precompile %s to not exist", hexAddr)
suite.Require().Equal(uint64(0), vmdb.GetNonce(addr))
suite.Require().Equal([]byte(nil), vmdb.GetCode(addr))
}
})
}
}

0 comments on commit 357e30a

Please sign in to comment.