Repository: Chia-Network/chia-blockchain
File: chia/full_node/mempool_manager.py (Python)

Code before:
        # Map of coin ID to SpendConditions
        spend_conditions = {bytes32(spend.coin_id): spend for spend in conds.spends}

        if len(new_spend.coin_spends) != len(spend_conditions):
            # if this happens, the SpendBundle doesn't match the
            # SpendBundleConditions.
            return Err.INVALID_SPEND_BUNDLE, None, []

        bundle_coin_spends: dict[bytes32, BundleCoinSpend] = {}
        for coin_spend in new_spend.coin_spends:
            coin_id = coin_spend.coin.name()
            removal_names.add(coin_id)
            spend_conds = spend_conditions.pop(coin_id, None)
            if spend_conds is None:
                # if this happens, the SpendBundle doesn't match the
                # SpendBundleConditions.
                return Err.INVALID_SPEND_BUNDLE, None, []

            if bool(spend_conds.flags & ELIGIBLE_FOR_DEDUP) and not is_clvm_canonical(bytes(coin_spend.solution)):
                return Err.INVALID_COIN_SOLUTION, None, []

            lineage_info = None
            mark_as_fast_forward = bool(spend_conds.flags & ELIGIBLE_FOR_FF) and supports_fast_forward(coin_spend)
            if mark_as_fast_forward:
                # Make sure the fast forward spend still has a version that is
                # still unspent, because if the singleton has been melted, the
                # fast forward spend will never become valid.
                lineage_info = await get_unspent_lineage_info_for_puzzle_hash(bytes32(spend_conds.puzzle_hash))
                if lineage_info is None:
                    return Err.DOUBLE_SPEND, None, []

            spend_additions = []
            for puzzle_hash, amount, _ in spend_conds.create_coin:
                child_coin = Coin(coin_id, puzzle_hash, uint64(amount))
                spend_additions.append(child_coin)
                additions_dict[child_coin.name()] = child_coin
                addition_amount += amount

            bundle_coin_spends[coin_id] = BundleCoinSpend(
                coin_spend=coin_spend,
                eligible_for_dedup=bool(spend_conds.flags & ELIGIBLE_FOR_DEDUP),
                eligible_for_fast_forward=mark_as_fast_forward,
                additions=spend_additions,
                latest_singleton_lineage=lineage_info,
            )

        # fast forward spends are only allowed when bundled with other, non-FF, spends
        # in order to evict an FF spend, it must be associated with a normal
        # spend that can be included in a block or invalidated some other way
        if all([s.eligible_for_fast_forward for s in bundle_coin_spends.values()]):
            return Err.INVALID_SPEND_BUNDLE, None, []

Review comment (bug):
Same thing here, the default value suggests that `eligibility_and_additions` may not have the coin ID in it. Which I think would be some internal error or an invalid `SpendBundle`. The new version fails with `Err.INVALID_SPEND_BUNDLE` if this happens

Code after:
        # Map of coin ID to SpendConditions
        spend_conditions = {bytes32(spend.coin_id): spend for spend in conds.spends}

        # if this happens, the SpendBundle doesn't match the
        # SpendBundleConditions.
        assert len(new_spend.coin_spends) == len(spend_conditions)

        bundle_coin_spends: dict[bytes32, BundleCoinSpend] = {}
        for coin_spend in new_spend.coin_spends:
            coin_id = coin_spend.coin.name()
            removal_names.add(coin_id)

            # if this coin_id isn't found, the SpendBundle doesn't match the
            # SpendBundleConditions.
            spend_conds = spend_conditions.pop(coin_id)

            if bool(spend_conds.flags & ELIGIBLE_FOR_DEDUP) and not is_clvm_canonical(bytes(coin_spend.solution)):
                return Err.INVALID_COIN_SOLUTION, None, []

            lineage_info = None
            eligible_for_ff = bool(spend_conds.flags & ELIGIBLE_FOR_FF) and supports_fast_forward(coin_spend)
            if eligible_for_ff:
                # Make sure the fast forward spend still has a version that is
                # still unspent, because if the singleton has been melted, the
                # fast forward spend will never become valid.
                lineage_info = await get_unspent_lineage_info_for_puzzle_hash(bytes32(spend_conds.puzzle_hash))
                if lineage_info is None:
                    return Err.DOUBLE_SPEND, None, []

            spend_additions = []
            for puzzle_hash, amount, _ in spend_conds.create_coin:
                child_coin = Coin(coin_id, puzzle_hash, uint64(amount))
                spend_additions.append(child_coin)
                additions_dict[child_coin.name()] = child_coin
                addition_amount += amount

            bundle_coin_spends[coin_id] = BundleCoinSpend(
                coin_spend=coin_spend,
                eligible_for_dedup=bool(spend_conds.flags & ELIGIBLE_FOR_DEDUP),
                eligible_for_fast_forward=eligible_for_ff,
                additions=spend_additions,
                latest_singleton_lineage=lineage_info,
            )

        # fast forward spends are only allowed when bundled with other, non-FF, spends
        # in order to evict an FF spend, it must be associated with a normal
        # spend that can be included in a block or invalidated some other way
        if all([s.eligible_for_fast_forward for s in bundle_coin_spends.values()]):
            return Err.INVALID_SPEND_BUNDLE, None, []

        removal_record_dict: dict[bytes32, CoinRecord] = {}