chia-blockchain 2.5.3rc1__py3-none-any.whl → 2.5.4rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2959,7 +2959,7 @@ async def test_skip_error_items() -> None:
2959
2959
 
2960
2960
  result = await mempool.create_block_generator(local_get_unspent_lineage_info, DEFAULT_CONSTANTS, uint32(10))
2961
2961
  assert result is not None
2962
- generator, _, _ = result
2962
+ generator, _, _, _ = result
2963
2963
 
2964
2964
  assert called == 3
2965
2965
  assert generator.program == SerializedProgram.from_bytes(bytes.fromhex("ff01ff8080"))
@@ -3249,7 +3249,7 @@ async def test_create_block_generator() -> None:
3249
3249
 
3250
3250
  block = await mempool.create_block_generator(get_unspent_lineage_info_for_puzzle_hash, test_constants, uint32(0))
3251
3251
  assert block is not None
3252
- generator, signature, additions = block
3252
+ generator, signature, additions, _ = block
3253
3253
 
3254
3254
  assert set(additions) == expected_additions
3255
3255
 
@@ -19,7 +19,7 @@ from chia_rs.sized_ints import uint8, uint32, uint64
19
19
  from chiabip158 import PyBIP158
20
20
 
21
21
  from chia._tests.conftest import ConsensusMode
22
- from chia._tests.util.misc import invariant_check_mempool
22
+ from chia._tests.util.misc import Marks, datacases, invariant_check_mempool
23
23
  from chia._tests.util.setup_nodes import OldSimulatorsAndWallets, setup_simulators_and_wallets
24
24
  from chia.consensus.condition_costs import ConditionCost
25
25
  from chia.consensus.default_constants import DEFAULT_CONSTANTS
@@ -32,7 +32,10 @@ from chia.full_node.mempool_manager import (
32
32
  MempoolManager,
33
33
  TimelockConditions,
34
34
  can_replace,
35
+ check_removals,
35
36
  compute_assert_height,
37
+ is_atom_canonical,
38
+ is_clvm_canonical,
36
39
  optional_max,
37
40
  optional_min,
38
41
  )
@@ -87,6 +90,74 @@ TEST_COIN_RECORD3 = CoinRecord(TEST_COIN3, uint32(0), uint32(0), False, TEST_TIM
87
90
  TEST_HEIGHT = uint32(5)
88
91
 
89
92
 
93
+ @pytest.mark.parametrize("clvm_hex", ["80", "ff8080", "ff7f03", "ffff8080ff8080"])
94
+ def test_clvm_canonical(clvm_hex: str) -> None:
95
+ clvm_buf = bytes.fromhex(clvm_hex)
96
+ assert is_clvm_canonical(clvm_buf)
97
+
98
+
99
+ @pytest.mark.parametrize(
100
+ "clvm_hex",
101
+ [
102
+ "fffe80",
103
+ "c000",
104
+ "c03f",
105
+ "e00000",
106
+ "e01fff",
107
+ "f0000000",
108
+ "f00fffff",
109
+ "f800000000",
110
+ "f807ffffff",
111
+ "fc0000000000",
112
+ "fc03ffffffff",
113
+ "fe",
114
+ "ff808080",
115
+ ],
116
+ )
117
+ def test_clvm_not_canonical(clvm_hex: str) -> None:
118
+ clvm_buf = bytes.fromhex(clvm_hex)
119
+ assert not is_clvm_canonical(clvm_buf)
120
+
121
+
122
+ @pytest.mark.parametrize(
123
+ "clvm_hex, expect",
124
+ [
125
+ ("c000", 2 + 0),
126
+ ("c03f", 2 + 0x3F),
127
+ ("e00000", 3 + 0),
128
+ ("e01fff", 3 + 0x1FFF),
129
+ ("f0000000", 4 + 0),
130
+ ("f00fffff", 4 + 0xFFFFF),
131
+ ("f800000000", 5 + 0),
132
+ ("f807ffffff", 5 + 0x7FFFFFF),
133
+ ("fc0000000000", 6 + 0),
134
+ ("fc03ffffffff", 6 + 0x3FFFFFFFF),
135
+ ],
136
+ )
137
+ def test_atom_not_canonical(clvm_hex: str, expect: int) -> None:
138
+ clvm_buf = bytes.fromhex(clvm_hex)
139
+ atom_len, is_canonical = is_atom_canonical(clvm_buf, 0)
140
+ assert atom_len == expect
141
+ assert not is_canonical
142
+
143
+
144
+ @pytest.mark.parametrize(
145
+ "clvm_hex, expect",
146
+ [
147
+ ("c040", 2 + 0x40),
148
+ ("e02000", 3 + 0x2000),
149
+ ("f0100000", 4 + 0x100000),
150
+ ("f808000000", 5 + 0x8000000),
151
+ ("fc0400000000", 6 + 0x400000000),
152
+ ],
153
+ )
154
+ def test_atom_canonical(clvm_hex: str, expect: int) -> None:
155
+ clvm_buf = bytes.fromhex(clvm_hex)
156
+ atom_len, is_canonical = is_atom_canonical(clvm_buf, 0)
157
+ assert atom_len == expect
158
+ assert is_canonical
159
+
160
+
90
161
  @dataclasses.dataclass(frozen=True)
91
162
  class TestBlockRecord:
92
163
  """
@@ -195,6 +266,9 @@ async def setup_mempool_with_coins(
195
266
  return (mempool_manager, coins)
196
267
 
197
268
 
269
+ CreateCoin = tuple[bytes32, int, Optional[bytes]]
270
+
271
+
198
272
  def make_test_conds(
199
273
  *,
200
274
  birth_height: Optional[int] = None,
@@ -209,13 +283,18 @@ def make_test_conds(
209
283
  before_seconds_absolute: Optional[int] = None,
210
284
  cost: int = 0,
211
285
  spend_ids: Sequence[tuple[Union[bytes32, Coin], int]] = [(TEST_COIN_ID, 0)],
286
+ created_coins: Optional[list[list[CreateCoin]]] = None,
212
287
  ) -> SpendBundleConditions:
213
- spend_info: list[tuple[bytes32, bytes32, bytes32, uint64, int]] = []
214
- for coin, flags in spend_ids:
288
+ if created_coins is None:
289
+ created_coins = []
290
+ if len(created_coins) < len(spend_ids):
291
+ created_coins.extend([[] for _ in range(len(spend_ids) - len(created_coins))])
292
+ spend_info: list[tuple[bytes32, bytes32, bytes32, uint64, int, list[CreateCoin]]] = []
293
+ for (coin, flags), create_coin in zip(spend_ids, created_coins):
215
294
  if isinstance(coin, Coin):
216
- spend_info.append((coin.name(), coin.parent_coin_info, coin.puzzle_hash, coin.amount, flags))
295
+ spend_info.append((coin.name(), coin.parent_coin_info, coin.puzzle_hash, coin.amount, flags, create_coin))
217
296
  else:
218
- spend_info.append((coin, IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT, flags))
297
+ spend_info.append((coin, IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, TEST_COIN_AMOUNT, flags, create_coin))
219
298
 
220
299
  return SpendBundleConditions(
221
300
  [
@@ -230,7 +309,7 @@ def make_test_conds(
230
309
  None if before_seconds_relative is None else uint64(before_seconds_relative),
231
310
  None if birth_height is None else uint32(birth_height),
232
311
  None if birth_seconds is None else uint64(birth_seconds),
233
- [],
312
+ create_coin,
234
313
  [],
235
314
  [],
236
315
  [],
@@ -240,7 +319,7 @@ def make_test_conds(
240
319
  [],
241
320
  flags,
242
321
  )
243
- for coin_id, parent_id, puzzle_hash, amount, flags in spend_info
322
+ for coin_id, parent_id, puzzle_hash, amount, flags, create_coin in spend_info
244
323
  ],
245
324
  0,
246
325
  uint32(height_absolute),
@@ -751,6 +830,23 @@ def test_optional_max() -> None:
751
830
  assert optional_max(uint32(123), uint32(234)) == uint32(234)
752
831
 
753
832
 
833
+ def mk_coin_spend(coin: Coin, solution: Optional[str] = None) -> CoinSpend:
834
+ return make_spend(
835
+ coin,
836
+ SerializedProgram.to(None),
837
+ SerializedProgram.fromhex(solution if solution is not None else "80"),
838
+ )
839
+
840
+
841
+ def mk_bcs(coin_spend: CoinSpend, flags: int = 0) -> BundleCoinSpend:
842
+ return BundleCoinSpend(
843
+ coin_spend=coin_spend,
844
+ eligible_for_dedup=bool(flags & ELIGIBLE_FOR_DEDUP),
845
+ eligible_for_fast_forward=bool(flags & ELIGIBLE_FOR_FF),
846
+ additions=[],
847
+ )
848
+
849
+
754
850
  def mk_item(
755
851
  coins: list[Coin],
756
852
  *,
@@ -759,6 +855,7 @@ def mk_item(
759
855
  assert_height: Optional[int] = None,
760
856
  assert_before_height: Optional[int] = None,
761
857
  assert_before_seconds: Optional[int] = None,
858
+ solution: Optional[str] = None,
762
859
  flags: list[int] = [],
763
860
  ) -> MempoolItem:
764
861
  # we don't actually care about the puzzle and solutions for the purpose of
@@ -771,14 +868,10 @@ def mk_item(
771
868
  for c, f in zip(coins, flags):
772
869
  coin_id = c.name()
773
870
  spend_ids.append((coin_id, f))
774
- spend = make_spend(c, SerializedProgram.to(None), SerializedProgram.to(None))
775
- coin_spends.append(spend)
776
- bundle_coin_spends[coin_id] = BundleCoinSpend(
777
- coin_spend=spend,
778
- eligible_for_dedup=bool(f & ELIGIBLE_FOR_DEDUP),
779
- eligible_for_fast_forward=bool(f & ELIGIBLE_FOR_FF),
780
- additions=[],
781
- )
871
+ coin_spend = mk_coin_spend(c, solution=solution)
872
+ solution = None
873
+ coin_spends.append(coin_spend)
874
+ bundle_coin_spends[coin_id] = mk_bcs(coin_spend, f)
782
875
  spend_bundle = SpendBundle(coin_spends, G2Element())
783
876
  conds = make_test_conds(cost=cost, spend_ids=spend_ids)
784
877
  return MempoolItem(
@@ -1625,6 +1718,7 @@ async def test_coin_spending_different_ways_then_finding_it_spent_in_new_peak(ne
1625
1718
 
1626
1719
  mempool_manager = await instantiate_mempool_manager(get_coin_records)
1627
1720
  # Create a bunch of mempool items that spend the coin in different ways
1721
+ # only the first one will be accepted
1628
1722
  for i in range(3):
1629
1723
  _, _, result = await generate_and_add_spendbundle(
1630
1724
  mempool_manager,
@@ -1634,10 +1728,13 @@ async def test_coin_spending_different_ways_then_finding_it_spent_in_new_peak(ne
1634
1728
  ],
1635
1729
  coin,
1636
1730
  )
1637
- assert result[1] == MempoolInclusionStatus.SUCCESS
1638
- assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 3
1639
- assert mempool_manager.mempool.size() == 3
1640
- assert len(list(mempool_manager.mempool.items_by_feerate())) == 3
1731
+ if i == 0:
1732
+ assert result[1] == MempoolInclusionStatus.SUCCESS
1733
+ else:
1734
+ assert result[1] == MempoolInclusionStatus.PENDING
1735
+ assert len(list(mempool_manager.mempool.get_items_by_coin_id(coin_id))) == 1
1736
+ assert mempool_manager.mempool.size() == 1
1737
+ assert len(list(mempool_manager.mempool.items_by_feerate())) == 1
1641
1738
  # Setup a new peak where the incoming block has spent the coin
1642
1739
  # Mark this coin as spent
1643
1740
  test_coin_records = {coin_id: CoinRecord(coin, uint32(0), TEST_HEIGHT, False, uint64(0))}
@@ -1816,7 +1913,7 @@ async def test_identical_spend_aggregation_e2e(
1816
1913
  sb_ef_name = sb_ef.name()
1817
1914
  await send_to_mempool(full_node_api, sb_ef)
1818
1915
  # Send also a transaction EG that spends E differently from DE and EF,
1819
- # so that it doesn't get deduplicated on E with them
1916
+ # to ensure it's rejected by the mempool
1820
1917
  conditions = [
1821
1918
  [ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, e_coin.amount],
1822
1919
  [ConditionOpcode.ASSERT_MY_COIN_ID, e_coin.name()],
@@ -1834,14 +1931,13 @@ async def test_identical_spend_aggregation_e2e(
1834
1931
  [tx_g] = action_scope.side_effects.transactions
1835
1932
  assert tx_g.spend_bundle is not None
1836
1933
  sb_e2g = SpendBundle.aggregate([sb_e2, tx_g.spend_bundle])
1837
- sb_e2g_name = sb_e2g.name()
1838
- await send_to_mempool(full_node_api, sb_e2g)
1934
+ await send_to_mempool(full_node_api, sb_e2g, expecting_conflict=True)
1839
1935
 
1840
1936
  # Make sure our coin IDs to spend bundles mappings are correct
1841
1937
  assert get_sb_names_by_coin_id(full_node_api, coins[4].coin.name()) == {sb_de_name}
1842
- assert get_sb_names_by_coin_id(full_node_api, e_coin_id) == {sb_de_name, sb_ef_name, sb_e2g_name}
1938
+ assert get_sb_names_by_coin_id(full_node_api, e_coin_id) == {sb_de_name, sb_ef_name}
1843
1939
  assert get_sb_names_by_coin_id(full_node_api, coins[5].coin.name()) == {sb_ef_name}
1844
- assert get_sb_names_by_coin_id(full_node_api, g_coin_id) == {sb_e2g_name}
1940
+ assert get_sb_names_by_coin_id(full_node_api, g_coin_id) == set()
1845
1941
 
1846
1942
  await farm_a_block(full_node_api, wallet_node, ph)
1847
1943
 
@@ -2501,3 +2597,215 @@ async def test_advancing_ff(use_optimization: bool) -> None:
2501
2597
  spend = item.bundle_coin_spends[spend_a.coin.name()]
2502
2598
  assert spend.eligible_for_fast_forward
2503
2599
  assert spend.latest_singleton_coin == spend_c.coin.name()
2600
+
2601
+
2602
+ @pytest.mark.parametrize("flags", [ELIGIBLE_FOR_DEDUP, ELIGIBLE_FOR_FF, ELIGIBLE_FOR_FF | ELIGIBLE_FOR_DEDUP])
2603
+ @pytest.mark.anyio
2604
+ async def test_check_removals_with_block_creation(flags: int) -> None:
2605
+ LAUNCHER_ID = bytes32([1] * 32)
2606
+ PARENT_PARENT = bytes32([2] * 32)
2607
+ singleton_spend = make_singleton_spend(LAUNCHER_ID, PARENT_PARENT)
2608
+ coins = TestCoins(
2609
+ coins=[singleton_spend.coin, TEST_COIN], lineage={singleton_spend.coin.puzzle_hash: singleton_spend.coin}
2610
+ )
2611
+ mempool_manager = await setup_mempool(coins)
2612
+ sb1 = SpendBundle([singleton_spend], G2Element())
2613
+ sb1_conds = make_test_conds(
2614
+ spend_ids=[(singleton_spend.coin, 0)],
2615
+ created_coins=[[(singleton_spend.coin.puzzle_hash, 1, None)]],
2616
+ cost=100_000_000,
2617
+ )
2618
+ bundle_add_info1 = await mempool_manager.add_spend_bundle(sb1, sb1_conds, sb1.name(), uint32(1))
2619
+ assert bundle_add_info1.status == MempoolInclusionStatus.SUCCESS
2620
+ invariant_check_mempool(mempool_manager.mempool)
2621
+ extra_spend = make_spend(
2622
+ TEST_COIN, IDENTITY_PUZZLE, Program.to([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, 42]])
2623
+ )
2624
+ sb2 = SpendBundle([singleton_spend, extra_spend], G2Element())
2625
+ sb2_conds = make_test_conds(
2626
+ spend_ids=[(singleton_spend.coin, flags), (TEST_COIN, 0)],
2627
+ created_coins=[[(singleton_spend.coin.puzzle_hash, 1, None)], []],
2628
+ cost=1337,
2629
+ )
2630
+ bundle_add_info2 = await mempool_manager.add_spend_bundle(sb2, sb2_conds, sb2.name(), uint32(1))
2631
+ assert bundle_add_info2.status == MempoolInclusionStatus.SUCCESS
2632
+ assert mempool_manager.peak is not None
2633
+ block_generator = await mempool_manager.create_block_generator(mempool_manager.peak.header_hash)
2634
+ assert block_generator is not None
2635
+ _, _, additions, removals = block_generator
2636
+ assert len(additions) == 1
2637
+ assert set(additions) == {Coin(singleton_spend.coin.name(), singleton_spend.coin.puzzle_hash, uint64(1))}
2638
+ assert len(removals) == 2
2639
+ assert set(removals) == {singleton_spend.coin, TEST_COIN}
2640
+
2641
+
2642
+ @pytest.mark.anyio
2643
+ async def test_dedup_not_canonical() -> None:
2644
+ # this is ((1)), but with a non-canonical encoding
2645
+ coin_spend = mk_coin_spend(TEST_COIN, solution="ffffc001018080")
2646
+ coins = TestCoins([TEST_COIN], lineage={})
2647
+ mempool_manager = await setup_mempool(coins)
2648
+ sb = SpendBundle([coin_spend], G2Element())
2649
+ sb_conds = make_test_conds(spend_ids=[(TEST_COIN, ELIGIBLE_FOR_DEDUP)])
2650
+ bundle_add_info = await mempool_manager.add_spend_bundle(sb, sb_conds, sb.name(), uint32(1))
2651
+ assert bundle_add_info.status == MempoolInclusionStatus.FAILED
2652
+ assert bundle_add_info.error == Err.INVALID_COIN_SOLUTION
2653
+
2654
+
2655
+ def make_coin_record(coin: Coin, spent_block_index: int = 0) -> CoinRecord:
2656
+ return CoinRecord(coin, uint32(0), uint32(spent_block_index), False, TEST_TIMESTAMP)
2657
+
2658
+
2659
+ @dataclasses.dataclass
2660
+ class CheckRemovalsCase:
2661
+ id: str
2662
+ removals: dict[bytes32, CoinRecord]
2663
+ bundle_coin_spends: dict[bytes32, BundleCoinSpend] = dataclasses.field(default_factory=dict)
2664
+ conflicting_mempool_items: dict[bytes32, list[MempoolItem]] = dataclasses.field(default_factory=dict)
2665
+ expected_result: tuple[Optional[Err], list[MempoolItem]] = dataclasses.field(default_factory=lambda: (None, []))
2666
+ marks: Marks = ()
2667
+
2668
+
2669
+ @datacases(
2670
+ CheckRemovalsCase(
2671
+ id="No removals",
2672
+ removals={},
2673
+ expected_result=(None, []),
2674
+ ),
2675
+ CheckRemovalsCase(
2676
+ id="Unspent removal, no mempool conflicts",
2677
+ removals={TEST_COIN_ID: TEST_COIN_RECORD},
2678
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN))},
2679
+ expected_result=(None, []),
2680
+ ),
2681
+ CheckRemovalsCase(
2682
+ id="Already spent non FF coin",
2683
+ removals={TEST_COIN_ID: make_coin_record(TEST_COIN, spent_block_index=1)},
2684
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN))},
2685
+ expected_result=(Err.DOUBLE_SPEND, []),
2686
+ ),
2687
+ CheckRemovalsCase(
2688
+ id="Already spent FF coin, no mempool conflicts",
2689
+ removals={TEST_COIN_ID: make_coin_record(TEST_COIN, spent_block_index=1)},
2690
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN), ELIGIBLE_FOR_FF)},
2691
+ expected_result=(None, []),
2692
+ ),
2693
+ CheckRemovalsCase(
2694
+ id="FF coin, non FF mempool conflict",
2695
+ removals={TEST_COIN_ID: TEST_COIN_RECORD},
2696
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN), ELIGIBLE_FOR_FF)},
2697
+ conflicting_mempool_items={TEST_COIN_ID: [mk_item([TEST_COIN])]},
2698
+ expected_result=(Err.MEMPOOL_CONFLICT, [mk_item([TEST_COIN])]),
2699
+ ),
2700
+ CheckRemovalsCase(
2701
+ id="Dedup coin, non dedup mempool conflict",
2702
+ removals={TEST_COIN_ID: TEST_COIN_RECORD},
2703
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN), ELIGIBLE_FOR_DEDUP)},
2704
+ conflicting_mempool_items={TEST_COIN_ID: [mk_item([TEST_COIN])]},
2705
+ expected_result=(Err.MEMPOOL_CONFLICT, [mk_item([TEST_COIN])]),
2706
+ ),
2707
+ CheckRemovalsCase(
2708
+ id="FF coin, FF mempool conflict",
2709
+ removals={TEST_COIN_ID: TEST_COIN_RECORD},
2710
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN), ELIGIBLE_FOR_FF)},
2711
+ conflicting_mempool_items={TEST_COIN_ID: [mk_item([TEST_COIN], flags=[ELIGIBLE_FOR_FF])]},
2712
+ expected_result=(None, []),
2713
+ ),
2714
+ CheckRemovalsCase(
2715
+ id="Dedup coin, Dedup mempool conflict",
2716
+ removals={TEST_COIN_ID: TEST_COIN_RECORD},
2717
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN), ELIGIBLE_FOR_DEDUP)},
2718
+ conflicting_mempool_items={TEST_COIN_ID: [mk_item([TEST_COIN], flags=[ELIGIBLE_FOR_DEDUP])]},
2719
+ expected_result=(None, []),
2720
+ ),
2721
+ CheckRemovalsCase(
2722
+ id="Dedup coin, Dedup mempool conflict with different solution",
2723
+ removals={TEST_COIN_ID: TEST_COIN_RECORD},
2724
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN, solution="ff8080"), ELIGIBLE_FOR_DEDUP)},
2725
+ conflicting_mempool_items={TEST_COIN_ID: [mk_item([TEST_COIN], flags=[ELIGIBLE_FOR_DEDUP])]},
2726
+ expected_result=(
2727
+ Err.MEMPOOL_CONFLICT,
2728
+ [
2729
+ mk_item(
2730
+ [TEST_COIN],
2731
+ flags=[ELIGIBLE_FOR_DEDUP],
2732
+ )
2733
+ ],
2734
+ ),
2735
+ ),
2736
+ CheckRemovalsCase(
2737
+ id="Regular coin, mempool conflict",
2738
+ removals={TEST_COIN_ID: TEST_COIN_RECORD},
2739
+ bundle_coin_spends={TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN))},
2740
+ conflicting_mempool_items={TEST_COIN_ID: [mk_item([TEST_COIN])]},
2741
+ expected_result=(Err.MEMPOOL_CONFLICT, [mk_item([TEST_COIN])]),
2742
+ ),
2743
+ CheckRemovalsCase(
2744
+ id="Both FF and non FF coins, FF one conflicts with existing non FF",
2745
+ removals={TEST_COIN_ID: TEST_COIN_RECORD, TEST_COIN_ID2: TEST_COIN_RECORD2},
2746
+ bundle_coin_spends={
2747
+ TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN)),
2748
+ TEST_COIN_ID2: mk_bcs(mk_coin_spend(TEST_COIN2), ELIGIBLE_FOR_FF),
2749
+ },
2750
+ conflicting_mempool_items={TEST_COIN_ID: [mk_item([TEST_COIN])], TEST_COIN_ID2: [mk_item([TEST_COIN2])]},
2751
+ expected_result=(Err.MEMPOOL_CONFLICT, [mk_item([TEST_COIN]), mk_item([TEST_COIN2])]),
2752
+ ),
2753
+ CheckRemovalsCase(
2754
+ id="Both FF and non FF coins, FF one conflicts with existing FF",
2755
+ removals={TEST_COIN_ID: TEST_COIN_RECORD, TEST_COIN_ID2: TEST_COIN_RECORD2},
2756
+ bundle_coin_spends={
2757
+ TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN)),
2758
+ TEST_COIN_ID2: mk_bcs(mk_coin_spend(TEST_COIN2), ELIGIBLE_FOR_FF),
2759
+ },
2760
+ conflicting_mempool_items={
2761
+ TEST_COIN_ID: [mk_item([TEST_COIN])],
2762
+ TEST_COIN_ID2: [mk_item([TEST_COIN2], flags=[ELIGIBLE_FOR_FF])],
2763
+ },
2764
+ expected_result=(Err.MEMPOOL_CONFLICT, [mk_item([TEST_COIN])]),
2765
+ ),
2766
+ CheckRemovalsCase(
2767
+ id="Two FF coins, only one with non FF conflict",
2768
+ removals={TEST_COIN_ID: TEST_COIN_RECORD, TEST_COIN_ID2: TEST_COIN_RECORD2},
2769
+ bundle_coin_spends={
2770
+ TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN), ELIGIBLE_FOR_FF),
2771
+ TEST_COIN_ID2: mk_bcs(mk_coin_spend(TEST_COIN2), ELIGIBLE_FOR_FF),
2772
+ },
2773
+ conflicting_mempool_items={
2774
+ TEST_COIN_ID: [mk_item([TEST_COIN], flags=[ELIGIBLE_FOR_FF])],
2775
+ TEST_COIN_ID2: [mk_item([TEST_COIN2])],
2776
+ },
2777
+ expected_result=(Err.MEMPOOL_CONFLICT, [mk_item([TEST_COIN2])]),
2778
+ ),
2779
+ CheckRemovalsCase(
2780
+ id="Conflicting items are added to conflicts only once",
2781
+ removals={TEST_COIN_ID: TEST_COIN_RECORD, TEST_COIN_ID2: TEST_COIN_RECORD2},
2782
+ bundle_coin_spends={
2783
+ TEST_COIN_ID: mk_bcs(mk_coin_spend(TEST_COIN)),
2784
+ TEST_COIN_ID2: mk_bcs(mk_coin_spend(TEST_COIN2)),
2785
+ },
2786
+ # Same item spends both coins
2787
+ conflicting_mempool_items={
2788
+ TEST_COIN_ID: [mk_item([TEST_COIN, TEST_COIN2])],
2789
+ TEST_COIN_ID2: [mk_item([TEST_COIN, TEST_COIN2])],
2790
+ },
2791
+ # Item should be added only once in conflicts
2792
+ expected_result=(Err.MEMPOOL_CONFLICT, [mk_item([TEST_COIN, TEST_COIN2])]),
2793
+ ),
2794
+ )
2795
+ def test_check_removals(case: CheckRemovalsCase) -> None:
2796
+ def test_get_items_by_coin_ids(coin_ids: list[bytes32]) -> list[MempoolItem]:
2797
+ items = set()
2798
+ for coin_id in coin_ids:
2799
+ items.update(case.conflicting_mempool_items.get(coin_id, []))
2800
+ return list(items)
2801
+
2802
+ result = check_removals(
2803
+ bundle_coin_spends=case.bundle_coin_spends,
2804
+ removals=case.removals,
2805
+ get_items_by_coin_ids=test_get_items_by_coin_ids,
2806
+ )
2807
+ expected_err, expected_conflicts = case.expected_result
2808
+ err, conflicts = result
2809
+ assert err == expected_err
2810
+ assert len(conflicts) == len(expected_conflicts)
2811
+ assert set(conflicts) == set(expected_conflicts)
@@ -348,6 +348,64 @@ class TestNewPeak:
348
348
  == new_peak.reward_chain_block.get_hash()
349
349
  )
350
350
 
351
+ @pytest.mark.anyio
352
+ async def test_timelord_new_peak_node_sync(
353
+ self,
354
+ one_node: tuple[list[FullNodeService], list[FullNodeSimulator], BlockTools],
355
+ timelord: tuple[TimelordAPI, ChiaServer],
356
+ default_1000_blocks: list[FullBlock],
357
+ ) -> None:
358
+ [full_node_service], _, bt = one_node
359
+ full_node = full_node_service._node
360
+ async with create_blockchain(bt.constants, 2) as (b1, _):
361
+ async with create_blockchain(bt.constants, 2) as (b2, _):
362
+ timelord_api, _ = timelord
363
+ for block in default_1000_blocks:
364
+ await _validate_and_add_block(b1, block)
365
+ await _validate_and_add_block(b2, block)
366
+ await full_node.add_block(block)
367
+
368
+ peak = timelord_peak_from_block(b1, default_1000_blocks[-1])
369
+ assert peak is not None
370
+ assert timelord_api.timelord.new_peak is None
371
+ await timelord_api.new_peak_timelord(peak)
372
+ assert timelord_api.timelord.new_peak is not None
373
+ assert (
374
+ timelord_api.timelord.new_peak.reward_chain_block.get_hash() == peak.reward_chain_block.get_hash()
375
+ )
376
+ await time_out_assert(60, peak_new_peak_is_none, True, timelord_api)
377
+ # make two new blocks on tip, block_2 has higher total iterations
378
+ block_1 = bt.get_consecutive_blocks(1, default_1000_blocks)[-1]
379
+ block_2 = bt.get_consecutive_blocks(
380
+ 1, default_1000_blocks, min_signage_point=block_1.reward_chain_block.signage_point_index
381
+ )[-1]
382
+ assert block_2.weight == block_1.weight
383
+ # make sure block_2 has higher iterations then block_1
384
+ assert block_2.total_iters > block_1.total_iters
385
+ # make sure block_1 and block_2 have higher iterations then peak
386
+ assert block_1.total_iters > default_1000_blocks[-1].total_iters
387
+ await full_node.add_block(block_2)
388
+ await _validate_and_add_block(b1, block_2)
389
+ peak_tl = timelord_peak_from_block(b1, block_2)
390
+ peak = await full_node.blockchain.get_full_peak()
391
+ assert peak is not None
392
+ assert timelord_api.timelord.new_peak is None
393
+ await timelord_api.new_peak_timelord(peak_tl)
394
+ assert timelord_api.timelord.new_peak is not None
395
+ assert peak.header_hash == block_2.header_hash
396
+ assert peak_tl.reward_chain_block.get_hash() == peak.reward_chain_block.get_hash()
397
+ await time_out_assert(60, peak_new_peak_is_none, True, timelord_api)
398
+
399
+ await full_node.add_block(block_1)
400
+ await _validate_and_add_block(b1, block_1)
401
+ peak = timelord_peak_from_block(b1, block_1)
402
+ assert peak is not None
403
+ await timelord_api.new_peak_timelord(peak)
404
+ peak = await full_node.blockchain.get_full_peak()
405
+ assert peak == block_1
406
+ peak_tl = timelord_api.timelord.new_peak
407
+ assert peak_tl.reward_chain_block.get_hash() == peak.reward_chain_block.get_hash()
408
+
351
409
 
352
410
  async def get_rc_prev(blockchain: Blockchain, block: FullBlock) -> bytes32:
353
411
  if block.reward_chain_block.signage_point_index == 0:
@@ -54,7 +54,7 @@ def compute_block_cost(generator: BlockGenerator, constants: ConsensusConstants,
54
54
  else:
55
55
  run_block = run_block_generator
56
56
 
57
- _, conds = run_block(
57
+ err, conds = run_block(
58
58
  bytes(generator.program),
59
59
  generator.generator_refs,
60
60
  constants.MAX_BLOCK_COST_CLVM,
@@ -63,6 +63,8 @@ def compute_block_cost(generator: BlockGenerator, constants: ConsensusConstants,
63
63
  None,
64
64
  constants,
65
65
  )
66
+ if conds is None: # pragma: no cover
67
+ log.error(f"unexpected error while computing block cost: {err} height: {height} generator: {generator.program}")
66
68
  return uint64(0 if conds is None else conds.cost)
67
69
 
68
70
 
@@ -2134,7 +2134,8 @@ class FullNode:
2134
2134
  )
2135
2135
  pre_validation_result = await future
2136
2136
  added: Optional[AddBlockResult] = None
2137
- pre_validation_time = time.monotonic() - validation_start
2137
+ add_block_start = time.monotonic()
2138
+ pre_validation_time = add_block_start - validation_start
2138
2139
  try:
2139
2140
  if pre_validation_result.error is not None:
2140
2141
  if Err(pre_validation_result.error) == Err.INVALID_PREV_BLOCK_HASH:
@@ -2153,6 +2154,7 @@ class FullNode:
2153
2154
  (added, error_code, state_change_summary) = await self.blockchain.add_block(
2154
2155
  block, pre_validation_result, ssi, fork_info
2155
2156
  )
2157
+ add_block_time = time.monotonic() - add_block_start
2156
2158
  if added == AddBlockResult.ALREADY_HAVE_BLOCK:
2157
2159
  return None
2158
2160
  elif added == AddBlockResult.INVALID_BLOCK:
@@ -2181,7 +2183,7 @@ class FullNode:
2181
2183
  self.log.info(
2182
2184
  f"Received orphan block of height {block.height} rh {block.reward_chain_block.get_hash()}"
2183
2185
  )
2184
- post_process_time = 0
2186
+ post_process_time = 0.0
2185
2187
  else:
2186
2188
  # Should never reach here, all the cases are covered
2187
2189
  raise RuntimeError(f"Invalid result from add_block {added}")
@@ -2197,7 +2199,11 @@ class FullNode:
2197
2199
 
2198
2200
  if ppp_result is not None:
2199
2201
  assert state_change_summary is not None
2202
+ post_process_time2 = time.monotonic()
2200
2203
  await self.peak_post_processing_2(block, peer, state_change_summary, ppp_result)
2204
+ post_process_time2 = time.monotonic() - post_process_time2
2205
+ else:
2206
+ post_process_time2 = 0.0
2201
2207
 
2202
2208
  percent_full_str = (
2203
2209
  (
@@ -2213,7 +2219,9 @@ class FullNode:
2213
2219
  f"Block validation: {validation_time:0.2f}s, "
2214
2220
  f"pre_validation: {pre_validation_time:0.2f}s, "
2215
2221
  f"CLVM: {pre_validation_result.timing / 1000.0:0.2f}s, "
2222
+ f"add-block: {add_block_time:0.2f}s, "
2216
2223
  f"post-process: {post_process_time:0.2f}s, "
2224
+ f"post-process2: {post_process_time2:0.2f}s, "
2217
2225
  f"cost: {block.transactions_info.cost if block.transactions_info is not None else 'None'}"
2218
2226
  f"{percent_full_str} header_hash: {header_hash.hex()} height: {block.height}",
2219
2227
  )
@@ -2847,10 +2855,16 @@ class FullNode:
2847
2855
 
2848
2856
  total_time = time.monotonic() - start_time
2849
2857
 
2850
- self.log.log(
2851
- logging.DEBUG if total_time < 0.5 else logging.WARNING,
2852
- f"Broadcasting added transaction {mempool_item.name} to {len(peer_ids)} peers took {total_time:.4f}s",
2853
- )
2858
+ if len(peer_ids) == 0:
2859
+ self.log.log(
2860
+ logging.DEBUG if total_time < 0.5 else logging.WARNING,
2861
+ f"Looking up hints for {len(conds.spends)} spends took {total_time:.4f}s",
2862
+ )
2863
+ else:
2864
+ self.log.log(
2865
+ logging.DEBUG if total_time < 0.5 else logging.WARNING,
2866
+ f"Broadcasting added transaction {mempool_item.name} to {len(peer_ids)} peers took {total_time:.4f}s",
2867
+ )
2854
2868
 
2855
2869
  async def broadcast_removed_tx(self, mempool_removals: list[MempoolRemoveInfo]) -> None:
2856
2870
  total_removals = sum(len(r.items) for r in mempool_removals)
@@ -865,7 +865,7 @@ class FullNodeAPI:
865
865
  try:
866
866
  block = await self.full_node.mempool_manager.create_block_generator(curr_l_tb.header_hash)
867
867
  if block is not None:
868
- block_generator, aggregate_signature, additions = block
868
+ block_generator, aggregate_signature, additions, removals = block
869
869
  except Exception as e:
870
870
  self.log.error(f"Traceback: {traceback.format_exc()}")
871
871
  self.full_node.log.error(f"Error making spend bundle {e} peak: {peak}")
chia/full_node/mempool.py CHANGED
@@ -493,7 +493,7 @@ class Mempool:
493
493
  constants: ConsensusConstants,
494
494
  height: uint32,
495
495
  item_inclusion_filter: Optional[Callable[[bytes32], bool]] = None,
496
- ) -> Optional[tuple[BlockGenerator, G2Element, list[Coin]]]:
496
+ ) -> Optional[tuple[BlockGenerator, G2Element, list[Coin], list[Coin]]]:
497
497
  """
498
498
  height is needed in case we fast-forward a transaction and we need to
499
499
  re-run its puzzle.
@@ -525,6 +525,7 @@ class Mempool:
525
525
  BlockGenerator(SerializedProgram.from_bytes(block_program), []),
526
526
  spend_bundle.aggregated_signature,
527
527
  additions,
528
+ removals,
528
529
  )
529
530
 
530
531
  async def create_bundle_from_mempool_items(
@@ -150,6 +150,143 @@ QUOTE_BYTES = 2
150
150
  QUOTE_EXECUTION_COST = 20
151
151
 
152
152
 
153
+ def is_atom_canonical(clvm_buffer: bytes, offset: int) -> tuple[int, bool]:
154
+ b = clvm_buffer[offset]
155
+ if (b & 0b11000000) == 0b10000000:
156
+ # 6 bits length prefix
157
+ mask = 0b00111111
158
+ prefix_len = 0
159
+ min_value = 1
160
+ elif (b & 0b11100000) == 0b11000000:
161
+ # 5 + 8 bits length prefix
162
+ mask = 0b00011111
163
+ prefix_len = 1
164
+ min_value = 1 << 6
165
+ elif (b & 0b11110000) == 0b11100000:
166
+ # 4 + 8 + 8 bits length prefix
167
+ mask = 0b00001111
168
+ prefix_len = 2
169
+ min_value = 1 << (5 + 8)
170
+ elif (b & 0b11111000) == 0b11110000:
171
+ # 3 + 8 + 8 + 8 bits length prefix
172
+ mask = 0b00000111
173
+ prefix_len = 3
174
+ min_value = 1 << (4 + 8 + 8)
175
+ elif (b & 0b11111100) == 0b11111000:
176
+ # 2 + 8 + 8 + 8 + 8 bits length prefix
177
+ mask = 0b00000011
178
+ prefix_len = 4
179
+ min_value = 1 << (3 + 8 + 8 + 8)
180
+ elif (b & 0b11111110) == 0b11111100:
181
+ # 1 + 8 + 8 + 8 + 8 + 8 bits length prefix
182
+ mask = 0b00000001
183
+ prefix_len = 5
184
+ min_value = 1 << (2 + 8 + 8 + 8 + 8)
185
+
186
+ atom_len = b & mask
187
+ for i in range(prefix_len):
188
+ atom_len <<= 8
189
+ offset += 1
190
+ atom_len |= clvm_buffer[offset]
191
+
192
+ return 1 + prefix_len + atom_len, atom_len >= min_value
193
+
194
+
195
+ def is_clvm_canonical(clvm_buffer: bytes) -> bool:
196
+ """
197
+ checks whether the CLVM serialization is all canonical representation.
198
+ atoms can be serialized in more than one way by using more bytes than
199
+ necessary to encode the length prefix. This functions ensures that all atoms are
200
+ encoded with the shortest representation. back-references are not allowed
201
+ and will make this function return false
202
+ """
203
+ assert clvm_buffer != b""
204
+
205
+ offset = 0
206
+ tokens_left = 1
207
+ while True:
208
+ b = clvm_buffer[offset]
209
+
210
+ # pair
211
+ if b == 0xFF:
212
+ tokens_left += 1
213
+ offset += 1
214
+ continue
215
+
216
+ # back references cannot be considered canonical, since they may be
217
+ # encoded in many different ways
218
+ if b == 0xFE:
219
+ return False
220
+
221
+ # small atom or NIL
222
+ if b <= 0x80:
223
+ tokens_left -= 1
224
+ offset += 1
225
+ else:
226
+ atom_len, canonical = is_atom_canonical(clvm_buffer, offset)
227
+ if not canonical:
228
+ return False
229
+ tokens_left -= 1
230
+ offset += atom_len
231
+
232
+ if tokens_left == 0:
233
+ break
234
+
235
+ # if there's garbage at the end, it's not canonical
236
+ return offset == len(clvm_buffer)
237
+
238
+
239
+ def check_removals(
240
+ removals: dict[bytes32, CoinRecord],
241
+ bundle_coin_spends: dict[bytes32, BundleCoinSpend],
242
+ *,
243
+ get_items_by_coin_ids: Callable[[list[bytes32]], list[MempoolItem]],
244
+ ) -> tuple[Optional[Err], list[MempoolItem]]:
245
+ """
246
+ This function checks for double spends, unknown spends and conflicting transactions in mempool.
247
+ Returns Error (if any), the set of existing MempoolItems with conflicting spends (if any).
248
+ Note that additions are not checked for duplicates, because having duplicate additions requires also
249
+ having duplicate removals.
250
+ """
251
+ conflicts = set()
252
+ for coin_id, coin_bcs in bundle_coin_spends.items():
253
+ # 1. Checks if it's been spent already
254
+ if removals[coin_id].spent and not coin_bcs.eligible_for_fast_forward:
255
+ return Err.DOUBLE_SPEND, []
256
+
257
+ # 2. Checks if there's a mempool conflict
258
+ conflicting_items = get_items_by_coin_ids([coin_id])
259
+ for item in conflicting_items:
260
+ if item in conflicts:
261
+ continue
262
+ conflict_bcs = item.bundle_coin_spends[coin_id]
263
+ # if the spend we're adding to the mempool is not DEDUP nor FF, it's
264
+ # just a regular conflict
265
+ if not coin_bcs.eligible_for_fast_forward and not coin_bcs.eligible_for_dedup:
266
+ conflicts.add(item)
267
+
268
+ # if the spend we're adding is FF, but there's a conflicting spend
269
+ # that isn't FF, they can't be chained, so that's a conflict
270
+ elif coin_bcs.eligible_for_fast_forward and not conflict_bcs.eligible_for_fast_forward:
271
+ conflicts.add(item)
272
+
273
+ # if the spend we're adding is DEDUP, but there's a conflicting spend
274
+ # that isn't DEDUP, we cannot merge them, so that's a conflict
275
+ elif coin_bcs.eligible_for_dedup and not conflict_bcs.eligible_for_dedup:
276
+ conflicts.add(item)
277
+
278
+ # if the spend we're adding is DEDUP but the existing spend has a
279
+ # different solution, we cannot merge them, so that's a conflict
280
+ elif coin_bcs.eligible_for_dedup and bytes(coin_bcs.coin_spend.solution) != bytes(
281
+ conflict_bcs.coin_spend.solution
282
+ ):
283
+ conflicts.add(item)
284
+
285
+ if len(conflicts) > 0:
286
+ return Err.MEMPOOL_CONFLICT, list(conflicts)
287
+ return None, []
288
+
289
+
153
290
  class MempoolManager:
154
291
  pool: Executor
155
292
  constants: ConsensusConstants
@@ -247,9 +384,9 @@ class MempoolManager:
247
384
  self,
248
385
  last_tb_header_hash: bytes32,
249
386
  item_inclusion_filter: Optional[Callable[[bytes32], bool]] = None,
250
- ) -> Optional[tuple[BlockGenerator, G2Element, list[Coin]]]:
387
+ ) -> Optional[tuple[BlockGenerator, G2Element, list[Coin], list[Coin]]]:
251
388
  """
252
- Returns a block generator program, the aggregate signature and all additions, for a new block
389
+ Returns a block generator program, the aggregate signature and all additions and removals, for a new block
253
390
  """
254
391
  if self.peak is None or self.peak.header_hash != last_tb_header_hash:
255
392
  return None
@@ -462,7 +599,6 @@ class MempoolManager:
462
599
  addition_amount: int = 0
463
600
  # Map of coin ID to eligibility information
464
601
  eligibility_and_additions: dict[bytes32, EligibilityAndAdditions] = {}
465
- non_eligible_coin_ids: list[bytes32] = []
466
602
  for spend in conds.spends:
467
603
  coin_id = bytes32(spend.coin_id)
468
604
  removal_names.add(coin_id)
@@ -480,7 +616,6 @@ class MempoolManager:
480
616
  ff_puzzle_hash=bytes32(spend.puzzle_hash) if is_eligible_for_ff else None,
481
617
  )
482
618
  removal_names_from_coin_spends: set[bytes32] = set()
483
- fast_forward_coin_ids: set[bytes32] = set()
484
619
  bundle_coin_spends: dict[bytes32, BundleCoinSpend] = {}
485
620
  for coin_spend in new_spend.coin_spends:
486
621
  coin_id = coin_spend.coin.name()
@@ -489,6 +624,11 @@ class MempoolManager:
489
624
  coin_id,
490
625
  EligibilityAndAdditions(is_eligible_for_dedup=False, spend_additions=[], ff_puzzle_hash=None),
491
626
  )
627
+
628
+ supports_dedup = eligibility_info.is_eligible_for_dedup
629
+ if supports_dedup and not is_clvm_canonical(bytes(coin_spend.solution)):
630
+ return Err.INVALID_COIN_SOLUTION, None, []
631
+
492
632
  mark_as_fast_forward = eligibility_info.ff_puzzle_hash is not None and supports_fast_forward(coin_spend)
493
633
  latest_singleton_coin = None
494
634
  if mark_as_fast_forward:
@@ -500,13 +640,9 @@ class MempoolManager:
500
640
  if lineage_info is None:
501
641
  return Err.DOUBLE_SPEND, None, []
502
642
  latest_singleton_coin = lineage_info.coin_id
503
- fast_forward_coin_ids.add(coin_id)
504
- # We are now able to check eligibility of both dedup and fast forward
505
- if not (eligibility_info.is_eligible_for_dedup or mark_as_fast_forward):
506
- non_eligible_coin_ids.append(coin_id)
507
643
  bundle_coin_spends[coin_id] = BundleCoinSpend(
508
644
  coin_spend=coin_spend,
509
- eligible_for_dedup=eligibility_info.is_eligible_for_dedup,
645
+ eligible_for_dedup=supports_dedup,
510
646
  eligible_for_fast_forward=mark_as_fast_forward,
511
647
  additions=eligibility_info.spend_additions,
512
648
  latest_singleton_coin=latest_singleton_coin,
@@ -580,7 +716,9 @@ class MempoolManager:
580
716
 
581
717
  # Check removals against UnspentDB + DiffStore + Mempool + SpendBundle
582
718
  # Use this information later when constructing a block
583
- fail_reason, conflicts = self.check_removals(non_eligible_coin_ids, removal_record_dict, fast_forward_coin_ids)
719
+ fail_reason, conflicts = check_removals(
720
+ removal_record_dict, bundle_coin_spends, get_items_by_coin_ids=self.mempool.get_items_by_coin_ids
721
+ )
584
722
 
585
723
  # If we have a mempool conflict, continue, since we still want to keep around the TX in the pending pool.
586
724
  if fail_reason is not None and fail_reason is not Err.MEMPOOL_CONFLICT:
@@ -650,33 +788,6 @@ class MempoolManager:
650
788
 
651
789
  return None, potential, [item.name for item in conflicts]
652
790
 
653
- def check_removals(
654
- self,
655
- non_eligible_coin_ids: list[bytes32],
656
- removals: dict[bytes32, CoinRecord],
657
- fast_forward_coin_ids: set[bytes32],
658
- ) -> tuple[Optional[Err], list[MempoolItem]]:
659
- """
660
- This function checks for double spends, unknown spends and conflicting transactions in mempool.
661
- Returns Error (if any), the set of existing MempoolItems with conflicting spends (if any).
662
- Note that additions are not checked for duplicates, because having duplicate additions requires also
663
- having duplicate removals.
664
- """
665
- assert self.peak is not None
666
- # 1. Checks if it's been spent already
667
- for record in removals.values():
668
- if record.spent:
669
- # Only consider it a double spend if this is not a fast forward
670
- if record.name not in fast_forward_coin_ids:
671
- return Err.DOUBLE_SPEND, []
672
- # 2. Checks if there's a mempool conflict
673
- # Only consider conflicts if the coin is not eligible for deduplication
674
- conflicts = self.mempool.get_items_by_coin_ids(non_eligible_coin_ids)
675
- if len(conflicts) > 0:
676
- return Err.MEMPOOL_CONFLICT, conflicts
677
- # 5. If coins can be spent return list of unspents as we see them in local storage
678
- return None, []
679
-
680
791
  def get_spendbundle(self, bundle_hash: bytes32) -> Optional[SpendBundle]:
681
792
  """Returns a full SpendBundle if it's inside one the mempools"""
682
793
  item: Optional[MempoolItem] = self.mempool.get_item_by_id(bundle_hash)
@@ -721,6 +832,7 @@ class MempoolManager:
721
832
  assert new_peak.timestamp is not None
722
833
  self.fee_estimator.new_block_height(new_peak.height)
723
834
  included_items: list[MempoolItemInfo] = []
835
+ new_peak_start = time.monotonic()
724
836
 
725
837
  expired = self.mempool.new_tx_block(new_peak.height, new_peak.timestamp)
726
838
  mempool_item_removals: list[MempoolRemoveInfo] = [expired]
@@ -887,6 +999,8 @@ class MempoolManager:
887
999
  f"minimum fee rate (in FPC) to get in for 5M cost tx: {self.mempool.get_min_fee_rate(5000000)}"
888
1000
  )
889
1001
  self.mempool.fee_estimator.new_block(FeeBlockInfo(new_peak.height, included_items))
1002
+ duration = time.monotonic() - new_peak_start
1003
+ log.log(logging.WARNING if duration > 1 else logging.INFO, f"new_peak() took {duration:0.2f} seconds")
890
1004
  return NewPeakInfo(txs_added, mempool_item_removals)
891
1005
 
892
1006
  def get_items_not_in_filter(self, mempool_filter: PyBIP158, limit: int = 100) -> list[SpendBundle]:
@@ -88,7 +88,7 @@ class HarvesterAPI:
88
88
  f"sp_hash: {new_challenge.sp_hash}, signage_point_index: {new_challenge.signage_point_index}"
89
89
  )
90
90
 
91
- start = time.time()
91
+ start = time.monotonic()
92
92
  assert len(new_challenge.challenge_hash) == 32
93
93
 
94
94
  loop = asyncio.get_running_loop()
@@ -254,11 +254,11 @@ class HarvesterAPI:
254
254
  self.harvester.log.debug(f"new_signage_point_harvester {passed} plots passed the plot filter")
255
255
 
256
256
  # Concurrently executes all lookups on disk, to take advantage of multiple disk parallelism
257
- time_taken = time.time() - start
257
+ time_taken = time.monotonic() - start
258
258
  total_proofs_found = 0
259
259
  for filename_sublist_awaitable in asyncio.as_completed(awaitables):
260
260
  filename, sublist = await filename_sublist_awaitable
261
- time_taken = time.time() - start
261
+ time_taken = time.monotonic() - start
262
262
  if time_taken > 8:
263
263
  self.harvester.log.warning(
264
264
  f"Looking up qualities on {filename} took: {time_taken}. This should be below 8 seconds"
@@ -19,7 +19,6 @@ from typing import Any, Callable, Optional
19
19
  import anyio
20
20
  from chia_puzzles_py.programs import CHIALISP_DESERIALISATION, ROM_BOOTSTRAP_GENERATOR
21
21
  from chia_rs import (
22
- MEMPOOL_MODE,
23
22
  AugSchemeMPL,
24
23
  ConsensusConstants,
25
24
  G1Element,
@@ -75,7 +74,7 @@ from chia.simulator.wallet_tools import WalletTool
75
74
  from chia.ssl.create_ssl import create_all_ssl
76
75
  from chia.types.blockchain_format.classgroup import ClassgroupElement
77
76
  from chia.types.blockchain_format.coin import Coin
78
- from chia.types.blockchain_format.program import INFINITE_COST, Program
77
+ from chia.types.blockchain_format.program import DEFAULT_FLAGS, INFINITE_COST, Program
79
78
  from chia.types.blockchain_format.proof_of_space import (
80
79
  ProofOfSpace,
81
80
  calculate_pos_challenge,
@@ -1951,7 +1950,7 @@ def compute_cost_test(generator: BlockGenerator, constants: ConsensusConstants,
1951
1950
 
1952
1951
  if height >= constants.HARD_FORK_HEIGHT:
1953
1952
  blocks = generator.generator_refs
1954
- cost, result = generator.program._run(INFINITE_COST, MEMPOOL_MODE, [DESERIALIZE_MOD, blocks])
1953
+ cost, result = generator.program._run(INFINITE_COST, DEFAULT_FLAGS, [DESERIALIZE_MOD, blocks])
1955
1954
  clvm_cost += cost
1956
1955
 
1957
1956
  for spend in result.first().as_iter():
@@ -1960,13 +1959,13 @@ def compute_cost_test(generator: BlockGenerator, constants: ConsensusConstants,
1960
1959
  puzzle = spend.at("rf")
1961
1960
  solution = spend.at("rrrf")
1962
1961
 
1963
- cost, result = puzzle._run(INFINITE_COST, MEMPOOL_MODE, solution)
1962
+ cost, result = puzzle._run(INFINITE_COST, DEFAULT_FLAGS, solution)
1964
1963
  clvm_cost += cost
1965
1964
  condition_cost += conditions_cost(result)
1966
1965
 
1967
1966
  else:
1968
1967
  block_program_args = SerializedProgram.to([generator.generator_refs])
1969
- clvm_cost, result = GENERATOR_MOD._run(INFINITE_COST, MEMPOOL_MODE, [generator.program, block_program_args])
1968
+ clvm_cost, result = GENERATOR_MOD._run(INFINITE_COST, DEFAULT_FLAGS, [generator.program, block_program_args])
1970
1969
 
1971
1970
  for res in result.first().as_iter():
1972
1971
  # each condition item is:
@@ -53,6 +53,22 @@ class TimelordAPI:
53
53
  self.timelord.state_changed("new_peak", {"height": new_peak.reward_chain_block.height})
54
54
  return
55
55
 
56
+ # new peak has equal weight but lower iterations
57
+ if (
58
+ self.timelord.last_state.get_weight() == new_peak.reward_chain_block.weight
59
+ and self.timelord.last_state.peak.reward_chain_block.total_iters
60
+ > new_peak.reward_chain_block.total_iters
61
+ ):
62
+ log.info(
63
+ "Not skipping peak, has equal weight but lower iterations,"
64
+ f"current peak:{self.timelord.last_state.total_iters} new peak "
65
+ f"{new_peak.reward_chain_block.total_iters}"
66
+ )
67
+ self.timelord.new_peak = new_peak
68
+ self.timelord.state_changed("new_peak", {"height": new_peak.reward_chain_block.height})
69
+ return
70
+
71
+ # new peak is heavier
56
72
  if self.timelord.last_state.get_weight() < new_peak.reward_chain_block.weight:
57
73
  # if there is an unfinished block with less iterations, skip so we dont orphan it
58
74
  if (
@@ -63,9 +79,9 @@ class TimelordAPI:
63
79
  self.timelord.state_changed("skipping_peak", {"height": new_peak.reward_chain_block.height})
64
80
  return
65
81
 
66
- log.info("Not skipping peak, don't have. Maybe we are not the fastest timelord")
67
82
  log.info(
68
- f"New peak: height: {new_peak.reward_chain_block.height} weight: "
83
+ "Not skipping peak, don't have. Maybe we are not the fastest timelord "
84
+ f"height: {new_peak.reward_chain_block.height} weight:"
69
85
  f"{new_peak.reward_chain_block.weight} "
70
86
  )
71
87
  self.timelord.new_peak = new_peak
@@ -75,9 +91,9 @@ class TimelordAPI:
75
91
  if self.timelord.last_state.peak.reward_chain_block.get_hash() == new_peak.reward_chain_block.get_hash():
76
92
  log.info("Skipping peak, already have.")
77
93
  else:
78
- log.info("Skipping peak, block has equal or lower weight then our peak.")
79
- log.debug(
80
- f"new peak height {new_peak.reward_chain_block.height} weight {new_peak.reward_chain_block.weight}"
94
+ log.info(
95
+ f"Skipping peak height {new_peak.reward_chain_block.height} "
96
+ f"weight {new_peak.reward_chain_block.weight}"
81
97
  )
82
98
 
83
99
  self.timelord.state_changed("skipping_peak", {"height": new_peak.reward_chain_block.height})
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import io
4
4
  from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar
5
5
 
6
- from chia_rs import MEMPOOL_MODE, run_chia_program, tree_hash
6
+ from chia_rs import ENABLE_KECCAK, MEMPOOL_MODE, run_chia_program, tree_hash
7
7
  from chia_rs.sized_bytes import bytes32
8
8
  from clvm.casts import int_from_bytes
9
9
  from clvm.CLVMObject import CLVMStorage
@@ -17,7 +17,7 @@ from chia.util.hash import std_hash
17
17
 
18
18
  INFINITE_COST = 11000000000
19
19
 
20
- DEFAULT_FLAGS = MEMPOOL_MODE
20
+ DEFAULT_FLAGS = MEMPOOL_MODE | ENABLE_KECCAK
21
21
 
22
22
  T_CLVMStorage = TypeVar("T_CLVMStorage", bound=CLVMStorage)
23
23
  T_Program = TypeVar("T_Program", bound="Program")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: chia-blockchain
3
- Version: 2.5.3rc1
3
+ Version: 2.5.4rc1
4
4
  Summary: Chia blockchain full node, farmer, timelord, and wallet.
5
5
  License: Apache-2.0
6
6
  Keywords: chia,blockchain,node
@@ -118,11 +118,11 @@ chia/_tests/core/large_block.py,sha256=Ifi3lzBcwtBHDI7no3OTemhJMQZc5LlKC32QFNZwM
118
118
  chia/_tests/core/make_block_generator.py,sha256=4a4Q-lAJUYOjPlJTkMazOna-Rgp4VeNs8ju__OwebK4,2835
119
119
  chia/_tests/core/mempool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
120
120
  chia/_tests/core/mempool/config.py,sha256=-GncDnWBQwvk5MHfOhjQ4DnUD3VlxrD8lLrt8Gd2D5c,86
121
- chia/_tests/core/mempool/test_mempool.py,sha256=KAOVmfUPk5QNY94iXYrm5JX3yRVqjnd-m_K1IiXfmUs,145380
121
+ chia/_tests/core/mempool/test_mempool.py,sha256=_3ydxbt-LfIiju-4PW6qgvRSpvaHL7GqrxIIKeITEF4,145386
122
122
  chia/_tests/core/mempool/test_mempool_fee_estimator.py,sha256=lZZiTy-zOfrxuIWoQRnk_JLV9OJzuFqQwKwOgMwh3dQ,3714
123
123
  chia/_tests/core/mempool/test_mempool_fee_protocol.py,sha256=XUTKqs82IOiiZMGPTPSRhB4Zp8clbus5GlOoUtPtL6A,2165
124
124
  chia/_tests/core/mempool/test_mempool_item_queries.py,sha256=BESoQXx8XG2jSJiE12nslC3y_vPWrfCWPCrv4tGl3Js,7027
125
- chia/_tests/core/mempool/test_mempool_manager.py,sha256=e1WqJy270oY-FJ6-8pG_yNJbLOTZwTi9B4z4dN986Y8,114191
125
+ chia/_tests/core/mempool/test_mempool_manager.py,sha256=KZCXRegcVxT6OUnXuOypJealnnLRZ6Dom4NxIWHH0lI,126728
126
126
  chia/_tests/core/mempool/test_mempool_performance.py,sha256=guLnMgE37swiQuyEXYPmtmMOXSa7o_fwBlXGHsOM4rU,2808
127
127
  chia/_tests/core/mempool/test_singleton_fast_forward.py,sha256=egp-7n5GCMlFebzwanQwPjOE24efhDBCvVrKEuEkQVU,38019
128
128
  chia/_tests/core/node_height.py,sha256=yfkEmmYCsAb3cIe1ZhtE9J2SFB9lG_49SgjxsITdrZk,917
@@ -237,7 +237,7 @@ chia/_tests/simulation/test_start_simulator.py,sha256=geCu_RMFuFlu1oaurcvubzwENY
237
237
  chia/_tests/testconfig.py,sha256=zLhG3F8aayQR34gLclQDmDfSsXu9GEaYpCuL4RteZpg,431
238
238
  chia/_tests/timelord/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
239
239
  chia/_tests/timelord/config.py,sha256=fTDhByaME5cEDbtyTKCWUf69F4kajs75aTQewZMjxak,69
240
- chia/_tests/timelord/test_new_peak.py,sha256=_zJtJoeo8PgT1D3tk3-EmRRV-s-fqwTvzgjoDaW4TLg,21961
240
+ chia/_tests/timelord/test_new_peak.py,sha256=qvRlrJ6V6-DjSDmx_P6CUU6BByonHiWU97hJh_f17es,25238
241
241
  chia/_tests/timelord/test_timelord.py,sha256=5v8AbvHMws2AsoWW-K6e2dS3oy3j4PW4SN4VJnjASMM,298
242
242
  chia/_tests/tools/1315537.json,sha256=lT_jyWxuxw9B3Wc1Akr80l6y2RhVAtY8DW4zlfik-iE,19256
243
243
  chia/_tests/tools/1315544.json,sha256=8_dMCtu8nPPvR2KK5iqRd-Vit-VCb5dmYsjOtFWtIXA,19163
@@ -448,7 +448,7 @@ chia/cmds/wallet.py,sha256=KQiT86fmZrycAopFMUCD_-PWy0Hzpv0FHh9bE944clM,60888
448
448
  chia/cmds/wallet_funcs.py,sha256=A3LBfELVgdEKBFa1Z4ciVa4gRCVEvekKfCYPeJ9kbGQ,77227
449
449
  chia/consensus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
450
450
  chia/consensus/block_body_validation.py,sha256=OQ2w7beihGMDyocYNcRBrvtoqk6sAG29E4BhWOaJZPk,24230
451
- chia/consensus/block_creation.py,sha256=8L33CT35nD2SZ3Sb2MS6UmuH3Xre-5uXs78SgGXLeU0,21822
451
+ chia/consensus/block_creation.py,sha256=IZwnY6v3RkTdrUHXbBJXdVV1jFx9XsuI1i5LynqJJ38,21987
452
452
  chia/consensus/block_header_validation.py,sha256=bM9R4z6Z4OLmViD4yYh2yFn-aNbFEtThjWkYq93EzUc,51035
453
453
  chia/consensus/block_record.py,sha256=hL-UP7_Jnuebc4ano1daBMcZCB3mtcHfabOsso3hMRU,670
454
454
  chia/consensus/block_rewards.py,sha256=vmdHKqYudVnKvzq-ALAsSpy4RsTPLhdw-giJkwecdkM,2250
@@ -513,14 +513,14 @@ chia/full_node/fee_estimator_constants.py,sha256=GsQfI9Z7YzPWGEe6ghJeYvzbG87C86K
513
513
  chia/full_node/fee_estimator_interface.py,sha256=y6ICUB_69vkF2whcKFlN8lhmM3bR88yRTJb8e2ThG6M,1610
514
514
  chia/full_node/fee_history.py,sha256=hcLmy3HLuuATidOQ94wD7MAtzZU9PadKaKt8q3m8fVI,591
515
515
  chia/full_node/fee_tracker.py,sha256=vgJbP-zfaeL8tYToZxKm3JoBy5LWog6rD28FHycDBvQ,22626
516
- chia/full_node/full_node.py,sha256=E7oOM5bnmkQVZ3fhJBkhHK7Z-nxUNL7EAWrTxuVqfM0,166368
517
- chia/full_node/full_node_api.py,sha256=N-q1AygJGIb-CDvJyL_EN_VaHQ8sT5x7sk4PIqbRjbk,96830
516
+ chia/full_node/full_node.py,sha256=fQwPCTVSmnYD36KR85QP6O4HOrdlhYlRtArMLScfXD8,167030
517
+ chia/full_node/full_node_api.py,sha256=h9TbahfJ3P8tdSI76jfo683dV6Hwnkxj6C5Ckdo-Wgg,96840
518
518
  chia/full_node/full_node_store.py,sha256=1YGBgdS1Kim5FHbv-IMIegKkRRj8Hbqj55X1se7Du_8,47369
519
519
  chia/full_node/hint_management.py,sha256=8dVKguFPm-h32GEnMWxX2qyITo4WVO1YBKaPFKCeni0,2285
520
520
  chia/full_node/hint_store.py,sha256=0m6y0_f0rhGIKqfAmhFSGEeyM3LCN-dv-bR1OfIr4Uo,3666
521
- chia/full_node/mempool.py,sha256=qt9JWbIAE3Aql4LoRMRFEjyzuArckithY1yMdDyGMm8,28116
521
+ chia/full_node/mempool.py,sha256=VK323fsHtnbn_zQMqE7dcBAf37_4K7X_wfYLmjvLK6c,28150
522
522
  chia/full_node/mempool_check_conditions.py,sha256=r_NXwXH5nLvO-YbhcZozrMIYOdHqyMRyDzBCVoIX1QA,5606
523
- chia/full_node/mempool_manager.py,sha256=IgW7eEwG2GbE2dZqvYVQQ1dWGnlUzilvwU2yqc2gvJI,46478
523
+ chia/full_node/mempool_manager.py,sha256=YIqhNc-IuELYaODSuEeIGAgxuGGK1VepB98ehF-lBtM,50170
524
524
  chia/full_node/pending_tx_cache.py,sha256=jEL004L7r1n7ppeSdLVe6nsVNfYw0jU86DHmRYe1rH4,3657
525
525
  chia/full_node/signage_point.py,sha256=vtHQk3P0HLfOVpenGqmgqBoWnf_WCETHRplL_id9KyA,414
526
526
  chia/full_node/subscriptions.py,sha256=ItPMnGTgrfdzoeyC5TBbamrVWKvYnpabSLYUDBI5bmI,8706
@@ -530,7 +530,7 @@ chia/full_node/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
530
530
  chia/full_node/weight_proof.py,sha256=oa-xC0Pzt-VZlFCvFYwZ5ZqlHiWzmUe1id8eEiVv6oE,71403
531
531
  chia/harvester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
532
532
  chia/harvester/harvester.py,sha256=u2_trZnVRdsXGmuqfivA05aD2qjJjv2DNOyejLh8KAw,11825
533
- chia/harvester/harvester_api.py,sha256=e7-gDXy0-YhTRT2zdw-BDTBCII-sZs9EwxpE0BnZdZE,18711
533
+ chia/harvester/harvester_api.py,sha256=-lzphxuk28fag82kpqLHmzhVrc441ForV6dDOja-zLo,18726
534
534
  chia/introducer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
535
535
  chia/introducer/introducer.py,sha256=InaxWLt8EprM7409cg2y03Qwm_641nLbnv6-sAjBp1Q,5535
536
536
  chia/introducer/introducer_api.py,sha256=VB1V0geTzVVi8UMdqUxqYt_fdFjUcATM8fjd-nMJnN8,2779
@@ -623,7 +623,7 @@ chia/server/upnp.py,sha256=o9zrYEBMFjnMq2-vUu7e5trPCP6JDPhn_5_jlQZX8f8,3746
623
623
  chia/server/ws_connection.py,sha256=6OM8xd0zb8RPtTqF0RRQ_UKLJjLHbRUov7BlszZsLvk,33801
624
624
  chia/simulator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
625
625
  chia/simulator/add_blocks_in_batches.py,sha256=YrQDNKkdrR9gaO0BDN_uSfycZWYQ2wZjw0FTxmmscmY,2460
626
- chia/simulator/block_tools.py,sha256=O7FK_1eAEsajZPbL9IOENcwwCd_JMQLKWkpxnJKRc-0,90747
626
+ chia/simulator/block_tools.py,sha256=w3HawvkcKcNLYKetFZIXxLAZHlN0lBDa7yvMfcBDQeM,90747
627
627
  chia/simulator/full_node_simulator.py,sha256=W-n6DemccWw42yws2PBtpkAw6G5JQvtCJ9K_EnmK7fg,34758
628
628
  chia/simulator/keyring.py,sha256=RcumEHJy51zKeK0MZr7iqlugF8vPNRJvXDzXUgQ3y2A,5067
629
629
  chia/simulator/setup_services.py,sha256=16ivXZM2iUfo9-6F0l-E0JHuOP00b15vQ4F-NwurhyY,19255
@@ -653,7 +653,7 @@ chia/ssl/dst_root_ca.pem,sha256=E5peSk4PpQU3jHLF9wCTTOgzP05rG1CIhsSw6xT0vpk,1200
653
653
  chia/timelord/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
654
654
  chia/timelord/iters_from_block.py,sha256=cN9bSrcL5_4zO330o-MykOW3hqJpoALXeGa_YZqTku4,1721
655
655
  chia/timelord/timelord.py,sha256=ChQidb48mV72XsNn-olApar-ME4Ma99Dqgf3biD_hiY,59990
656
- chia/timelord/timelord_api.py,sha256=iHl4cMIZ6EMQuaIZKnEzYeg78xW6yxkHZh0m3w8VaeM,6743
656
+ chia/timelord/timelord_api.py,sha256=14iD1CZt0ltXa5GNigo7KB0Ac8cD_z7HAlOhhm2srgA,7475
657
657
  chia/timelord/timelord_launcher.py,sha256=pgSYZd4-WxDJ7Mq92F7mUSuZfpGIzCpVJ4JdI9UTu04,6239
658
658
  chia/timelord/timelord_state.py,sha256=XLk5ITk1_sKG0SMq4X2J9ejLWSJNmvVAdKhWucrIt90,12022
659
659
  chia/timelord/types.py,sha256=FC5jywOnZvuWZmmM7Kr_O7UaZZLRSFI4WMZuzT-tZAg,354
@@ -663,7 +663,7 @@ chia/types/block_protocol.py,sha256=gXXp3tC8E-__sjC9ZkDzPxw085OifROeT7UuYBl3OcM,
663
663
  chia/types/blockchain_format/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
664
664
  chia/types/blockchain_format/classgroup.py,sha256=H4s9BO1lBroha6IAS96VUFPLKVy4dr8D8EXWhUFP0l8,107
665
665
  chia/types/blockchain_format/coin.py,sha256=n5uynipkU-N0c7vIJAxsbkO2V_zSA2R1LiiNkOqtl0Q,672
666
- chia/types/blockchain_format/program.py,sha256=fVXvH5F5hoH4X6XJ6FWUUMsCfUyXw1MP_v45HqTapWM,9528
666
+ chia/types/blockchain_format/program.py,sha256=3JLAEzFg0EXj1vTuj4LqPXSzY1vnWbYwGAvQj2jMtjs,9559
667
667
  chia/types/blockchain_format/proof_of_space.py,sha256=EyRb5dvFY4Uf4uIn03mOhoVfHmt9r_KdMu3xurSBPp8,4946
668
668
  chia/types/blockchain_format/serialized_program.py,sha256=4pN6DiiLOx3CfXILguFbJCtDOkIKcRrTA8GWfuUU0pY,88
669
669
  chia/types/blockchain_format/slots.py,sha256=te3s0DCBR9XnFJMgzz1e3KahR2zX95noKj0bei_-67g,308
@@ -884,8 +884,8 @@ chia/wallet/wallet_transaction_store.py,sha256=PoSJLKuNNx0X8KVSQ92C5zGKIbBBDtPVW
884
884
  chia/wallet/wallet_user_store.py,sha256=rXiQpk5g8t1X0Chx0bValcQsHonjB1oQ_F_K16bphDA,4089
885
885
  chia/wallet/wallet_weight_proof_handler.py,sha256=d3UvjSP5X2gVIephBR9grutvYRC58Vr-HnV1ezJXrJ8,4914
886
886
  mozilla-ca/cacert.pem,sha256=UKYnfsaRE_AMX9RfCei5eks-MtqjXTqVqzATelU4bO8,233263
887
- chia_blockchain-2.5.3rc1.dist-info/LICENSE,sha256=0tuU-jTzeRDJJaxF2YCEpBwbywgpbrVSXq1i6fJq63U,11347
888
- chia_blockchain-2.5.3rc1.dist-info/METADATA,sha256=MR1qO_acIwnlHPayRZ2mg1mDAmhGWED9Gh7B05AMDbA,10673
889
- chia_blockchain-2.5.3rc1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
890
- chia_blockchain-2.5.3rc1.dist-info/entry_points.txt,sha256=GL2-UvicPVdKz72IP4shnmV3XImfoD5pMzoURfoAYk4,742
891
- chia_blockchain-2.5.3rc1.dist-info/RECORD,,
887
+ chia_blockchain-2.5.4rc1.dist-info/LICENSE,sha256=0tuU-jTzeRDJJaxF2YCEpBwbywgpbrVSXq1i6fJq63U,11347
888
+ chia_blockchain-2.5.4rc1.dist-info/METADATA,sha256=qWftsOSNM2yhNHLtgjKRed9qbLgCvj2_nitjClANJBI,10673
889
+ chia_blockchain-2.5.4rc1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
890
+ chia_blockchain-2.5.4rc1.dist-info/entry_points.txt,sha256=GL2-UvicPVdKz72IP4shnmV3XImfoD5pMzoURfoAYk4,742
891
+ chia_blockchain-2.5.4rc1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.1
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any