Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHIA-1703] ignore ephemeral spends in mempool superset rule #18834

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions chia/_tests/core/mempool/test_mempool_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,16 @@ def make_test_coins() -> list[Coin]:
return ret


def make_ephemeral(coins: list[Coin]) -> list[Coin]:
ret: list[Coin] = []
for i, parent in enumerate(coins):
ret.append(Coin(parent.name(), height_hash(i + 150), uint64(i * 100)))
return ret


coins = make_test_coins()
eph = make_ephemeral(coins)
eph2 = make_ephemeral(eph)


@pytest.mark.parametrize(
Expand Down Expand Up @@ -800,6 +809,26 @@ def make_test_coins() -> list[Coin]:
([mk_item(coins[0:2])], mk_item(coins[0:2], fee=10000000), True),
# or if we spend the same coins with additional coins
([mk_item(coins[0:2])], mk_item(coins[0:3], fee=10000000), True),
# SUPERSET RULE WITH EPHEMERAL COINS
# the super set rule only takes non-ephemeral coins into account. The
# ephmeral coins depend on how we spend, and might prevent legitimate
# replace-by-fee attempts.
# replace a spend that includes an ephemeral coin with one that doesn't
([mk_item(coins[0:2] + eph[0:1])], mk_item(coins[0:2], fee=10000000), True),
# replace a spend with two-levels of ephemeral coins, with one that
# only has 1-level
([mk_item(coins[0:2] + eph[0:1] + eph2[0:1])], mk_item(coins[0:2] + eph[0:1], fee=10000000), True),
# replace a spend with two-levels of ephemeral coins, with one that
# doesn't
([mk_item(coins[0:2] + eph[0:1] + eph2[0:1])], mk_item(coins[0:2], fee=10000000), True),
# replace a spend with two-levels of ephemeral coins, with one that
# has *different* ephemeral coins
([mk_item(coins[0:2] + eph[0:1] + eph2[0:1])], mk_item(coins[0:2] + eph[1:2] + eph2[1:2], fee=10000000), True),
# it's OK to add new ephemeral spends
([mk_item(coins[0:2])], mk_item(coins[0:2] + eph[1:2] + eph2[1:2], fee=10000000), True),
# eph2[0:1] is not an ephemeral coin here, this violates the superset
# rule. eph[0:1] is missing for that
([mk_item(coins[0:2] + eph2[0:1])], mk_item(coins[0:2] + eph[1:2] + eph2[1:2], fee=10000000), False),
# FEE- AND FEE RATE RULES
# if we're replacing two items, each paying a fee of 100, we need to
# spend (at least) the same coins and pay at least 10000000 higher fee
Expand Down
11 changes: 8 additions & 3 deletions chia/full_node/mempool_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ async def validate_spend_bundle(
)

if removal_names != removal_names_from_coin_spends:
# If you reach here it's probably because your program reveal doesn't match the coin's puzzle hash
# If you reach here it's probably because your puzzle reveal doesn't match the coin's puzzle hash
return Err.INVALID_SPEND_BUNDLE, None, []

removal_record_dict: dict[bytes32, CoinRecord] = {}
Expand Down Expand Up @@ -806,8 +806,13 @@ def can_replace(
# bundle with AB with a higher fee. An attacker then replaces the bundle with just B with a higher
# fee than AB therefore kicking out A altogether. The better way to solve this would be to keep a cache
# of booted transactions like A, and retry them after they get removed from mempool due to a conflict.
for coin in item.removals:
if coin.name() not in removal_names:
conflicting_removals = {c.name(): c for c in item.removals}
for coin in conflicting_removals.values():
coin_name = coin.name()
# if the parent of this coin is one of the spends in this
# transaction, it means it's an ephemeral coin spend. Such spends
# are not considered by the superset rule
if coin_name not in removal_names and coin.parent_coin_info not in conflicting_removals:
log.debug(f"Rejecting conflicting tx as it does not spend conflicting coin {coin.name()}")
return False

Expand Down
Loading