From 6cd555a552010e2e118fbe2d849f6f673f18cd3f Mon Sep 17 00:00:00 2001 From: Alexis Bruneteau Date: Fri, 7 Nov 2025 17:00:14 +0100 Subject: [PATCH] feat: Add transaction broadcasting between PoA validators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Votes were being submitted to one validator but not shared with other validators, preventing them from being included in blocks. Root cause: When a validator received a transaction via eth_sendTransaction, it added it to its pending_transactions pool but did NOT broadcast it to peer validators. Only blocks were being broadcast. This meant: - validator-1 receives vote → adds to pending_transactions - validator-2 (responsible for next block) never receives the vote - validator-2 can't include vote in block because it doesn't know about it - Result: votes sit in pending queue forever Solution: - Add broadcast_transaction() method following same pattern as broadcast_block() - Broadcast transaction to all known peers via /p2p/new_transaction endpoint - Call broadcast on receipt of each transaction - Peer validators receive and add to their pending_transactions pool - All validators now have same pending transactions - Any validator can create blocks with all pending transactions The /p2p/new_transaction endpoint already existed, so validators can now receive and process transactions from peers. This fixes the issue where votes were submitted successfully but never appeared on the blockchain. 🤖 Generated with Claude Code Co-Authored-By: Claude --- e-voting-system/validator/validator.py | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/e-voting-system/validator/validator.py b/e-voting-system/validator/validator.py index c6b9aac..b2a0818 100644 --- a/e-voting-system/validator/validator.py +++ b/e-voting-system/validator/validator.py @@ -411,6 +411,38 @@ class PoAValidator: except Exception as e: logger.error(f"Error creating block: {e}") + async def broadcast_transaction(self, transaction: Transaction) -> None: + """Broadcast transaction to all peers""" + if not self.peer_connections: + logger.debug("No peers to broadcast transaction to") + return + + payload = json.dumps({ + "type": "new_transaction", + "transaction": { + "voter_id": transaction.voter_id, + "election_id": transaction.election_id, + "encrypted_vote": transaction.encrypted_vote, + "ballot_hash": transaction.ballot_hash, + "proof": transaction.proof, + "timestamp": transaction.timestamp + } + }) + + async with aiohttp.ClientSession() as session: + for node_id, peer_url in self.peer_connections.items(): + try: + async with session.post( + f"{peer_url}/p2p/new_transaction", + data=payload, + headers={"Content-Type": "application/json"}, + timeout=aiohttp.ClientTimeout(total=5) + ) as resp: + if resp.status == 200: + logger.debug(f"Transaction broadcast to {node_id}") + except Exception as e: + logger.debug(f"Failed to broadcast transaction to {node_id}: {e}") + async def broadcast_block(self, block: Block) -> None: """Broadcast block to all peers""" if not self.peer_connections: @@ -586,6 +618,9 @@ async def handle_json_rpc(request: dict): transaction = Transaction(**tx_dict) tx_id = validator.add_transaction(transaction) + # Broadcast transaction to other validators + asyncio.create_task(validator.broadcast_transaction(transaction)) + return { "jsonrpc": "2.0", "result": tx_id,