Skip to content

Commit

Permalink
Problem: require gas in relayer precompile is higher than consumed
Browse files Browse the repository at this point in the history
  • Loading branch information
mmsqe committed Oct 31, 2023
1 parent dbd86c4 commit a61fecc
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 38 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
- [#1216](https://github.com/crypto-org-chain/cronos/pull/1216) Update ethermint to fix of avoid redundant parse chainID from gensis when start server.
- [#1230](https://github.com/crypto-org-chain/cronos/pull/1230) Fix mem store in versiondb multistore.

### Improvements

- [#](https://github.com/crypto-org-chain/cronos/pull/) Adjust require gas in relayer precompile to be closed with actual consumed.


*October 17, 2023*

## v1.1.0-rc1
Expand Down
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ func New(
tracer,
evmS,
[]vm.PrecompiledContract{
cronosprecompiles.NewRelayerContract(app.IBCKeeper, appCodec, gasConfig),
cronosprecompiles.NewRelayerContract(app.IBCKeeper, appCodec, app.Logger()),
cronosprecompiles.NewIcaContract(&app.ICAAuthKeeper, &app.CronosKeeper, appCodec, gasConfig),
},
allKeys,
Expand Down
17 changes: 16 additions & 1 deletion integration_tests/configs/ibc_rly.jsonnet
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
local ibc = import 'ibc.jsonnet';

ibc {
'chainmain-1'+: {
validators: [
{
coins: '987870000000000000cro',
staked: '20000000000000cro',
mnemonic: '${VALIDATOR' + i + '_MNEMONIC}',
client_config: {
'broadcast-mode': 'block',
},
base_port: 26800 + i * 10,
}
for i in std.range(1, 2)
],
},
relayer+: {
chains: [super.chains[0] {
precompiled_contract_address: '0x0000000000000000000000000000000000000065',
max_gas: 1000000,
gas_multiplier: 1.2,
}] + super.chains[1:],
},
}
9 changes: 9 additions & 0 deletions integration_tests/configs/ibc_rly_evm.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
local ibc = import 'ibc_rly.jsonnet';

ibc {
relayer+: {
chains: [super.chains[0] {
precompiled_contract_address: '0x0000000000000000000000000000000000000065',
}] + super.chains[1:],
},
}
12 changes: 12 additions & 0 deletions integration_tests/cosmoscli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from collections import namedtuple

import bech32
import requests
from dateutil.parser import isoparse
from pystarport.utils import build_cli_args_safe, format_doc_string, interact

Expand Down Expand Up @@ -221,6 +222,17 @@ def tx_search(self, events: str):
self.raw("query", "txs", events=events, output="json", node=self.node_rpc)
)

def tx_search_rpc(self, criteria: str):
node_rpc_http = "http" + self.node_rpc.removeprefix("tcp")
rsp = requests.get(
f"{node_rpc_http}/tx_search",
params={
"query": f'"{criteria}"',
},
).json()
assert "error" not in rsp, rsp["error"]
return rsp["result"]["txs"]

def distribution_commission(self, addr):
coin = json.loads(
self.raw(
Expand Down
33 changes: 33 additions & 0 deletions integration_tests/ibc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,25 @@ def hermes_transfer(ibc):
return src_amount


def rly_transfer(ibc):
# chainmain-1 -> cronos_777-1
my_ibc0 = "chainmain-1"
my_ibc1 = "cronos_777-1"
channel = "channel-0"
dst_addr = eth_to_bech32(ADDRS["signer2"])
src_amount = 10
src_denom = "basecro"
path = ibc.cronos.base_dir.parent / "relayer"
# srcchainid dstchainid amount dst_addr srchannelid
cmd = (
f"rly tx transfer {my_ibc0} {my_ibc1} {src_amount}{src_denom} "
f"{dst_addr} {channel} "
f"--path chainmain-cronos "
f"--home {str(path)}"
)
subprocess.run(cmd, check=True, shell=True)


def find_duplicate(attributes):
res = set()
key = attributes[0]["key"]
Expand Down Expand Up @@ -639,3 +658,17 @@ def gen_send_msg(sender, receiver, denom, amount):
"to_address": receiver,
"amount": [{"denom": denom, "amount": f"{amount}"}],
}


def log_gas_records(cli):
criteria = "tx.height >= 0"
txs = cli.tx_search_rpc(criteria)
records = []
for tx in txs:
res = tx["tx_result"]
actions = []
for event in res["events"]:
for attribute in event["attributes"]:
if attribute["key"] == "action":
actions.append(attribute["value"])
records.append(res["gas_used"])
19 changes: 2 additions & 17 deletions integration_tests/test_ibc_rly.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
import subprocess

import pytest
from eth_utils import keccak, to_checksum_address
Expand All @@ -14,6 +13,7 @@
ibc_denom,
ibc_incentivized_transfer,
prepare_network,
rly_transfer,
)
from .utils import (
ADDRS,
Expand Down Expand Up @@ -42,7 +42,7 @@
@pytest.fixture(scope="module")
def ibc(request, tmp_path_factory):
"prepare-network"
name = "ibc_rly"
name = "ibc_rly_evm"
path = tmp_path_factory.mktemp(name)
yield from prepare_network(
path,
Expand All @@ -51,21 +51,6 @@ def ibc(request, tmp_path_factory):
)


def rly_transfer(ibc):
# chainmain-1 -> cronos_777-1
my_ibc0 = "chainmain-1"
my_ibc1 = "cronos_777-1"
path = ibc.cronos.base_dir.parent / "relayer"
# srcchainid dstchainid amount dst_addr srchannelid
cmd = (
f"rly tx transfer {my_ibc0} {my_ibc1} {src_amount}{src_denom} "
f"{eth_to_bech32(cronos_signer2)} {channel} "
f"--path chainmain-cronos "
f"--home {str(path)}"
)
subprocess.run(cmd, check=True, shell=True)


def coin_received(receiver, amt, denom):
return {
"receiver": receiver,
Expand Down
31 changes: 31 additions & 0 deletions integration_tests/test_ibc_rly_gas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import pytest
from pystarport import cluster

from .ibc_utils import log_gas_records, prepare_network, rly_transfer
from .utils import wait_for_new_blocks


@pytest.fixture(scope="module", params=["ibc_rly", "ibc_rly_evm"])
def ibc(request, tmp_path_factory):
"prepare-network"
name = request.param
path = tmp_path_factory.mktemp(name)
yield from prepare_network(path, name, relayer=cluster.Relayer.RLY.value)


records = []


def test_ibc(ibc):
# chainmain-1 relayer -> cronos_777-1 signer2
cli = ibc.cronos.cosmos_cli()
wait_for_new_blocks(cli, 1)
rly_transfer(ibc)
diff = 0.5
record = log_gas_records(cli)
if record:
records.append(record)
if len(records) == 2:
for e1, e2 in zip(*records):
res = e2 / e1
assert 1 - diff <= res <= 1 + diff, res
53 changes: 34 additions & 19 deletions x/cronos/keeper/precompiles/relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import (
"encoding/binary"
"errors"

storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/cometbft/cometbft/libs/log"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"

authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
cronosevents "github.com/crypto-org-chain/cronos/v2/x/cronos/events"
"github.com/crypto-org-chain/cronos/v2/x/cronos/types"
)
Expand All @@ -20,19 +22,19 @@ var (
)

func init() {
relayerGasRequiredByMethod[prefixCreateClient] = 200000
relayerGasRequiredByMethod[prefixUpdateClient] = 400000
relayerGasRequiredByMethod[prefixCreateClient] = 117462
relayerGasRequiredByMethod[prefixUpdateClient] = 111894
relayerGasRequiredByMethod[prefixUpgradeClient] = 400000
relayerGasRequiredByMethod[prefixSubmitMisbehaviour] = 100000
relayerGasRequiredByMethod[prefixConnectionOpenInit] = 100000
relayerGasRequiredByMethod[prefixConnectionOpenTry] = 100000
relayerGasRequiredByMethod[prefixConnectionOpenTry] = 38468
relayerGasRequiredByMethod[prefixConnectionOpenAck] = 100000
relayerGasRequiredByMethod[prefixConnectionOpenConfirm] = 100000
relayerGasRequiredByMethod[prefixConnectionOpenConfirm] = 12865
relayerGasRequiredByMethod[prefixChannelOpenInit] = 100000
relayerGasRequiredByMethod[prefixChannelOpenTry] = 100000
relayerGasRequiredByMethod[prefixChannelOpenTry] = 70562
relayerGasRequiredByMethod[prefixChannelOpenAck] = 100000
relayerGasRequiredByMethod[prefixChannelOpenConfirm] = 100000
relayerGasRequiredByMethod[prefixRecvPacket] = 250000
relayerGasRequiredByMethod[prefixChannelOpenConfirm] = 21190
relayerGasRequiredByMethod[prefixRecvPacket] = 144925
relayerGasRequiredByMethod[prefixAcknowledgement] = 250000
relayerGasRequiredByMethod[prefixTimeout] = 100000
relayerGasRequiredByMethod[prefixTimeoutOnClose] = 100000
Expand All @@ -41,17 +43,17 @@ func init() {
type RelayerContract struct {
BaseContract

cdc codec.Codec
ibcKeeper types.IbcKeeper
kvGasConfig storetypes.GasConfig
cdc codec.Codec
ibcKeeper types.IbcKeeper
logger log.Logger
}

func NewRelayerContract(ibcKeeper types.IbcKeeper, cdc codec.Codec, kvGasConfig storetypes.GasConfig) vm.PrecompiledContract {
func NewRelayerContract(ibcKeeper types.IbcKeeper, cdc codec.Codec, logger log.Logger) vm.PrecompiledContract {
return &RelayerContract{
BaseContract: NewBaseContract(relayerContractAddress),
ibcKeeper: ibcKeeper,
cdc: cdc,
kvGasConfig: kvGasConfig,
logger: logger.With("precompiles", "relayer"),
}
}

Expand All @@ -60,18 +62,31 @@ func (bc *RelayerContract) Address() common.Address {
}

// RequiredGas calculates the contract gas use
func (bc *RelayerContract) RequiredGas(input []byte) uint64 {
// base cost to prevent large input size
baseCost := uint64(len(input)) * bc.kvGasConfig.WriteCostPerByte
func (bc *RelayerContract) RequiredGas(input []byte) (gas uint64) {
if len(input) < prefixSize4Bytes {
return 0
}
intrinsicGas, err := core.IntrinsicGas(input, nil, false, true, true)
if err != nil {
return 0
}
prefix := int(binary.LittleEndian.Uint32(input[:prefixSize4Bytes]))
requiredGas, ok := relayerGasRequiredByMethod[prefix]
if ok {
return requiredGas + baseCost
if !ok {
requiredGas = 0
}
// base cost to prevent large input size
baseCost := uint64(len(input)) * authtypes.DefaultTxSizeCostPerByte
var methodID [4]byte
copy(methodID[:], input[:4])
defer func() {
bc.logger.Debug("required", "gas", gas, "method", prefix, "len", len(input), "intrinsic", intrinsicGas)
}()
total := requiredGas + baseCost
if total < intrinsicGas {
return 0
}
return baseCost
return total - intrinsicGas
}

func (bc *RelayerContract) IsStateful() bool {
Expand Down

0 comments on commit a61fecc

Please sign in to comment.