mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-20 08:05:33 +03:00
Added average_block_time
to get_blockchain_state
FullNode RPC API (#15777)
* Added `average_block_time` to `get_blockchain_state` FullNode RPC API * Updated the code according to the review * Fixed lint error * Fixed lint error 2 * Fixed lint error 3 * Applied review suggestion * Fixed lint error * Update * Added tests * Fixed test
This commit is contained in:
parent
e772e4ff69
commit
3b116c738b
@ -4,7 +4,7 @@ from datetime import datetime, timezone
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from chia.consensus.block_record import BlockRecord
|
||||
from chia.consensus.blockchain import BlockchainMutexPriority
|
||||
from chia.consensus.blockchain import Blockchain, BlockchainMutexPriority
|
||||
from chia.consensus.cost_calculator import NPCResult
|
||||
from chia.consensus.pos_quality import UI_ACTUAL_SPACE_CONSTANT_FACTOR
|
||||
from chia.full_node.fee_estimator_interface import FeeEstimatorInterface
|
||||
@ -32,6 +32,46 @@ def coin_record_dict_backwards_compat(coin_record: Dict[str, Any]) -> Dict[str,
|
||||
return coin_record
|
||||
|
||||
|
||||
async def get_nearest_transaction_block(blockchain: Blockchain, block: BlockRecord) -> BlockRecord:
|
||||
if block.is_transaction_block:
|
||||
return block
|
||||
|
||||
prev_hash = blockchain.height_to_hash(block.prev_transaction_block_height)
|
||||
# Genesis block is a transaction block, so theoretically `prev_hash` of all blocks
|
||||
# other than genesis block cannot be `None`.
|
||||
assert prev_hash
|
||||
|
||||
tb = await blockchain.get_block_record_from_db(prev_hash)
|
||||
assert tb
|
||||
|
||||
return tb
|
||||
|
||||
|
||||
async def get_average_block_time(
|
||||
blockchain: Blockchain,
|
||||
base_block: BlockRecord,
|
||||
height_distance: int,
|
||||
) -> Optional[uint32]:
|
||||
newer_block = await get_nearest_transaction_block(blockchain, base_block)
|
||||
if newer_block.height < 1:
|
||||
return None
|
||||
|
||||
prev_height = uint32(max(newer_block.height - 1, newer_block.height - height_distance))
|
||||
prev_hash = blockchain.height_to_hash(prev_height)
|
||||
assert prev_hash
|
||||
prev_block = await blockchain.get_block_record_from_db(prev_hash)
|
||||
assert prev_block
|
||||
|
||||
older_block = await get_nearest_transaction_block(blockchain, prev_block)
|
||||
|
||||
assert newer_block.timestamp is not None and older_block.timestamp is not None
|
||||
|
||||
average_block_time = uint32(
|
||||
(newer_block.timestamp - older_block.timestamp) / (newer_block.height - older_block.height)
|
||||
)
|
||||
return average_block_time
|
||||
|
||||
|
||||
class FullNodeRpcApi:
|
||||
def __init__(self, service: FullNode) -> None:
|
||||
self.service = service
|
||||
@ -129,6 +169,7 @@ class FullNodeRpcApi:
|
||||
"difficulty": 0,
|
||||
"sub_slot_iters": 0,
|
||||
"space": 0,
|
||||
"average_block_time": None,
|
||||
"mempool_size": 0,
|
||||
"mempool_cost": 0,
|
||||
"mempool_min_fees": {
|
||||
@ -167,6 +208,7 @@ class FullNodeRpcApi:
|
||||
else:
|
||||
sync_progress_height = uint32(0)
|
||||
|
||||
average_block_time: Optional[uint32] = None
|
||||
if peak is not None and peak.height > 1:
|
||||
newer_block_hex = peak.header_hash.hex()
|
||||
# Average over the last day
|
||||
@ -176,6 +218,7 @@ class FullNodeRpcApi:
|
||||
space = await self.get_network_space(
|
||||
{"newer_block_header_hash": newer_block_hex, "older_block_header_hash": older_block_hex}
|
||||
)
|
||||
average_block_time = await get_average_block_time(self.service.blockchain, peak, 4608)
|
||||
else:
|
||||
space = {"space": uint128(0)}
|
||||
|
||||
@ -213,6 +256,7 @@ class FullNodeRpcApi:
|
||||
"difficulty": difficulty,
|
||||
"sub_slot_iters": sub_slot_iters,
|
||||
"space": space["space"],
|
||||
"average_block_time": average_block_time,
|
||||
"mempool_size": mempool_size,
|
||||
"mempool_cost": mempool_cost,
|
||||
"mempool_fees": mempool_fees,
|
||||
|
@ -7,9 +7,11 @@ import pytest
|
||||
from blspy import AugSchemeMPL
|
||||
from clvm.casts import int_to_bytes
|
||||
|
||||
from chia.consensus.block_record import BlockRecord
|
||||
from chia.consensus.pot_iterations import is_overflow_block
|
||||
from chia.full_node.signage_point import SignagePoint
|
||||
from chia.protocols import full_node_protocol
|
||||
from chia.rpc.full_node_rpc_api import get_average_block_time, get_nearest_transaction_block
|
||||
from chia.rpc.full_node_rpc_client import FullNodeRpcClient
|
||||
from chia.server.outbound_message import NodeType
|
||||
from chia.simulator.block_tools import get_signage_point
|
||||
@ -427,3 +429,85 @@ class TestRpc:
|
||||
# Checks that the RPC manages to stop the node
|
||||
client.close()
|
||||
await client.await_closed()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_blockchain_state(self, one_wallet_and_one_simulator_services, self_hostname):
|
||||
num_blocks = 5
|
||||
nodes, _, bt = one_wallet_and_one_simulator_services
|
||||
(full_node_service_1,) = nodes
|
||||
full_node_api_1 = full_node_service_1._api
|
||||
|
||||
try:
|
||||
client = await FullNodeRpcClient.create(
|
||||
self_hostname,
|
||||
full_node_service_1.rpc_server.listen_port,
|
||||
full_node_service_1.root_path,
|
||||
full_node_service_1.config,
|
||||
)
|
||||
await validate_get_routes(client, full_node_service_1.rpc_server.rpc_api)
|
||||
state = await client.get_blockchain_state()
|
||||
assert state["peak"] is None
|
||||
assert not state["sync"]["sync_mode"]
|
||||
assert state["difficulty"] > 0
|
||||
assert state["sub_slot_iters"] > 0
|
||||
assert state["space"] == 0
|
||||
assert state["average_block_time"] is None
|
||||
|
||||
blocks: List[FullBlock] = bt.get_consecutive_blocks(num_blocks)
|
||||
blocks = bt.get_consecutive_blocks(num_blocks, block_list_input=blocks, guarantee_transaction_block=True)
|
||||
|
||||
for block in blocks:
|
||||
unf = UnfinishedBlock(
|
||||
block.finished_sub_slots,
|
||||
block.reward_chain_block.get_unfinished(),
|
||||
block.challenge_chain_sp_proof,
|
||||
block.reward_chain_sp_proof,
|
||||
block.foliage,
|
||||
block.foliage_transaction_block,
|
||||
block.transactions_info,
|
||||
block.transactions_generator,
|
||||
[],
|
||||
)
|
||||
await full_node_api_1.full_node.add_unfinished_block(unf, None)
|
||||
await full_node_api_1.full_node.add_block(block, None)
|
||||
|
||||
state = await client.get_blockchain_state()
|
||||
|
||||
assert state["space"] > 0
|
||||
assert state["average_block_time"] > 0
|
||||
|
||||
block_records: List[BlockRecord] = [
|
||||
await full_node_api_1.full_node.blockchain.get_block_record_from_db(rec.header_hash) for rec in blocks
|
||||
]
|
||||
first_non_transaction_block_index = -1
|
||||
for i, b in enumerate(block_records):
|
||||
if not b.is_transaction_block:
|
||||
first_non_transaction_block_index = i
|
||||
break
|
||||
# Genesis block(height=0) must be a transaction block
|
||||
# so first_non_transaction_block_index != 0
|
||||
assert first_non_transaction_block_index > 0
|
||||
|
||||
transaction_blocks: List[BlockRecord] = [b for b in block_records if b.is_transaction_block]
|
||||
non_transaction_block: List[BlockRecord] = [b for b in block_records if not b.is_transaction_block]
|
||||
assert len(transaction_blocks) > 0
|
||||
assert len(non_transaction_block) > 0
|
||||
assert transaction_blocks[0] == await get_nearest_transaction_block(
|
||||
full_node_api_1.full_node.blockchain, transaction_blocks[0]
|
||||
)
|
||||
|
||||
nearest_transaction_block = block_records[first_non_transaction_block_index - 1]
|
||||
expected_nearest_transaction_block = await get_nearest_transaction_block(
|
||||
full_node_api_1.full_node.blockchain, block_records[first_non_transaction_block_index]
|
||||
)
|
||||
assert expected_nearest_transaction_block == nearest_transaction_block
|
||||
# When supplying genesis block, there are no older blocks so `None` should be returned
|
||||
assert await get_average_block_time(full_node_api_1.full_node.blockchain, block_records[0], 4608) is None
|
||||
assert (
|
||||
await get_average_block_time(full_node_api_1.full_node.blockchain, block_records[-1], 4608) is not None
|
||||
)
|
||||
|
||||
finally:
|
||||
# Checks that the RPC manages to stop the node
|
||||
client.close()
|
||||
await client.await_closed()
|
||||
|
Loading…
Reference in New Issue
Block a user