Merge commit '507899ff19b0d0bbe14bc7ed8a192f5ad0bc7aa2' into checkpoint/main_from_release_2.1.4_507899ff19b0d0bbe14bc7ed8a192f5ad0bc7aa2

This commit is contained in:
Amine Khaldi 2023-12-23 00:24:41 +01:00
commit c431c6675c
No known key found for this signature in database
GPG Key ID: B1C074FFC904E2D9
9 changed files with 148 additions and 65 deletions

View File

@ -3,7 +3,7 @@ from __future__ import annotations
import asyncio
from dataclasses import dataclass
from time import monotonic
from typing import Dict, Optional
from typing import Collection, Dict, List, Optional
from chia_rs import G2Element
from clvm.casts import int_to_bytes
@ -81,8 +81,13 @@ def fake_block_record(block_height: uint32, timestamp: uint64) -> BenchBlockReco
async def run_mempool_benchmark() -> None:
coin_records: Dict[bytes32, CoinRecord] = {}
async def get_coin_record(coin_id: bytes32) -> Optional[CoinRecord]:
return coin_records.get(coin_id)
async def get_coin_record(coin_ids: Collection[bytes32]) -> List[CoinRecord]:
ret: List[CoinRecord] = []
for name in coin_ids:
r = coin_records.get(name)
if r is not None:
ret.append(r)
return ret
timestamp = uint64(1631794488)

View File

@ -6,7 +6,7 @@ from contextlib import contextmanager
from dataclasses import dataclass
from subprocess import check_call
from time import monotonic
from typing import Dict, Iterator, List, Optional, Tuple
from typing import Collection, Dict, Iterator, List, Optional, Tuple
from chia.consensus.coinbase import create_farmer_coin, create_pool_coin
from chia.consensus.default_constants import DEFAULT_CONSTANTS
@ -78,8 +78,13 @@ def fake_block_record(block_height: uint32, timestamp: uint64) -> BenchBlockReco
async def run_mempool_benchmark() -> None:
all_coins: Dict[bytes32, CoinRecord] = {}
async def get_coin_record(coin_id: bytes32) -> Optional[CoinRecord]:
return all_coins.get(coin_id)
async def get_coin_records(coin_ids: Collection[bytes32]) -> List[CoinRecord]:
ret: List[CoinRecord] = []
for name in coin_ids:
r = all_coins.get(name)
if r is not None:
ret.append(r)
return ret
wt = WalletTool(DEFAULT_CONSTANTS)
@ -156,7 +161,7 @@ async def run_mempool_benchmark() -> None:
else:
print("\n== Multi-threaded")
mempool = MempoolManager(get_coin_record, DEFAULT_CONSTANTS, single_threaded=single_threaded)
mempool = MempoolManager(get_coin_records, DEFAULT_CONSTANTS, single_threaded=single_threaded)
height = start_height
rec = fake_block_record(height, timestamp)
@ -186,7 +191,7 @@ async def run_mempool_benchmark() -> None:
print(f" time: {stop - start:0.4f}s")
print(f" per call: {(stop - start) / total_bundles * 1000:0.2f}ms")
mempool = MempoolManager(get_coin_record, DEFAULT_CONSTANTS, single_threaded=single_threaded)
mempool = MempoolManager(get_coin_records, DEFAULT_CONSTANTS, single_threaded=single_threaded)
height = start_height
rec = fake_block_record(height, timestamp)

View File

@ -156,7 +156,7 @@ class SpendSim:
async with DBWrapper2.managed(database=uri, uri=True, reader_count=1, db_version=2) as self.db_wrapper:
self.coin_store = await CoinStore.create(self.db_wrapper)
self.mempool_manager = MempoolManager(self.coin_store.get_coin_record, defaults)
self.mempool_manager = MempoolManager(self.coin_store.get_coin_records, defaults)
self.defaults = defaults
# Load the next data if there is any

View File

@ -146,7 +146,7 @@ class CoinStore:
return CoinRecord(coin, row[0], row[1], row[2], row[6])
return None
async def get_coin_records(self, names: List[bytes32]) -> List[CoinRecord]:
async def get_coin_records(self, names: Collection[bytes32]) -> List[CoinRecord]:
if len(names) == 0:
return []

View File

@ -276,7 +276,7 @@ class FullNode:
)
self._mempool_manager = MempoolManager(
get_coin_record=self.coin_store.get_coin_record,
get_coin_records=self.coin_store.get_coin_records,
consensus_constants=self.constants,
multiprocessing_context=self.multiprocessing_context,
single_threaded=single_threaded,

View File

@ -7,7 +7,7 @@ from concurrent.futures import Executor
from concurrent.futures.process import ProcessPoolExecutor
from dataclasses import dataclass
from multiprocessing.context import BaseContext
from typing import Awaitable, Callable, Dict, List, Optional, Set, Tuple, TypeVar
from typing import Awaitable, Callable, Collection, Dict, List, Optional, Set, Tuple, TypeVar
from chia_rs import ELIGIBLE_FOR_DEDUP, GTElement
from chiabip158 import PyBIP158
@ -152,7 +152,7 @@ class MempoolManager:
pool: Executor
constants: ConsensusConstants
seen_bundle_hashes: Dict[bytes32, bytes32]
get_coin_record: Callable[[bytes32], Awaitable[Optional[CoinRecord]]]
get_coin_records: Callable[[Collection[bytes32]], Awaitable[List[CoinRecord]]]
nonzero_fee_minimum_fpc: int
mempool_max_total_cost: int
# a cache of MempoolItems that conflict with existing items in the pool
@ -165,7 +165,7 @@ class MempoolManager:
def __init__(
self,
get_coin_record: Callable[[bytes32], Awaitable[Optional[CoinRecord]]],
get_coin_records: Callable[[Collection[bytes32]], Awaitable[List[CoinRecord]]],
consensus_constants: ConsensusConstants,
multiprocessing_context: Optional[BaseContext] = None,
*,
@ -176,7 +176,7 @@ class MempoolManager:
# Keep track of seen spend_bundles
self.seen_bundle_hashes: Dict[bytes32, bytes32] = {}
self.get_coin_record = get_coin_record
self.get_coin_records = get_coin_records
# The fee per cost must be above this amount to consider the fee "nonzero", and thus able to kick out other
# transactions. This prevents spam. This is equivalent to 0.055 XCH per block, or about 0.00005 XCH for two
@ -310,7 +310,12 @@ class MempoolManager:
return ret
async def add_spend_bundle(
self, new_spend: SpendBundle, npc_result: NPCResult, spend_name: bytes32, first_added_height: uint32
self,
new_spend: SpendBundle,
npc_result: NPCResult,
spend_name: bytes32,
first_added_height: uint32,
get_coin_records: Optional[Callable[[Collection[bytes32]], Awaitable[List[CoinRecord]]]] = None,
) -> Tuple[Optional[uint64], MempoolInclusionStatus, Optional[Err]]:
"""
Validates and adds to mempool a new_spend with the given NPCResult, and spend_name, and the current mempool.
@ -334,8 +339,14 @@ class MempoolManager:
if existing_item is not None:
return existing_item.cost, MempoolInclusionStatus.SUCCESS, None
if get_coin_records is None:
get_coin_records = self.get_coin_records
err, item, remove_items = await self.validate_spend_bundle(
new_spend, npc_result, spend_name, first_added_height
new_spend,
npc_result,
spend_name,
first_added_height,
get_coin_records,
)
if err is None:
# No error, immediately add to mempool, after removing conflicting TXs.
@ -365,6 +376,7 @@ class MempoolManager:
npc_result: NPCResult,
spend_name: bytes32,
first_added_height: uint32,
get_coin_records: Callable[[Collection[bytes32]], Awaitable[List[CoinRecord]]],
) -> Tuple[Optional[Err], Optional[MempoolItem], List[bytes32]]:
"""
Validates new_spend with the given NPCResult, and spend_name, and the current mempool. The mempool should
@ -427,11 +439,14 @@ class MempoolManager:
removal_record_dict: Dict[bytes32, CoinRecord] = {}
removal_amount: int = 0
removal_records = await get_coin_records(removal_names)
for record in removal_records:
removal_record_dict[record.coin.name()] = record
for name in removal_names:
removal_record = await self.get_coin_record(name)
if removal_record is None and name not in additions_dict:
if name not in removal_record_dict and name not in additions_dict:
return Err.UNKNOWN_UNSPENT, None, []
elif name in additions_dict:
if name in additions_dict:
removal_coin = additions_dict[name]
# The timestamp and block-height of this coin being spent needs
# to be consistent with what we use to check time-lock
@ -447,10 +462,10 @@ class MempoolManager:
False,
self.peak.timestamp,
)
assert removal_record is not None
removal_record_dict[name] = removal_record
else:
removal_record = removal_record_dict[name]
removal_amount = removal_amount + removal_record.coin.amount
removal_record_dict[name] = removal_record
fees = uint64(removal_amount - addition_amount)
@ -641,9 +656,35 @@ class MempoolManager:
old_pool = self.mempool
self.mempool = Mempool(old_pool.mempool_info, old_pool.fee_estimator)
self.seen_bundle_hashes = {}
# in order to make this a bit quicker, we look-up all the spends in
# a single query, rather than one at a time.
coin_records: Dict[bytes32, CoinRecord] = {}
removals: Set[bytes32] = set()
for item in old_pool.all_items():
for s in item.spend_bundle.coin_spends:
removals.add(s.coin.name())
for record in await self.get_coin_records(removals):
name = record.coin.name()
coin_records[name] = record
async def local_get_coin_records(names: Collection[bytes32]) -> List[CoinRecord]:
ret: List[CoinRecord] = []
for name in names:
r = coin_records.get(name)
if r is not None:
ret.append(r)
return ret
for item in old_pool.all_items():
_, result, err = await self.add_spend_bundle(
item.spend_bundle, item.npc_result, item.spend_bundle_name, item.height_added_to_mempool
item.spend_bundle,
item.npc_result,
item.spend_bundle_name,
item.height_added_to_mempool,
local_get_coin_records,
)
# Only add to `seen` if inclusion worked, so it can be resubmitted in case of a reorg
if result == MempoolInclusionStatus.SUCCESS:
@ -660,7 +701,11 @@ class MempoolManager:
txs_added = []
for item in potential_txs.values():
cost, status, error = await self.add_spend_bundle(
item.spend_bundle, item.npc_result, item.spend_bundle_name, item.height_added_to_mempool
item.spend_bundle,
item.npc_result,
item.spend_bundle_name,
item.height_added_to_mempool,
self.get_coin_records,
)
if status == MempoolInclusionStatus.SUCCESS:
txs_added.append((item.spend_bundle, item.npc_result, item.spend_bundle_name))

View File

@ -63,7 +63,7 @@ async def test_basics() -> None:
async def test_fee_increase() -> None:
async with DBConnection(db_version=2) as db_wrapper:
coin_store = await CoinStore.create(db_wrapper)
mempool_manager = MempoolManager(coin_store.get_coin_record, test_constants)
mempool_manager = MempoolManager(coin_store.get_coin_records, test_constants)
assert test_constants.MAX_BLOCK_COST_CLVM == mempool_manager.constants.MAX_BLOCK_COST_CLVM
btc_fee_estimator: BitcoinFeeEstimator = mempool_manager.mempool.fee_estimator # type: ignore
fee_tracker = btc_fee_estimator.get_tracker()

View File

@ -1,7 +1,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Awaitable, Callable, Dict, List, Optional, Set, Tuple
from typing import Any, Awaitable, Callable, Collection, Dict, List, Optional, Set, Tuple
import pytest
from chia_rs import ELIGIBLE_FOR_DEDUP, G1Element, G2Element
@ -85,17 +85,24 @@ class TestBlockRecord:
return self.timestamp is not None
async def zero_calls_get_coin_record(_: bytes32) -> Optional[CoinRecord]:
assert False
async def zero_calls_get_coin_records(coin_ids: Collection[bytes32]) -> List[CoinRecord]:
assert len(coin_ids) == 0
return []
async def get_coin_record_for_test_coins(coin_id: bytes32) -> Optional[CoinRecord]:
async def get_coin_records_for_test_coins(coin_ids: Collection[bytes32]) -> List[CoinRecord]:
test_coin_records = {
TEST_COIN_ID: TEST_COIN_RECORD,
TEST_COIN_ID2: TEST_COIN_RECORD2,
TEST_COIN_ID3: TEST_COIN_RECORD3,
}
return test_coin_records.get(coin_id)
ret: List[CoinRecord] = []
for name in coin_ids:
r = test_coin_records.get(name)
if r is not None:
ret.append(r)
return ret
def height_hash(height: int) -> bytes32:
@ -113,13 +120,13 @@ def create_test_block_record(*, height: uint32 = TEST_HEIGHT, timestamp: uint64
async def instantiate_mempool_manager(
get_coin_record: Callable[[bytes32], Awaitable[Optional[CoinRecord]]],
get_coin_records: Callable[[Collection[bytes32]], Awaitable[List[CoinRecord]]],
*,
block_height: uint32 = TEST_HEIGHT,
block_timestamp: uint64 = TEST_TIMESTAMP,
constants: ConsensusConstants = DEFAULT_CONSTANTS,
) -> MempoolManager:
mempool_manager = MempoolManager(get_coin_record, constants)
mempool_manager = MempoolManager(get_coin_records, constants)
test_block_record = create_test_block_record(height=block_height, timestamp=block_timestamp)
await mempool_manager.new_peak(test_block_record, None)
invariant_check_mempool(mempool_manager.mempool)
@ -134,10 +141,15 @@ async def setup_mempool_with_coins(*, coin_amounts: List[int]) -> Tuple[MempoolM
coins.append(coin)
test_coin_records[coin.name()] = CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))
async def get_coin_record(coin_id: bytes32) -> Optional[CoinRecord]:
return test_coin_records.get(coin_id)
async def get_coin_records(coin_ids: Collection[bytes32]) -> List[CoinRecord]:
ret: List[CoinRecord] = []
for name in coin_ids:
r = test_coin_records.get(name)
if r is not None:
ret.append(r)
return ret
mempool_manager = await instantiate_mempool_manager(get_coin_record)
mempool_manager = await instantiate_mempool_manager(get_coin_records)
return (mempool_manager, coins)
@ -408,7 +420,7 @@ def mempool_item_from_spendbundle(spend_bundle: SpendBundle) -> MempoolItem:
@pytest.mark.anyio
async def test_empty_spend_bundle() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
sb = SpendBundle([], G2Element())
with pytest.raises(ValidationError, match="INVALID_SPEND_BUNDLE"):
await mempool_manager.pre_validate_spendbundle(sb, None, sb.name())
@ -416,7 +428,7 @@ async def test_empty_spend_bundle() -> None:
@pytest.mark.anyio
async def test_negative_addition_amount() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, -1]]
sb = spend_bundle_from_conditions(conditions)
with pytest.raises(ValidationError, match="COIN_AMOUNT_NEGATIVE"):
@ -425,7 +437,7 @@ async def test_negative_addition_amount() -> None:
@pytest.mark.anyio
async def test_valid_addition_amount() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount]]
coin = Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, max_amount)
@ -436,7 +448,7 @@ async def test_valid_addition_amount() -> None:
@pytest.mark.anyio
async def test_too_big_addition_amount() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
max_amount = mempool_manager.constants.MAX_COIN_AMOUNT
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, max_amount + 1]]
sb = spend_bundle_from_conditions(conditions)
@ -446,7 +458,7 @@ async def test_too_big_addition_amount() -> None:
@pytest.mark.anyio
async def test_duplicate_output() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
conditions = [
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
@ -458,7 +470,7 @@ async def test_duplicate_output() -> None:
@pytest.mark.anyio
async def test_block_cost_exceeds_max() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
conditions = []
for i in range(2400):
conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i])
@ -469,7 +481,7 @@ async def test_block_cost_exceeds_max() -> None:
@pytest.mark.anyio
async def test_double_spend_prevalidation() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
sb = spend_bundle_from_conditions(conditions)
sb_twice: SpendBundle = SpendBundle.aggregate([sb, sb])
@ -479,7 +491,7 @@ async def test_double_spend_prevalidation() -> None:
@pytest.mark.anyio
async def test_minting_coin() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT]]
sb = spend_bundle_from_conditions(conditions)
npc_result = await mempool_manager.pre_validate_spendbundle(sb, None, sb.name())
@ -492,7 +504,7 @@ async def test_minting_coin() -> None:
@pytest.mark.anyio
async def test_reserve_fee_condition() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
conditions = [[ConditionOpcode.RESERVE_FEE, TEST_COIN_AMOUNT]]
sb = spend_bundle_from_conditions(conditions)
npc_result = await mempool_manager.pre_validate_spendbundle(sb, None, sb.name())
@ -505,10 +517,10 @@ async def test_reserve_fee_condition() -> None:
@pytest.mark.anyio
async def test_unknown_unspent() -> None:
async def get_coin_record(_: bytes32) -> Optional[CoinRecord]:
return None
async def get_coin_records(_: Collection[bytes32]) -> List[CoinRecord]:
return []
mempool_manager = await instantiate_mempool_manager(get_coin_record)
mempool_manager = await instantiate_mempool_manager(get_coin_records)
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
_, _, result = await generate_and_add_spendbundle(mempool_manager, conditions)
assert result == (None, MempoolInclusionStatus.FAILED, Err.UNKNOWN_UNSPENT)
@ -516,7 +528,7 @@ async def test_unknown_unspent() -> None:
@pytest.mark.anyio
async def test_same_sb_twice_with_eligible_coin() -> None:
mempool_manager = await instantiate_mempool_manager(get_coin_record_for_test_coins)
mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
sb1_conditions = [
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
@ -540,7 +552,7 @@ async def test_same_sb_twice_with_eligible_coin() -> None:
@pytest.mark.anyio
async def test_sb_twice_with_eligible_coin_and_different_spends_order() -> None:
mempool_manager = await instantiate_mempool_manager(get_coin_record_for_test_coins)
mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
sb1_conditions = [
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1],
[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2],
@ -637,7 +649,7 @@ async def test_ephemeral_timelock(
expected_error: Optional[Err],
) -> None:
mempool_manager = await instantiate_mempool_manager(
get_coin_record=get_coin_record_for_test_coins,
get_coin_records=get_coin_records_for_test_coins,
block_height=uint32(5),
block_timestamp=uint64(10050),
constants=DEFAULT_CONSTANTS,
@ -650,7 +662,7 @@ async def test_ephemeral_timelock(
# sb spends TEST_COIN and creates created_coin which gets spent too
sb = SpendBundle.aggregate([sb1, sb2])
# We shouldn't have a record of this ephemeral coin
assert await get_coin_record_for_test_coins(created_coin.name()) is None
assert await get_coin_records_for_test_coins([created_coin.name()]) == []
try:
_, status, error = await add_spendbundle(mempool_manager, sb, sb.name())
assert (status, error) == (expected_status, expected_error)
@ -848,7 +860,7 @@ def test_can_replace(existing_items: List[MempoolItem], new_item: MempoolItem, e
@pytest.mark.anyio
async def test_get_items_not_in_filter() -> None:
mempool_manager = await instantiate_mempool_manager(get_coin_record_for_test_coins)
mempool_manager = await instantiate_mempool_manager(get_coin_records_for_test_coins)
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
sb1, sb1_name, _ = await generate_and_add_spendbundle(mempool_manager, conditions)
conditions2 = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 2]]
@ -895,10 +907,15 @@ async def test_get_items_not_in_filter() -> None:
async def test_total_mempool_fees() -> None:
coin_records: Dict[bytes32, CoinRecord] = {}
async def get_coin_record(coin_id: bytes32) -> Optional[CoinRecord]:
return coin_records.get(coin_id)
async def get_coin_records(coin_ids: Collection[bytes32]) -> List[CoinRecord]:
ret: List[CoinRecord] = []
for name in coin_ids:
r = coin_records.get(name)
if r is not None:
ret.append(r)
return ret
mempool_manager = await instantiate_mempool_manager(get_coin_record)
mempool_manager = await instantiate_mempool_manager(get_coin_records)
conditions = [[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 1]]
# the limit of total fees in the mempool is 2^63
@ -1013,11 +1030,17 @@ async def test_create_bundle_from_mempool_on_max_cost() -> None:
async def test_assert_before_expiration(
opcode: ConditionOpcode, arg: int, expect_eviction: bool, expect_limit: Optional[int]
) -> None:
async def get_coin_record(coin_id: bytes32) -> Optional[CoinRecord]:
return {TEST_COIN.name(): CoinRecord(TEST_COIN, uint32(5), uint32(0), False, uint64(9900))}.get(coin_id)
async def get_coin_records(coin_ids: Collection[bytes32]) -> List[CoinRecord]:
all_coins = {TEST_COIN.name(): CoinRecord(TEST_COIN, uint32(5), uint32(0), False, uint64(9900))}
ret: List[CoinRecord] = []
for name in coin_ids:
r = all_coins.get(name)
if r is not None:
ret.append(r)
return ret
mempool_manager = await instantiate_mempool_manager(
get_coin_record,
get_coin_records,
block_height=uint32(10),
block_timestamp=uint64(10000),
constants=DEFAULT_CONSTANTS,
@ -1372,10 +1395,15 @@ async def test_coin_spending_different_ways_then_finding_it_spent_in_new_peak(ne
coin_id = coin.name()
test_coin_records = {coin_id: CoinRecord(coin, uint32(0), uint32(0), False, uint64(0))}
async def get_coin_record(coin_id: bytes32) -> Optional[CoinRecord]:
return test_coin_records.get(coin_id)
async def get_coin_records(coin_ids: Collection[bytes32]) -> List[CoinRecord]:
ret: List[CoinRecord] = []
for name in coin_ids:
r = test_coin_records.get(name)
if r is not None:
ret.append(r)
return ret
mempool_manager = await instantiate_mempool_manager(get_coin_record)
mempool_manager = await instantiate_mempool_manager(get_coin_records)
# Create a bunch of mempool items that spend the coin in different ways
for i in range(3):
_, _, result = await generate_and_add_spendbundle(

View File

@ -29,7 +29,7 @@ from chia.util.ints import uint32, uint64
from tests.core.mempool.test_mempool_manager import (
create_test_block_record,
instantiate_mempool_manager,
zero_calls_get_coin_record,
zero_calls_get_coin_records,
)
@ -217,7 +217,7 @@ def test_current_block_height_new_block_then_new_height() -> None:
@pytest.mark.anyio
async def test_mm_new_peak_changes_fee_estimator_block_height() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
block2 = create_test_block_record(height=uint32(2))
await mempool_manager.new_peak(block2, None)
assert mempool_manager.mempool.fee_estimator.block_height == uint32(2) # type: ignore[attr-defined]
@ -225,7 +225,7 @@ async def test_mm_new_peak_changes_fee_estimator_block_height() -> None:
@pytest.mark.anyio
async def test_mm_calls_new_block_height() -> None:
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_record)
mempool_manager = await instantiate_mempool_manager(zero_calls_get_coin_records)
new_block_height_called = False
def test_new_block_height_called(self: FeeEstimatorInterface, height: uint32) -> None: