Skip to content

Commit 79c911c

Browse files
authored
Problem: precompiles required-gas need adjustments (#1178)
* adjust precompile required gas update integration tests config add switch fix lint rename const revert gas cost estimate cost convert to map fix alert fix golangci update gas value based on estimation add basecost to the precompile increase gas in integration test * adjust config
1 parent 071bc21 commit 79c911c

File tree

6 files changed

+134
-40
lines changed

6 files changed

+134
-40
lines changed

app/app.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,16 +544,18 @@ func New(
544544
for k, v := range memKeys {
545545
allKeys[k] = v
546546
}
547+
548+
gasConfig := storetypes.TransientGasConfig()
547549
app.EvmKeeper = evmkeeper.NewKeeper(
548550
appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName),
549551
app.AccountKeeper, app.BankKeeper, app.StakingKeeper,
550552
app.FeeMarketKeeper,
551553
tracer,
552554
evmS,
553555
[]vm.PrecompiledContract{
554-
cronosprecompiles.NewBankContract(app.BankKeeper, appCodec),
555-
cronosprecompiles.NewRelayerContract(app.IBCKeeper, appCodec),
556-
cronosprecompiles.NewIcaContract(&app.ICAAuthKeeper, &app.CronosKeeper, appCodec),
556+
cronosprecompiles.NewBankContract(app.BankKeeper, appCodec, gasConfig),
557+
cronosprecompiles.NewRelayerContract(app.IBCKeeper, appCodec, gasConfig),
558+
cronosprecompiles.NewIcaContract(&app.ICAAuthKeeper, &app.CronosKeeper, appCodec, gasConfig),
557559
},
558560
allKeys,
559561
)

integration_tests/configs/ibc.jsonnet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ config {
140140
{
141141
id: 'cronos_777-1',
142142
max_gas: 500000,
143-
gas_multiplier: 2,
143+
gas_multiplier: 4,
144144
address_type: {
145145
derivation: 'ethermint',
146146
proto_type: {

integration_tests/test_ica_precompile.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def test_call(ibc):
131131
name = "signer2"
132132
addr = ADDRS[name]
133133
contract = w3.eth.contract(address=CONTRACT, abi=contract_info)
134-
data = {"from": ADDRS[name], "gas": 200000}
134+
data = {"from": ADDRS[name], "gas": 400000}
135135
ica_address = register_acc(
136136
cli_controller,
137137
w3,
@@ -173,7 +173,7 @@ def test_sc_call(ibc):
173173
name = "signer2"
174174
signer = ADDRS[name]
175175
keys = KEYS[name]
176-
data = {"from": signer, "gas": 200000}
176+
data = {"from": signer, "gas": 400000}
177177
ica_address = register_acc(
178178
cli_controller,
179179
w3,
@@ -189,7 +189,7 @@ def test_sc_call(ibc):
189189

190190
# register from another user should fail
191191
name = "signer1"
192-
data = {"from": ADDRS[name], "gas": 200000}
192+
data = {"from": ADDRS[name], "gas": 400000}
193193
version = ""
194194
tx = tcontract.functions.callRegister(connid, version).build_transaction(data)
195195
res = send_transaction(w3, tx, KEYS[name])

x/cronos/keeper/precompiles/bank.go

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"errors"
55
"math/big"
66

7+
storetypes "github.com/cosmos/cosmos-sdk/store/types"
8+
79
sdkmath "cosmossdk.io/math"
810
"github.com/cosmos/cosmos-sdk/codec"
911
"github.com/ethereum/go-ethereum/accounts/abi"
@@ -18,43 +20,69 @@ import (
1820
)
1921

2022
const (
21-
EVMDenomPrefix = "evm/"
22-
BankContractRequiredGas = 10000
23+
EVMDenomPrefix = "evm/"
24+
MintMethodName = "mint"
25+
BurnMethodName = "burn"
26+
BalanceOfMethodName = "balanceOf"
27+
TransferMethodName = "transfer"
2328
)
2429

2530
var (
26-
bankABI abi.ABI
27-
BankContractAddress = common.BytesToAddress([]byte{100})
31+
bankABI abi.ABI
32+
bankContractAddress = common.BytesToAddress([]byte{100})
33+
bankGasRequiredByMethod = map[[4]byte]uint64{}
2834
)
2935

3036
func init() {
3137
if err := bankABI.UnmarshalJSON([]byte(bank.BankModuleMetaData.ABI)); err != nil {
3238
panic(err)
3339
}
40+
for methodName := range bankABI.Methods {
41+
var methodID [4]byte
42+
copy(methodID[:], bankABI.Methods[methodName].ID[:4])
43+
switch methodName {
44+
case MintMethodName, BurnMethodName:
45+
bankGasRequiredByMethod[methodID] = 200000
46+
case BalanceOfMethodName:
47+
bankGasRequiredByMethod[methodID] = 10000
48+
case TransferMethodName:
49+
bankGasRequiredByMethod[methodID] = 150000
50+
default:
51+
bankGasRequiredByMethod[methodID] = 0
52+
}
53+
}
3454
}
3555

3656
func EVMDenom(token common.Address) string {
3757
return EVMDenomPrefix + token.Hex()
3858
}
3959

4060
type BankContract struct {
41-
bankKeeper types.BankKeeper
42-
cdc codec.Codec
61+
bankKeeper types.BankKeeper
62+
cdc codec.Codec
63+
kvGasConfig storetypes.GasConfig
4364
}
4465

4566
// NewBankContract creates the precompiled contract to manage native tokens
46-
func NewBankContract(bankKeeper types.BankKeeper, cdc codec.Codec) vm.PrecompiledContract {
47-
return &BankContract{bankKeeper, cdc}
67+
func NewBankContract(bankKeeper types.BankKeeper, cdc codec.Codec, kvGasConfig storetypes.GasConfig) vm.PrecompiledContract {
68+
return &BankContract{bankKeeper, cdc, kvGasConfig}
4869
}
4970

5071
func (bc *BankContract) Address() common.Address {
51-
return BankContractAddress
72+
return bankContractAddress
5273
}
5374

5475
// RequiredGas calculates the contract gas use
5576
func (bc *BankContract) RequiredGas(input []byte) uint64 {
56-
// TODO estimate required gas
57-
return BankContractRequiredGas
77+
// base cost to prevent large input size
78+
baseCost := uint64(len(input)) * bc.kvGasConfig.WriteCostPerByte
79+
var methodID [4]byte
80+
copy(methodID[:], input[:4])
81+
requiredGas, ok := bankGasRequiredByMethod[methodID]
82+
if ok {
83+
return requiredGas + baseCost
84+
}
85+
return baseCost
5886
}
5987

6088
func (bc *BankContract) IsStateful() bool {
@@ -82,7 +110,7 @@ func (bc *BankContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) (
82110
stateDB := evm.StateDB.(ExtStateDB)
83111
precompileAddr := bc.Address()
84112
switch method.Name {
85-
case "mint", "burn":
113+
case MintMethodName, BurnMethodName:
86114
if readonly {
87115
return nil, errors.New("the method is not readonly")
88116
}
@@ -126,7 +154,7 @@ func (bc *BankContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) (
126154
return nil, err
127155
}
128156
return method.Outputs.Pack(true)
129-
case "balanceOf":
157+
case BalanceOfMethodName:
130158
args, err := method.Inputs.Unpack(contract.Input[4:])
131159
if err != nil {
132160
return nil, errors.New("fail to unpack input arguments")
@@ -136,7 +164,7 @@ func (bc *BankContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) (
136164
// query from storage
137165
balance := bc.bankKeeper.GetBalance(stateDB.CacheContext(), sdk.AccAddress(addr.Bytes()), EVMDenom(token)).Amount.BigInt()
138166
return method.Outputs.Pack(balance)
139-
case "transfer":
167+
case TransferMethodName:
140168
if readonly {
141169
return nil, errors.New("the method is not readonly")
142170
}

x/cronos/keeper/precompiles/ica.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"math/big"
77
"time"
88

9+
storetypes "github.com/cosmos/cosmos-sdk/store/types"
10+
911
"github.com/cosmos/cosmos-sdk/codec"
1012
sdk "github.com/cosmos/cosmos-sdk/types"
1113
icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types"
@@ -21,13 +23,17 @@ import (
2123
"github.com/ethereum/go-ethereum/core/vm"
2224
)
2325

24-
// TODO: Replace this const with adjusted gas cost corresponding to input when executing precompile contract.
25-
const ICAContractRequiredGas = 10000
26+
const (
27+
RegisterAccountMethodName = "registerAccount"
28+
QueryAccountMethodName = "queryAccount"
29+
SubmitMsgsMethodName = "submitMsgs"
30+
)
2631

2732
var (
28-
icaABI abi.ABI
29-
icaCallbackABI abi.ABI
30-
icaContractAddress = common.BytesToAddress([]byte{102})
33+
icaABI abi.ABI
34+
icaCallbackABI abi.ABI
35+
icaContractAddress = common.BytesToAddress([]byte{102})
36+
icaGasRequiredByMethod = map[[4]byte]uint64{}
3137
)
3238

3339
func init() {
@@ -37,6 +43,21 @@ func init() {
3743
if err := icaCallbackABI.UnmarshalJSON([]byte(icacallback.ICACallbackMetaData.ABI)); err != nil {
3844
panic(err)
3945
}
46+
47+
for methodName := range icaABI.Methods {
48+
var methodID [4]byte
49+
copy(methodID[:], icaABI.Methods[methodName].ID[:4])
50+
switch methodName {
51+
case RegisterAccountMethodName:
52+
icaGasRequiredByMethod[methodID] = 300000
53+
case QueryAccountMethodName:
54+
icaGasRequiredByMethod[methodID] = 100000
55+
case SubmitMsgsMethodName:
56+
icaGasRequiredByMethod[methodID] = 300000
57+
default:
58+
icaGasRequiredByMethod[methodID] = 0
59+
}
60+
}
4061
}
4162

4263
func OnPacketResultCallback(args ...interface{}) ([]byte, error) {
@@ -49,14 +70,16 @@ type IcaContract struct {
4970
cdc codec.Codec
5071
icaauthKeeper types.Icaauthkeeper
5172
cronosKeeper types.CronosKeeper
73+
kvGasConfig storetypes.GasConfig
5274
}
5375

54-
func NewIcaContract(icaauthKeeper types.Icaauthkeeper, cronosKeeper types.CronosKeeper, cdc codec.Codec) vm.PrecompiledContract {
76+
func NewIcaContract(icaauthKeeper types.Icaauthkeeper, cronosKeeper types.CronosKeeper, cdc codec.Codec, kvGasConfig storetypes.GasConfig) vm.PrecompiledContract {
5577
return &IcaContract{
5678
BaseContract: NewBaseContract(icaContractAddress),
5779
cdc: cdc,
5880
icaauthKeeper: icaauthKeeper,
5981
cronosKeeper: cronosKeeper,
82+
kvGasConfig: kvGasConfig,
6083
}
6184
}
6285

@@ -66,7 +89,15 @@ func (ic *IcaContract) Address() common.Address {
6689

6790
// RequiredGas calculates the contract gas use
6891
func (ic *IcaContract) RequiredGas(input []byte) uint64 {
69-
return ICAContractRequiredGas
92+
// base cost to prevent large input size
93+
baseCost := uint64(len(input)) * ic.kvGasConfig.WriteCostPerByte
94+
var methodID [4]byte
95+
copy(methodID[:], input[:4])
96+
requiredGas, ok := icaGasRequiredByMethod[methodID]
97+
if ok {
98+
return requiredGas + baseCost
99+
}
100+
return baseCost
70101
}
71102

72103
func (ic *IcaContract) IsStateful() bool {
@@ -87,7 +118,7 @@ func (ic *IcaContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([
87118
converter := cronosevents.IcaConvertEvent
88119
var execErr error
89120
switch method.Name {
90-
case "registerAccount":
121+
case RegisterAccountMethodName:
91122
if readonly {
92123
return nil, errors.New("the method is not readonly")
93124
}
@@ -109,7 +140,7 @@ func (ic *IcaContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([
109140
return nil, execErr
110141
}
111142
return method.Outputs.Pack(true)
112-
case "queryAccount":
143+
case QueryAccountMethodName:
113144
args, err := method.Inputs.Unpack(contract.Input[4:])
114145
if err != nil {
115146
return nil, errors.New("fail to unpack input arguments")
@@ -131,7 +162,7 @@ func (ic *IcaContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([
131162
icaAddress = response.InterchainAccountAddress
132163
}
133164
return method.Outputs.Pack(icaAddress)
134-
case "submitMsgs":
165+
case SubmitMsgsMethodName:
135166
if readonly {
136167
return nil, errors.New("the method is not readonly")
137168
}

x/cronos/keeper/precompiles/relayer.go

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"encoding/binary"
55
"errors"
66

7+
storetypes "github.com/cosmos/cosmos-sdk/store/types"
8+
79
"github.com/cosmos/cosmos-sdk/codec"
810
"github.com/ethereum/go-ethereum/common"
911
"github.com/ethereum/go-ethereum/core/vm"
@@ -12,33 +14,64 @@ import (
1214
cronosevents "github.com/crypto-org-chain/cronos/v2/x/cronos/events"
1315
)
1416

15-
// TODO: Replace this const with adjusted gas cost corresponding to input when executing precompile contract.
16-
const RelayerContractRequiredGas = 10000
17+
var (
18+
relayerContractAddress = common.BytesToAddress([]byte{101})
19+
relayerGasRequiredByMethod = map[int]uint64{}
20+
)
1721

18-
var RelayerContractAddress = common.BytesToAddress([]byte{101})
22+
func init() {
23+
relayerGasRequiredByMethod[prefixCreateClient] = 200000
24+
relayerGasRequiredByMethod[prefixUpdateClient] = 400000
25+
relayerGasRequiredByMethod[prefixUpgradeClient] = 400000
26+
relayerGasRequiredByMethod[prefixSubmitMisbehaviour] = 100000
27+
relayerGasRequiredByMethod[prefixConnectionOpenInit] = 100000
28+
relayerGasRequiredByMethod[prefixConnectionOpenTry] = 100000
29+
relayerGasRequiredByMethod[prefixConnectionOpenAck] = 100000
30+
relayerGasRequiredByMethod[prefixConnectionOpenConfirm] = 100000
31+
relayerGasRequiredByMethod[prefixChannelOpenInit] = 100000
32+
relayerGasRequiredByMethod[prefixChannelOpenTry] = 100000
33+
relayerGasRequiredByMethod[prefixChannelOpenAck] = 100000
34+
relayerGasRequiredByMethod[prefixChannelOpenConfirm] = 100000
35+
relayerGasRequiredByMethod[prefixRecvPacket] = 250000
36+
relayerGasRequiredByMethod[prefixAcknowledgement] = 250000
37+
relayerGasRequiredByMethod[prefixTimeout] = 100000
38+
relayerGasRequiredByMethod[prefixTimeoutOnClose] = 100000
39+
}
1940

2041
type RelayerContract struct {
2142
BaseContract
2243

23-
cdc codec.Codec
24-
ibcKeeper *ibckeeper.Keeper
44+
cdc codec.Codec
45+
ibcKeeper *ibckeeper.Keeper
46+
kvGasConfig storetypes.GasConfig
2547
}
2648

27-
func NewRelayerContract(ibcKeeper *ibckeeper.Keeper, cdc codec.Codec) vm.PrecompiledContract {
49+
func NewRelayerContract(ibcKeeper *ibckeeper.Keeper, cdc codec.Codec, kvGasConfig storetypes.GasConfig) vm.PrecompiledContract {
2850
return &RelayerContract{
29-
BaseContract: NewBaseContract(RelayerContractAddress),
51+
BaseContract: NewBaseContract(relayerContractAddress),
3052
ibcKeeper: ibcKeeper,
3153
cdc: cdc,
54+
kvGasConfig: kvGasConfig,
3255
}
3356
}
3457

3558
func (bc *RelayerContract) Address() common.Address {
36-
return RelayerContractAddress
59+
return relayerContractAddress
3760
}
3861

3962
// RequiredGas calculates the contract gas use
4063
func (bc *RelayerContract) RequiredGas(input []byte) uint64 {
41-
return RelayerContractRequiredGas
64+
// base cost to prevent large input size
65+
baseCost := uint64(len(input)) * bc.kvGasConfig.WriteCostPerByte
66+
if len(input) < prefixSize4Bytes {
67+
return 0
68+
}
69+
prefix := int(binary.LittleEndian.Uint32(input[:prefixSize4Bytes]))
70+
requiredGas, ok := relayerGasRequiredByMethod[prefix]
71+
if ok {
72+
return requiredGas + baseCost
73+
}
74+
return baseCost
4275
}
4376

4477
func (bc *RelayerContract) IsStateful() bool {

0 commit comments

Comments
 (0)