Repository: Chia-Network/chia-blockchain
Pull request #19527: use index when fetching SP
Author: login: almogdepaz
Labels: name: full_node
name: Fixed
description: Required label for PR that categorizes merge commit message as "Fixed" for changelog
Created: 2025-04-21T16:22:53Z | Merged: 2025-04-24T15:24:56Z

<!-- Merging Requirements:
- Please give your PR a title that is release-note friendly
- In order to be merged, you must add the most appropriate category Label (Added, Changed, Fixed) to your PR
-->
<!-- Explain why this is an improvement (Does this add missing functionality, improve performance, or reduce complexity?) -->

### Purpose:
improve sp cache lookup

<!-- Does this PR introduce a breaking change? -->

### Current Behavior:
we use the output hash to lookup SP's


### New Behavior:
use SP index as well as the output
<!-- As we aim for complete code coverage, please include details regarding unit, and regression tests -->

### Testing Notes:

<!-- Attach any visual examples, or supporting evidence (attach any .gif/video/console output below) -->



Diff:
diff --git a/chia/_tests/core/full_node/stores/test_full_node_store.py b/chia/_tests/core/full_node/stores/test_full_node_store.py
index cb352ed4f581..7b43fa66e9f7 100644
--- a/chia/_tests/core/full_node/stores/test_full_node_store.py
+++ b/chia/_tests/core/full_node/stores/test_full_node_store.py
@@ -673,6 +673,8 @@ async def test_basic_store(
             )
             assert sp.cc_vdf is not None
             saved_sp_hash = sp.cc_vdf.output.get_hash()
+            saved_index = uint8(i)
+            saved_challenge = sp.cc_vdf.challenge
             assert store.new_signage_point(uint8(i), blockchain, peak, peak.sub_slot_iters, sp)
 
     # Test adding future signage point (bad)
@@ -726,7 +728,10 @@ async def test_basic_store(
     # Get signage point by hash
     assert saved_sp_hash is not None
     assert store.get_signage_point(saved_sp_hash) is not None
+    assert store.get_signage_point_by_index_and_cc_output(saved_sp_hash, saved_challenge, saved_index) is not None
+
     assert store.get_signage_point(std_hash(b"2")) is None
+    assert store.get_signage_point_by_index_and_cc_output(std_hash(b"2"), saved_challenge, saved_index) is None
 
     # Test adding signage points before genesis
     store.initialize_genesis_sub_slot()
@@ -1024,6 +1029,10 @@ def assert_sp_none(sp_index: int, is_none: bool) -> None:
                 assert sp_to_check is not None
                 assert sp_to_check.cc_vdf is not None
                 fetched = store.get_signage_point(sp_to_check.cc_vdf.output.get_hash())
+                fetched_new = store.get_signage_point_by_index_and_cc_output(
+                    sp_to_check.cc_vdf.output.get_hash(), sp_to_check.cc_vdf.challenge, uint8(sp_index)
+                )
+                assert fetched_new == fetched
                 assert (fetched is None) == is_none
                 if fetched is not None:
                     assert fetched == sp_to_check
diff --git a/chia/_tests/core/full_node/test_full_node.py b/chia/_tests/core/full_node/test_full_node.py
index 404197dfe9c4..7c89a339de63 100644
--- a/chia/_tests/core/full_node/test_full_node.py
+++ b/chia/_tests/core/full_node/test_full_node.py
@@ -1925,6 +1925,12 @@ async def test_new_signage_point_caching(
         fnp.RespondSignagePoint(uint8(4), sp.cc_vdf, sp.cc_proof, sp.rc_vdf, sp.rc_proof), peer
     )
     assert full_node_1.full_node.full_node_store.get_signage_point(sp.cc_vdf.output.get_hash()) is None
+    assert (
+        full_node_1.full_node.full_node_store.get_signage_point_by_index_and_cc_output(
+            sp.cc_vdf.output.get_hash(), sp.cc_vdf.challenge, uint8(4)
+        )
+        is None
+    )
     assert len(full_node_1.full_node.full_node_store.future_sp_cache[sp.rc_vdf.challenge]) == 1
 
     # Add block
@@ -1932,6 +1938,16 @@ async def test_new_signage_point_caching(
 
     # Now signage point should be added
     assert full_node_1.full_node.full_node_store.get_signage_point(sp.cc_vdf.output.get_hash()) is not None
+    assert (
+        full_node_1.full_node.full_node_store.get_signage_point_by_index_and_cc_output(
+            sp.cc_vdf.output.get_hash(), sp.cc_vdf.challenge, uint8(4)
+        )
+        is not None
+    )
+
+    assert full_node_1.full_node.full_node_store.get_signage_point_by_index_and_cc_output(
+        full_node_1.full_node.constants.GENESIS_CHALLENGE, bytes32.zeros, uint8(0)
+    ) == SignagePoint(None, None, None, None)
 
 
 @pytest.mark.anyio
@@ -2906,7 +2922,13 @@ async def test_declare_proof_of_space_no_overflow(
     full_node_api, _, server_1, _, _, _, _ = wallet_nodes
     wallet = WalletTool(test_constants)
     coinbase_puzzlehash = wallet.get_new_puzzlehash()
-    blocks = get_blocks_for_test(10, bt, coinbase=coinbase_puzzlehash, force_overflow=False)
+    blocks = bt.get_consecutive_blocks(
+        num_blocks=10,
+        skip_overflow=True,
+        force_overflow=False,
+        farmer_reward_puzzle_hash=coinbase_puzzlehash,
+        guarantee_transaction_block=True,
+    )
     await add_blocks_in_batches(blocks, full_node_api.full_node)
     _, dummy_node_id = await add_dummy_connection(server_1, self_hostname, 12312)
     dummy_peer = server_1.all_connections[dummy_node_id]
@@ -2915,7 +2937,13 @@ async def test_declare_proof_of_space_no_overflow(
         sb = await add_tx_to_mempool(
             full_node_api, wallet, blocks[-8], coinbase_puzzlehash, bytes32(i.to_bytes(32, "big")), uint64(i)
         )
-        blocks = get_blocks_for_test(1, bt, blocks, coinbase_puzzlehash, False, spend_bundle=sb)
+        blocks = bt.get_consecutive_blocks(
+            block_list_input=blocks,
+            num_blocks=1,
+            farmer_reward_puzzle_hash=coinbase_puzzlehash,
+            guarantee_transaction_block=True,
+            transaction_data=sb,
+        )
         block = blocks[-1]
         unfinised_block = await declare_pos_unfinished_block(full_node_api, dummy_peer, block)
         compare_unfinished_blocks(unfinished_from_full_block(block), unfinised_block)
@@ -2935,7 +2963,11 @@ async def test_declare_proof_of_space_overflow(
     full_node_api, _, server_1, _, _, _, _ = wallet_nodes
     wallet = WalletTool(test_constants)
     coinbase_puzzlehash = wallet.get_new_puzzlehash()
-    blocks = get_blocks_for_test(10, bt, coinbase=coinbase_puzzlehash, force_overflow=True)
+    blocks = bt.get_consecutive_blocks(
+        num_blocks=10,
+        farmer_reward_puzzle_hash=coinbase_puzzlehash,
+        guarantee_transaction_block=True,
+    )
     await add_blocks_in_batches(blocks, full_node_api.full_node)
     _, dummy_node_id = await add_dummy_connection(server_1, self_hostname, 12312)
     dummy_peer = server_1.all_connections[dummy_node_id]
@@ -2944,7 +2976,17 @@ async def test_declare_proof_of_space_overflow(
         sb = await add_tx_to_mempool(
             full_node_api, wallet, blocks[-8], coinbase_puzzlehash, bytes32(i.to_bytes(32, "big")), uint64(i)
         )
-        blocks = get_blocks_for_test(1, bt, blocks, coinbase_puzzlehash, force_overflow=True, spend_bundle=sb)
+
+        blocks = bt.get_consecutive_blocks(
+            block_list_input=blocks,
+            num_blocks=1,
+            skip_overflow=False,
+            force_overflow=(i % 10 == 0),
+            farmer_reward_puzzle_hash=coinbase_puzzlehash,
+            guarantee_transaction_block=True,
+            transaction_data=sb,
+        )
+
         block = blocks[-1]
         unfinised_block = await declare_pos_unfinished_block(full_node_api, dummy_peer, block)
         compare_unfinished_blocks(unfinished_from_full_block(block), unfinised_block)
@@ -2952,89 +2994,6 @@ async def test_declare_proof_of_space_overflow(
         assert full_node_api.full_node.blockchain.get_peak_height() == block.height
 
 
-def get_blocks_for_test(
-    num_of_blocks: int,
-    bt: BlockTools,
-    blocks: list[FullBlock] = [],
-    coinbase: Optional[bytes32] = None,
-    force_overflow: bool = False,
-    spend_bundle: Optional[SpendBundle] = None,
-) -> list[FullBlock]:
-    collisions = 0
-    cc_sub_slot_sps: dict[bytes32, None] = {}
-    rc_sub_slot_sps: dict[bytes32, None] = {}
-    transaction_block = False if coinbase is None else True
-    slots = 0
-    for i in range(len(blocks) - 1, -1, -1):
-        curr = blocks[i]
-        if curr.reward_chain_block.challenge_chain_sp_vdf is not None:
-            cc_sub_slot_sps[curr.reward_chain_block.challenge_chain_sp_vdf.output.get_hash()] = None
-            assert curr.reward_chain_block.reward_chain_sp_vdf is not None
-            rc_sub_slot_sps[curr.reward_chain_block.reward_chain_sp_vdf.output.get_hash()] = None
-        if curr.finished_sub_slots and (slots := slots + 1) == 2:
-            break
-
-    for i in range(0, num_of_blocks):
-        new_block = bt.get_consecutive_blocks(
-            block_list_input=blocks,
-            num_blocks=1,
-            skip_overflow=not force_overflow,
-            force_overflow=(i % 10 == 0) and force_overflow,
-            farmer_reward_puzzle_hash=coinbase,
-            guarantee_transaction_block=transaction_block,
-            transaction_data=spend_bundle,
-        )[-1]
-        if new_block.reward_chain_block.challenge_chain_sp_vdf is not None:
-            cc_sp_hash = new_block.reward_chain_block.challenge_chain_sp_vdf.output.get_hash()
-            assert new_block.reward_chain_block.reward_chain_sp_vdf is not None
-            rc_sp_hash = new_block.reward_chain_block.reward_chain_sp_vdf.output.get_hash()
-            min_signage_point = -1
-            number_of_slots = len(new_block.finished_sub_slots)
-            while cc_sp_hash in cc_sub_slot_sps or rc_sp_hash in rc_sub_slot_sps:
-                collisions += 1
-                skip_slots = False
-                if (
-                    new_block.reward_chain_block.signage_point_index > bt.constants.NUM_SPS_SUB_SLOT - 2
-                    or len(new_block.finished_sub_slots) > number_of_slots
-                    or min_signage_point == new_block.reward_chain_block.signage_point_index + 1
-                ):
-                    skip_slots = True
-                    min_signage_point = -1
-                else:
-                    min_signage_point = new_block.reward_chain_block.signage_point_index + 1
-
-                number_of_slots = len(new_block.finished_sub_slots)
-                new_block = bt.get_consecutive_blocks(
-                    block_list_input=blocks,
-                    min_signage_point=min_signage_point,
-                    num_blocks=1,
-                    skip_slots=number_of_slots + 1 if skip_slots else 0,
-                    force_overflow=(i % 10 == 0) and force_overflow,
-                    skip_overflow=not force_overflow,
-                    farmer_reward_puzzle_hash=coinbase,
-                    guarantee_transaction_block=transaction_block,
-                    transaction_data=spend_bundle,
-                )[-1]
-                if new_block.reward_chain_block.challenge_chain_sp_vdf is None:
-                    break
-                else:
-                    cc_sp_hash = new_block.reward_chain_block.challenge_chain_sp_vdf.output.get_hash()
-                    flag_one, flag_two = False, False
-                    if cc_sp_hash not in cc_sub_slot_sps:
-                        cc_sub_slot_sps[cc_sp_hash] = None
-                        flag_one = True
-                    assert new_block.reward_chain_block.reward_chain_sp_vdf is not None
-                    rc_sp_hash = new_block.reward_chain_block.reward_chain_sp_vdf.output.get_hash()
-                    if rc_sp_hash not in rc_sub_slot_sps:
-                        rc_sub_slot_sps[rc_sp_hash] = None
-                        flag_two = True
-                    if flag_one and flag_two:
-                        break
-        blocks.append(new_block)
-    logging.debug(f"Collisions: {collisions} height {blocks[-1].height}")
-    return blocks
-
-
 def unfinished_from_full_block(block: FullBlock) -> UnfinishedBlock:
     unfinished_block_expected = UnfinishedBlock(
         block.finished_sub_slots,
diff --git a/chia/full_node/full_node_api.py b/chia/full_node/full_node_api.py
index 713616d5acdb..d8290b1e6195 100644
--- a/chia/full_node/full_node_api.py
+++ b/chia/full_node/full_node_api.py
@@ -717,8 +717,10 @@ async def respond_signage_point(
                 request.reward_chain_vdf.challenge,
             ):
                 return None
-            existing_sp = self.full_node.full_node_store.get_signage_point(
-                request.challenge_chain_vdf.output.get_hash()
+            existing_sp = self.full_node.full_node_store.get_signage_point_by_index_and_cc_output(
+                request.challenge_chain_vdf.output.get_hash(),
+                request.challenge_chain_vdf.challenge,
+                request.index_from_challenge,
             )
             if existing_sp is not None and existing_sp.rc_vdf == request.reward_chain_vdf:
                 return None
@@ -795,8 +797,8 @@ async def declare_proof_of_space(
             return None
 
         async with self.full_node.timelord_lock:
-            sp_vdfs: Optional[SignagePoint] = self.full_node.full_node_store.get_signage_point(
-                request.challenge_chain_sp
+            sp_vdfs: Optional[SignagePoint] = self.full_node.full_node_store.get_signage_point_by_index_and_cc_output(
+                request.challenge_chain_sp, request.challenge_hash, request.signage_point_index
             )
 
             if sp_vdfs is None:
diff --git a/chia/full_node/full_node_store.py b/chia/full_node/full_node_store.py
index 5e90a29adffc..e5c6ad55a3a4 100644
--- a/chia/full_node/full_node_store.py
+++ b/chia/full_node/full_node_store.py
@@ -830,6 +830,25 @@ def get_signage_point(self, cc_signage_point: bytes32) -> Optional[SignagePoint]
                         return sp
         return None
 
+    def get_signage_point_by_index_and_cc_output(
+        self, cc_signage_point: bytes32, challenge: bytes32, index: uint8
+    ) -> Optional[SignagePoint]:
+        assert len(self.finished_sub_slots) >= 1
+        if cc_signage_point == self.constants.GENESIS_CHALLENGE:
+            return SignagePoint(None, None, None, None)
+        for sub_slot, sps, _ in self.finished_sub_slots:
+            if sub_slot is not None and sub_slot.challenge_chain.get_hash() == challenge:
+                if index == 0:
+                    # first SP in the sub slot
+                    return SignagePoint(None, None, None, None)
+                sp: Optional[SignagePoint] = sps[index]
+                if sp is None:
+                    return None
+                assert sp.cc_vdf is not None
+                if sp.cc_vdf.output.get_hash() == cc_signage_point:
+                    return sp
+        return None
+
     def get_signage_point_by_index(
         self, challenge_hash: bytes32, index: uint8, last_rc_infusion: bytes32
     ) -> Optional[SignagePoint]:


Reviews:
user: login: arvidn
body: feel free to resolve my comments once you've considered them
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:14:24Z
user: login: almogdepaz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T11:52:57Z
user: login: almogdepaz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T11:56:08Z
user: login: almogdepaz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T11:59:36Z
user: login: arvidn
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T12:35:14Z

Review comments:
diff_hunk: @@ -830,6 +830,24 @@ def get_signage_point(self, cc_signage_point: bytes32) -> Optional[SignagePoint]
                         return sp
         return None
 
+    def get_signage_point_by_index_and_cc_output(
+        self, cc_signage_point: bytes32, challenge: bytes32, index: uint8
+    ) -> Optional[SignagePoint]:
+        assert len(self.finished_sub_slots) >= 1
+        if cc_signage_point == self.constants.GENESIS_CHALLENGE:
+            return SignagePoint(None, None, None, None)
+        for sub_slot, sps, _ in self.finished_sub_slots:
+            if sub_slot is not None and sub_slot.challenge_chain.get_hash() == challenge:
+                if index == 0:
+                    return SignagePoint(None, None, None, None)
+                sp: Optional[SignagePoint] = sps[index]
+                if sp is None:
+                    return None
+                assert sp.cc_vdf is not None
path: chia/full_node/full_node_store.py
user: login: arvidn
body: an alternative here would be to `continue` instead of asserting, but maybe this is an invariant that warrants a more fatal error if violated
created_at: 2025-04-24T09:10:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -830,6 +830,24 @@ def get_signage_point(self, cc_signage_point: bytes32) -> Optional[SignagePoint]
                         return sp
         return None
 
+    def get_signage_point_by_index_and_cc_output(
+        self, cc_signage_point: bytes32, challenge: bytes32, index: uint8
+    ) -> Optional[SignagePoint]:
+        assert len(self.finished_sub_slots) >= 1
+        if cc_signage_point == self.constants.GENESIS_CHALLENGE:
+            return SignagePoint(None, None, None, None)
+        for sub_slot, sps, _ in self.finished_sub_slots:
+            if sub_slot is not None and sub_slot.challenge_chain.get_hash() == challenge:
+                if index == 0:
+                    return SignagePoint(None, None, None, None)
path: chia/full_node/full_node_store.py
user: login: arvidn
body: It's not obvious what the distinction is between a `SignagePoint` full of `None` and just returning `None`.
It might warrant a comment
created_at: 2025-04-24T09:12:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -830,6 +830,24 @@ def get_signage_point(self, cc_signage_point: bytes32) -> Optional[SignagePoint]
                         return sp
         return None
 
+    def get_signage_point_by_index_and_cc_output(
path: chia/full_node/full_node_store.py
user: login: arvidn
body: I think it might make sense to document, in a comment, that we really only need to also look-up by index in tests, where the discriminant is very small causing collisions in the vdf proofs.
created_at: 2025-04-24T09:13:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -830,6 +830,24 @@ def get_signage_point(self, cc_signage_point: bytes32) -> Optional[SignagePoint]
                         return sp
         return None
 
+    def get_signage_point_by_index_and_cc_output(
+        self, cc_signage_point: bytes32, challenge: bytes32, index: uint8
+    ) -> Optional[SignagePoint]:
+        assert len(self.finished_sub_slots) >= 1
+        if cc_signage_point == self.constants.GENESIS_CHALLENGE:
+            return SignagePoint(None, None, None, None)
+        for sub_slot, sps, _ in self.finished_sub_slots:
+            if sub_slot is not None and sub_slot.challenge_chain.get_hash() == challenge:
+                if index == 0:
+                    return SignagePoint(None, None, None, None)
+                sp: Optional[SignagePoint] = sps[index]
+                if sp is None:
+                    return None
+                assert sp.cc_vdf is not None
path: chia/full_node/full_node_store.py
user: login: almogdepaz
body: yes the cc_vdf can only be None at the start of the slot i.e index 0
created_at: 2025-04-24T11:52:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -830,6 +830,24 @@ def get_signage_point(self, cc_signage_point: bytes32) -> Optional[SignagePoint]
                         return sp
         return None
 
+    def get_signage_point_by_index_and_cc_output(
+        self, cc_signage_point: bytes32, challenge: bytes32, index: uint8
+    ) -> Optional[SignagePoint]:
+        assert len(self.finished_sub_slots) >= 1
+        if cc_signage_point == self.constants.GENESIS_CHALLENGE:
+            return SignagePoint(None, None, None, None)
+        for sub_slot, sps, _ in self.finished_sub_slots:
+            if sub_slot is not None and sub_slot.challenge_chain.get_hash() == challenge:
+                if index == 0:
+                    return SignagePoint(None, None, None, None)
path: chia/full_node/full_node_store.py
user: login: almogdepaz
body: SignagePoint(None, None, None, None) is the first SP in the slot, None is we failed to find the SP
created_at: 2025-04-24T11:56:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -830,6 +830,24 @@ def get_signage_point(self, cc_signage_point: bytes32) -> Optional[SignagePoint]
                         return sp
         return None
 
+    def get_signage_point_by_index_and_cc_output(
path: chia/full_node/full_node_store.py
user: login: almogdepaz
body: im not yet sure that this is the case, i think its more correct to lookup like this
we already do that in get_signage_point_by_index but that uses reward chain hash to validate which i dont have in the cases i handeld, so this is pretty much the same just with the challenge chain value
created_at: 2025-04-24T11:59:36Z
author_association: CONTRIBUTOR

PR comments:
user: login: coveralls-official[bot]
created_at: 2025-04-21T19:50:32Z
body: ## Pull Request Test Coverage Report for [Build 14641095606](https://coveralls.io/builds/73366593)

### Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

- For more information on this, see <a target="_blank" href="https://docs.coveralls.io/build-types#tracking-coverage-changes-with-pull_request-builds">Tracking coverage changes with pull request builds</a>.
- To avoid this issue with future PRs, see these <a target="_blank" href="https://docs.coveralls.io/build-types#recommended-ci-configurations">Recommended CI Configurations</a>.
- For a quick fix, <a target="_blank" href="https://github.blog/changelog/2022-02-03-more-ways-to-keep-your-pull-request-branch-up-to-date/#update-your-pull-request-branch-by-rebasing">rebase this PR at GitHub</a>. Your next report should be accurate.

### Details

* **35** of **36**   **(97.22%)**  changed or added relevant lines in **4** files are covered.
* **1206** unchanged lines in **71** files lost coverage.
* Overall coverage increased (+**0.1%**) to **91.232%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [chia/full_node/full_node_api.py](https://coveralls.io/builds/73366593/source?filename=chia%2Ffull_node%2Ffull_node_api.py#L720) | 3 | 4 | 75.0%
<!-- | **Total:** | **35** | **36** | **97.22%** | -->

|  Files with Coverage Reduction | New Missed Lines | % |
| :-----|--------------|--: |
| [chia/consensus/full_block_to_block_record.py](https://coveralls.io/builds/73366593/source?filename=chia%2Fconsensus%2Ffull_block_to_block_record.py#L30) | 1 | 98.61% |
| [chia/consensus/make_sub_epoch_summary.py](https://coveralls.io/builds/73366593/source?filename=chia%2Fconsensus%2Fmake_sub_epoch_summary.py#L141) | 1 | 98.73% |
| [chia/consensus/vdf_info_computation.py](https://coveralls.io/builds/73366593/source?filename=chia%2Fconsensus%2Fvdf_info_computation.py#L146) | 1 | 99.06% |
| [chia/daemon/server.py](https://coveralls.io/builds/73366593/source?filename=chia%2Fdaemon%2Fserver.py#L309) | 1 | 83.46% |
| [chia/protocols/pool_protocol.py](https://coveralls.io/builds/73366593/source?filename=chia%2Fprotocols%2Fpool_protocol.py#L176) | 1 | 99.14% |
| [chia/_tests/clvm/test_puzzles.py](https://coveralls.io/builds/73366593/source?filename=chia%2F_tests%2Fclvm%2Ftest_puzzles.py#L85) | 1 | 99.23% |
| [chia/_tests/core/full_node/test_conditions.py](https://coveralls.io/builds/73366593/source?filename=chia%2F_tests%2Fcore%2Ffull_node%2Ftest_conditions.py#L33) | 1 | 99.53% |
| [chia/_tests/core/mempool/test_mempool_manager.py](https://coveralls.io/builds/73366593/source?filename=chia%2F_tests%2Fcore%2Fmempool%2Ftest_mempool_manager.py#L1315) | 1 | 99.93% |
| [chia/_tests/core/test_full_node_rpc.py](https://coveralls.io/builds/73366593/source?filename=chia%2F_tests%2Fcore%2Ftest_full_node_rpc.py#L67) | 1 | 99.76% |
| [chia/_tests/farmer_harvester/test_farmer.py](https://coveralls.io/builds/73366593/source?filename=chia%2F_tests%2Ffarmer_harvester%2Ftest_farmer.py#L1001) | 1 | 99.7% |
<!-- | **Total:** | **1206** |  | -->

|  Totals | [![Coverage Status](https://coveralls.io/builds/73366593/badge)](https://coveralls.io/builds/73366593) |
| :-- | --: |
| Change from base [Build 14576056907](https://coveralls.io/builds/73313657): |  0.1% |
| Covered Lines: | 100815 |
| Relevant Lines: | 110389 |

---
##### 💛  - [Coveralls](https://coveralls.io)

author_association: NONE