Skip to content

Commit

Permalink
Get block spends (#16451)
Browse files Browse the repository at this point in the history
  • Loading branch information
arvidn authored Oct 10, 2023
1 parent 0ada945 commit fe8ae58
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 41 deletions.
2 changes: 1 addition & 1 deletion chia/clvm/spend_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ 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, 0)
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)

async def get_all_mempool_tx_ids(self) -> List[bytes32]:
Expand Down
12 changes: 6 additions & 6 deletions chia/full_node/full_node_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple

from blspy import AugSchemeMPL, G1Element, G2Element
from chia_rs import ALLOW_BACKREFS
from chiabip158 import PyBIP158

from chia.consensus.block_creation import create_unfinished_block
Expand Down Expand Up @@ -1323,12 +1322,13 @@ async def request_puzzle_solution(self, request: wallet_protocol.RequestPuzzleSo
block_generator: Optional[BlockGenerator] = await self.full_node.blockchain.get_block_generator(block)
assert block_generator is not None
try:
flags = 0
if height >= self.full_node.constants.HARD_FORK_HEIGHT:
flags = ALLOW_BACKREFS

spend_info = await asyncio.get_running_loop().run_in_executor(
self.executor, get_puzzle_and_solution_for_coin, block_generator, coin_record.coin, flags
self.executor,
get_puzzle_and_solution_for_coin,
block_generator,
coin_record.coin,
height,
self.full_node.constants,
)
except ValueError:
return reject_msg
Expand Down
42 changes: 25 additions & 17 deletions chia/full_node/mempool_check_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,8 @@
log = logging.getLogger(__name__)


def get_name_puzzle_conditions(
generator: BlockGenerator,
max_cost: int,
*,
mempool_mode: bool,
height: uint32,
constants: ConsensusConstants,
) -> NPCResult:
run_block = run_block_generator

flags = 0
if mempool_mode:
flags = flags | MEMPOOL_MODE
def get_flags_for_height_and_constants(height: int, constants: ConsensusConstants) -> int:
flags = ENABLE_ASSERT_BEFORE | NO_RELATIVE_CONDITIONS_ON_EPHEMERAL

if height >= constants.SOFT_FORK2_HEIGHT:
flags = flags | ENABLE_ASSERT_BEFORE | NO_RELATIVE_CONDITIONS_ON_EPHEMERAL
Expand Down Expand Up @@ -93,6 +82,23 @@ def get_name_puzzle_conditions(
| ALLOW_BACKREFS
)

return flags


def get_name_puzzle_conditions(
generator: BlockGenerator,
max_cost: int,
*,
mempool_mode: bool,
height: uint32,
constants: ConsensusConstants,
) -> NPCResult:
run_block = run_block_generator
flags = get_flags_for_height_and_constants(height, constants)

if mempool_mode:
flags = flags | MEMPOOL_MODE

if height >= constants.HARD_FORK_FIX_HEIGHT:
run_block = run_block_generator2

Expand All @@ -110,7 +116,9 @@ 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, flags: int) -> SpendInfo:
def get_puzzle_and_solution_for_coin(
generator: BlockGenerator, coin: Coin, height: int, constants: ConsensusConstants
) -> SpendInfo:
try:
args = bytearray(b"\xff")
args += bytes(DESERIALIZE_MOD)
Expand All @@ -125,14 +133,14 @@ def get_puzzle_and_solution_for_coin(generator: BlockGenerator, coin: Coin, flag
coin.parent_coin_info,
coin.amount,
coin.puzzle_hash,
flags,
get_flags_for_height_and_constants(height, constants),
)
return SpendInfo(SerializedProgram.from_bytes(puzzle), SerializedProgram.from_bytes(solution))
except Exception as e:
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]:
def get_spends_for_block(generator: BlockGenerator, height: int, constants: ConsensusConstants) -> List[CoinSpend]:
args = bytearray(b"\xff")
args += bytes(DESERIALIZE_MOD)
args += b"\xff"
Expand All @@ -143,7 +151,7 @@ def get_spends_for_block(generator: BlockGenerator) -> List[CoinSpend]:
bytes(generator.program),
bytes(args),
DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM,
0,
get_flags_for_height_and_constants(height, constants),
)

spends: List[CoinSpend] = []
Expand Down
11 changes: 4 additions & 7 deletions chia/rpc/full_node_rpc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional

from chia_rs import ALLOW_BACKREFS

from chia.consensus.block_record import BlockRecord
from chia.consensus.blockchain import Blockchain, BlockchainMutexPriority
from chia.consensus.cost_calculator import NPCResult
Expand Down Expand Up @@ -478,7 +476,7 @@ async def get_block_spends(self, request: Dict[str, Any]) -> EndpointResult:
if block_generator is None: # if block is not a transaction block.
return {"block_spends": spends}

spends = get_spends_for_block(block_generator)
spends = get_spends_for_block(block_generator, full_block.height, self.service.constants)

return {"block_spends": spends}

Expand Down Expand Up @@ -745,11 +743,10 @@ async def get_puzzle_and_solution(self, request: Dict[str, Any]) -> EndpointResu

block_generator: Optional[BlockGenerator] = await self.service.blockchain.get_block_generator(block)
assert block_generator is not None
flags = 0
if height >= self.service.constants.HARD_FORK_HEIGHT:
flags = ALLOW_BACKREFS

spend_info = get_puzzle_and_solution_for_coin(block_generator, coin_record.coin, flags)
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)}

async def get_additions_and_removals(self, request: Dict[str, Any]) -> EndpointResult:
Expand Down
2 changes: 1 addition & 1 deletion tests/core/mempool/test_mempool.py
Original file line number Diff line number Diff line change
Expand Up @@ -2971,4 +2971,4 @@ 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, 0)
get_puzzle_and_solution_for_coin(BlockGenerator(SerializedProgram(), [], []), TEST_COIN, 0, test_constants)
6 changes: 3 additions & 3 deletions tests/core/test_cost_calculation.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ async def test_basics(self, softfork_height, bt):

coin_spend = spend_bundle.coin_spends[0]
assert coin_spend.coin.name() == npc_result.conds.spends[0].coin_id
spend_info = get_puzzle_and_solution_for_coin(program, coin_spend.coin, 0)
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

Expand Down Expand Up @@ -170,7 +170,7 @@ async def test_mempool_mode(self, softfork_height, bt):
bytes32.fromhex("14947eb0e69ee8fc8279190fc2d38cb4bbb61ba28f1a270cfd643a0e8d759576"),
300,
)
spend_info = get_puzzle_and_solution_for_coin(generator, coin, 0)
spend_info = get_puzzle_and_solution_for_coin(generator, coin, softfork_height, bt.constants)
assert spend_info.puzzle.to_program() == puzzle

@pytest.mark.asyncio
Expand Down Expand Up @@ -304,5 +304,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:
spend_info = get_puzzle_and_solution_for_coin(generator, c, 0)
spend_info = get_puzzle_and_solution_for_coin(generator, c, 0, test_constants)
assert spend_info.puzzle.get_tree_hash() == c.puzzle_hash
9 changes: 6 additions & 3 deletions tests/core/test_full_node_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
from chia.util.hash import std_hash
from chia.util.ints import uint8
from tests.blockchain.blockchain_test_utils import _validate_and_add_block
from tests.conftest import ConsensusMode
from tests.connection_utils import connect_and_get_peer
from tests.util.rpc import validate_get_routes


class TestRpc:
@pytest.mark.limit_consensus_modes(reason="does not depend on consensus rules")
@pytest.mark.asyncio
async def test1(self, two_nodes_sim_and_wallets_services, self_hostname):
async def test1(self, two_nodes_sim_and_wallets_services, self_hostname, consensus_mode):
num_blocks = 5
nodes, _, bt = two_nodes_sim_and_wallets_services
full_node_service_1, full_node_service_2 = nodes
Expand Down Expand Up @@ -222,7 +222,10 @@ async def test1(self, two_nodes_sim_and_wallets_services, self_hostname):
await full_node_api_1.farm_new_transaction_block(FarmNewBlockProtocol(ph_2))
block: FullBlock = (await full_node_api_1.get_all_full_blocks())[-1]

assert len(block.transactions_generator_ref_list) > 0 # compression has occurred
if consensus_mode != ConsensusMode.HARD_FORK_2_0:
# after the hard fork, we don't compress blocks using
# block references anymore
assert len(block.transactions_generator_ref_list) > 0 # compression has occurred

block_spends = await client.get_block_spends(block.header_hash)

Expand Down
7 changes: 4 additions & 3 deletions tests/generator/test_compression.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
simple_solution_generator_backrefs,
)
from chia.full_node.mempool_check_conditions import get_puzzle_and_solution_for_coin
from chia.simulator.block_tools import test_constants
from chia.types.blockchain_format.program import INFINITE_COST, Program
from chia.types.blockchain_format.serialized_program import SerializedProgram
from chia.types.generator_types import BlockGenerator, CompressorArg
Expand Down Expand Up @@ -161,18 +162,18 @@ def test_get_removals_for_single_coin(self) -> None:
ca = CompressorArg(uint32(0), SerializedProgram.from_bytes(original_generator), start, end)
c = compressed_spend_bundle_solution(ca, sb)
removal = sb.coin_spends[0].coin
spend_info = get_puzzle_and_solution_for_coin(c, removal, 0)
spend_info = get_puzzle_and_solution_for_coin(c, removal, 0, test_constants)
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)
spend_info = get_puzzle_and_solution_for_coin(s, removal, 0)
spend_info = get_puzzle_and_solution_for_coin(s, removal, 0, test_constants)
assert bytes(spend_info.puzzle) == bytes(sb.coin_spends[0].puzzle_reveal)
assert bytes(spend_info.solution) == bytes(sb.coin_spends[0].solution)

# test with backrefs (2.0 hard-fork)
s = simple_solution_generator_backrefs(sb)
spend_info = get_puzzle_and_solution_for_coin(s, removal, ALLOW_BACKREFS)
spend_info = get_puzzle_and_solution_for_coin(s, removal, test_constants.HARD_FORK_HEIGHT + 1, test_constants)
assert Program.from_bytes(bytes(spend_info.puzzle)) == Program.from_bytes(
bytes(sb.coin_spends[0].puzzle_reveal)
)
Expand Down

0 comments on commit fe8ae58

Please sign in to comment.