laconicd-deprecated/tests/integration_tests/test_priority.py

205 lines
6.9 KiB
Python
Raw Normal View History

import sys
import pytest
from .network import setup_ethermint
from .utils import ADDRS, KEYS, eth_to_bech32, sign_transaction, wait_for_new_blocks
PRIORITY_REDUCTION = 1000000
@pytest.fixture(scope="module")
def custom_ethermint(tmp_path_factory):
path = tmp_path_factory.mktemp("priority")
yield from setup_ethermint(path, 26800, long_timeout_commit=True)
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):
"""
test priorities of different tx types
use a relatively large priority number to counter
the effect of base fee change during the testing.
"""
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 * 600000,
"maxPriorityFeePerGas": 0,
},
),
(
"community",
{
"to": "0x0000000000000000000000000000000000000000",
"value": amount,
"gas": 21000,
"gasPrice": base_fee + PRIORITY_REDUCTION * 200000,
},
),
(
"signer2",
{
"to": "0x0000000000000000000000000000000000000000",
"value": amount,
"gasPrice": base_fee + PRIORITY_REDUCTION * 400000,
"accessList": [
{
"address": "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
"storageKeys": (
"0x00000000000000000000000000000000000000000000000000000000"
"00000003",
"0x00000000000000000000000000000000000000000000000000000000"
"00000007",
),
}
],
},
),
(
"signer1",
{
"to": "0x0000000000000000000000000000000000000000",
"value": amount,
"gas": 21000,
"maxFeePerGas": base_fee + PRIORITY_REDUCTION * 600000,
"maxPriorityFeePerGas": PRIORITY_REDUCTION * 600000,
},
),
]
# test cases are ordered by priority
expect_priorities = [tx_priority(tx, base_fee) for _, tx in test_cases]
assert expect_priorities == [0, 200000, 400000, 600000]
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
tx_indexes = [(r.blockNumber, r.transactionIndex) for r in receipts]
print(tx_indexes)
# the first sent tx are included later, because of lower priority
assert all(i1 > i2 for i1, i2 in zip(tx_indexes, tx_indexes[1:]))
def test_native_tx_priority(ethermint):
cli = ethermint.cosmos_cli()
base_fee = cli.query_base_fee()
print("base_fee", base_fee)
test_cases = [
{
"from": eth_to_bech32(ADDRS["community"]),
"to": eth_to_bech32(ADDRS["validator"]),
"amount": "1000aphoton",
"gas_prices": f"{base_fee + PRIORITY_REDUCTION * 600000}aphoton",
"max_priority_price": 0,
},
{
"from": eth_to_bech32(ADDRS["signer1"]),
"to": eth_to_bech32(ADDRS["signer2"]),
"amount": "1000aphoton",
"gas_prices": f"{base_fee + PRIORITY_REDUCTION * 600000}aphoton",
"max_priority_price": PRIORITY_REDUCTION * 200000,
},
{
"from": eth_to_bech32(ADDRS["signer2"]),
"to": eth_to_bech32(ADDRS["signer1"]),
"amount": "1000aphoton",
"gas_prices": f"{base_fee + PRIORITY_REDUCTION * 400000}aphoton",
"max_priority_price": PRIORITY_REDUCTION * 400000,
},
{
"from": eth_to_bech32(ADDRS["validator"]),
"to": eth_to_bech32(ADDRS["community"]),
"amount": "1000aphoton",
"gas_prices": f"{base_fee + PRIORITY_REDUCTION * 600000}aphoton",
"max_priority_price": None, # no extension, maximum tipFeeCap
},
]
txs = []
expect_priorities = []
for tc in test_cases:
tx = cli.transfer(
tc["from"],
tc["to"],
tc["amount"],
gas_prices=tc["gas_prices"],
generate_only=True,
)
txs.append(
cli.sign_tx_json(
tx, tc["from"], max_priority_price=tc.get("max_priority_price")
)
)
gas_price = int(tc["gas_prices"].removesuffix("aphoton"))
expect_priorities.append(
min(
get_max_priority_price(tc.get("max_priority_price")),
gas_price - base_fee,
)
// PRIORITY_REDUCTION
)
assert expect_priorities == [0, 200000, 400000, 600000]
txhashes = []
for tx in txs:
rsp = cli.broadcast_tx_json(tx, broadcast_mode="sync")
assert rsp["code"] == 0, rsp["raw_log"]
txhashes.append(rsp["txhash"])
print("wait for two new blocks, so the sent txs are all included")
wait_for_new_blocks(cli, 2)
tx_results = [cli.tx_search_rpc(f"tx.hash='{txhash}'")[0] for txhash in txhashes]
tx_indexes = [(int(r["height"]), r["index"]) for r in tx_results]
print(tx_indexes)
# the first sent tx are included later, because of lower priority
# ensure desc within continuous block
2023-03-13 10:12:04 +00:00
assert all(
(b1 < b2 or (b1 == b2 and i1 > i2))
for (b1, i1), (b2, i2) in zip(tx_indexes, tx_indexes[1:])
)
def get_max_priority_price(max_priority_price):
"default to max int64 if None"
return max_priority_price if max_priority_price is not None else sys.maxsize