Skip to content

Latest commit

 

History

History
270 lines (204 loc) · 11.3 KB

File metadata and controls

270 lines (204 loc) · 11.3 KB

CSEE 4119 Spring 2025, Group Project Design File

Team members: Angela Peng, Luigi Liu, Duy Dang, Shivam Chaturvedi

Implementation language: Python

P2P Network:

File: p2p.py

P2PNode class

Instantiated inside a peer node (kind of like a network handler in assignments)

  1. __init__(self, tracker_ip, tracker_port)

    1. Variables:
      1. self.tracker_ip: tracker’s ip address
      2. self.tracker_port: tracker’s port
      3. Self.peer_connection_map
        1. Type: map of tuples
        2. Key: (ip_address: str, port: int)
        3. Value: tuple: (connection: str, listening_thread: thread)
      4. self.socket: socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      5. self.tracker_connection: connection to tracker
      6. self.inbox: a Queue object that buffers data coming from peers
    2. Logic:
      1. Call join_network()
  2. join_network(self)

    1. Use self.socket to connect to tracker
    2. Save the connection to self.tracker_connection
    3. Send a message to the tracker to request to join the network and get the list of peer ip addresses and ports
    4. For each ip address and port:
      1. establish a connection, create a new listening_thread object (passing ip address and port for id)
      2. Add ip address, port, connection, listening_thread, and outbox queue as a tuple to self.peer_connection_map (use lock for thread safety)
      3. Start a looping thread to listen for new message (i.e. connection.recv()) from that connection
        1. Push any new message into self.inbox
        2. If connection is closed:
          1. Remove the corresponding tuple from self.peer_connection_map
          2. Stop the thread
    5. Start a looping thread to wait for new connection from newly joined peer (i.e. self.socket.accept())
      1. 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)
  3. leave_network(self)

    1. Close all connections including self.tracker_connection and self.peer_connection_map
      1. Later the tracker and peers will notice that connection is stale when sending a message and can remove it from their list
    2. Stop all listening_threads
  4. broadcast(self, message)

    1. Send message to all the peers in self.peer_connection_map
  5. receiveData(self)

    1. Pop the top message from self.inbox

P2PTracker class

  1. Has a list of peer ip addresses, ports, and connections
    1. Has a looping thread to periodically check the connections for closed ones
      1. If found a closed connection, remove corresponding data from the list
  2. wait_for_request()
    1. handle_join_request()
      1. Respond to new peer with updated list
      2. Update the main list with the new node

Blockchain

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 appplication

Demo application - My Crypto Wallet

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

Some UI structures

  • DemoApp/script.js
  • DemoApp/wallet_ui.html
  • DemoApp/style.css The interface is composed of several Bootstrap-powered cards:

Wallet Info

  1. Address display with a "Copy" button
  2. Balance display that updates on load and via polling

Send Transaction

  1. Text input for recipient address
  2. Number input for amount
  3. Checkbox to toggle digital signing
  4. Button to submit a transaction
  5. Output area showing transaction result

Blockchain View

  1. Block data displayed as individual cards
  2. Clicking a card opens a modal with detailed transaction data per block

Data Flow & Backend Interaction

On Page Load

  • fetchAddress() retrieves the wallet's public key from the backend
  • fetchBalance() gets blockchain data and computes current balance
  • fetchTransactions() 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

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

Methods

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 address
  • POST /connect — register wallet with blockchain
  • GET /blockchain — get the current blockchain state
  • POST /transaction/send — send a new transaction

Other Technical Notes for DemoApp

  • 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

Flow

  1. User initiates a transaction (e.g. Duy send 5 coins to Luigi)
    1. User provide recipients address, amount to send
    2. Wallet need to check user’s balance to ensure enough funds are available
  2. Wallet creates the transaction, signs it, and sends it to the tracker, which propagates it to all peers.
  3. Tracker propagates transaction to peers
    1. Tracker receive transaction and add to some list with all unconfirmed transaction
    2. Tracker broadcast transaction to all peers in the network.
  4. Each peer adds the transaction to its mempool and peers compete to mine a block and update their blockchain copy.
  5. Tracker propagate updated chain
  6. 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...