Skip to content

Commit

Permalink
fix: audit (#71)
Browse files Browse the repository at this point in the history
* deteach burn permission from the owner

* convert precompiles out-of-gas to evm out-of-gas

* change burn function on initiaerc20

* burn left balance in the self destructed account at commit

* return zero hash if the account does not have code

* fix to update balance before commit

* return empty code hash when account exists but code is empty

* implement gas refunds at post handler

* use gas consumped to limit

* add IsEVMTx check at gas refunds

* remove unnecessary override

* call next()

* remove print
  • Loading branch information
beer-1 authored Oct 15, 2024
1 parent aec86a1 commit b8164ad
Show file tree
Hide file tree
Showing 36 changed files with 690 additions and 194 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ proto-pulsar-gen:
@$(protoImage) sh ./scripts/protocgen-pulsar.sh

proto-format:
@$(protoImage) find ./ -name "*.proto" -exec clang-format -i {} \;
@$(protoImage) find ./ -name "*.proto" -exec buf format {} -w \;

proto-lint:
@$(protoImage) buf lint --error-format=json ./proto
Expand Down
2 changes: 1 addition & 1 deletion app/ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type HandlerOptions struct {
IBCkeeper *ibckeeper.Keeper
OPChildKeeper opchildtypes.AnteKeeper
AuctionKeeper auctionkeeper.Keeper
EVMKeeper *evmkeeper.Keeper
EVMKeeper EVMKeeper

TxEncoder sdk.TxEncoder
MevLane auctionante.MEVLane
Expand Down
12 changes: 12 additions & 0 deletions app/ante/expected_keepers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ante

import (
"context"

evmtypes "github.com/initia-labs/minievm/x/evm/types"
)

type EVMKeeper interface {
GetFeeDenom(ctx context.Context) (string, error)
TxUtils() evmtypes.TxUtils
}
6 changes: 2 additions & 4 deletions app/ante/fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/types"

evmkeeper "github.com/initia-labs/minievm/x/evm/keeper"
)

// feeDeductionGasAmount is a estimated gas amount of fee payment
Expand All @@ -23,12 +21,12 @@ type GasFreeFeeDecorator struct {
inner ante.DeductFeeDecorator

// ek is used to get the fee denom from the x/evm params.
ek *evmkeeper.Keeper
ek EVMKeeper
}

func NewGasFreeFeeDecorator(
ak ante.AccountKeeper, bk types.BankKeeper,
fk ante.FeegrantKeeper, ek *evmkeeper.Keeper,
fk ante.FeegrantKeeper, ek EVMKeeper,
tfc ante.TxFeeChecker) GasFreeFeeDecorator {
return GasFreeFeeDecorator{
inner: ante.NewDeductFeeDecorator(ak, bk, fk, tfc),
Expand Down
5 changes: 2 additions & 3 deletions app/ante/sigverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/initia-labs/initia/crypto/ethsecp256k1"
evmkeeper "github.com/initia-labs/minievm/x/evm/keeper"
evmtypes "github.com/initia-labs/minievm/x/evm/types"
)

Expand All @@ -33,13 +32,13 @@ import (
// CONTRACT: Tx must implement SigVerifiableTx interface
type SigVerificationDecorator struct {
ak authante.AccountKeeper
ek *evmkeeper.Keeper
ek EVMKeeper
signModeHandler *txsigning.HandlerMap
}

func NewSigVerificationDecorator(
ak authante.AccountKeeper,
ek *evmkeeper.Keeper,
ek EVMKeeper,
signModeHandler *txsigning.HandlerMap) SigVerificationDecorator {
return SigVerificationDecorator{
ak: ak,
Expand Down
4 changes: 2 additions & 2 deletions app/ante/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ func verifySignature(
handler *txsigning.HandlerMap,
txData txsigning.TxData,
// required to verify EVM signatures
ek *evmkeeper.Keeper,
ek EVMKeeper,
tx sdk.Tx,
) error {
switch data := signatureData.(type) {
case *signing.SingleSignatureData:
if data.SignMode == evmkeeper.SignMode_SIGN_MODE_ETHEREUM {
// eth sign mode
ethTx, expectedSender, err := evmkeeper.NewTxUtils(ek).ConvertCosmosTxToEthereumTx(ctx, tx)
ethTx, expectedSender, err := ek.TxUtils().ConvertCosmosTxToEthereumTx(ctx, tx)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ func (app *MinitiaApp) SetCheckTx(handler blockchecktx.CheckTx) {
func (app *MinitiaApp) setPostHandler() {
app.SetPostHandler(posthandler.NewPostHandler(
app.AccountKeeper,
app.EVMKeeper,
))
}

Expand Down
91 changes: 91 additions & 0 deletions app/posthandler/gasrefund.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package posthandler

import (
"fmt"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

evmante "github.com/initia-labs/minievm/x/evm/ante"
)

var _ sdk.PostDecorator = &GasRefundDecorator{}

type GasRefundDecorator struct {
ek EVMKeeper
}

func NewGasRefundDecorator(ek EVMKeeper) sdk.PostDecorator {
return &GasRefundDecorator{
ek,
}
}

// PostHandle handles the gas refund logic for EVM transactions.
func (erd *GasRefundDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate, success bool, next sdk.PostHandler) (newCtx sdk.Context, err error) {
if success && ctx.ExecMode() == sdk.ExecModeFinalize {
// Conduct gas refund only for the successful EVM transactions
if ok, err := erd.ek.TxUtils().IsEthereumTx(ctx, tx); err != nil || !ok {
return next(ctx, tx, simulate, success)
}

feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}

value := ctx.Value(evmante.ContextKeyGasPrices)
if value == nil {
return next(ctx, tx, simulate, success)
}
gasRefundRatio, err := erd.ek.GasRefundRatio(ctx)
if err != nil {
return ctx, err
}

gasPrices := value.(sdk.DecCoins)
gasLeft := ctx.GasMeter().Limit() - ctx.GasMeter().GasConsumedToLimit()
gasRefund := gasRefundRatio.MulInt64(int64(gasLeft)).TruncateInt().Uint64()

// gas used for refund operation
coinsRefund, _ := gasPrices.MulDec(math.LegacyNewDecFromInt(math.NewIntFromUint64(gasRefund))).TruncateDecimal()
if coinsRefund.Empty() || coinsRefund.IsZero() {
return next(ctx, tx, simulate, success)
}

feePayer := feeTx.FeePayer()
if feeGranter := feeTx.FeeGranter(); feeGranter != nil {
feePayer = feeGranter
}

// emit gas refund event
ctx.EventManager().EmitEvent(sdk.NewEvent(
EventTypeGasRefund,
sdk.NewAttribute(AttributeKeyGas, fmt.Sprintf("%d", gasRefund)),
sdk.NewAttribute(AttributeKeyCoins, coinsRefund.String()),
))

// TODO - should we charge gas for refund?
//
// for now, we use infinite gas meter to prevent out of gas error or inconsistency between
// used gas and refunded gas.
feeCollectorAddr := authtypes.NewModuleAddress(authtypes.FeeCollectorName)
err = erd.ek.ERC20Keeper().SendCoins(ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()), feeCollectorAddr, feePayer, coinsRefund)
if err != nil {
return ctx, err
}
}

return next(ctx, tx, simulate, success)
}

const (
EventTypeGasRefund = "gas_refund"
AttributeKeyGas = "gas"
AttributeKeyCoins = "coins"
)
21 changes: 19 additions & 2 deletions app/posthandler/posthandler.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
package posthandler

import (
"context"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"

evmtypes "github.com/initia-labs/minievm/x/evm/types"
)

// NewPostHandler returns a new sdk.PostHandler that is composed of the sdk.ChainPostDecorators
func NewPostHandler(ak authante.AccountKeeper) sdk.PostHandler {
return sdk.ChainPostDecorators(NewSequenceIncrementDecorator(ak))
func NewPostHandler(
ak authante.AccountKeeper,
ek EVMKeeper,
) sdk.PostHandler {
return sdk.ChainPostDecorators(
NewSequenceIncrementDecorator(ak),
NewGasRefundDecorator(ek),
)
}

type EVMKeeper interface {
GasRefundRatio(context.Context) (math.LegacyDec, error)
ERC20Keeper() evmtypes.IERC20Keeper
TxUtils() evmtypes.TxUtils
}
2 changes: 1 addition & 1 deletion indexer/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (e *EVMIndexerImpl) ListenFinalizeBlock(ctx context.Context, req abci.Reque
EffectiveGasPrice: ethTx.GasPrice(),
}

// currently we do not support fee refund, so the effective gas price is the same as the gas price
// currently we do not support fee refund for gas tip, so the effective gas price is the same as the gas price
// if ethTx.Type() == coretypes.DynamicFeeTxType {
// receipt.EffectiveGasPrice = new(big.Int).Add(ethTx.EffectiveGasTipValue(baseFee.ToInt()), baseFee.ToInt())
// }
Expand Down
10 changes: 5 additions & 5 deletions proto/minievm/evm/v1/auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ option go_package = "github.com/initia-labs/minievm/x/evm/types";

// ContractAccount defines an account of contract.
message ContractAccount {
option (amino.name) = "evm/ContractAccount";
option (amino.name) = "evm/ContractAccount";
option (gogoproto.goproto_getters) = false;

cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true];
bytes code_hash = 2;
bytes code_hash = 2;
}

// ShorthandAccount defines an account of shorthand address
Expand All @@ -22,9 +22,9 @@ message ContractAccount {
// Also it is used to check the existence of the account before
// creating a new account.
message ShorthandAccount {
option (amino.name) = "evm/ShorthandAccount";
option (amino.name) = "evm/ShorthandAccount";
option (gogoproto.goproto_getters) = false;

cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true];
string original_address = 2;
cosmos.auth.v1beta1.BaseAccount base_account = 1 [(gogoproto.embed) = true];
string original_address = 2;
}
29 changes: 18 additions & 11 deletions proto/minievm/evm/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,40 @@ message GenesisState {
Params params = 1 [(gogoproto.nullable) = false];

// vm kv store
repeated GenesisKeyValue key_values = 2 [(gogoproto.moretags) = "yaml:\"key_values\"", (gogoproto.nullable) = false];
repeated GenesisKeyValue key_values = 2 [
(gogoproto.moretags) = "yaml:\"key_values\"",
(gogoproto.nullable) = false
];

// erc20 stores
repeated GenesisERC20Stores erc20_stores = 3
[(gogoproto.moretags) = "yaml:\"erc20_stores\"", (gogoproto.nullable) = false];
repeated GenesisERC20Stores erc20_stores = 3 [
(gogoproto.moretags) = "yaml:\"erc20_stores\"",
(gogoproto.nullable) = false
];

repeated GenesisDenomAddress denom_addresses = 4
[(gogoproto.moretags) = "yaml:\"denom_addresses\"", (gogoproto.nullable) = false];
repeated GenesisDenomAddress denom_addresses = 4 [
(gogoproto.moretags) = "yaml:\"denom_addresses\"",
(gogoproto.nullable) = false
];

// erc20 factory contract address
bytes erc20_factory = 5;
}

// GenesisKeyValue defines store KV values.
message GenesisKeyValue {
bytes key = 1;
bytes key = 1;
bytes value = 2;
}

// GenesisERC20Stores defines erc20 contract addresses of an account.
message GenesisERC20Stores {
bytes address = 1;
repeated bytes stores = 2;
bytes address = 1;
repeated bytes stores = 2;
}

// GenesisDenomAddress defines erc20 contract address of denom.
message GenesisDenomAddress {
string denom = 1;
bytes contract_address = 2;
}
string denom = 1;
bytes contract_address = 2;
}
28 changes: 17 additions & 11 deletions proto/minievm/evm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "minievm/evm/v1/types.proto";

option go_package = "github.com/initia-labs/minievm/x/evm/types";
option (gogoproto.equal_all) = false;
option go_package = "github.com/initia-labs/minievm/x/evm/types";
option (gogoproto.equal_all) = false;
option (gogoproto.goproto_getters_all) = false;

// Query provides defines the gRPC querier service
Expand Down Expand Up @@ -62,7 +62,7 @@ message QueryCodeRequest {
// method
message QueryCodeResponse {
option (gogoproto.equal) = true;
string code = 1;
string code = 1;
}

// QueryStateRequest is the request type for the Query/State RPC
Expand Down Expand Up @@ -119,7 +119,7 @@ message QueryDenomRequest {
// method
message QueryDenomResponse {
option (gogoproto.equal) = true;
string denom = 1;
string denom = 1;
}

// QueryCallRequest is the request type for the Query/Call RPC
Expand All @@ -132,8 +132,11 @@ message QueryCallRequest {
// hex encoded call input
string input = 3;
// Value is the amount of fee denom token to transfer to the contract.
string value = 4
[(gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false, (amino.dont_omitempty) = true];
string value = 4 [
(gogoproto.customtype) = "cosmossdk.io/math.Int",
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true
];
// whether to trace the call
// `nil` means no trace
TraceOptions trace_options = 5;
Expand All @@ -155,11 +158,14 @@ message TraceOptions {
// method
message QueryCallResponse {
// hex encoded response bytes.
string response = 1;
uint64 used_gas = 2;
repeated Log logs = 3 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
string trace_output = 4;
string error = 5;
string response = 1;
uint64 used_gas = 2;
repeated Log logs = 3 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true
];
string trace_output = 4;
string error = 5;
}

// QueryParamsRequest is the request type for the Query/Params RPC method.
Expand Down
Loading

0 comments on commit b8164ad

Please sign in to comment.