Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Follow the [migration document](docs/migrations/v0.5.x_to_v0.6.0.md) for upgrade

### BUG FIXES

- Align precompile gas calculation with expected EVM gas semantics.

## v0.5.1

### DEPENDENCIES
Expand Down
3 changes: 2 additions & 1 deletion contracts/solidity/ContractCreationTester.sol
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ contract ContractCreationTester {
uint256 successCreationValue
) external payable {
// 1. Try to create contract (will revert after creation, catch it)
try this.createAndRevert{value: revertCreationValue}(revertCreationValue) returns (SimpleReceiver newContract1) {
try this.createAndRevert{value: revertCreationValue}
(revertCreationValue) returns (SimpleReceiver newContract1) {
// This won't execute because createAndRevert reverts
createdContracts.push(address(newContract1));
emit ContractCreated(address(newContract1), revertCreationValue);
Expand Down
10 changes: 10 additions & 0 deletions contracts/solidity/precompiles/bank/testdata/BankCaller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ contract BankCaller {
function callSupplyOf(address erc20Address) external view returns (uint256) {
return IBANK_CONTRACT.supplyOf(erc20Address);
}

// Calls totalSupply with explicit gas forwarding and measures the gas consumed
// by the inner call. Returns whether the call succeeded and the actual gas used.
function callTotalSupplyWithGas(uint256 gasForward) external view returns (bool success, uint256 innerGasUsed) {
uint256 gasBefore = gasleft();
(success, ) = IBANK_PRECOMPILE_ADDRESS.staticcall{gas: gasForward}(
abi.encodeWithSelector(IBank.totalSupply.selector)
);
innerGasUsed = gasBefore - gasleft();
}
}
28 changes: 26 additions & 2 deletions precompiles/bank/testdata/BankCaller.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,34 @@
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "gasForward",
"type": "uint256"
}
],
"name": "callTotalSupplyWithGas",
"outputs": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "uint256",
"name": "innerGasUsed",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x6080806040523461001657610378908161001c8239f35b600080fdfe60806040908082526004918236101561001757600080fd5b600091823560e01c90816389129c681461016257508063acab2f94146100e95763bba60ca01461004657600080fd5b346100e557602092836003193601126100e15780356001600160a01b038116908190036100dd578251631890039360e21b81529182015283816024816108045afa9283156100d257809361009d575b505051908152f35b909192508382813d83116100cb575b6100b68183610234565b810103126100c8575051903880610095565b80fd5b503d6100ac565b8251903d90823e3d90fd5b8380fd5b8280fd5b5080fd5b508290346100e157826003193601126100e15782815180936318160ddd60e01b8252816108045afa918215610158578361012f9493610133575b505051918291826101e1565b0390f35b6101509293503d8091833e6101488183610234565b81019061026c565b908380610123565b81513d85823e3d90fd5b92939050346100dd5760203660031901126100dd5780356001600160a01b03811691908290036101dd576327e235e360e01b845283015282826024816108045afa918215610158578361012f94936101c057505051918291826101e1565b6101d59293503d8091833e6101488183610234565b903880610123565b8480fd5b60208082019080835283518092528060408094019401926000905b83821061020b57505050505090565b845180516001600160a01b031687528301518684015294850194938201936001909101906101fc565b90601f8019910116810190811067ffffffffffffffff82111761025657604052565b634e487b7160e01b600052604160045260246000fd5b6020808284031261032857815167ffffffffffffffff9283821161032857019083601f83011215610328578151838111610256576040938451956102b5848460051b0188610234565b828752838088019360061b86010194818611610328578401925b8584106102e0575050505050505090565b8684830312610328578651908782018281108582111761032d5788528451906001600160a01b0382168203610328578287928a945282870151838201528152019301926102cf565b600080fd5b60246000634e487b7160e01b81526041600452fdfea2646970667358221220069405aa45fc21b29f725237543db2b8600a62c69a04e7a4c44dce45d314303e64736f6c63430008140033",
"deployedBytecode": "0x60806040908082526004918236101561001757600080fd5b600091823560e01c90816389129c681461016257508063acab2f94146100e95763bba60ca01461004657600080fd5b346100e557602092836003193601126100e15780356001600160a01b038116908190036100dd578251631890039360e21b81529182015283816024816108045afa9283156100d257809361009d575b505051908152f35b909192508382813d83116100cb575b6100b68183610234565b810103126100c8575051903880610095565b80fd5b503d6100ac565b8251903d90823e3d90fd5b8380fd5b8280fd5b5080fd5b508290346100e157826003193601126100e15782815180936318160ddd60e01b8252816108045afa918215610158578361012f9493610133575b505051918291826101e1565b0390f35b6101509293503d8091833e6101488183610234565b81019061026c565b908380610123565b81513d85823e3d90fd5b92939050346100dd5760203660031901126100dd5780356001600160a01b03811691908290036101dd576327e235e360e01b845283015282826024816108045afa918215610158578361012f94936101c057505051918291826101e1565b6101d59293503d8091833e6101488183610234565b903880610123565b8480fd5b60208082019080835283518092528060408094019401926000905b83821061020b57505050505090565b845180516001600160a01b031687528301518684015294850194938201936001909101906101fc565b90601f8019910116810190811067ffffffffffffffff82111761025657604052565b634e487b7160e01b600052604160045260246000fd5b6020808284031261032857815167ffffffffffffffff9283821161032857019083601f83011215610328578151838111610256576040938451956102b5848460051b0188610234565b828752838088019360061b86010194818611610328578401925b8584106102e0575050505050505090565b8684830312610328578651908782018281108582111761032d5788528451906001600160a01b0382168203610328578287928a945282870151838201528152019301926102cf565b600080fd5b60246000634e487b7160e01b81526041600452fdfea2646970667358221220069405aa45fc21b29f725237543db2b8600a62c69a04e7a4c44dce45d314303e64736f6c63430008140033",
"bytecode": "0x608080604052346100165761045b908161001c8239f35b600080fdfe60806040908082526004908136101561001757600080fd5b600090813560e01c90816389129c681461024557508063acab2f94146101cd578063bba60ca01461012b5763d47052031461005157600080fd5b34610128576020366003190112610128575a90835192602084016318160ddd60e01b81528185528585019467ffffffffffffffff95818110878211176101155787525183918291906108048535fa933d1561010f573d9081116100fc578551906100c5601f8201601f191660200183610317565b81528260203d92013e5b5a83039283116100e9575050825191151582526020820152f35b634e487b7160e01b825260119052602490fd5b634e487b7160e01b835260418252602483fd5b506100cf565b634e487b7160e01b855260418452602485fd5b80fd5b5082346101c957602092836003193601126101c55780356001600160a01b038116908190036101c1578251631890039360e21b81529182015283816024816108045afa9283156101b6578093610184575b505051908152f35b909192508382813d83116101af575b61019d8183610317565b8101031261012857505190838061017c565b503d610193565b8251903d90823e3d90fd5b8380fd5b8280fd5b5080fd5b5091346101c557826003193601126101c55782815180936318160ddd60e01b8252816108045afa91821561023b57836102129493610216575b505051918291826102c4565b0390f35b6102339293503d8091833e61022b8183610317565b81019061034f565b903880610206565b81513d85823e3d90fd5b90508383346101c15760203660031901126101c15780356001600160a01b03811691908290036102c0576327e235e360e01b845283015282826024816108045afa91821561023b578361021294936102a357505051918291826102c4565b6102b89293503d8091833e61022b8183610317565b908380610206565b8480fd5b60208082019080835283518092528060408094019401926000905b8382106102ee57505050505090565b845180516001600160a01b031687528301518684015294850194938201936001909101906102df565b90601f8019910116810190811067ffffffffffffffff82111761033957604052565b634e487b7160e01b600052604160045260246000fd5b6020808284031261040b57815167ffffffffffffffff9283821161040b57019083601f8301121561040b57815183811161033957604093845195610398848460051b0188610317565b828752838088019360061b8601019481861161040b578401925b8584106103c3575050505050505090565b868483031261040b57865190878201828110858211176104105788528451906001600160a01b038216820361040b578287928a945282870151838201528152019301926103b2565b600080fd5b60246000634e487b7160e01b81526041600452fdfea26469706673582212206291c412c52d95f56ebd6bcace337eb3149a918c5ed8398ed4845c859755136a64736f6c63430008140033",
"deployedBytecode": "0x60806040908082526004908136101561001757600080fd5b600090813560e01c90816389129c681461024557508063acab2f94146101cd578063bba60ca01461012b5763d47052031461005157600080fd5b34610128576020366003190112610128575a90835192602084016318160ddd60e01b81528185528585019467ffffffffffffffff95818110878211176101155787525183918291906108048535fa933d1561010f573d9081116100fc578551906100c5601f8201601f191660200183610317565b81528260203d92013e5b5a83039283116100e9575050825191151582526020820152f35b634e487b7160e01b825260119052602490fd5b634e487b7160e01b835260418252602483fd5b506100cf565b634e487b7160e01b855260418452602485fd5b80fd5b5082346101c957602092836003193601126101c55780356001600160a01b038116908190036101c1578251631890039360e21b81529182015283816024816108045afa9283156101b6578093610184575b505051908152f35b909192508382813d83116101af575b61019d8183610317565b8101031261012857505190838061017c565b503d610193565b8251903d90823e3d90fd5b8380fd5b8280fd5b5080fd5b5091346101c557826003193601126101c55782815180936318160ddd60e01b8252816108045afa91821561023b57836102129493610216575b505051918291826102c4565b0390f35b6102339293503d8091833e61022b8183610317565b81019061034f565b903880610206565b81513d85823e3d90fd5b90508383346101c15760203660031901126101c15780356001600160a01b03811691908290036102c0576327e235e360e01b845283015282826024816108045afa91821561023b578361021294936102a357505051918291826102c4565b6102b89293503d8091833e61022b8183610317565b908380610206565b8480fd5b60208082019080835283518092528060408094019401926000905b8382106102ee57505050505090565b845180516001600160a01b031687528301518684015294850194938201936001909101906102df565b90601f8019910116810190811067ffffffffffffffff82111761033957604052565b634e487b7160e01b600052604160045260246000fd5b6020808284031261040b57815167ffffffffffffffff9283821161040b57019083601f8301121561040b57815183811161033957604093845195610398848460051b0188610317565b828752838088019360061b8601019481861161040b578401925b8584106103c3575050505050505090565b868483031261040b57865190878201828110858211176104105788528451906001600160a01b038216820361040b578287928a945282870151838201528152019301926103b2565b600080fd5b60246000634e487b7160e01b81526041600452fdfea26469706673582212206291c412c52d95f56ebd6bcace337eb3149a918c5ed8398ed4845c859755136a64736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
10 changes: 10 additions & 0 deletions precompiles/bank/testdata/BankCaller.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ contract BankCaller {
function callSupplyOf(address erc20Address) external view returns (uint256) {
return IBANK_CONTRACT.supplyOf(erc20Address);
}

// Calls totalSupply with explicit gas forwarding and measures the gas consumed
// by the inner call. Returns whether the call succeeded and the actual gas used.
function callTotalSupplyWithGas(uint256 gasForward) external view returns (bool success, uint256 innerGasUsed) {
uint256 gasBefore = gasleft();
(success, ) = IBANK_PRECOMPILE_ADDRESS.staticcall{gas: gasForward}(
abi.encodeWithSelector(IBank.totalSupply.selector)
);
innerGasUsed = gasBefore - gasleft();
}
}
7 changes: 5 additions & 2 deletions precompiles/common/precompile.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func (p Precompile) RequiredGas(input []byte, isTransaction bool) uint64 {
func (p Precompile) RunNativeAction(evm *vm.EVM, contract *vm.Contract, action NativeAction) ([]byte, error) {
bz, err := p.runNativeAction(evm, contract, action)
if err != nil {
if errors.Is(err, vm.ErrOutOfGas) {
return nil, vm.ErrOutOfGas
}
return ReturnRevertError(evm, err)
}

Expand Down Expand Up @@ -84,14 +87,14 @@ func (p Precompile) runNativeAction(evm *vm.EVM, contract *vm.Contract, action N

initialGas := ctx.GasMeter().GasConsumed()

defer HandleGasError(ctx, contract, initialGas, &err)()

// set the default SDK gas configuration to track gas usage
// we are changing the gas meter type, so it panics gracefully when out of gas
ctx = ctx.WithGasMeter(storetypes.NewGasMeter(contract.Gas)).
WithKVGasConfig(p.KvGasConfig).
WithTransientKVGasConfig(p.TransientKVGasConfig)

defer HandleGasError(ctx, contract, initialGas, &err)()

// we need to consume the gas that was already used by the EVM
ctx.GasMeter().ConsumeGas(initialGas, "creating a new gas meter")

Expand Down
63 changes: 63 additions & 0 deletions tests/integration/precompiles/bank/test_integration.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bank

import (
"fmt"
"math/big"
"testing"

Expand All @@ -25,6 +26,7 @@ import (
evmtypes "github.com/cosmos/evm/x/vm/types"

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

sdk "github.com/cosmos/cosmos-sdk/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
Expand Down Expand Up @@ -248,6 +250,67 @@ func TestIntegrationSuite(t *testing.T, create network.CreateEvmApp, options ...
Expect(balances[0].Amount.String()).To(Equal(cosmosEVMTotalSupply.String()))
Expect(balances[1].Amount.String()).To(Equal(xmplTotalSupply.String()))
})

It("should properly handle OOG in precompile and consume all gas", func() {
numDenoms := 5000
// Mint denoms to make TotalSupply expensive enough to OOG.
ctx := is.network.GetContext()
for i := 0; i < numDenoms; i++ {
denom := fmt.Sprintf("token%d", i)
err = is.network.App.GetBankKeeper().MintCoins(
ctx, minttypes.ModuleName,
sdk.Coins{{Denom: denom, Amount: math.NewInt(1e18)}},
)
Expect(err).ToNot(HaveOccurred(), "failed to mint coin %s", denom)
}
// Commit keeper changes directly to state.
store := is.network.GetContext().MultiStore()
cms, ok := store.(storetypes.CacheMultiStore)
Expect(ok).To(BeTrue())
cms.Write()

Expect(is.network.NextBlock()).ToNot(HaveOccurred(), "failed to advance block")

// Use callTotalSupplyWithGas to measure inner call gas consumption.
// Forward enough gas for the precompile to OOG iterating 5000+ denoms.
gasForward := big.NewInt(9_000_000)

txArgs := evmtypes.EvmTxArgs{
To: &bankCallerContractAddr,
GasLimit: 20_000_000,
}
callArgs := testutiltypes.CallArgs{
ContractABI: bankCallerContract.ABI,
MethodName: "callTotalSupplyWithGas",
Args: []interface{}{gasForward},
}

res, execErr := is.factory.ExecuteContractCall(sender.Priv, txArgs, callArgs)
Expect(execErr).ToNot(HaveOccurred(), "failed to execute callTotalSupplyWithGas")

ethRes, decErr := evmtypes.DecodeTxResponse(res.Data)
Expect(decErr).ToNot(HaveOccurred(), "failed to decode eth tx response")
Expect(ethRes.VmError).To(BeEmpty(), "outer call should not revert")

// Unpack: (bool success, uint256 innerGasUsed)
out, unpackErr := bankCallerContract.ABI.Unpack("callTotalSupplyWithGas", ethRes.Ret)
Expect(unpackErr).ToNot(HaveOccurred(), "failed to unpack result")

innerSuccess := out[0].(bool)
innerGasUsed := out[1].(*big.Int)

fmt.Printf("\nInner call: success=%v, innerGasUsed=%s, gasForwarded=%s, txGasUsed=%d\n",
innerSuccess, innerGasUsed.String(), gasForward.String(), ethRes.GasUsed)

// The inner call should have failed (OOG with 5000 denoms).
Expect(innerSuccess).To(BeFalse(), "inner call should OOG with insufficient gas for 5000 denoms")

// The inner call should have consumed all (or nearly all) forwarded gas.
// With correct gas accounting, innerGasUsed ≈ gasForward.
// With the bug, innerGasUsed would be ~3k (only EVM overhead, no precompile charge).
Expect(innerGasUsed.Uint64()).To(BeNumerically(">=", gasForward.Uint64()*90/100),
"inner OOG call should consume all forwarded gas, got %s out of %s", innerGasUsed.String(), gasForward.String())
})
})

Context("supplyOf query", func() {
Expand Down
32 changes: 24 additions & 8 deletions tests/integration/precompiles/staking/test_staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func (s *PrecompileTestSuite) TestRun() {
gas uint64
readOnly bool
expPass bool
expRevert bool // true if error returns ABI-encoded revert reason bytes (not OOG)
errContains string
}{
{
Expand All @@ -149,6 +150,7 @@ func (s *PrecompileTestSuite) TestRun() {
8000,
false,
false,
false,
"out of gas",
},
{
Expand All @@ -166,6 +168,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
false,
true,
false,
"",
},
{
Expand All @@ -183,6 +186,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
false,
true,
false,
"",
},
{
Expand All @@ -201,6 +205,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
false,
true,
false,
"failed to redelegate tokens",
},
{
Expand Down Expand Up @@ -241,6 +246,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
false,
true,
false,
"",
},
{
Expand All @@ -257,6 +263,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
false,
true,
false,
"",
},
{
Expand All @@ -275,6 +282,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
false,
true,
false,
"",
},
{
Expand Down Expand Up @@ -313,6 +321,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
false,
true,
false,
"",
},
{
Expand All @@ -329,6 +338,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
true,
true,
false,
"",
},
{
Expand Down Expand Up @@ -367,6 +377,7 @@ func (s *PrecompileTestSuite) TestRun() {
1000000,
true,
true,
false,
"",
},
{
Expand All @@ -384,6 +395,7 @@ func (s *PrecompileTestSuite) TestRun() {
21295, // use enough gas to avoid out of gas error
true,
false,
true,
"write protection",
},
{
Expand All @@ -394,6 +406,7 @@ func (s *PrecompileTestSuite) TestRun() {
21295, // use enough gas to avoid out of gas error
false,
false,
true,
"no method with id",
},
}
Expand Down Expand Up @@ -454,17 +467,20 @@ func (s *PrecompileTestSuite) TestRun() {
bz, err := s.precompile.Run(evm, contract, tc.readOnly)

// Check results
if tc.expPass {
switch {
case tc.expPass:
s.Require().NoError(err, "expected no error when running the precompile")
s.Require().NotNil(bz, "expected returned bytes not to be nil")
} else {
s.Require().Error(err, "expected error to be returned when running the precompile")
s.Require().NotNil(bz, "expected returned bytes to be nil")
execRevertErr := evmtypes.NewExecErrorWithReason(bz)
s.Require().ErrorContains(execRevertErr, tc.errContains)
case tc.expRevert:
s.Require().ErrorIs(err, vm.ErrExecutionReverted)
s.Require().NotNil(bz, "expected revert reason bytes")
revertErr := evmtypes.NewExecErrorWithReason(bz)
s.Require().ErrorContains(revertErr, tc.errContains)
default:
s.Require().ErrorIs(err, vm.ErrOutOfGas)
s.Require().Nil(bz, "expected nil bytes on out of gas")
consumed := ctx.GasMeter().GasConsumed()
// LessThanOrEqual because the gas is consumed before the error is returned
s.Require().LessOrEqual(tc.gas, consumed, "expected gas consumed to be equal to gas limit")
s.Require().LessOrEqual(tc.gas, consumed, "expected all gas to be consumed on OOG")
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion tests/systemtests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ test:
go test -failfast -timeout=30m -p=1 -mod=readonly -tags='system_test' -v ./... -run TestExceptions --wait-time=$(WAIT_TIME) --block-time=8s --binary evmd --chain-id local-4221
go test -failfast -timeout=30m -p=1 -mod=readonly -tags='system_test' -v ./... -run TestEIP7702 --wait-time=$(WAIT_TIME) --block-time=5s --binary evmd --chain-id local-4221
go test -failfast -timeout=30m -p=1 -mod=readonly -tags='system_test' -v ./... -run TestEIP712 --wait-time=$(WAIT_TIME) --block-time=5s --binary evmd --chain-id local-4221
go test -failfast -timeout=30m -p=1 -mod=readonly -tags='system_test' -v ./... -run 'TestChainUpgrade|TestEth' --wait-time=$(WAIT_TIME) --block-time=5s --binary evmd --chain-id local-4221
go test -failfast -timeout=30m -p=1 -mod=readonly -tags='system_test' -v ./... -run 'TestChainUpgrade|TestEth' --wait-time=$(WAIT_TIME) --block-time=3s --binary evmd --chain-id local-4221
2 changes: 1 addition & 1 deletion x/vm/statedb/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ func (s *StateDB) setStateObject(object *stateObject) {
// to the precompile call.
func (s *StateDB) AddPrecompileFn(snapshot int) error {
// Capture events before the precompile call
var prevEvents sdk.Events = s.cacheCtx.EventManager().Events()
prevEvents := s.cacheCtx.EventManager().Events()

s.journal.append(precompileCallChange{
snapshot: snapshot,
Expand Down
Loading