Skip to main content

Overview

Ubu-Block employs a multi-node architecture where different node types serve distinct roles in the network. This design ensures transparency, accessibility, and community-driven validation.

Submission Nodes

Allow authorized users to submit polling station results

Observer Nodes

Provide public, read-only access for transparency

Verification Nodes

Cross-reference results with official sources

Submission Nodes

Purpose

Submission nodes are authorized nodes that can:
  • Accept polling station results from users
  • Create new blocks with election data
  • Sign blocks with their private keys
  • Broadcast new blocks to the network

How They Work

  1. Authentication: Node has authorized signing keys
  2. Result Collection: Receives results via HTTP API
  3. Block Creation: Packages results into a new block
  4. Validation: Checks data integrity and creates Merkle tree
  5. Signing: Signs the block with private key
  6. Broadcasting: Announces new block to all peers

Running a Submission Node

# Start submission node
cargo run --bin submission --config config.toml
config.toml:
main_db = "data/blockchain.db"
private_db = "data/private.db"
node_addr = "0.0.0.0:9090"  # P2P port
http_addr = "0.0.0.0:9091"  # HTTP API port

[peer_config]
max_peers = 50
ping_interval = 30
connection_timeout = 10
sync_batch_size = 100

[[peers]]
name = "peer1"
addr = "157.230.xxx.xxx:9090"

Implementation Details

From nodes/submission/src/main.rs:18:
let blockchain = BlockChain::new(
    Database::new(
        SqlitePool::connect(&config.main_db).await.unwrap(),
        SqlitePool::connect(&config.private_db).await.unwrap(),
    ),
    config.peer_config,
);

// Start P2P server
let node = blockchain.start_p2p_server(bind_addr);

// Connect to existing peers
if let Some(peers) = config.peers {
    for peer in peers {
        blockchain.connect_to_peer(peer.parse().unwrap()).await;
    }
}

// Start HTTP API
let api_routes = api::run_api_server()
    .layer(Extension(blockchain.clone()));

API Endpoints

Submission nodes expose HTTP endpoints:
  • POST /api/v1/results/submit - Submit new results
  • GET /api/v1/results/station/{id} - Get station results
  • GET /api/v1/blocks/{height} - Get block by height
  • GET /api/v1/peers - View connected peers
Only nodes with valid signing keys in private.db can create and sign blocks. The public key hash is recorded in each block for accountability.

Observer Nodes

Purpose

Observer nodes provide read-only access to blockchain data:
  • Sync with submission nodes to maintain a copy of the chain
  • Allow public querying of results
  • Provide transparency and independent verification
  • No ability to create or modify blocks

How They Work

  1. Connect: Establish P2P connection to submission node
  2. Handshake: Exchange chain heights
  3. Sync: Request missing blocks from peers
  4. Monitor: Receive new block announcements
  5. Validate: Verify received blocks
  6. Serve: Respond to read-only queries

Running an Observer Node

// From nodes/observer/src/main.rs:78
log::info!("Starting an observer node...");
let peer_addr = "127.0.0.1:9090".parse().unwrap();
let config = P2PConfig::default();

// In-memory database for observer
let chain_db = SqlitePool::connect_lazy("sqlite::memory:").unwrap();
let private_db = SqlitePool::connect_lazy("sqlite::memory:").unwrap();

let blockchain = BlockChain::new(
    Database::new(chain_db, private_db), 
    Some(config)
);

// Initialize schema
sqlx::query(database::MAIN_SETUP)
    .execute(&blockchain.db.chain_db)
    .await
    .unwrap();

// Connect to submission node
blockchain.connect_to_peer(peer_addr).await.unwrap();

Key Features

  • No Private Keys: Observer nodes don’t have signing keys
  • In-Memory Storage: Can use sqlite::memory: for lightweight operation
  • Automatic Sync: Catches up with network when starting
  • Public Access: Anyone can run an observer node

Chain Synchronization

When an observer connects, it automatically syncs:
// From blockchain/src/lib.rs:318
if chain_height > our_height {
    self.request_chain_sync(peer_addr, stream).await?;
}
The sync process:
  1. Compare Heights: Check if peer has more blocks
  2. Request Blocks: Ask for missing blocks in batches
  3. Validate: Verify each received block
  4. Apply: Add validated blocks to local chain
Observer nodes participate in the audit trail by maintaining independent copies of the blockchain, making it virtually impossible to rewrite history.

Verification Nodes

Purpose

Verification nodes add an extra layer of trust:
  • Cross-reference submitted results with official sources (e.g., IEBC)
  • Participate in reward distribution for accurate submissions
  • Flag discrepancies between submitted and official data
  • Enhance consensus reliability
Status: Verification node functionality is currently under development (v0.4 roadmap).

Planned Features

v0.4 - Cross-Reference

  • Automated checking against official IEBC results
  • Discrepancy detection and reporting
  • Integration with official APIs

v0.5 - Rewards

  • Reward distribution for correct submissions
  • Penalty system for inaccurate data
  • Incentive mechanism for community participation

How They Will Work

// Conceptual implementation
pub struct VerificationNode {
    blockchain: BlockChain,
    official_source: OfficialDataSource,
    reward_pool: RewardPool,
}

impl VerificationNode {
    async fn verify_result(&self, result: &CandidateResult) -> VerificationStatus {
        let official_data = self.official_source
            .fetch_station_results(result.station_id)
            .await?;
        
        if official_data.matches(result) {
            VerificationStatus::Verified
        } else {
            VerificationStatus::Disputed {
                expected: official_data,
                actual: result.clone(),
            }
        }
    }
}

P2P Communication

All node types communicate via the P2P protocol:

Message Types

pub enum P2PMessage {
    // Handshake
    Hello { node_id: String, version: u32, chain_height: i64 },
    HelloResponse { node_id: String, chain_height: i64, accepted: bool },
    
    // Block sync
    BlockAnnouncement(Block),
    BlockRequest { hash: String },
    GetBlocks { start_height: i64, count: u32 },
    BlocksResponse { blocks: Vec<Block> },
    
    // Peer discovery
    GetPeers,
    PeersResponse { peers: Vec<SocketAddr> },
    
    // Keep-alive
    Ping,
    Pong,
}

Connection Flow

P2P Configuration

pub struct P2PConfig {
    pub max_peers: usize,              // Default: 50
    pub ping_interval: Duration,       // Default: 30s
    pub connection_timeout: Duration,  // Default: 10s
    pub sync_batch_size: u32,         // Default: 100 blocks
    pub max_message_size: usize,      // Default: 10MB
}

Node Comparison

FeatureSubmissionObserverVerification
Create blocks✅ Yes❌ No❌ No
Sign blocks✅ Yes❌ No❌ No
Read data✅ Yes✅ Yes✅ Yes
Validate blocks✅ Yes✅ Yes✅ Yes
Private keys✅ Required❌ Not needed❌ Not needed
HTTP API✅ Yes⚠️ Optional⚠️ Optional
Public access⚠️ Restricted✅ Yes✅ Yes
Verify w/ officials❌ No❌ No✅ Yes (v0.4)
Distribute rewards❌ No❌ No✅ Yes (v0.5)

Security Considerations

Submission Node Security

Protect Private Keys: The private.db file contains signing keys. If compromised, an attacker could create fraudulent blocks.
  • Store private.db with restricted permissions (600)
  • Consider hardware security modules (HSM) for production
  • Rotate keys periodically
  • Monitor for unauthorized access

Network Security

  • Encrypted Communication: End-to-end encryption for all node interactions (v0.4)
  • Peer Limits: Max 50 peers prevents resource exhaustion
  • Message Size Limits: Max 10MB prevents DoS attacks
  • Connection Timeouts: 10s timeout prevents hanging connections

Running Multiple Node Types

You can run multiple node types on the same machine:
# Terminal 1: Submission node
cargo run --bin submission --config submission.toml

# Terminal 2: Observer node connecting to submission node
cargo run --bin observer

# Terminal 3: Another observer node
cargo run --bin observer

Blockchain

Understand the underlying blockchain architecture

Consensus

Learn about BFT consensus mechanism

API Reference

Explore the HTTP API endpoints

Deployment

Deploy nodes in production

Build docs developers (and LLMs) love