Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions chia/_tests/core/full_node/test_generator_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import pytest
from chia_rs import Program as SerializedProgram
from chia_rs import SpendBundleConditions, SpendConditions
from chia_rs import (
SpendBundleConditions,
SpendConditions,
get_spends_for_trusted_block,
get_spends_for_trusted_block_with_conditions,
)
from chia_rs.sized_bytes import bytes32
from chia_rs.sized_ints import uint32, uint64

from chia.consensus.generator_tools import tx_removals_and_additions
from chia.full_node.mempool_check_conditions import get_spends_for_block, get_spends_for_block_with_conditions
from chia.simulator.block_tools import test_constants
from chia.types.blockchain_format.coin import Coin
from chia.types.generator_types import BlockGenerator
Expand Down Expand Up @@ -102,12 +106,14 @@ def test_empty_conditions() -> None:


def test_get_spends_for_block(caplog: pytest.LogCaptureFixture) -> None:
conditions = get_spends_for_block(TEST_GENERATOR, 100, test_constants)
assert conditions == []
assert "get_spends_for_block() encountered a puzzle we couldn't serialize: " in caplog.text
conditions = get_spends_for_trusted_block(
test_constants, TEST_GENERATOR.program, TEST_GENERATOR.generator_refs, 100
)
assert conditions[0]["block_spends"] == []


def test_get_spends_for_block_with_conditions(caplog: pytest.LogCaptureFixture) -> None:
conditions = get_spends_for_block_with_conditions(TEST_GENERATOR, 100, test_constants)
conditions = get_spends_for_trusted_block_with_conditions(
test_constants, TEST_GENERATOR.program, TEST_GENERATOR.generator_refs, 100
)
assert conditions == []
assert "get_spends_for_block_with_conditions() encountered a puzzle we couldn't serialize: " in caplog.text
14 changes: 12 additions & 2 deletions chia/_tests/core/mempool/test_mempool.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
G2Element,
SpendBundle,
SpendBundleConditions,
get_flags_for_height_and_constants,
run_block_generator2,
)
from chia_rs import get_puzzle_and_solution_for_coin2 as get_puzzle_and_solution_for_coin
from chia_rs.sized_bytes import bytes32
from chia_rs.sized_ints import uint32, uint64
from clvm_tools import binutils
Expand Down Expand Up @@ -46,7 +48,6 @@
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_puzzle_and_solution_for_coin
from chia.full_node.mempool_manager import MEMPOOL_MIN_FEE_INCREASE, LineageInfoCache
from chia.full_node.pending_tx_cache import ConflictTxCache, PendingTxCache
from chia.protocols import full_node_protocol, wallet_protocol
Expand Down Expand Up @@ -3198,7 +3199,16 @@ def test_get_puzzle_and_solution_for_coin_failure() -> None:
with pytest.raises(
ValueError, match=f"Failed to get puzzle and solution for coin {TEST_COIN}, error: \\('coin not found', '80'\\)"
):
get_puzzle_and_solution_for_coin(BlockGenerator(SerializedProgram.to(None), []), TEST_COIN, 0, test_constants)
try:
get_puzzle_and_solution_for_coin(
SerializedProgram.to(None),
[],
test_constants.MAX_BLOCK_COST_CLVM,
TEST_COIN,
get_flags_for_height_and_constants(0, test_constants),
)
except Exception as e:
raise ValueError(f"Failed to get puzzle and solution for coin {TEST_COIN}, error: {e}") from e


@pytest.mark.parametrize("old", [True, False])
Expand Down
41 changes: 31 additions & 10 deletions chia/_tests/core/test_cost_calculation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import pathlib

import pytest
from chia_rs import G1Element
from chia_rs import G1Element, get_flags_for_height_and_constants
from chia_rs import get_puzzle_and_solution_for_coin2 as get_puzzle_and_solution_for_coin
from chia_rs.sized_bytes import bytes32
from chia_rs.sized_ints import uint32, uint64
from clvm_tools import binutils
Expand All @@ -16,7 +17,6 @@
from chia.consensus.cost_calculator import NPCResult
from chia.consensus.default_constants import DEFAULT_CONSTANTS
from chia.full_node.bundle_tools import simple_solution_generator
from chia.full_node.mempool_check_conditions import get_puzzle_and_solution_for_coin
from chia.simulator.block_tools import BlockTools, test_constants
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program, run_with_cost
Expand Down Expand Up @@ -89,9 +89,15 @@ async def test_basics(softfork_height: int, bt: BlockTools) -> None:
coin_spend = spend_bundle.coin_spends[0]
assert npc_result.conds is not None
assert coin_spend.coin.name() == npc_result.conds.spends[0].coin_id
spend_info = get_puzzle_and_solution_for_coin(program, coin_spend.coin, softfork_height, bt.constants)
assert spend_info.puzzle == coin_spend.puzzle_reveal
assert spend_info.solution == coin_spend.solution
puzzle, solution = get_puzzle_and_solution_for_coin(
program.program,
program.generator_refs,
bt.constants.MAX_BLOCK_COST_CLVM,
coin_spend.coin,
get_flags_for_height_and_constants(softfork_height, bt.constants),
)
assert puzzle == coin_spend.puzzle_reveal
assert solution == coin_spend.solution

if softfork_height >= bt.constants.HARD_FORK_HEIGHT:
clvm_cost = 27360
Expand Down Expand Up @@ -170,8 +176,14 @@ async def test_mempool_mode(softfork_height: int, bt: BlockTools) -> None:
bytes32.fromhex("14947eb0e69ee8fc8279190fc2d38cb4bbb61ba28f1a270cfd643a0e8d759576"),
uint64(300),
)
spend_info = get_puzzle_and_solution_for_coin(generator, coin, softfork_height, bt.constants)
assert spend_info.puzzle == puzzle.to_serialized()
puz, _solution = get_puzzle_and_solution_for_coin(
generator.program,
generator.generator_refs,
bt.constants.MAX_BLOCK_COST_CLVM,
coin,
get_flags_for_height_and_constants(0, bt.constants),
)
assert puz == puzzle.to_serialized()


@pytest.mark.anyio
Expand Down Expand Up @@ -282,8 +294,11 @@ async def test_standard_tx(benchmark_runner: BenchmarkRunner) -> None:

@pytest.mark.anyio
async def test_get_puzzle_and_solution_for_coin_performance(benchmark_runner: BenchmarkRunner) -> None:
from chia_puzzles_py.programs import CHIALISP_DESERIALISATION

from chia._tests.core.large_block import LARGE_BLOCK
from chia.full_node.mempool_check_conditions import DESERIALIZE_MOD

DESERIALIZE_MOD = Program.from_bytes(CHIALISP_DESERIALISATION)

assert LARGE_BLOCK.transactions_generator is not None
# first, list all spent coins in the block
Expand All @@ -309,5 +324,11 @@ async def test_get_puzzle_and_solution_for_coin_performance(benchmark_runner: Be
with benchmark_runner.assert_runtime(seconds=8.5):
for _ in range(3):
for c in spent_coins:
spend_info = get_puzzle_and_solution_for_coin(generator, c, 0, test_constants)
assert spend_info.puzzle.get_tree_hash() == c.puzzle_hash
puz, _solution = get_puzzle_and_solution_for_coin(
generator.program,
generator.generator_refs,
test_constants.MAX_BLOCK_COST_CLVM,
c,
get_flags_for_height_and_constants(0, test_constants),
)
assert puz.get_tree_hash() == c.puzzle_hash
5 changes: 4 additions & 1 deletion chia/_tests/core/test_full_node_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ async def test1(
assert coin_spend_with_conditions.coin_spend.solution == SerializedProgram.fromhex(
"ff80ffff01ffff33ffa063c767818f8b7cc8f3760ce34a09b7f34cd9ddf09d345c679b6897e7620c575cff8601977420dc0080ffff3cffa0a2366d6d8e1ce7496175528f5618a13da8401b02f2bac1eaae8f28aea9ee54798080ff8080"
)
assert coin_spend_with_conditions.conditions == [

expected = [
ConditionWithArgs(
ConditionOpcode(b"2"),
[
Expand All @@ -279,6 +280,8 @@ async def test1(
),
]

assert coin_spend_with_conditions.conditions == expected

coin_spend_with_conditions = block_spends_with_conditions[2]

assert coin_spend_with_conditions.coin_spend.coin == Coin(
Expand Down
12 changes: 9 additions & 3 deletions chia/_tests/util/spend_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
get_flags_for_height_and_constants,
run_block_generator2,
)
from chia_rs import get_puzzle_and_solution_for_coin2 as get_puzzle_and_solution_for_coin
from chia_rs.sized_bytes import bytes32
from chia_rs.sized_ints import uint32, uint64
from typing_extensions import Self
Expand All @@ -30,7 +31,6 @@
from chia.full_node.coin_store import CoinStore
from chia.full_node.hint_store import HintStore
from chia.full_node.mempool import Mempool
from chia.full_node.mempool_check_conditions import get_puzzle_and_solution_for_coin
from chia.full_node.mempool_manager import MempoolManager
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import INFINITE_COST
Expand Down Expand Up @@ -442,8 +442,14 @@ async def get_puzzle_and_solution(self, coin_id: bytes32, height: uint32) -> Coi
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
spend_info = get_puzzle_and_solution_for_coin(generator, coin_record.coin, height, self.service.defaults)
return CoinSpend(coin_record.coin, spend_info.puzzle, spend_info.solution)
puzzle, solution = get_puzzle_and_solution_for_coin(
generator.program,
generator.generator_refs,
self.service.defaults.MAX_BLOCK_COST_CLVM,
coin_record.coin,
get_flags_for_height_and_constants(height, self.service.defaults),
)
return CoinSpend(coin_record.coin, puzzle, solution)

async def get_all_mempool_tx_ids(self) -> list[bytes32]:
return self.service.mempool_manager.mempool.all_item_ids()
Expand Down
13 changes: 7 additions & 6 deletions chia/full_node/full_node_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
additions_and_removals,
get_flags_for_height_and_constants,
)
from chia_rs import get_puzzle_and_solution_for_coin2 as get_puzzle_and_solution_for_coin
from chia_rs.sized_bytes import bytes32
from chia_rs.sized_ints import uint8, uint32, uint64, uint128
from chiabip158 import PyBIP158
Expand All @@ -43,7 +44,6 @@
from chia.full_node.coin_store import CoinStore
from chia.full_node.fee_estimator_interface import FeeEstimatorInterface
from chia.full_node.full_block_utils import get_height_and_tx_status_from_block, header_block_from_block
from chia.full_node.mempool_check_conditions import get_puzzle_and_solution_for_coin
from chia.full_node.tx_processing_queue import TransactionQueueEntry, TransactionQueueFull
from chia.protocols import farmer_protocol, full_node_protocol, introducer_protocol, timelord_protocol, wallet_protocol
from chia.protocols.fee_estimate import FeeEstimate, FeeEstimateGroup, fee_rate_v2_to_v1
Expand Down Expand Up @@ -1427,17 +1427,18 @@ async def request_puzzle_solution(self, request: wallet_protocol.RequestPuzzleSo
)
assert block_generator is not None
try:
spend_info = await asyncio.get_running_loop().run_in_executor(
puzzle, solution = await asyncio.get_running_loop().run_in_executor(
self.executor,
get_puzzle_and_solution_for_coin,
block_generator,
block_generator.program,
block_generator.generator_refs,
self.full_node.constants.MAX_BLOCK_COST_CLVM,
coin_record.coin,
height,
self.full_node.constants,
get_flags_for_height_and_constants(height, self.full_node.constants),
)
except ValueError:
return reject_msg
wrapper = PuzzleSolutionResponse(coin_name, height, spend_info.puzzle, spend_info.solution)
wrapper = PuzzleSolutionResponse(coin_name, height, puzzle, solution)
response = wallet_protocol.RespondPuzzleSolution(wrapper)
response_msg = make_msg(ProtocolMessageTypes.respond_puzzle_solution, response)
return response_msg
Expand Down
56 changes: 29 additions & 27 deletions chia/full_node/full_node_rpc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
PlotSize,
SpendBundle,
SpendBundleConditions,
get_flags_for_height_and_constants,
get_spends_for_trusted_block,
get_spends_for_trusted_block_with_conditions,
run_block_generator2,
)
from chia_rs import get_puzzle_and_solution_for_coin2 as get_puzzle_and_solution_for_coin
from chia_rs.sized_bytes import bytes32
from chia_rs.sized_ints import uint32, uint64, uint128

Expand All @@ -23,11 +27,6 @@
from chia.consensus.pos_quality import UI_ACTUAL_SPACE_CONSTANT_FACTOR
from chia.full_node.fee_estimator_interface import FeeEstimatorInterface
from chia.full_node.full_node import FullNode
from chia.full_node.mempool_check_conditions import (
get_puzzle_and_solution_for_coin,
get_spends_for_block,
get_spends_for_block_with_conditions,
)
from chia.protocols.outbound_message import NodeType
from chia.rpc.rpc_server import Endpoint, EndpointResult
from chia.types.blockchain_format.proof_of_space import calculate_prefix_bits
Expand Down Expand Up @@ -482,14 +481,19 @@ async def get_block_spends(self, request: dict[str, Any]) -> EndpointResult:
if full_block is None:
raise ValueError(f"Block {header_hash.hex()} not found")

spends: list[CoinSpend] = []
spends: list[dict[str, list[CoinSpend]]] = []
block_generator = await get_block_generator(self.service.blockchain.lookup_block_generators, full_block)
if block_generator is None: # if block is not a transaction block.
return {"block_spends": spends}

spends = get_spends_for_block(block_generator, full_block.height, self.service.constants)
spends = get_spends_for_trusted_block(
self.service.constants,
block_generator.program,
block_generator.generator_refs,
get_flags_for_height_and_constants(full_block.height, self.service.constants),
)

return {"block_spends": spends}
return spends[0]

async def get_block_spends_with_conditions(self, request: dict[str, Any]) -> EndpointResult:
if "header_hash" not in request:
Expand All @@ -503,22 +507,13 @@ async def get_block_spends_with_conditions(self, request: dict[str, Any]) -> End
if block_generator is None: # if block is not a transaction block.
return {"block_spends_with_conditions": []}

spends_with_conditions = get_spends_for_block_with_conditions(
block_generator, full_block.height, self.service.constants
spends_with_conditions = get_spends_for_trusted_block_with_conditions(
self.service.constants,
block_generator.program,
block_generator.generator_refs,
get_flags_for_height_and_constants(full_block.height, self.service.constants),
)

return {
"block_spends_with_conditions": [
{
"coin_spend": spend_with_conditions.coin_spend,
"conditions": [
{"opcode": condition.opcode, "vars": [var.hex() for var in condition.vars]}
for condition in spend_with_conditions.conditions
],
}
for spend_with_conditions in spends_with_conditions
]
}
return {"block_spends_with_conditions": spends_with_conditions}

async def get_block_record_by_height(self, request: dict[str, Any]) -> EndpointResult:
if "height" not in request:
Expand Down Expand Up @@ -787,10 +782,17 @@ async def get_puzzle_and_solution(self, request: dict[str, Any]) -> EndpointResu
)
assert block_generator is not None

spend_info = get_puzzle_and_solution_for_coin(
block_generator, coin_record.coin, block.height, self.service.constants
)
return {"coin_solution": CoinSpend(coin_record.coin, spend_info.puzzle, spend_info.solution)}
try:
puzzle, solution = get_puzzle_and_solution_for_coin(
block_generator.program,
block_generator.generator_refs,
self.service.constants.MAX_BLOCK_COST_CLVM,
coin_record.coin,
get_flags_for_height_and_constants(block.height, self.service.constants),
)
return {"coin_solution": CoinSpend(coin_record.coin, puzzle, solution)}
except Exception as e:
raise ValueError(f"Failed to get puzzle and solution for coin {coin_record.coin}, error: {e}") from e

async def get_additions_and_removals(self, request: dict[str, Any]) -> EndpointResult:
if "header_hash" not in request:
Expand Down
19 changes: 15 additions & 4 deletions chia/full_node/full_node_rpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
from chia.rpc.rpc_client import RpcClient
from chia.types.coin_record import CoinRecord
from chia.types.coin_spend import CoinSpendWithConditions
from chia.types.condition_opcodes import ConditionOpcode
from chia.types.condition_with_args import ConditionWithArgs
from chia.types.unfinished_header_block import UnfinishedHeaderBlock
from chia.util.byte_types import hexstr_to_bytes


def coin_record_dict_backwards_compat(coin_record: dict[str, Any]) -> dict[str, Any]:
Expand Down Expand Up @@ -202,10 +205,10 @@ async def get_block_records(self, start: int, end: int) -> list[dict[str, Any]]:
async def get_block_spends(self, header_hash: bytes32) -> Optional[list[CoinSpend]]:
try:
response = await self.fetch("get_block_spends", {"header_hash": header_hash.hex()})
block_spends = []
output = []
for block_spend in response["block_spends"]:
block_spends.append(CoinSpend.from_json_dict(block_spend))
return block_spends
output.append(CoinSpend.from_json_dict(block_spend))
return output
except Exception:
return None

Expand All @@ -214,7 +217,15 @@ async def get_block_spends_with_conditions(self, header_hash: bytes32) -> Option
response = await self.fetch("get_block_spends_with_conditions", {"header_hash": header_hash.hex()})
block_spends: list[CoinSpendWithConditions] = []
for block_spend in response["block_spends_with_conditions"]:
block_spends.append(CoinSpendWithConditions.from_json_dict(block_spend))
coin_spend = CoinSpend.from_json_dict(block_spend["coin_spend"])
cond_tuples = block_spend["conditions"]
conditions = []
for condition in cond_tuples:
cwa = ConditionWithArgs(
opcode=ConditionOpcode(bytes([condition[0]])), vars=[hexstr_to_bytes(b) for b in condition[1]]
)
conditions.append(cwa)
block_spends.append(CoinSpendWithConditions(coin_spend=coin_spend, conditions=conditions))
return block_spends

except Exception:
Expand Down
Loading
Loading