fix(rpc): fix gRPC query failure on legacy blocks (#1354)

* Problem: grpc query fail on legacy blocks

`BaseFee` and `EthCall`.

Solution:
- since grpc query handlers are used for all versions of the blocks, it need to be compatible with legacy formats.

debug

fix basefee fetch

Revert "debug"

This reverts commit 50ebaf697fc06b0d6e26abd8de8f89717e8a219d.

update gomod2nix

Update CHANGELOG.md

debug

fix panic

Revert "debug"

This reverts commit e08af04b0776bd390c42706cc9ec978e00bcb3bb.

* add upgrade integration test

* Update tests/integration_tests/configs/upgrade-test-package.nix

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
yihuang
2022-09-23 18:06:25 +02:00
committed by GitHub
co-authored by Federico Kunze Küllmer
parent d15007b4d9
commit 35850e620d
17 changed files with 405 additions and 84 deletions
@@ -0,0 +1,17 @@
local config = import 'default.jsonnet';
config {
'ethermint_9000-1'+: {
genesis+: {
app_state+: {
feemarket+: {
params+: {
no_base_fee: false,
base_fee:: super.base_fee,
initial_base_fee: super.base_fee,
},
},
},
},
},
}
@@ -0,0 +1,17 @@
let
pkgs = import ../../../nix { };
fetchEthermint = rev: builtins.fetchTarball "https://github.com/evmos/ethermint/archive/${rev}.tar.gz";
released = pkgs.buildGo118Module rec {
name = "ethermintd";
# the commit before https://github.com/evmos/ethermint/pull/943
src = fetchEthermint "f21592ebfe74da7590eb42ed926dae970b2a9a3f";
subPackages = [ "cmd/ethermintd" ];
vendorSha256 = "sha256-ABm5t6R/u2S6pThGrgdsqe8n3fH5tIWw7a57kxJPbYw=";
doCheck = false;
};
current = pkgs.callPackage ../../../. { };
in
pkgs.linkFarm "upgrade-test-package" [
{ name = "genesis"; path = released; }
{ name = "integration-test-upgrade"; path = current; }
]
+3
View File
@@ -829,3 +829,6 @@ class CosmosCLI:
def rollback(self):
self.raw("rollback", home=self.data_dir)
def migrate_keystore(self):
return self.raw("keys", "migrate", home=self.data_dir)
+10 -3
View File
@@ -11,14 +11,17 @@ from web3.middleware import geth_poa_middleware
from .cosmoscli import CosmosCLI
from .utils import wait_for_port
DEFAULT_CHAIN_BINARY = "ethermintd"
class Ethermint:
def __init__(self, base_dir):
def __init__(self, base_dir, chain_binary=DEFAULT_CHAIN_BINARY):
self._w3 = None
self.base_dir = base_dir
self.config = json.loads((base_dir / "config.json").read_text())
self.enable_auto_deployment = False
self._use_websockets = False
self.chain_binary = chain_binary
def copy(self):
return Ethermint(self.base_dir)
@@ -55,7 +58,9 @@ class Ethermint:
self._use_websockets = use
def cosmos_cli(self, i=0):
return CosmosCLI(self.base_dir / f"node{i}", self.node_rpc(i), "ethermintd")
return CosmosCLI(
self.base_dir / f"node{i}", self.node_rpc(i), self.chain_binary
)
class Geth:
@@ -124,7 +129,9 @@ def setup_custom_ethermint(
if wait_port:
wait_for_port(ports.evmrpc_port(base_port))
wait_for_port(ports.evmrpc_ws_port(base_port))
yield Ethermint(path / "ethermint_9000-1")
yield Ethermint(
path / "ethermint_9000-1", chain_binary=chain_binary or DEFAULT_CHAIN_BINARY
)
finally:
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
proc.wait()
+173
View File
@@ -0,0 +1,173 @@
import configparser
import json
import re
import subprocess
from pathlib import Path
import pytest
from dateutil.parser import isoparse
from pystarport import ports
from pystarport.cluster import SUPERVISOR_CONFIG_FILE
from .network import Ethermint, setup_custom_ethermint
from .utils import (
ADDRS,
CONTRACTS,
deploy_contract,
parse_events,
send_transaction,
wait_for_block,
wait_for_block_time,
wait_for_port,
)
def init_cosmovisor(home):
"""
build and setup cosmovisor directory structure in each node's home directory
"""
cosmovisor = home / "cosmovisor"
cosmovisor.mkdir()
(cosmovisor / "upgrades").symlink_to("../../../upgrades")
(cosmovisor / "genesis").symlink_to("./upgrades/genesis")
def post_init(path, base_port, config):
"""
prepare cosmovisor for each node
"""
chain_id = "ethermint_9000-1"
cfg = json.loads((path / chain_id / "config.json").read_text())
for i, _ in enumerate(cfg["validators"]):
home = path / chain_id / f"node{i}"
init_cosmovisor(home)
# patch supervisord ini config
ini_path = path / chain_id / SUPERVISOR_CONFIG_FILE
ini = configparser.RawConfigParser()
ini.read(ini_path)
reg = re.compile(rf"^program:{chain_id}-node(\d+)")
for section in ini.sections():
m = reg.match(section)
if m:
i = m.group(1)
ini[section].update(
{
"command": f"cosmovisor start --home %(here)s/node{i}",
"environment": (
f"DAEMON_NAME=ethermintd,DAEMON_HOME=%(here)s/node{i}"
),
}
)
with ini_path.open("w") as fp:
ini.write(fp)
@pytest.fixture(scope="module")
def custom_ethermint(tmp_path_factory):
path = tmp_path_factory.mktemp("upgrade")
cmd = [
"nix-build",
Path(__file__).parent / "configs/upgrade-test-package.nix",
"-o",
path / "upgrades",
]
print(*cmd)
subprocess.run(cmd, check=True)
# init with genesis binary
yield from setup_custom_ethermint(
path,
26100,
Path(__file__).parent / "configs/cosmovisor.jsonnet",
post_init=post_init,
chain_binary=str(path / "upgrades/genesis/bin/ethermintd"),
)
def test_cosmovisor_upgrade(custom_ethermint: Ethermint):
"""
- propose an upgrade and pass it
- wait for it to happen
- it should work transparently
- check that queries on legacy blocks still works after upgrade.
"""
cli = custom_ethermint.cosmos_cli()
height = cli.block_height()
target_height = height + 5
print("upgrade height", target_height)
w3 = custom_ethermint.w3
contract = deploy_contract(w3, CONTRACTS["TestERC20A"])
old_height = w3.eth.block_number
old_balance = w3.eth.get_balance(ADDRS["validator"], block_identifier=old_height)
old_base_fee = w3.eth.get_block(old_height).baseFeePerGas
old_erc20_balance = contract.caller.balanceOf(ADDRS["validator"])
print("old values", old_height, old_balance, old_base_fee)
plan_name = "integration-test-upgrade"
rsp = cli.gov_propose(
"community",
"software-upgrade",
{
"name": plan_name,
"title": "upgrade test",
"description": "ditto",
"upgrade-height": target_height,
"deposit": "10000aphoton",
},
)
assert rsp["code"] == 0, rsp["raw_log"]
# get proposal_id
ev = parse_events(rsp["logs"])["submit_proposal"]
assert ev["proposal_type"] == "SoftwareUpgrade", rsp
proposal_id = ev["proposal_id"]
rsp = cli.gov_vote("validator", proposal_id, "yes")
assert rsp["code"] == 0, rsp["raw_log"]
# rsp = custom_ethermint.cosmos_cli(1).gov_vote("validator", proposal_id, "yes")
# assert rsp["code"] == 0, rsp["raw_log"]
proposal = cli.query_proposal(proposal_id)
wait_for_block_time(cli, isoparse(proposal["voting_end_time"]))
proposal = cli.query_proposal(proposal_id)
assert proposal["status"] == "PROPOSAL_STATUS_PASSED", proposal
# update cli chain binary
custom_ethermint.chain_binary = (
Path(custom_ethermint.chain_binary).parent.parent.parent
/ f"{plan_name}/bin/ethermintd"
)
cli = custom_ethermint.cosmos_cli()
# block should pass the target height
wait_for_block(cli, target_height + 1, timeout=480)
wait_for_port(ports.rpc_port(custom_ethermint.base_port(0)))
# test migrate keystore
cli.migrate_keystore()
# check basic tx works after upgrade
wait_for_port(ports.evmrpc_port(custom_ethermint.base_port(0)))
receipt = send_transaction(
w3,
{
"to": ADDRS["community"],
"value": 1000,
"maxFeePerGas": 1000000000000,
"maxPriorityFeePerGas": 10000,
},
)
assert receipt.status == 1
# check json-rpc query on older blocks works
assert old_balance == w3.eth.get_balance(
ADDRS["validator"], block_identifier=old_height
)
assert old_base_fee == w3.eth.get_block(old_height).baseFeePerGas
# check eth_call on older blocks works
assert old_erc20_balance == contract.caller(
block_identifier=target_height - 2
).balanceOf(ADDRS["validator"])
+18
View File
@@ -7,6 +7,7 @@ import time
from pathlib import Path
import bech32
from dateutil.parser import isoparse
from dotenv import load_dotenv
from eth_account import Account
from hexbytes import HexBytes
@@ -109,6 +110,16 @@ def w3_wait_for_block(w3, height, timeout=240):
raise TimeoutError(f"wait for block {height} timeout")
def wait_for_block_time(cli, t):
print("wait for block time", t)
while True:
now = isoparse((cli.status())["SyncInfo"]["latest_block_time"])
print("block time now:", now)
if now >= t:
break
time.sleep(0.5)
def deploy_contract(w3, jsonfile, args=(), key=KEYS["validator"]):
"""
deploy contract and return the deployed contract instance
@@ -160,3 +171,10 @@ def supervisorctl(inipath, *args):
(sys.executable, "-msupervisor.supervisorctl", "-c", inipath, *args),
check=True,
)
def parse_events(logs):
return {
ev["type"]: {attr["key"]: attr["value"] for attr in ev["attributes"]}
for ev in logs[0]["events"]
}