fix: Query all validators for blockchain state, use longest chain

Problem: When querying blockchain state via get_blockchain_state(),
the backend only queried validator-1 (via _get_healthy_validator()).
If validator-1 was behind other validators in block synchronization,
the backend would return stale data without the latest blocks.

This caused: Users' votes would be submitted to all validators and
included in blocks, but when querying the blockchain, the backend
would return an old state without those blocks.

Root cause: No block synchronization between validators yet. When a
validator creates a block, it doesn't immediately get to all peers.
So different validators can have different chain lengths.

Solution: Query ALL healthy validators for their blockchain state
and return the state from the one with the longest chain. This ensures
the client always gets the most up-to-date blockchain state.

Implementation:
- Loop through all healthy_validators
- Query each one's blockchain endpoint
- Track the state with the highest block count
- Return that state

This is a best-effort approach while block synchronization is being
established between validators.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Alexis Bruneteau 2025-11-07 17:07:34 +01:00
parent d7ec538ed2
commit 38369a7f88

View File

@ -360,21 +360,28 @@ class BlockchainClient:
""" """
Get the current state of the blockchain for an election. Get the current state of the blockchain for an election.
Queries ALL healthy validators and returns the state from the validator
with the longest chain (to ensure latest blocks).
Args: Args:
election_id: Election ID election_id: Election ID
Returns: Returns:
Blockchain state with block count and verification status Blockchain state with block count and verification status
""" """
validator = self._get_healthy_validator() if not self.healthy_validators:
if not validator:
return None return None
try:
if not self._client: if not self._client:
raise Exception("AsyncClient not initialized") raise Exception("AsyncClient not initialized")
# Query blockchain info endpoint on validator # Query all validators and get the one with longest chain
best_state = None
best_block_count = 0
for validator in self.healthy_validators:
try:
logger.debug(f"Querying blockchain state from {validator.node_id}")
response = await self._client.get( response = await self._client.get(
f"{validator.rpc_url}/blockchain", f"{validator.rpc_url}/blockchain",
params={"election_id": election_id}, params={"election_id": election_id},
@ -382,11 +389,23 @@ class BlockchainClient:
) )
response.raise_for_status() response.raise_for_status()
return response.json() state = response.json()
# Get block count from this validator
block_count = len(state.get("blocks", []))
logger.debug(f"{validator.node_id} has {block_count} blocks")
# Keep the state with the most blocks (longest chain)
if block_count > best_block_count:
best_state = state
best_block_count = block_count
logger.info(f"Using state from {validator.node_id} ({block_count} blocks)")
except Exception as e: except Exception as e:
logger.warning(f"Failed to get blockchain state: {e}") logger.warning(f"Failed to get blockchain state from {validator.node_id}: {e}")
return None continue
return best_state if best_state else None
async def verify_blockchain_integrity(self, election_id: int) -> bool: async def verify_blockchain_integrity(self, election_id: int) -> bool:
""" """