Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
44 changes: 21 additions & 23 deletions chia/_tests/core/test_full_node_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ async def test1(

peak_block = await client.get_block(state["peak"].header_hash)
assert peak_block == blocks[-1]
assert (await client.get_block(bytes32([1] * 32))) is None

with pytest.raises(ValueError, match="not found"):
await client.get_block(bytes32([1] * 32))
block_record = await client.get_block_record_by_height(2)
assert block_record is not None
assert block_record.header_hash == blocks[2].header_hash
Expand Down Expand Up @@ -150,8 +150,9 @@ async def test1(

assert len(await client.get_all_mempool_items()) == 0
assert len(await client.get_all_mempool_tx_ids()) == 0
assert (await client.get_mempool_item_by_tx_id(spend_bundle.name())) is None
assert (await client.get_mempool_item_by_tx_id(spend_bundle.name(), False)) is None
with pytest.raises(ValueError, match="not in the mempool"):
await client.get_mempool_item_by_tx_id(spend_bundle.name())
await client.get_mempool_item_by_tx_id(spend_bundle.name(), False)

await client.push_tx(spend_bundle)
coin = spend_bundle.additions()[0]
Expand All @@ -168,7 +169,8 @@ async def test1(
mempool_item = await client.get_mempool_item_by_tx_id(spend_bundle.name())
assert mempool_item is not None
assert WalletSpendBundle.from_json_dict(mempool_item["spend_bundle"]) == spend_bundle
assert (await client.get_coin_record_by_name(coin.name())) is None
with pytest.raises(ValueError, match="not found"):
await client.get_coin_record_by_name(coin.name())

# Verify that the include_pending arg to get_mempool_item_by_tx_id works
coin_to_spend_pending = included_reward_coins[1]
Expand All @@ -181,11 +183,11 @@ async def test1(
condition_dic=condition_dic,
)
await client.push_tx(spend_bundle_pending)
# not strictly in the mempool
assert (await client.get_mempool_item_by_tx_id(spend_bundle_pending.name(), False)) is None
with pytest.raises(ValueError, match="not in the mempool"):
# not strictly in the mempool
await client.get_mempool_item_by_tx_id(spend_bundle_pending.name(), False)
# pending entry into mempool, so include_pending fetches
mempool_item = await client.get_mempool_item_by_tx_id(spend_bundle_pending.name(), True)
assert mempool_item is not None
assert WalletSpendBundle.from_json_dict(mempool_item["spend_bundle"]) == spend_bundle_pending

await full_node_api_1.farm_new_transaction_block(FarmNewBlockProtocol(ph_2))
Expand Down Expand Up @@ -454,17 +456,14 @@ async def test_signage_points(
full_node_service_1.config,
) as client:
# Only provide one
res = await client.get_recent_signage_point_or_eos(None, None)
assert res is None
res = await client.get_recent_signage_point_or_eos(std_hash(b"0"), std_hash(b"1"))
assert res is None

with pytest.raises(ValueError, match="sp_hash or challenge_hash must be provided."):
await client.get_recent_signage_point_or_eos(None, None)
with pytest.raises(ValueError, match="Either sp_hash or challenge_hash must be provided, not both."):
await client.get_recent_signage_point_or_eos(std_hash(b"0"), std_hash(b"1"))
# Not found
res = await client.get_recent_signage_point_or_eos(std_hash(b"0"), None)
assert res is None
res = await client.get_recent_signage_point_or_eos(None, std_hash(b"0"))
assert res is None

with pytest.raises(ValueError, match="in cache"):
await client.get_recent_signage_point_or_eos(std_hash(b"0"), None)
await client.get_recent_signage_point_or_eos(None, std_hash(b"0"))
blocks = bt.get_consecutive_blocks(5)
for block in blocks:
await full_node_api_1.full_node.add_block(block)
Expand Down Expand Up @@ -494,8 +493,8 @@ async def test_signage_points(
assert sp.rc_proof is not None
assert sp.rc_vdf is not None
# Don't have SP yet
res = await client.get_recent_signage_point_or_eos(sp.cc_vdf.output.get_hash(), None)
assert res is None
with pytest.raises(ValueError, match="Did not find sp"):
await client.get_recent_signage_point_or_eos(sp.cc_vdf.output.get_hash(), None)

# Add the last block
await full_node_api_1.full_node.add_block(blocks[-1])
Expand All @@ -517,9 +516,8 @@ async def test_signage_points(
selected_eos = blocks[-1].finished_sub_slots[0]

# Don't have EOS yet
res = await client.get_recent_signage_point_or_eos(None, selected_eos.challenge_chain.get_hash())
assert res is None

with pytest.raises(ValueError, match="Did not find eos"):
await client.get_recent_signage_point_or_eos(None, selected_eos.challenge_chain.get_hash())
# Properly fetch an EOS
for eos in blocks[-1].finished_sub_slots:
await full_node_api_1.full_node.add_end_of_sub_slot(eos, peer)
Expand Down
4 changes: 2 additions & 2 deletions chia/farmer/farmer_rpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class FarmerRpcClient(RpcClient):
async def get_signage_point(self, sp_hash: bytes32) -> Optional[dict[str, Any]]:
try:
return await self.fetch("get_signage_point", {"sp_hash": sp_hash.hex()})
except ValueError:
except ValueError: # not synced
return None

async def get_signage_points(self) -> list[dict[str, Any]]:
Expand Down Expand Up @@ -83,5 +83,5 @@ async def get_pool_login_link(self, launcher_id: bytes32) -> Optional[str]:
try:
result = await self.fetch("get_pool_login_link", {"launcher_id": launcher_id.hex()})
return cast(Optional[str], result["login_link"])
except ValueError:
except ValueError: # not connected to pool.
return None
151 changes: 64 additions & 87 deletions chia/full_node/full_node_rpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from chia_rs.sized_ints import uint32

from chia.consensus.signage_point import SignagePoint
from chia.rpc.rpc_client import RpcClient
from chia.rpc.rpc_client import ResponseFailureError, RpcClient
from chia.types.coin_record import CoinRecord
from chia.types.coin_spend import CoinSpendWithConditions
from chia.types.condition_opcodes import ConditionOpcode
Expand Down Expand Up @@ -36,11 +36,8 @@ async def get_blockchain_state(self) -> dict[str, Any]:
response["blockchain_state"]["peak"] = BlockRecord.from_json_dict(response["blockchain_state"]["peak"])
return cast(dict[str, Any], response["blockchain_state"])

async def get_block(self, header_hash: bytes32) -> Optional[FullBlock]:
try:
response = await self.fetch("get_block", {"header_hash": header_hash.hex()})
except Exception:
return None
async def get_block(self, header_hash: bytes32) -> FullBlock:
response = await self.fetch("get_block", {"header_hash": header_hash.hex()})
return FullBlock.from_json_dict(response["block"])

async def get_blocks(self, start: int, end: int, exclude_reorged: bool = False) -> list[FullBlock]:
Expand All @@ -52,16 +49,15 @@ async def get_blocks(self, start: int, end: int, exclude_reorged: bool = False)
async def get_block_record_by_height(self, height: int) -> Optional[BlockRecord]:
try:
response = await self.fetch("get_block_record_by_height", {"height": height})
except Exception:
return None
except ResponseFailureError as e: # Block Height not found
if e.response["error"] == f"Block height {height} not found in chain":
return None
raise e
return BlockRecord.from_json_dict(response["block_record"])

async def get_block_record(self, header_hash: bytes32) -> Optional[BlockRecord]:
try:
response = await self.fetch("get_block_record", {"header_hash": header_hash.hex()})
if response["block_record"] is None:
return None
except Exception:
response = await self.fetch("get_block_record", {"header_hash": header_hash.hex()})
if response["block_record"] is None:
return None
return BlockRecord.from_json_dict(response["block_record"])

Expand All @@ -84,12 +80,8 @@ async def get_network_space(self, newer_block_header_hash: bytes32, older_block_

return cast(int, network_space_bytes_estimate["space"])

async def get_coin_record_by_name(self, coin_id: bytes32) -> Optional[CoinRecord]:
try:
response = await self.fetch("get_coin_record_by_name", {"name": coin_id.hex()})
except Exception:
return None

async def get_coin_record_by_name(self, coin_id: bytes32) -> CoinRecord:
response = await self.fetch("get_coin_record_by_name", {"name": coin_id.hex()})
return CoinRecord.from_json_dict(coin_record_dict_backwards_compat(response["coin_record"]))

async def get_coin_records_by_names(
Expand Down Expand Up @@ -180,10 +172,7 @@ async def get_coin_records_by_hint(
return [CoinRecord.from_json_dict(coin_record_dict_backwards_compat(coin)) for coin in response["coin_records"]]

async def get_additions_and_removals(self, header_hash: bytes32) -> tuple[list[CoinRecord], list[CoinRecord]]:
try:
response = await self.fetch("get_additions_and_removals", {"header_hash": header_hash.hex()})
except Exception:
return [], []
response = await self.fetch("get_additions_and_removals", {"header_hash": header_hash.hex()})
removals = []
additions = []
for coin_record in response["removals"]:
Expand All @@ -195,51 +184,43 @@ async def get_additions_and_removals(self, header_hash: bytes32) -> tuple[list[C
async def get_block_records(self, start: int, end: int) -> list[dict[str, Any]]:
try:
response = await self.fetch("get_block_records", {"start": start, "end": end})
if response["block_records"] is None:
except ResponseFailureError as e: # No Peak Yet
if e.response["error"] == "Peak is None":
return []
except Exception:
raise e
if response["block_records"] is None:
return []
# TODO: return block records
return cast(list[dict[str, Any]], response["block_records"])

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()})
output = []
for block_spend in response["block_spends"]:
output.append(CoinSpend.from_json_dict(block_spend))
return output
except Exception:
return None

async def get_block_spends_with_conditions(self, header_hash: bytes32) -> Optional[list[CoinSpendWithConditions]]:
try:
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"]:
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:
return None
async def get_block_spends(self, header_hash: bytes32) -> list[CoinSpend]:
response = await self.fetch("get_block_spends", {"header_hash": header_hash.hex()})
output = []
for block_spend in response["block_spends"]:
output.append(CoinSpend.from_json_dict(block_spend))
return output

async def get_block_spends_with_conditions(self, header_hash: bytes32) -> list[CoinSpendWithConditions]:
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"]:
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

async def push_tx(self, spend_bundle: SpendBundle) -> dict[str, Any]:
return await self.fetch("push_tx", {"spend_bundle": spend_bundle.to_json_dict()})

async def get_puzzle_and_solution(self, coin_id: bytes32, height: uint32) -> Optional[CoinSpend]:
try:
response = await self.fetch("get_puzzle_and_solution", {"coin_id": coin_id.hex(), "height": height})
return CoinSpend.from_json_dict(response["coin_solution"])
except Exception:
return None
async def get_puzzle_and_solution(self, coin_id: bytes32, height: uint32) -> CoinSpend:
response = await self.fetch("get_puzzle_and_solution", {"coin_id": coin_id.hex(), "height": height})
return CoinSpend.from_json_dict(response["coin_solution"])

async def get_all_mempool_tx_ids(self) -> list[bytes32]:
response = await self.fetch("get_all_mempool_tx_ids", {})
Expand All @@ -256,14 +237,11 @@ async def get_mempool_item_by_tx_id(
self,
tx_id: bytes32,
include_pending: bool = False,
) -> Optional[dict[str, Any]]:
try:
response = await self.fetch(
"get_mempool_item_by_tx_id", {"tx_id": tx_id.hex(), "include_pending": include_pending}
)
return cast(dict[str, Any], response["mempool_item"])
except Exception:
return None
) -> dict[str, Any]:
response = await self.fetch(
"get_mempool_item_by_tx_id", {"tx_id": tx_id.hex(), "include_pending": include_pending}
)
return cast(dict[str, Any], response["mempool_item"])

async def get_mempool_items_by_coin_name(self, coin_name: bytes32) -> dict[str, Any]:
response = await self.fetch("get_mempool_items_by_coin_name", {"coin_name": coin_name.hex()})
Expand All @@ -275,26 +253,25 @@ async def create_block_generator(self) -> Optional[dict[str, Any]]:

async def get_recent_signage_point_or_eos(
self, sp_hash: Optional[bytes32], challenge_hash: Optional[bytes32]
) -> Optional[Any]:
try:
if sp_hash is not None:
assert challenge_hash is None
response = await self.fetch("get_recent_signage_point_or_eos", {"sp_hash": sp_hash.hex()})
return {
"signage_point": SignagePoint.from_json_dict(response["signage_point"]),
"time_received": response["time_received"],
"reverted": response["reverted"],
}
else:
assert challenge_hash is not None
response = await self.fetch("get_recent_signage_point_or_eos", {"challenge_hash": challenge_hash.hex()})
return {
"eos": EndOfSubSlotBundle.from_json_dict(response["eos"]),
"time_received": response["time_received"],
"reverted": response["reverted"],
}
except Exception:
return None
) -> dict[str, Any]:
if sp_hash is not None and challenge_hash is not None:
raise ValueError("Either sp_hash or challenge_hash must be provided, not both.")
elif sp_hash is not None:
response = await self.fetch("get_recent_signage_point_or_eos", {"sp_hash": sp_hash.hex()})
return {
"signage_point": SignagePoint.from_json_dict(response["signage_point"]),
"time_received": response["time_received"],
"reverted": response["reverted"],
}
elif challenge_hash is not None:
response = await self.fetch("get_recent_signage_point_or_eos", {"challenge_hash": challenge_hash.hex()})
return {
"eos": EndOfSubSlotBundle.from_json_dict(response["eos"]),
"time_received": response["time_received"],
"reverted": response["reverted"],
}
else:
raise ValueError("sp_hash or challenge_hash must be provided.")

async def get_fee_estimate(
self,
Expand Down
2 changes: 1 addition & 1 deletion chia/wallet/wallet_rpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ async def cat_asset_id_to_name(self, asset_id: bytes32) -> Optional[tuple[Option
request = {"asset_id": asset_id.hex()}
try:
res = await self.fetch("cat_asset_id_to_name", request)
except ValueError:
except ValueError: # This happens if the asset_id is unknown
return None

wallet_id: Optional[uint32] = None if res["wallet_id"] is None else uint32(int(res["wallet_id"]))
Expand Down
18 changes: 12 additions & 6 deletions tools/validate_rpcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,29 +153,35 @@ async def node_spends_with_conditions(
block_hash: bytes32,
height: int,
) -> None:
result = await node_client.get_block_spends_with_conditions(block_hash)
if result is None:
try:
await node_client.get_block_spends_with_conditions(block_hash)
except Exception as e:
print(f"ERROR: [{height}] get_block_spends_with_conditions returned invalid result")
raise e


async def node_block_spends(
node_client: FullNodeRpcClient,
block_hash: bytes32,
height: int,
) -> None:
result = await node_client.get_block_spends(block_hash)
if result is None:
try:
await node_client.get_block_spends(block_hash)
except Exception as e:
print(f"ERROR: [{height}] get_block_spends returned invalid result")
raise e


async def node_additions_removals(
node_client: FullNodeRpcClient,
block_hash: bytes32,
height: int,
) -> None:
response = await node_client.get_additions_and_removals(block_hash)
if response is None:
try:
await node_client.get_additions_and_removals(block_hash)
except Exception as e:
print(f"ERROR: [{height}] get_additions_and_removals returned invalid result")
raise e


async def cli_async(
Expand Down
Loading