ante, evm: update gas consumption logic (#178)

* clean up logic by ignoring cosmos gas meter

* set gas used in txResponse

* reset and set the gas in context

* rename ante handler

* fix refundedgas logic

* remove gas update logic in keeper

* update context in keeper

* add test for EthSetupContextDecorator

* fix broken test due to gas logic change
This commit is contained in:
Thomas Nguy
2021-06-30 05:31:30 -04:00
committed by GitHub
parent 036ffb7a39
commit 5ba8ffe669
8 changed files with 169 additions and 165 deletions
+31 -61
View File
@@ -112,12 +112,7 @@ func (k Keeper) GetHashFn() vm.GetHashFunc {
func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumTxResponse, error) {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.MetricKeyTransitionDB)
gasMeter := k.ctx.GasMeter() // tx gas meter
// ignore gas consumption costs
infCtx := k.ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
cfg, found := k.GetChainConfig(infCtx)
cfg, found := k.GetChainConfig(k.ctx)
if !found {
return nil, stacktrace.Propagate(types.ErrChainConfigNotFound, "configuration not found")
}
@@ -135,9 +130,6 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
k.IncreaseTxIndexTransient()
// set the original gas meter to apply the message and perform the state transition
k.WithContext(k.ctx.WithGasMeter(gasMeter))
// create an ethereum StateTransition instance and run TransitionDb
res, err := k.ApplyMessage(evm, msg, ethCfg)
if err != nil {
@@ -157,6 +149,7 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
bloom.Or(bloom, big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs)))
k.SetBlockBloomTransient(bloom)
k.resetGasMeterAndConsumeGas(res.GasUsed)
return res, nil
}
@@ -209,24 +202,13 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainCo
sender := vm.AccountRef(msg.From())
contractCreation := msg.To() == nil
// transaction gas meter (tracks limit and usage)
gasConsumed := k.ctx.GasMeter().GasConsumed()
leftoverGas := k.ctx.GasMeter().Limit() - k.ctx.GasMeter().GasConsumedToLimit()
// NOTE: Since CRUD operations on the SDK store consume gas we need to set up an infinite gas meter so that we only consume
// the gas used by the Ethereum message execution.
// Not setting the infinite gas meter here would mean that we are incurring in additional gas costs
k.WithContext(k.ctx.WithGasMeter(sdk.NewInfiniteGasMeter()))
// NOTE: gas limit is the GasLimit defied in the message minus the Intrinsic Gas that has already been
// consumed on the AnteHandler.
// ensure gas is consistent during CheckTx
if k.ctx.IsCheckTx() {
if err := k.CheckGasConsumption(msg, cfg, gasConsumed, contractCreation); err != nil {
return nil, stacktrace.Propagate(err, "gas consumption check failed during CheckTx")
}
intrinsicGas, err := k.GetEthIntrinsicGas(msg, cfg, contractCreation)
if err != nil {
// should have already been checked on Ante Handler
return nil, stacktrace.Propagate(err, "intrinsic gas failed")
}
// should be > 0 as it is checked on Ante Handler
leftoverGas := msg.Gas() - intrinsicGas
if contractCreation {
ret, _, leftoverGas, vmErr = evm.Create(sender, msg.Data(), leftoverGas, msg.Value())
@@ -235,7 +217,8 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainCo
}
// refund gas prior to handling the vm error in order to set the updated gas meter
if err := k.RefundGas(msg, leftoverGas); err != nil {
leftoverGas, err = k.RefundGas(msg, leftoverGas)
if err != nil {
return nil, stacktrace.Propagate(err, "failed to refund gas leftover gas to sender %s", msg.From())
}
@@ -249,38 +232,30 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainCo
return nil, stacktrace.Propagate(sdkerrors.Wrap(types.ErrVMExecution, vmErr.Error()), "vm execution failed")
}
gasUsed := msg.Gas() - leftoverGas
return &types.MsgEthereumTxResponse{
Ret: ret,
Reverted: false,
GasUsed: gasUsed,
}, nil
}
// 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 {
// GetEthIntrinsicGas get the transaction intrinsic gas cost
func (k *Keeper) GetEthIntrinsicGas(msg core.Message, cfg *params.ChainConfig, isContractCreation bool) (uint64, error) {
height := big.NewInt(k.ctx.BlockHeight())
homestead := cfg.IsHomestead(height)
istanbul := cfg.IsIstanbul(height)
intrinsicGas, err := core.IntrinsicGas(msg.Data(), msg.AccessList(), isContractCreation, homestead, istanbul)
if err != nil {
// should have already been checked on Ante Handler
return stacktrace.Propagate(err, "intrinsic gas failed")
}
if intrinsicGas != gasConsumed {
return sdkerrors.Wrapf(types.ErrInconsistentGas, "expected gas consumption to be %d (intrinsic gas only), got %d", intrinsicGas, gasConsumed)
}
return nil
return core.IntrinsicGas(msg.Data(), msg.AccessList(), isContractCreation, homestead, istanbul)
}
// 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
// returned by the EVM execution, thus ignoring the previous intrinsic gas consumed during in the
// AnteHandler.
func (k *Keeper) RefundGas(msg core.Message, leftoverGas uint64) error {
func (k *Keeper) RefundGas(msg core.Message, leftoverGas uint64) (uint64, error) {
if leftoverGas > msg.Gas() {
return stacktrace.Propagate(
return leftoverGas, stacktrace.Propagate(
sdkerrors.Wrapf(types.ErrInconsistentGas, "leftover gas cannot be greater than gas limit (%d > %d)", leftoverGas, msg.Gas()),
"failed to update gas consumed after refund of leftover gas",
)
@@ -298,47 +273,42 @@ func (k *Keeper) RefundGas(msg core.Message, leftoverGas uint64) error {
leftoverGas += refund
if leftoverGas > msg.Gas() {
return stacktrace.Propagate(
return leftoverGas, stacktrace.Propagate(
sdkerrors.Wrapf(types.ErrInconsistentGas, "leftover gas cannot be greater than gas limit (%d > %d)", leftoverGas, msg.Gas()),
"failed to update gas consumed after refund of %d gas", refund,
)
}
gasConsumed = msg.Gas() - leftoverGas
// 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 sdkerrors.Wrapf(types.ErrInvalidRefund, "refunded amount value cannot be negative %d", remaining.Int64())
return leftoverGas, sdkerrors.Wrapf(types.ErrInvalidRefund, "refunded amount value cannot be negative %d", remaining.Int64())
case 1:
// positive amount refund
params := k.GetParams(infCtx)
params := k.GetParams(k.ctx)
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
err := k.bankKeeper.SendCoinsFromModuleToAccount(infCtx, authtypes.FeeCollectorName, msg.From().Bytes(), refundedCoins)
err := k.bankKeeper.SendCoinsFromModuleToAccount(k.ctx, authtypes.FeeCollectorName, msg.From().Bytes(), refundedCoins)
if err != nil {
err = sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "fee collector account failed to refund fees: %s", err.Error())
return stacktrace.Propagate(err, "failed to refund %d leftover gas (%s)", leftoverGas, refundedCoins.String())
return leftoverGas, stacktrace.Propagate(err, "failed to refund %d leftover gas (%s)", leftoverGas, refundedCoins.String())
}
default:
// no refund, consume gas and update the tx gas meter
}
// set the gas consumed into the context with the new gas meter. This gas meter will have the
// original gas limit defined in the msg and will consume the gas now that the amount has been
// refunded
gasMeter := sdk.NewGasMeter(msg.Gas())
// NOTE: gas consumed will always be less than the limit
gasMeter.ConsumeGas(gasConsumed, "update gas consumption after refund")
k.WithContext(k.ctx.WithGasMeter(gasMeter))
return leftoverGas, nil
}
return nil
// resetGasMeterAndConsumeGas reset first the gas meter consumed value to zero and set it back to the new value
// 'gasUsed'
func (k *Keeper) resetGasMeterAndConsumeGas(gasUsed uint64) {
// reset the gas count
k.ctx.GasMeter().RefundGas(k.ctx.GasMeter().GasConsumed(), "reset the gas count")
k.ctx.GasMeter().ConsumeGas(gasUsed, "apply evm transaction")
}
+57 -26
View File
@@ -160,6 +160,8 @@ type MsgEthereumTxResponse struct {
Ret []byte `protobuf:"bytes,3,opt,name=ret,proto3" json:"ret,omitempty"`
// reverted flag is set to true when the call has been reverted
Reverted bool `protobuf:"varint,4,opt,name=reverted,proto3" json:"reverted,omitempty"`
// gas consumed by the transaction
GasUsed uint64 `protobuf:"varint,5,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"`
}
func (m *MsgEthereumTxResponse) Reset() { *m = MsgEthereumTxResponse{} }
@@ -205,32 +207,34 @@ func init() {
func init() { proto.RegisterFile("ethermint/evm/v1alpha1/tx.proto", fileDescriptor_6a305e80b084ab0e) }
var fileDescriptor_6a305e80b084ab0e = []byte{
// 400 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0xeb, 0xd3, 0x30,
0x18, 0xc6, 0x9b, 0xb5, 0xea, 0xcc, 0x14, 0x24, 0xe8, 0xa8, 0x15, 0xd2, 0x52, 0x10, 0x7a, 0x59,
0xcb, 0xba, 0xdb, 0x8e, 0xc5, 0xdd, 0x1c, 0x42, 0x18, 0x08, 0xde, 0x52, 0x17, 0xdb, 0xc2, 0xda,
0x94, 0x24, 0x2b, 0xd5, 0x4f, 0xe0, 0x71, 0x57, 0x6f, 0x7e, 0x1c, 0x8f, 0x3b, 0x7a, 0x1a, 0xb2,
0xdd, 0x3c, 0xfa, 0x09, 0xa4, 0x99, 0xdb, 0x9c, 0x38, 0xf8, 0xdf, 0xde, 0xf6, 0xfd, 0x25, 0xcf,
0xf3, 0x84, 0x07, 0xba, 0x4c, 0xe5, 0x4c, 0x94, 0x45, 0xa5, 0x22, 0xd6, 0x94, 0x51, 0x33, 0xa6,
0xab, 0x3a, 0xa7, 0xe3, 0x48, 0xb5, 0x61, 0x2d, 0xb8, 0xe2, 0x68, 0x78, 0x06, 0x42, 0xd6, 0x94,
0xe1, 0x09, 0x70, 0x9e, 0x66, 0x3c, 0xe3, 0x1a, 0x89, 0xba, 0xe9, 0x48, 0x3b, 0xde, 0x8d, 0xeb,
0xba, 0xa3, 0x9a, 0xf0, 0xbf, 0x00, 0xf8, 0x78, 0x2e, 0xb3, 0x59, 0xc7, 0xb1, 0x75, 0xb9, 0x68,
0x51, 0x0c, 0xad, 0x25, 0x55, 0xd4, 0x06, 0x1e, 0x08, 0x06, 0x31, 0x0e, 0xff, 0x2f, 0x18, 0x2e,
0xda, 0x57, 0x54, 0x51, 0xa2, 0x59, 0xf4, 0x1c, 0x5a, 0xb2, 0xf8, 0xc4, 0xec, 0x9e, 0x07, 0x02,
0x90, 0xdc, 0xfb, 0xb9, 0x73, 0xc1, 0x88, 0xe8, 0x5f, 0xc8, 0x85, 0x56, 0x4e, 0x65, 0x6e, 0x9b,
0x1e, 0x08, 0x1e, 0x26, 0x83, 0x5f, 0x3b, 0xf7, 0x81, 0x58, 0xd5, 0x53, 0x7f, 0xe4, 0x13, 0xbd,
0x40, 0x08, 0x5a, 0x1f, 0x04, 0x2f, 0x6d, 0xab, 0x03, 0x88, 0x9e, 0xa7, 0xd6, 0xe7, 0xaf, 0xae,
0xe1, 0xfb, 0xd0, 0x99, 0xb5, 0x8a, 0x55, 0xb2, 0xe0, 0xd5, 0x9b, 0x5a, 0x15, 0xbc, 0x92, 0x17,
0x9f, 0x7f, 0x18, 0x0c, 0x87, 0xff, 0x32, 0x6f, 0x59, 0x3a, 0x39, 0xef, 0x37, 0x00, 0x3e, 0xbb,
0xca, 0x47, 0x98, 0xac, 0x79, 0x25, 0x59, 0xa7, 0xab, 0x8d, 0x81, 0xa3, 0xae, 0xf6, 0x12, 0x41,
0x6b, 0xc5, 0x33, 0x69, 0xf7, 0x3c, 0x33, 0x18, 0xc4, 0x2f, 0x6e, 0x65, 0x7f, 0xcd, 0x33, 0xa2,
0x41, 0xf4, 0x04, 0x9a, 0x82, 0x29, 0x1d, 0xee, 0x11, 0xe9, 0x46, 0xe4, 0xc0, 0xbe, 0x60, 0x0d,
0x13, 0x8a, 0x2d, 0x75, 0xa4, 0x3e, 0x39, 0x7f, 0x1f, 0x2d, 0xc5, 0x05, 0x34, 0xe7, 0x32, 0x43,
0x29, 0x84, 0x7f, 0xbd, 0xfa, 0xcb, 0x5b, 0x5a, 0x57, 0xe6, 0x9d, 0xd1, 0x9d, 0xb0, 0x53, 0xc6,
0x24, 0xf9, 0xb6, 0xc7, 0x60, 0xbb, 0xc7, 0xe0, 0xc7, 0x1e, 0x83, 0xcd, 0x01, 0x1b, 0xdb, 0x03,
0x36, 0xbe, 0x1f, 0xb0, 0xf1, 0x2e, 0xc8, 0x0a, 0x95, 0xaf, 0xd3, 0xf0, 0x3d, 0x2f, 0x23, 0x95,
0x53, 0x21, 0x0b, 0x19, 0x5d, 0xca, 0xd2, 0xea, 0xba, 0xa8, 0x8f, 0x35, 0x93, 0xe9, 0x7d, 0x5d,
0x94, 0xc9, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4e, 0x06, 0x55, 0xd8, 0x9b, 0x02, 0x00, 0x00,
// 426 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0x8b, 0xd3, 0x40,
0x14, 0xc7, 0x3b, 0xdb, 0xac, 0x5b, 0x5f, 0x15, 0x64, 0xd0, 0x25, 0x1b, 0x21, 0x09, 0x01, 0x21,
0x97, 0x26, 0x6c, 0xf7, 0xb6, 0xc7, 0xe2, 0xde, 0x5c, 0x84, 0x61, 0x45, 0xf0, 0x22, 0x53, 0xfb,
0x9c, 0x04, 0x9a, 0x4c, 0x98, 0x37, 0x2d, 0xd1, 0x4f, 0xe0, 0xd1, 0xab, 0x37, 0xef, 0x7e, 0x11,
0x8f, 0x7b, 0xf4, 0xb4, 0x48, 0x7b, 0xf3, 0xe8, 0x27, 0x90, 0x4c, 0xdd, 0xae, 0x2b, 0x16, 0xbc,
0xbd, 0xe4, 0xfd, 0x66, 0xde, 0xff, 0xc7, 0x3c, 0x88, 0xd0, 0x16, 0x68, 0xaa, 0xb2, 0xb6, 0x39,
0x2e, 0xab, 0x7c, 0x79, 0x2c, 0xe7, 0x4d, 0x21, 0x8f, 0x73, 0xdb, 0x66, 0x8d, 0xd1, 0x56, 0xf3,
0xc3, 0x2d, 0x90, 0xe1, 0xb2, 0xca, 0xae, 0x81, 0xe0, 0xa1, 0xd2, 0x4a, 0x3b, 0x24, 0xef, 0xaa,
0x0d, 0x1d, 0xc4, 0x3b, 0xae, 0xeb, 0x8e, 0x3a, 0x22, 0xf9, 0xc4, 0xe0, 0xfe, 0x39, 0xa9, 0xb3,
0x8e, 0xc3, 0x45, 0x75, 0xd1, 0xf2, 0x31, 0x78, 0x33, 0x69, 0xa5, 0xcf, 0x62, 0x96, 0x0e, 0xc7,
0x61, 0xf6, 0xef, 0x81, 0xd9, 0x45, 0xfb, 0x54, 0x5a, 0x29, 0x1c, 0xcb, 0x8f, 0xc0, 0xa3, 0xf2,
0x3d, 0xfa, 0x7b, 0x31, 0x4b, 0xd9, 0x64, 0xff, 0xc7, 0x55, 0xc4, 0x46, 0xc2, 0xfd, 0xe2, 0x11,
0x78, 0x85, 0xa4, 0xc2, 0xef, 0xc7, 0x2c, 0xbd, 0x3b, 0x19, 0xfe, 0xbc, 0x8a, 0x0e, 0xcc, 0xbc,
0x39, 0x4d, 0x46, 0x89, 0x70, 0x0d, 0xce, 0xc1, 0x7b, 0x6b, 0x74, 0xe5, 0x7b, 0x1d, 0x20, 0x5c,
0x7d, 0xea, 0x7d, 0xf8, 0x1c, 0xf5, 0x92, 0x04, 0x82, 0xb3, 0xd6, 0x62, 0x4d, 0xa5, 0xae, 0x9f,
0x37, 0xb6, 0xd4, 0x35, 0xdd, 0xe4, 0xfc, 0xcd, 0x84, 0x70, 0xf8, 0x37, 0xf3, 0x12, 0xa7, 0x27,
0xdb, 0xfe, 0x17, 0x06, 0x8f, 0x6e, 0xf9, 0x09, 0xa4, 0x46, 0xd7, 0x84, 0xdd, 0x5c, 0x17, 0x8c,
0x6d, 0xe6, 0xba, 0x2c, 0x39, 0x78, 0x73, 0xad, 0xc8, 0xdf, 0x8b, 0xfb, 0xe9, 0x70, 0xfc, 0x78,
0x97, 0xfb, 0x33, 0xad, 0x84, 0x03, 0xf9, 0x03, 0xe8, 0x1b, 0xb4, 0x4e, 0xee, 0x9e, 0xe8, 0x4a,
0x1e, 0xc0, 0xc0, 0xe0, 0x12, 0x8d, 0xc5, 0x99, 0x53, 0x1a, 0x88, 0xed, 0x37, 0x3f, 0x82, 0x81,
0x92, 0xf4, 0x7a, 0x41, 0x38, 0xf3, 0xf7, 0x63, 0x96, 0x7a, 0xe2, 0x40, 0x49, 0x7a, 0x41, 0x38,
0xdb, 0xa4, 0x1d, 0x97, 0xd0, 0x3f, 0x27, 0xc5, 0xa7, 0x00, 0x7f, 0x3c, 0xc8, 0x93, 0x5d, 0x31,
0x6e, 0x79, 0x05, 0xa3, 0xff, 0xc2, 0xae, 0xf5, 0x27, 0x93, 0xaf, 0xab, 0x90, 0x5d, 0xae, 0x42,
0xf6, 0x7d, 0x15, 0xb2, 0x8f, 0xeb, 0xb0, 0x77, 0xb9, 0x0e, 0x7b, 0xdf, 0xd6, 0x61, 0xef, 0x55,
0xaa, 0x4a, 0x5b, 0x2c, 0xa6, 0xd9, 0x1b, 0x5d, 0xe5, 0xb6, 0x90, 0x86, 0x4a, 0xca, 0x6f, 0xf6,
0xa8, 0x75, 0x9b, 0x64, 0xdf, 0x35, 0x48, 0xd3, 0x3b, 0x6e, 0x87, 0x4e, 0x7e, 0x05, 0x00, 0x00,
0xff, 0xff, 0x0f, 0x24, 0xe9, 0xff, 0xb6, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -436,6 +440,11 @@ func (m *MsgEthereumTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.GasUsed != 0 {
i = encodeVarintTx(dAtA, i, uint64(m.GasUsed))
i--
dAtA[i] = 0x28
}
if m.Reverted {
i--
if m.Reverted {
@@ -553,6 +562,9 @@ func (m *MsgEthereumTxResponse) Size() (n int) {
if m.Reverted {
n += 2
}
if m.GasUsed != 0 {
n += 1 + sovTx(uint64(m.GasUsed))
}
return n
}
@@ -981,6 +993,25 @@ func (m *MsgEthereumTxResponse) Unmarshal(dAtA []byte) error {
}
}
m.Reverted = bool(v != 0)
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field GasUsed", wireType)
}
m.GasUsed = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTx
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.GasUsed |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipTx(dAtA[iNdEx:])