full_node: Refactor `get_puzzle_and_solution_for_coin` (#15323)
This commit is contained in:
parent
eb7376a8c1
commit
9e9c75912f
|
@ -412,19 +412,14 @@ class SimClient:
|
|||
removals: List[CoinRecord] = await self.service.coin_store.get_coins_removed_at_height(block_height)
|
||||
return additions, removals
|
||||
|
||||
async def get_puzzle_and_solution(self, coin_id: bytes32, height: uint32) -> Optional[CoinSpend]:
|
||||
async def get_puzzle_and_solution(self, coin_id: bytes32, height: uint32) -> CoinSpend:
|
||||
filtered_generators = list(filter(lambda block: block.height == height, self.service.blocks))
|
||||
# real consideration should be made for the None cases instead of just hint ignoring
|
||||
generator: BlockGenerator = filtered_generators[0].transactions_generator # type: ignore[assignment]
|
||||
coin_record = await self.service.coin_store.get_coin_record(coin_id)
|
||||
assert coin_record is not None
|
||||
error, puzzle, solution = get_puzzle_and_solution_for_coin(generator, coin_record.coin)
|
||||
if error:
|
||||
return None
|
||||
else:
|
||||
assert puzzle is not None
|
||||
assert solution is not None
|
||||
return CoinSpend(coin_record.coin, puzzle, solution)
|
||||
spend_info = get_puzzle_and_solution_for_coin(generator, coin_record.coin)
|
||||
return CoinSpend(coin_record.coin, spend_info.puzzle, spend_info.solution)
|
||||
|
||||
async def get_all_mempool_tx_ids(self) -> List[bytes32]:
|
||||
return self.service.mempool_manager.mempool.all_item_ids()
|
||||
|
|
|
@ -1302,17 +1302,13 @@ class FullNodeAPI:
|
|||
|
||||
block_generator: Optional[BlockGenerator] = await self.full_node.blockchain.get_block_generator(block)
|
||||
assert block_generator is not None
|
||||
error, puzzle, solution = await asyncio.get_running_loop().run_in_executor(
|
||||
self.executor, get_puzzle_and_solution_for_coin, block_generator, coin_record.coin
|
||||
)
|
||||
|
||||
if error is not None:
|
||||
try:
|
||||
spend_info = await asyncio.get_running_loop().run_in_executor(
|
||||
self.executor, get_puzzle_and_solution_for_coin, block_generator, coin_record.coin
|
||||
)
|
||||
except ValueError:
|
||||
return reject_msg
|
||||
|
||||
assert puzzle is not None
|
||||
assert solution is not None
|
||||
|
||||
wrapper = PuzzleSolutionResponse(coin_name, height, puzzle, solution)
|
||||
wrapper = PuzzleSolutionResponse(coin_name, height, spend_info.puzzle, spend_info.solution)
|
||||
response = wallet_protocol.RespondPuzzleSolution(wrapper)
|
||||
response_msg = make_msg(ProtocolMessageTypes.respond_puzzle_solution, response)
|
||||
return response_msg
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from chia_rs import ENABLE_ASSERT_BEFORE, LIMIT_STACK, MEMPOOL_MODE, NO_RELATIVE_CONDITIONS_ON_EPHEMERAL
|
||||
from chia_rs import get_puzzle_and_solution_for_coin as get_puzzle_and_solution_for_coin_rust
|
||||
|
@ -16,7 +16,7 @@ from chia.types.blockchain_format.program import Program
|
|||
from chia.types.blockchain_format.serialized_program import SerializedProgram
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.coin_record import CoinRecord
|
||||
from chia.types.coin_spend import CoinSpend
|
||||
from chia.types.coin_spend import CoinSpend, SpendInfo
|
||||
from chia.types.generator_types import BlockGenerator
|
||||
from chia.types.spend_bundle_conditions import SpendBundleConditions
|
||||
from chia.util.errors import Err
|
||||
|
@ -62,9 +62,7 @@ def get_name_puzzle_conditions(
|
|||
return NPCResult(uint16(Err.GENERATOR_RUNTIME_ERROR.value), None, uint64(0))
|
||||
|
||||
|
||||
def get_puzzle_and_solution_for_coin(
|
||||
generator: BlockGenerator, coin: Coin
|
||||
) -> Tuple[Optional[Exception], Optional[SerializedProgram], Optional[SerializedProgram]]:
|
||||
def get_puzzle_and_solution_for_coin(generator: BlockGenerator, coin: Coin) -> SpendInfo:
|
||||
try:
|
||||
args = bytearray(b"\xff")
|
||||
args += bytes(DESERIALIZE_MOD)
|
||||
|
@ -80,10 +78,9 @@ def get_puzzle_and_solution_for_coin(
|
|||
coin.amount,
|
||||
coin.puzzle_hash,
|
||||
)
|
||||
|
||||
return None, SerializedProgram.from_bytes(puzzle), SerializedProgram.from_bytes(solution)
|
||||
return SpendInfo(SerializedProgram.from_bytes(puzzle), SerializedProgram.from_bytes(solution))
|
||||
except Exception as e:
|
||||
return e, None, None
|
||||
raise ValueError(f"Failed to get puzzle and solution for coin {coin}, error: {e}") from e
|
||||
|
||||
|
||||
def get_spends_for_block(generator: BlockGenerator) -> List[CoinSpend]:
|
||||
|
|
|
@ -694,14 +694,8 @@ class FullNodeRpcApi:
|
|||
|
||||
block_generator: Optional[BlockGenerator] = await self.service.blockchain.get_block_generator(block)
|
||||
assert block_generator is not None
|
||||
error, puzzle, solution = get_puzzle_and_solution_for_coin(block_generator, coin_record.coin)
|
||||
if error is not None:
|
||||
raise ValueError(f"Error: {error}")
|
||||
|
||||
assert puzzle is not None
|
||||
assert solution is not None
|
||||
|
||||
return {"coin_solution": CoinSpend(coin_record.coin, puzzle, solution)}
|
||||
spend_info = get_puzzle_and_solution_for_coin(block_generator, coin_record.coin)
|
||||
return {"coin_solution": CoinSpend(coin_record.coin, spend_info.puzzle, spend_info.solution)}
|
||||
|
||||
async def get_additions_and_removals(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
if "header_hash" not in request:
|
||||
|
|
|
@ -65,3 +65,10 @@ def compute_additions_with_cost(
|
|||
|
||||
def compute_additions(cs: CoinSpend, *, max_cost: int = DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM) -> List[Coin]:
|
||||
return compute_additions_with_cost(cs, max_cost=max_cost)[0]
|
||||
|
||||
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class SpendInfo(Streamable):
|
||||
puzzle: SerializedProgram
|
||||
solution: SerializedProgram
|
||||
|
|
|
@ -128,8 +128,8 @@ class TestSimClient:
|
|||
assert removals
|
||||
|
||||
# get_puzzle_and_solution
|
||||
coin_solution = await sim_client.get_puzzle_and_solution(spendable_coin.name(), latest_block.height)
|
||||
assert coin_solution
|
||||
coin_spend = await sim_client.get_puzzle_and_solution(spendable_coin.name(), latest_block.height)
|
||||
assert coin_spend == bundle.coin_spends[0]
|
||||
|
||||
# get_coin_records_by_parent_ids
|
||||
new_coin = next(x.coin for x in additions if x.coin.puzzle_hash == puzzle_hash)
|
||||
|
|
|
@ -16,7 +16,7 @@ from chia.full_node.bitcoin_fee_estimator import create_bitcoin_fee_estimator
|
|||
from chia.full_node.fee_estimation import EmptyMempoolInfo, MempoolInfo
|
||||
from chia.full_node.full_node_api import FullNodeAPI
|
||||
from chia.full_node.mempool import Mempool
|
||||
from chia.full_node.mempool_check_conditions import get_name_puzzle_conditions
|
||||
from chia.full_node.mempool_check_conditions import get_name_puzzle_conditions, get_puzzle_and_solution_for_coin
|
||||
from chia.full_node.mempool_manager import MEMPOOL_MIN_FEE_INCREASE
|
||||
from chia.full_node.pending_tx_cache import ConflictTxCache, PendingTxCache
|
||||
from chia.protocols import full_node_protocol, wallet_protocol
|
||||
|
@ -52,6 +52,7 @@ from tests.blockchain.blockchain_test_utils import _validate_and_add_block
|
|||
from tests.connection_utils import add_dummy_connection, connect_and_get_peer
|
||||
from tests.core.mempool.test_mempool_manager import (
|
||||
IDENTITY_PUZZLE_HASH,
|
||||
TEST_COIN,
|
||||
make_test_coins,
|
||||
mempool_item_from_spendbundle,
|
||||
mk_item,
|
||||
|
@ -2945,3 +2946,10 @@ def test_aggregating_on_a_solution_then_a_more_cost_saving_one_appears() -> None
|
|||
# We ran with solution A and missed bigger savings on solution B
|
||||
assert mempool.size() == 5
|
||||
assert [c.coin for c in agg.coin_spends] == [coins[0], coins[1], coins[2]]
|
||||
|
||||
|
||||
def test_get_puzzle_and_solution_for_coin_failure():
|
||||
with pytest.raises(
|
||||
ValueError, match=f"Failed to get puzzle and solution for coin {TEST_COIN}, error: failed to fill whole buffer"
|
||||
):
|
||||
get_puzzle_and_solution_for_coin(BlockGenerator(SerializedProgram(), [], []), TEST_COIN)
|
||||
|
|
|
@ -85,10 +85,9 @@ class TestCostCalculation:
|
|||
|
||||
coin_spend = spend_bundle.coin_spends[0]
|
||||
assert coin_spend.coin.name() == npc_result.conds.spends[0].coin_id
|
||||
error, puzzle, solution = get_puzzle_and_solution_for_coin(program, coin_spend.coin)
|
||||
assert error is None
|
||||
assert puzzle == coin_spend.puzzle_reveal
|
||||
assert solution == coin_spend.solution
|
||||
spend_info = get_puzzle_and_solution_for_coin(program, coin_spend.coin)
|
||||
assert spend_info.puzzle == coin_spend.puzzle_reveal
|
||||
assert spend_info.solution == coin_spend.solution
|
||||
|
||||
clvm_cost = 404560
|
||||
byte_cost = len(bytes(program.program)) * test_constants.COST_PER_BYTE
|
||||
|
@ -156,8 +155,8 @@ class TestCostCalculation:
|
|||
bytes32.fromhex("14947eb0e69ee8fc8279190fc2d38cb4bbb61ba28f1a270cfd643a0e8d759576"),
|
||||
300,
|
||||
)
|
||||
error, puzzle, solution = get_puzzle_and_solution_for_coin(generator, coin)
|
||||
assert error is None
|
||||
spend_info = get_puzzle_and_solution_for_coin(generator, coin)
|
||||
assert spend_info.puzzle.to_program() == puzzle
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_clvm_mempool_mode(self, softfork_height):
|
||||
|
@ -274,6 +273,5 @@ async def test_get_puzzle_and_solution_for_coin_performance():
|
|||
with assert_runtime(seconds=7, label="get_puzzle_and_solution_for_coin"):
|
||||
for i in range(3):
|
||||
for c in spends:
|
||||
err, puzzle, solution = get_puzzle_and_solution_for_coin(generator, c)
|
||||
assert err is None
|
||||
assert puzzle.get_tree_hash() == c.puzzle_hash
|
||||
spend_info = get_puzzle_and_solution_for_coin(generator, c)
|
||||
assert spend_info.puzzle.get_tree_hash() == c.puzzle_hash
|
||||
|
|
|
@ -158,20 +158,14 @@ class TestCompression:
|
|||
ca = CompressorArg(uint32(0), SerializedProgram.from_bytes(original_generator), start, end)
|
||||
c = compressed_spend_bundle_solution(ca, sb)
|
||||
removal = sb.coin_spends[0].coin
|
||||
error, puzzle, solution = get_puzzle_and_solution_for_coin(c, removal)
|
||||
assert error is None
|
||||
assert puzzle is not None
|
||||
assert solution is not None
|
||||
assert bytes(puzzle) == bytes(sb.coin_spends[0].puzzle_reveal)
|
||||
assert bytes(solution) == bytes(sb.coin_spends[0].solution)
|
||||
spend_info = get_puzzle_and_solution_for_coin(c, removal)
|
||||
assert bytes(spend_info.puzzle) == bytes(sb.coin_spends[0].puzzle_reveal)
|
||||
assert bytes(spend_info.solution) == bytes(sb.coin_spends[0].solution)
|
||||
# Test non compressed generator as well
|
||||
s = simple_solution_generator(sb)
|
||||
error, puzzle, solution = get_puzzle_and_solution_for_coin(s, removal)
|
||||
assert error is None
|
||||
assert puzzle is not None
|
||||
assert solution is not None
|
||||
assert bytes(puzzle) == bytes(sb.coin_spends[0].puzzle_reveal)
|
||||
assert bytes(solution) == bytes(sb.coin_spends[0].solution)
|
||||
spend_info = get_puzzle_and_solution_for_coin(s, removal)
|
||||
assert bytes(spend_info.puzzle) == bytes(sb.coin_spends[0].puzzle_reveal)
|
||||
assert bytes(spend_info.solution) == bytes(sb.coin_spends[0].solution)
|
||||
|
||||
def test_spend_byndle_coin_spend(self) -> None:
|
||||
for i in range(0, 10):
|
||||
|
|
Loading…
Reference in New Issue