forked from cerc-io/laconicd-deprecated
feat(ante, evm): set priority for eth transactions (#1214)
* Set priority for eth transactions Set the tx priority to the lowest priority in the messages. fix unit tests code cleanup and spec update spec fix go lint add priority integration test add python linter job add access list tx type fix gas limit remove ledger tag, so no need to replace hid dependency fix earlier check ibc-go v5.0.0-beta1 * fix pruned node integration test * Update x/feemarket/spec/09_antehandlers.md Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
co-authored by
Federico Kunze Küllmer
parent
53f160cbb0
commit
e1560849dd
@@ -1,8 +1,18 @@
|
||||
{
|
||||
dotenv: '../../../scripts/.env',
|
||||
'ethermintd_777-1': {
|
||||
'ethermint_9000-1': {
|
||||
cmd: 'ethermintd',
|
||||
'start-flags': '--trace',
|
||||
config: {
|
||||
consensus: {
|
||||
// larger timeout for more stable mempool tests
|
||||
timeout_commit: '10s',
|
||||
},
|
||||
mempool: {
|
||||
// use v1 mempool to enable tx prioritization
|
||||
version: 'v1',
|
||||
},
|
||||
},
|
||||
'app-config': {
|
||||
'minimum-gas-prices': '0aphoton',
|
||||
'index-events': ['ethereum_tx.ethereumTxHash'],
|
||||
|
||||
@@ -4,7 +4,6 @@ import signal
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import tomlkit
|
||||
import web3
|
||||
from pystarport import ports
|
||||
from web3.middleware import geth_poa_middleware
|
||||
@@ -61,9 +60,10 @@ class Geth:
|
||||
|
||||
|
||||
def setup_ethermint(path, base_port):
|
||||
cfg = Path(__file__).parent / "../../scripts/ethermint-devnet.yaml"
|
||||
cfg = Path(__file__).parent / "configs/default.jsonnet"
|
||||
yield from setup_custom_ethermint(path, base_port, cfg)
|
||||
|
||||
|
||||
def setup_geth(path, base_port):
|
||||
with (path / "geth.log").open("w") as logfile:
|
||||
cmd = [
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
from .network import Ethermint
|
||||
from .utils import KEYS, sign_transaction
|
||||
|
||||
PRIORITY_REDUCTION = 1000000
|
||||
|
||||
|
||||
def effective_gas_price(tx, base_fee):
|
||||
if "maxFeePerGas" in tx:
|
||||
# dynamic fee tx
|
||||
return min(base_fee + tx["maxPriorityFeePerGas"], tx["maxFeePerGas"])
|
||||
else:
|
||||
# legacy tx
|
||||
return tx["gasPrice"]
|
||||
|
||||
|
||||
def tx_priority(tx, base_fee):
|
||||
if "maxFeePerGas" in tx:
|
||||
# dynamic fee tx
|
||||
return (
|
||||
min(tx["maxPriorityFeePerGas"], tx["maxFeePerGas"] - base_fee)
|
||||
// PRIORITY_REDUCTION
|
||||
)
|
||||
else:
|
||||
# legacy tx
|
||||
return (tx["gasPrice"] - base_fee) // PRIORITY_REDUCTION
|
||||
|
||||
|
||||
def test_priority(ethermint: Ethermint):
|
||||
"""
|
||||
test priorities of different tx types
|
||||
"""
|
||||
w3 = ethermint.w3
|
||||
amount = 10000
|
||||
base_fee = w3.eth.get_block("latest").baseFeePerGas
|
||||
|
||||
# [ ( sender, tx ), ... ]
|
||||
# use different senders to avoid nonce conflicts
|
||||
test_cases = [
|
||||
(
|
||||
"validator",
|
||||
{
|
||||
"to": "0x0000000000000000000000000000000000000000",
|
||||
"value": amount,
|
||||
"gas": 21000,
|
||||
"maxFeePerGas": base_fee + PRIORITY_REDUCTION * 6,
|
||||
"maxPriorityFeePerGas": 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
"community",
|
||||
{
|
||||
"to": "0x0000000000000000000000000000000000000000",
|
||||
"value": amount,
|
||||
"gas": 21000,
|
||||
"gasPrice": base_fee + PRIORITY_REDUCTION * 2,
|
||||
},
|
||||
),
|
||||
(
|
||||
"signer2",
|
||||
{
|
||||
"to": "0x0000000000000000000000000000000000000000",
|
||||
"value": amount,
|
||||
"gasPrice": base_fee + PRIORITY_REDUCTION * 4,
|
||||
"accessList": [
|
||||
{
|
||||
"address": "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
|
||||
"storageKeys": (
|
||||
"0x00000000000000000000000000000000000000000000000000000000"
|
||||
"00000003",
|
||||
"0x00000000000000000000000000000000000000000000000000000000"
|
||||
"00000007",
|
||||
),
|
||||
}
|
||||
],
|
||||
},
|
||||
),
|
||||
(
|
||||
"signer1",
|
||||
{
|
||||
"to": "0x0000000000000000000000000000000000000000",
|
||||
"value": amount,
|
||||
"gas": 21000,
|
||||
"maxFeePerGas": base_fee + PRIORITY_REDUCTION * 6,
|
||||
"maxPriorityFeePerGas": PRIORITY_REDUCTION * 6,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
# test cases are ordered by priority
|
||||
priorities = [tx_priority(tx, base_fee) for _, tx in test_cases]
|
||||
assert all(a < b for a, b in zip(priorities, priorities[1:]))
|
||||
|
||||
signed = [sign_transaction(w3, tx, key=KEYS[sender]) for sender, tx in test_cases]
|
||||
# send the txs from low priority to high,
|
||||
# but the later sent txs should be included earlier.
|
||||
txhashes = [w3.eth.send_raw_transaction(tx.rawTransaction) for tx in signed]
|
||||
|
||||
receipts = [w3.eth.wait_for_transaction_receipt(txhash) for txhash in txhashes]
|
||||
print(receipts)
|
||||
assert all(receipt.status == 1 for receipt in receipts), "expect all txs success"
|
||||
|
||||
# the later txs should be included earlier because of higher priority
|
||||
# FIXME there's some non-deterministics due to mempool logic
|
||||
assert all(included_earlier(r2, r1) for r1, r2 in zip(receipts, receipts[1:]))
|
||||
|
||||
|
||||
def included_earlier(receipt1, receipt2):
|
||||
"returns true if receipt1 is earlier than receipt2"
|
||||
if receipt1.blockNumber < receipt2.blockNumber:
|
||||
return True
|
||||
elif receipt1.blockNumber == receipt2.blockNumber:
|
||||
return receipt1.transactionIndex < receipt2.transactionIndex
|
||||
else:
|
||||
return False
|
||||
@@ -1,6 +1,22 @@
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
|
||||
from eth_account import Account
|
||||
from web3._utils.transactions import fill_nonce, fill_transaction_defaults
|
||||
|
||||
Account.enable_unaudited_hdwallet_features()
|
||||
|
||||
ACCOUNTS = {
|
||||
"validator": Account.from_mnemonic(os.getenv("VALIDATOR1_MNEMONIC")),
|
||||
"community": Account.from_mnemonic(os.getenv("COMMUNITY_MNEMONIC")),
|
||||
"signer1": Account.from_mnemonic(os.getenv("SIGNER1_MNEMONIC")),
|
||||
"signer2": Account.from_mnemonic(os.getenv("SIGNER2_MNEMONIC")),
|
||||
}
|
||||
KEYS = {name: account.key for name, account in ACCOUNTS.items()}
|
||||
ADDRS = {name: account.address for name, account in ACCOUNTS.items()}
|
||||
|
||||
|
||||
def wait_for_port(port, host="127.0.0.1", timeout=40.0):
|
||||
start_time = time.perf_counter()
|
||||
while True:
|
||||
@@ -14,3 +30,21 @@ def wait_for_port(port, host="127.0.0.1", timeout=40.0):
|
||||
"Waited too long for the port {} on host {} to start accepting "
|
||||
"connections.".format(port, host)
|
||||
) from ex
|
||||
|
||||
|
||||
def fill_defaults(w3, tx):
|
||||
return fill_nonce(w3, fill_transaction_defaults(w3, tx))
|
||||
|
||||
|
||||
def sign_transaction(w3, tx, key=KEYS["validator"]):
|
||||
"fill default fields and sign"
|
||||
acct = Account.from_key(key)
|
||||
tx["from"] = acct.address
|
||||
tx = fill_defaults(w3, tx)
|
||||
return acct.sign_transaction(tx)
|
||||
|
||||
|
||||
def send_transaction(w3, tx, key=KEYS["validator"]):
|
||||
signed = sign_transaction(w3, tx, key)
|
||||
txhash = w3.eth.send_raw_transaction(signed.rawTransaction)
|
||||
return w3.eth.wait_for_transaction_receipt(txhash)
|
||||
|
||||
Reference in New Issue
Block a user