Add unit tests for replace by fee dynamics (#14815)

* Add unit tests for replace by fee dynamics.

* Separate scenarios into their own tests.

* Remove the TestReplaceByFee class.
This commit is contained in:
Amine Khaldi 2023-03-28 16:41:08 +01:00 committed by GitHub
parent 5e5395305d
commit db6386ab3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -12,6 +12,7 @@ from chia.consensus.cost_calculator import NPCResult
from chia.consensus.default_constants import DEFAULT_CONSTANTS
from chia.full_node.mempool_check_conditions import mempool_check_time_locks
from chia.full_node.mempool_manager import (
MEMPOOL_MIN_FEE_INCREASE,
MempoolManager,
TimelockConditions,
can_replace,
@ -972,3 +973,133 @@ async def test_assert_before_expiration(opcode: ConditionOpcode, arg: int, expec
still_in_pool = mempool_manager.get_spendbundle(bundle_name) == bundle
assert still_in_pool != expect_eviction
def make_test_spendbundle(coin: Coin, *, fee: int = 0) -> SpendBundle:
conditions = [
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, uint64(coin.amount - fee)],
[ConditionOpcode.AGG_SIG_UNSAFE, G1Element(), IDENTITY_PUZZLE_HASH],
]
return spend_bundle_from_conditions(conditions, coin)
async def send_spendbundle(
mempool_manager: MempoolManager,
sb: SpendBundle,
expected_result: Tuple[MempoolInclusionStatus, Optional[Err]] = (MempoolInclusionStatus.SUCCESS, None),
) -> None:
result = await add_spendbundle(mempool_manager, sb, sb.name())
assert (result[1], result[2]) == expected_result
async def make_and_send_spendbundle(
mempool_manager: MempoolManager,
coin: Coin,
*,
fee: int = 0,
expected_result: Tuple[MempoolInclusionStatus, Optional[Err]] = (MempoolInclusionStatus.SUCCESS, None),
) -> SpendBundle:
sb = make_test_spendbundle(coin, fee=fee)
await send_spendbundle(mempool_manager, sb, expected_result)
return sb
def assert_sb_in_pool(mempool_manager: MempoolManager, sb: SpendBundle) -> None:
assert sb == mempool_manager.get_spendbundle(sb.name())
def assert_sb_not_in_pool(mempool_manager: MempoolManager, sb: SpendBundle) -> None:
assert mempool_manager.get_spendbundle(sb.name()) is None
@pytest.mark.asyncio
async def test_insufficient_fee_increase() -> None:
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
sb1_2 = await make_and_send_spendbundle(
mempool_manager, coins[0], fee=1, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
)
# The old spendbundle must stay
assert_sb_in_pool(mempool_manager, sb1_1)
assert_sb_not_in_pool(mempool_manager, sb1_2)
@pytest.mark.asyncio
async def test_sufficient_fee_increase() -> None:
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
sb1_1 = await make_and_send_spendbundle(mempool_manager, coins[0])
sb1_2 = await make_and_send_spendbundle(mempool_manager, coins[0], fee=MEMPOOL_MIN_FEE_INCREASE)
# sb1_1 gets replaced with sb1_2
assert_sb_not_in_pool(mempool_manager, sb1_1)
assert_sb_in_pool(mempool_manager, sb1_2)
@pytest.mark.asyncio
async def test_superset() -> None:
# Aggregated spendbundle sb12 replaces sb1 since it spends a superset
# of coins spent in sb1
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
sb1 = await make_and_send_spendbundle(mempool_manager, coins[0])
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE)
sb12 = SpendBundle.aggregate([sb2, sb1])
await send_spendbundle(mempool_manager, sb12)
assert_sb_in_pool(mempool_manager, sb12)
assert_sb_not_in_pool(mempool_manager, sb1)
@pytest.mark.asyncio
async def test_superset_violation() -> None:
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
sb1 = make_test_spendbundle(coins[0])
sb2 = make_test_spendbundle(coins[1])
sb12 = SpendBundle.aggregate([sb1, sb2])
await send_spendbundle(mempool_manager, sb12)
assert_sb_in_pool(mempool_manager, sb12)
# sb23 must not replace existing sb12 as the former does not spend all
# coins that are spent in the latter (specifically, the first coin)
sb3 = make_test_spendbundle(coins[2], fee=MEMPOOL_MIN_FEE_INCREASE)
sb23 = SpendBundle.aggregate([sb2, sb3])
await send_spendbundle(
mempool_manager, sb23, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
)
assert_sb_in_pool(mempool_manager, sb12)
assert_sb_not_in_pool(mempool_manager, sb23)
@pytest.mark.asyncio
async def test_total_fpc_decrease() -> None:
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
sb1 = make_test_spendbundle(coins[0])
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
sb12 = SpendBundle.aggregate([sb1, sb2])
await send_spendbundle(mempool_manager, sb12)
sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
assert_sb_in_pool(mempool_manager, sb12)
assert_sb_in_pool(mempool_manager, sb3)
# sb1234 should not be in pool as it decreases total fees per cost
sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE)
sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
await send_spendbundle(
mempool_manager, sb1234, expected_result=(MempoolInclusionStatus.PENDING, Err.MEMPOOL_CONFLICT)
)
assert_sb_not_in_pool(mempool_manager, sb1234)
@pytest.mark.asyncio
async def test_sufficient_total_fpc_increase() -> None:
mempool_manager, coins = await setup_mempool_with_coins(coin_amounts=list(range(1000000000, 1000000010)))
sb1 = make_test_spendbundle(coins[0])
sb2 = make_test_spendbundle(coins[1], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
sb12 = SpendBundle.aggregate([sb1, sb2])
await send_spendbundle(mempool_manager, sb12)
sb3 = await make_and_send_spendbundle(mempool_manager, coins[2], fee=MEMPOOL_MIN_FEE_INCREASE * 2)
assert_sb_in_pool(mempool_manager, sb12)
assert_sb_in_pool(mempool_manager, sb3)
# sb1234 has a higher fee per cost than its conflicts and should get
# into the mempool
sb4 = make_test_spendbundle(coins[3], fee=MEMPOOL_MIN_FEE_INCREASE * 3)
sb1234 = SpendBundle.aggregate([sb12, sb3, sb4])
await send_spendbundle(mempool_manager, sb1234)
assert_sb_in_pool(mempool_manager, sb1234)
assert_sb_not_in_pool(mempool_manager, sb12)
assert_sb_not_in_pool(mempool_manager, sb3)