From c9e4e3d571c11796de4e85032b9daef81926a89a Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 27 May 2021 17:09:20 +0200 Subject: [PATCH 1/4] feat: add RefundGas function to GasMeter --- store/types/gas.go | 26 ++++++++++++++++++++++++++ store/types/gas_test.go | 8 ++++++++ 2 files changed, 34 insertions(+) diff --git a/store/types/gas.go b/store/types/gas.go index 028edc350ecd..517d5d87b199 100644 --- a/store/types/gas.go +++ b/store/types/gas.go @@ -20,6 +20,12 @@ const ( // Gas measured by the SDK type Gas = uint64 +// ErrorNegativeGasConsumed defines an error thrown when the amount of gas refunded results in a +// negative gas consumed amount. +type ErrorNegativeGasConsumed struct { + Descriptor string +} + // ErrorOutOfGas defines an error thrown when an action results in out of gas. type ErrorOutOfGas struct { Descriptor string @@ -37,6 +43,7 @@ type GasMeter interface { GasConsumedToLimit() Gas Limit() Gas ConsumeGas(amount Gas, descriptor string) + RefundGas(amount Gas, descriptor string) IsPastLimit() bool IsOutOfGas() bool String() string @@ -91,7 +98,16 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { if g.consumed > g.limit { panic(ErrorOutOfGas{descriptor}) } +} + +// RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the +// gas consumed, the function will panic. +func (g *basicGasMeter) RefundGas(amount Gas, descriptor string) { + if g.consumed < amount { + panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) + } + g.consumed -= amount } func (g *basicGasMeter) IsPastLimit() bool { @@ -138,6 +154,16 @@ func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { } } +// RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the +// gas consumed, the function will panic. +func (g *infiniteGasMeter) RefundGas(amount Gas, descriptor string) { + if g.consumed < amount { + panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) + } + + g.consumed -= amount +} + func (g *infiniteGasMeter) IsPastLimit() bool { return false } diff --git a/store/types/gas_test.go b/store/types/gas_test.go index f4490747135b..8a02df4cfa18 100644 --- a/store/types/gas_test.go +++ b/store/types/gas_test.go @@ -16,10 +16,13 @@ func TestInfiniteGasMeter(t *testing.T) { meter.ConsumeGas(10, "consume 10") require.Equal(t, uint64(10), meter.GasConsumed()) require.Equal(t, uint64(10), meter.GasConsumedToLimit()) + meter.RefundGas(1, "refund 1") + require.Equal(t, uint64(9), meter.GasConsumed()) require.False(t, meter.IsPastLimit()) require.False(t, meter.IsOutOfGas()) meter.ConsumeGas(Gas(math.MaxUint64/2), "consume half max uint64") require.Panics(t, func() { meter.ConsumeGas(Gas(math.MaxUint64/2)+2, "panic") }) + require.Panics(t, func() { meter.RefundGas(meter.GasConsumed()+1, "refund greater than consumed") }) } func TestGasMeter(t *testing.T) { @@ -57,6 +60,11 @@ func TestGasMeter(t *testing.T) { require.Panics(t, func() { meter.ConsumeGas(1, "") }, "Exceeded but not panicked. tc #%d", tcnum) require.Equal(t, meter.GasConsumedToLimit(), meter.Limit(), "Gas consumption (to limit) not match limit") require.Equal(t, meter.GasConsumed(), meter.Limit()+1, "Gas consumption not match limit+1") + + require.NotPanics(t, func() { meter.RefundGas(1, "refund 1") }) + require.Equal(t, meter.GasConsumed(), meter.Limit(), "Gas consumption not match limit+1") + require.Panics(t, func() { meter.RefundGas(meter.GasConsumed()+1, "refund greater than consumed") }) + meter2 := NewGasMeter(math.MaxUint64) meter2.ConsumeGas(Gas(math.MaxUint64/2), "consume half max uint64") require.Panics(t, func() { meter2.ConsumeGas(Gas(math.MaxUint64/2)+2, "panic") }) From e37050ddf2b4db949448be21786038e50db5760b Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 27 May 2021 17:11:49 +0200 Subject: [PATCH 2/4] changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0249a1d31d4e..bba0581a5db8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -127,7 +127,8 @@ if input key is empty, or input data contains empty key. ### Improvements -* (baseapp, types) [#\9390](https://github.com/cosmos/cosmos-sdk/pull/9390) Add current block header hash to `Context` +* (store) [\#9403](https://github.com/cosmos/cosmos-sdk/pull/9403) Add `RefundGas` function to `GasMeter` interface +* (baseapp, types) [\#9390](https://github.com/cosmos/cosmos-sdk/pull/9390) Add current block header hash to `Context` * (x/bank) [\#8614](https://github.com/cosmos/cosmos-sdk/issues/8614) Add `Name` and `Symbol` fields to denom metadata * (x/auth) [\#8522](https://github.com/cosmos/cosmos-sdk/pull/8522) Allow to query all stored accounts * (crypto/types) [\#8600](https://github.com/cosmos/cosmos-sdk/pull/8600) `CompactBitArray`: optimize the `NumTrueBitsBefore` method and add an `Equal` method. From 17623a4190f55ee9d8475380f33a6cd855e1679c Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Tue, 1 Jun 2021 11:45:07 +0200 Subject: [PATCH 3/4] add comment about use case --- store/types/gas.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/store/types/gas.go b/store/types/gas.go index 517d5d87b199..b252d7be73f3 100644 --- a/store/types/gas.go +++ b/store/types/gas.go @@ -102,6 +102,10 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { // RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the // gas consumed, the function will panic. +// +// Use case: This functionality enables refunding gas to the trasaction or block gas pools so that +// EVM-compatible chains can fully support the go-ethereum StateDb interface. +// See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference. func (g *basicGasMeter) RefundGas(amount Gas, descriptor string) { if g.consumed < amount { panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) @@ -156,6 +160,10 @@ func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { // RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the // gas consumed, the function will panic. +// +// Use case: This functionality enables refunding gas to the trasaction or block gas pools so that +// EVM-compatible chains can fully support the go-ethereum StateDb interface. +// See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference. func (g *infiniteGasMeter) RefundGas(amount Gas, descriptor string) { if g.consumed < amount { panic(ErrorNegativeGasConsumed{Descriptor: descriptor}) From 6d09288e31b6acd6c2a8265f30fea30e58330d80 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 1 Jun 2021 11:10:00 -0400 Subject: [PATCH 4/4] Apply suggestions from code review --- store/types/gas.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/types/gas.go b/store/types/gas.go index b252d7be73f3..bf44e717d095 100644 --- a/store/types/gas.go +++ b/store/types/gas.go @@ -103,7 +103,7 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { // RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the // gas consumed, the function will panic. // -// Use case: This functionality enables refunding gas to the trasaction or block gas pools so that +// Use case: This functionality enables refunding gas to the transaction or block gas pools so that // EVM-compatible chains can fully support the go-ethereum StateDb interface. // See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference. func (g *basicGasMeter) RefundGas(amount Gas, descriptor string) {