Implementation language: Python
File: p2p.py
Instantiated inside a peer node (kind of like a network handler in assignments)
-
__init__(self, tracker_ip, tracker_port)
- Variables:
- self.tracker_ip: tracker’s ip address
- self.tracker_port: tracker’s port
- Self.peer_connection_map
- Type: map of tuples
- Key: (ip_address: str, port: int)
- Value: tuple: (connection: str, listening_thread: thread)
- self.socket: socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.tracker_connection: connection to tracker
- self.inbox: a Queue object that buffers data coming from peers
- Logic:
- Call join_network()
- Variables:
-
join_network(self)
- Use self.socket to connect to tracker
- Save the connection to self.tracker_connection
- Send a message to the tracker to request to join the network and get the list of peer ip addresses and ports
- For each ip address and port:
- establish a connection, create a new listening_thread object (passing ip address and port for id)
- Add ip address, port, connection, listening_thread, and outbox queue as a tuple to self.peer_connection_map (use lock for thread safety)
- Start a looping thread to listen for new message (i.e. connection.recv()) from that connection
- Push any new message into self.inbox
- If connection is closed:
- Remove the corresponding tuple from self.peer_connection_map
- Stop the thread
- Start a looping thread to wait for new connection from newly joined peer (i.e. self.socket.accept())
- When a new peer established connection, add that peer’s ip_address, port, connection together with a new inbox queue and outbox queue to self.peer_connection_map (use lock for thread safety)
-
leave_network(self)
- Close all connections including self.tracker_connection and self.peer_connection_map
- Later the tracker and peers will notice that connection is stale when sending a message and can remove it from their list
- Stop all listening_threads
- Close all connections including self.tracker_connection and self.peer_connection_map
-
broadcast(self, message)
- Send message to all the peers in self.peer_connection_map
-
receiveData(self)
- Pop the top message from self.inbox
- Has a list of peer ip addresses, ports, and connections
- Has a looping thread to periodically check the connections for closed ones
- If found a closed connection, remove corresponding data from the list
- Has a looping thread to periodically check the connections for closed ones
- wait_for_request()
- handle_join_request()
- Respond to new peer with updated list
- Update the main list with the new node
- handle_join_request()
BlockchainNode Class:
Responsibilities:
- Maintains a local copy of the blockchain.
- Connects to the network via a tracker by calling joinNetwork(trackerUrl).
- Handles block creation, mining, and broadcasting.
- Manages forks and resolves conflicts.
APIs:
- joinNetwork(trackerUrl): Connects to the tracker and initiates handshake with peers.
- broadcast(block): Sends a mined block to all connected peers using the P2P layer.
- ping(): Periodically pings the tracker to signal liveness (ask on Ed).
- receive(): Handles incoming blocks and data. Internally, it:
- Creates new blocks from incoming data (e.g., from a TransactionServer if applicable).
- Mines blocks by finding a valid nonce (i.e., performs proof of work).
- Broadcasts mined blocks to peers.
- Verifies received blocks before appending them to the chain.
Mining (Proof of Work):
- A block is mined by finding a nonce such that the block hash meets a predefined difficulty requirement.
Fork Handling:
- Maintains a local copy of all active forks.
- When multiple forks are of equal length, randomly selects one for new block additions (or according to some deterministic method).
- When one fork is longer by a defined threshold (e.g., 6 blocks), shorter forks are discarded.
- Before discarding, check whether the shorter fork contains any unique data not yet included in the main chain.
- If such data exists, it is validated using application-specific logic (provided via callback).
- If valid, the data is returned to the mempool for future inclusion (to be mined again).
Block Class:
Fields:
- previousBlockIndex: Index of the preceding block in the chain.
- blockId: Unique identifier for the block's contents (e.g., to distinguish similar-looking transactions).
- previousHash: Hash of the previous block.
- nonce: Value used in mining to satisfy proof-of-work.
- data: Payload (e.g., transaction list or application data).
- hash: Hash of the current block.
- timestamp and optional headers.
Methods:
- computeHash(): Central method to hash the entire block structure deterministically
- setNonceAndGetHash(): Assigns a nonce and returns the resulting block hash (used during mining).
Blockchain Class:
Responsibilities:
- Manages the main chain and any forks.
- Verifies and appends valid blocks.
Methods:
- addBlock(block): Appends a block after validation.
- verifyBlock(block): Ensures block integrity, including:
- Valid proof of work
- Correct linkage to previous hash
- Application-specific data validity
- Optional utility methods
- getLongestChain()
- getChainByForkId()
- isValidChain()
Additional considerations:
- Block: Include Block Height: blockHeight might be useful for fork comparison and indexing.
- Block: Include a Merkle root if you're handling large batches of transactions efficiently.
- Mempool Integration: Add hooks to remove confirmed transactions from the mempool.
Note:
Need the block to contain the signature of the transaction as well
Need 2 types of transaction:
Assign amount to a wallet
Transfer amount between 2 wallets
Add an interface to calculate balance of a wallet after a transaction
Demo application - My Crypto Wallet
- https://developer.bitcoin.org/devguide/wallets.html
- https://bitcoin.stackexchange.com/questions/13069/how-does-the-network-know-my-current-balance
Overview: This demo web app provides a simple user interface to interact with a blockchain wallet. It allows users to:
- View their wallet's public address
- Check their current balance
- Send transactions to other wallets
- View blocks and transactions on the blockchain
Features in wallet:
- Key management: each wallet generate a public private key pair
- Public for wallet address
- Private for signing transactions
- Create Transaction
- User create transaction by specifying recipients wallet address, amount, and sign transaction or not
- Sign transaction using private key (sign by default, by can choose to not sign for demo invalid transaction purposes)
- A unique transaction id for the transaction
- Check balance
- Queries blockchain to calculate balance (summing transaction output associated with its public key)
- Send/ Broadcast transaction (Done in the backend)
- Broadcast transaction to a few block chain nodes
- Peers validate transaction until mined into a block
DemoApp/script.jsDemoApp/wallet_ui.htmlDemoApp/style.cssThe interface is composed of several Bootstrap-powered cards:
Wallet Info
- Address display with a "Copy" button
- Balance display that updates on load and via polling
Send Transaction
- Text input for recipient address
- Number input for amount
- Checkbox to toggle digital signing
- Button to submit a transaction
- Output area showing transaction result
Blockchain View
- Block data displayed as individual cards
- Clicking a card opens a modal with detailed transaction data per block
On Page Load
fetchAddress()retrieves the wallet's public key from the backendfetchBalance()gets blockchain data and computes current balancefetchTransactions()displays current blockchain blocks- If not already connected, a POST /connect request is sent to the blockchain
On Transaction Send
- Input values are validated (non-empty, sufficient balance)
- Transaction is sent via
/transaction/send - If successful:
- UI is updated
- Balance is refreshed
- Blockchain is re-fetched
Periodic Balance Update
fetchBalance()is polled every 3 seconds to keep balance current
WalletApp Class DemoApp/wallet.py
The WalletApp class is the core of the wallet logic for a blockchain-based cryptocurrency application. It manages key generation, transaction creation, signing, and serialization for communication with the blockchain network. This class is meant for frontend-facing APIs. It has the following functionalities:
- Generates a new public/private key pair
- Creates signed or unsigned transactions
- Serializes transactions for API use
- Computes transaction hashes (txids)
- Prepares transaction payloads for broadcast
| Method | Description |
|---|---|
get_public_key_str() |
Returns the public key as a string (used as the wallet address). |
sign_transaction(message: dict) |
Signs the basic transaction payload using the private key. |
create_transaction(recipient, amount, sign=True) |
Builds a transaction to the given recipient and optionally signs it. |
compute_txid(tx: dict) |
Computes a SHA-256 hash of the transaction data, excluding the signature (used as the transaction ID). |
API Endpoints Used DemoApp/wallet_api.py
GET /wallet/address— fetch public wallet addressPOST /connect— register wallet with blockchainGET /blockchain— get the current blockchain statePOST /transaction/send— send a new transaction
- Frontend stack: HTML, Bootstrap 5, JavaScript (vanilla)
- No frontend frameworks: Kept lightweight and dependency-free for demo purposes
- Modal transactions: Uses Bootstrap modal to show per-block transaction lists
- Resilience:
fetchAddress()result is cached for reuse, and connection failures are logged gracefully
- User initiates a transaction (e.g. Duy send 5 coins to Luigi)
- User provide recipients address, amount to send
- Wallet need to check user’s balance to ensure enough funds are available
- Wallet creates the transaction, signs it, and sends it to the tracker, which propagates it to all peers.
- Tracker propagates transaction to peers
- Tracker receive transaction and add to some list with all unconfirmed transaction
- Tracker broadcast transaction to all peers in the network.
- Each peer adds the transaction to its mempool and peers compete to mine a block and update their blockchain copy.
- Tracker propagate updated chain
- Wallet confirms the transaction once the block is added to the main chain.
Demonstration of how blockchain is resilient to invalid transactions and modifications made to blocks.
Have some peers in the Blockchain network constantly trying to add invalid blocks of different types: invalid signature, wrong hash, different block added in the middle, etc...