Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Follow the [migration document](docs/migrations/v0.5.x_to_v0.6.0.md) for upgrade
- Bound the message `GasLimit` in `CallEVMWithData` to the caller-provided `gasCap` when set (`min(gasCap, DefaultGasCap)`) instead of always using `DefaultGasCap`, so callers (e.g. the CosmWasm ERC20 query bindings) can constrain internal EVM execution to their remaining gas budget.
- Fix EVM fee-abstraction gas refund to compute the refund against the full transaction gas (`gasUsed + leftoverGas`) instead of `gasUsed`, bounding the refund by the paid fee and preventing the fee collector from being drained by high gas limit transactions.
- [\#15](https://github.com/KiiChain/evm/pull/15) Fix distribution precompile 32-byte withdraw address inflating native supply by skipping non-20-byte accounts when mirroring balance changes to the StateDB.
- Align precompile gas calculation with expected EVM gas semantics.

## v0.5.1

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();
}
}
1 change: 1 addition & 0 deletions evmd/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func (app *EVMD) configureEVMMempool(appOpts servertypes.AppOptions, logger log.
),
)
app.SetPrepareProposal(abciProposalHandler.PrepareProposalHandler())
app.SetProcessProposal(abciProposalHandler.ProcessProposalHandler())

return nil
}
Expand Down
19 changes: 12 additions & 7 deletions evmd/tests/ibc/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ func SetupNativeErc20(t *testing.T, chain *evmibctesting.TestChain, senderAcc ev
evmCtx := chain.GetContext()
evmApp := chain.App.(evm.EvmApp)

// Deploy new ERC20 contract with default metadata
ak := evmApp.GetAccountKeeper()
deployerAccAddr := sdk.AccAddress(erc20TestDeployer.Bytes())
if ak.GetAccount(evmCtx, deployerAccAddr) == nil {
ak.SetAccount(evmCtx, ak.NewAccountWithAddress(evmCtx, deployerAccAddr))
}

stateDB := statedb.New(chain.GetContext(), chain.App.(evm.EvmApp).GetEVMKeeper(), statedb.NewEmptyTxConfig())
contractAddr, err := DeployERC20Contract(evmCtx, stateDB, evmApp.GetAccountKeeper(), evmApp.GetEVMKeeper(), banktypes.Metadata{
DenomUnits: []*banktypes.DenomUnit{
Expand Down Expand Up @@ -88,7 +93,7 @@ func SetupNativeErc20(t *testing.T, chain *evmibctesting.TestChain, senderAcc ev
evmCtx,
stateDB,
contractAbi,
erc20types.ModuleAddress,
erc20TestDeployer,
contractAddr,
true,
false,
Expand Down Expand Up @@ -145,8 +150,8 @@ func DeployContract(t *testing.T, chain *evmibctesting.TestChain, deploymentData
return crypto.CreateAddress(from, account.Nonce), nil
}

// DeployERC20Contract creates and deploys an ERC20 contract on the EVM with the
// erc20 module account as owner.
var erc20TestDeployer = common.HexToAddress("0x000000000000000000000000000000000000beef")

func DeployERC20Contract(
ctx sdk.Context,
stateDB *statedb.StateDB,
Expand All @@ -173,13 +178,13 @@ func DeployERC20Contract(
copy(data[:len(contracts.ERC20MinterBurnerDecimalsContract.Bin)], contracts.ERC20MinterBurnerDecimalsContract.Bin)
copy(data[len(contracts.ERC20MinterBurnerDecimalsContract.Bin):], ctorArgs)

nonce, err := accountKeeper.GetSequence(ctx, erc20types.ModuleAddress.Bytes())
nonce, err := accountKeeper.GetSequence(ctx, erc20TestDeployer.Bytes())
if err != nil {
return common.Address{}, err
}

contractAddr := crypto.CreateAddress(erc20types.ModuleAddress, nonce)
_, err = evmKeeper.CallEVMWithData(ctx, stateDB, erc20types.ModuleAddress, nil, data, true, false, nil)
contractAddr := crypto.CreateAddress(erc20TestDeployer, nonce)
_, err = evmKeeper.CallEVMWithData(ctx, stateDB, erc20TestDeployer, nil, data, true, false, nil)
if err != nil {
return common.Address{}, errorsmod.Wrapf(err, "failed to deploy contract for %s", coinMetadata.Name)
}
Expand Down
2 changes: 1 addition & 1 deletion evmd/testutil/eth_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
var EthDefaultConsensusParams = &cmtypes.ConsensusParams{
Block: cmtypes.BlockParams{
MaxBytes: 200000,
MaxGas: -1, // no limit
MaxGas: 80_000_000,
},
Evidence: cmtypes.EvidenceParams{
MaxAgeNumBlocks: 302400,
Expand Down
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
4 changes: 2 additions & 2 deletions tests/integration/precompiles/gov/test_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -1344,7 +1344,7 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp

// 2. Deposit to gov prop from contract 2
txArgs.To = &contractAddrDupe
txArgs.GasLimit = 1_000_000_000
txArgs.GasLimit = 50_000_000
callArgs.MethodName = testDepositFromContract
callArgs.Args = []interface{}{
contractProposalID,
Expand All @@ -1367,7 +1367,7 @@ func TestPrecompileIntegrationTestSuite(t *testing.T, create network.CreateEvmAp
It("should cancel proposal and fund to communityPool", func() {
baseDenom := s.network.GetBaseDenom()
txArgs.To = &contractAddr
txArgs.GasLimit = 1_000_000_000
txArgs.GasLimit = 50_000_000
callArgs.MethodName = "testTransferCancelFund"
callArgs.Args = []interface{}{
contractAddrDupe,
Expand Down
Loading
Loading