Validators & RPC Nodes

Understanding the different types of nodes in the Solana network is crucial for both developers and operators. This chapter explores the roles, requirements, and interactions of validators and RPC nodes.

Node Types Overview

Solana has two primary node types:

Text
┌─────────────────────────────────────────────────────────────────┐
│                      SOLANA NETWORK                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────────┐    ┌─────────────┐    ┌─────────────┐        │
│   │  Validator  │    │  Validator  │    │  Validator  │        │
│   │             │    │             │    │             │        │
│   │ • Stakes    │    │ • Stakes    │    │ • Stakes    │        │
│   │ • Votes     │    │ • Votes     │    │ • Votes     │        │
│   │ • Produces  │    │ • Produces  │    │ • Produces  │        │
│   │   blocks    │    │   blocks    │    │   blocks    │        │
│   │   (leader)  │    │   (leader)  │    │   (leader)  │        │
│   └─────────────┘    └─────────────┘    └─────────────┘        │
│          │                  │                  │                │
│          └──────────────────┼──────────────────┘                │
│                             │                                   │
│                    ┌────────┴────────┐                          │
│                    │    Gossip Net   │                          │
│                    └────────┬────────┘                          │
│                             │                                   │
│   ┌─────────────┐    ┌─────┴───────┐    ┌─────────────┐        │
│   │  RPC Node   │    │  RPC Node   │    │  RPC Node   │        │
│   │             │    │             │    │             │        │
│   │ • No stake  │    │ • No stake  │    │ • No stake  │        │
│   │ • No votes  │    │ • No votes  │    │ • No votes  │        │
│   │ • Serves    │    │ • Serves    │    │ • Serves    │        │
│   │   queries   │    │   queries   │    │   queries   │        │
│   └──────┬──────┘    └─────────────┘    └──────┬──────┘        │
│          │                                      │                │
└──────────┼──────────────────────────────────────┼───────────────┘
           │                                      │
     ┌─────┴─────┐                          ┌─────┴─────┐
     │   dApps   │                          │   Users   │
     └───────────┘                          └───────────┘

Validators

Validators are the backbone of the network, responsible for consensus and block production.

Validator Responsibilities

Text
1. TRANSACTION PROCESSING
   └── Receive transactions from TPU
   └── Execute programs
   └── Update account states

2. CONSENSUS PARTICIPATION
   └── Vote on block validity
   └── Build on longest valid chain
   └── Participate in Tower BFT

3. BLOCK PRODUCTION (when leader)
   └── Order transactions
   └── Create entries
   └── Broadcast blocks

4. REPLICATION
   └── Store blockchain history
   └── Serve data to peers
   └── Participate in gossip

Validator Architecture

TypeScript
interface ValidatorComponents {
  // Network components
  tpu: TransactionProcessingUnit; // Receives transactions
  tvu: TransactionValidationUnit; // Validates blocks
  gossip: GossipService; // P2P communication

  // Processing components
  banking: BankingStage; // Transaction execution
  poh: PoHService; // Proof of History

  // Storage components
  blockstore: Blockstore; // Block storage
  ledger: Ledger; // Account states
  snapshot: SnapshotService; // State snapshots

  // Consensus components
  towerBft: TowerBFT; // Voting logic
  voteAccount: VoteAccount; // On-chain votes
}

Validator Economics

Validators earn rewards through:

Text
VALIDATOR INCOME
================

1. Inflation Rewards
   └── ~6.9% annual inflation (at launch, decreasing)
   └── Distributed to staked validators
   └── Proportional to stake

2. Transaction Fees
   └── 50% of base fee burned
   └── 50% goes to leader
   └── Priority fees go to leader

3. MEV (Maximal Extractable Value)
   └── Block ordering opportunities
   └── Arbitrage inclusion
   └── Jito-style rewards

Example calculation for 10,000 SOL stake:
├── Inflation rewards: ~690 SOL/year (6.9%)
├── Commission (10%): 69 SOL to validator
├── To stakers: 621 SOL
└── Plus transaction fees as leader

Hardware Requirements

Text
MINIMUM VALIDATOR SPECS (as of 2024)
====================================

CPU:
├── 24+ cores @ 3.0+ GHz
├── AMD EPYC or Intel Xeon
└── AVX2 support required

RAM:
├── 512 GB (mainnet)
├── 256 GB (minimum)
└── ECC recommended

Storage:
├── NVMe SSD
├── 1 TB for ledger
├── 2+ TB for accounts
└── 500K+ IOPS

Network:
├── 1 Gbps (minimum)
├── 10 Gbps (recommended)
├── Low latency to other validators
└── Unmetered bandwidth

GPU (optional but helps):
├── NVIDIA GPU
├── CUDA support
└── For PoH verification

Running a Validator

Bash
# Download Solana CLI
sh -c "$(curl -sSfL https://release.solana.com/v1.18.0/install)"

# Create keys
solana-keygen new -o ~/validator-keypair.json
solana-keygen new -o ~/vote-account-keypair.json
solana-keygen new -o ~/withdrawer-keypair.json

# Create vote account
solana create-vote-account \
  ~/vote-account-keypair.json \
  ~/validator-keypair.json \
  ~/withdrawer-keypair.json \
  --commission 10

# Start validator (simplified)
solana-validator \
  --identity ~/validator-keypair.json \
  --vote-account ~/vote-account-keypair.json \
  --ledger ~/validator-ledger \
  --rpc-port 8899 \
  --dynamic-port-range 8000-8020 \
  --entrypoint mainnet-beta.solana.com:8001 \
  --expected-genesis-hash 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d \
  --limit-ledger-size

RPC Nodes

RPC nodes provide the API interface for applications to interact with Solana.

RPC Node Role

Text
RPC nodes:Full copy of blockchain
✓ Execute read queries
✓ Submit transactions
✓ Serve historical data
✓ Provide WebSocket subscriptions

RPC nodes DON'T:
✗ Vote on consensus
✗ Produce blocks
✗ Earn staking rewards
✗ Participate in Tower BFT

RPC Methods

TypeScript
// Account queries
const account = await connection.getAccountInfo(publicKey);
const balance = await connection.getBalance(publicKey);
const tokenAccounts = await connection.getTokenAccountsByOwner(owner, {
  programId: TOKEN_PROGRAM_ID,
});

// Transaction queries
const transaction = await connection.getTransaction(signature);
const signatures = await connection.getSignaturesForAddress(address);
const status = await connection.getSignatureStatuses([signature]);

// Block queries
const block = await connection.getBlock(slot);
const blockHeight = await connection.getBlockHeight();
const slot = await connection.getSlot();

// Cluster info
const clusterNodes = await connection.getClusterNodes();
const epochInfo = await connection.getEpochInfo();
const version = await connection.getVersion();

// Sending transactions
const signature = await connection.sendTransaction(transaction, [signer]);
const result = await connection.confirmTransaction(signature);

WebSocket Subscriptions

TypeScript
import { Connection } from "@solana/web3.js";

const connection = new Connection("wss://api.mainnet-beta.solana.com");

// Account change subscription
const accountSubId = connection.onAccountChange(
  publicKey,
  (accountInfo, context) => {
    console.log("Account changed:", accountInfo);
    console.log("Slot:", context.slot);
  },
  "confirmed",
);

// Log subscription
const logSubId = connection.onLogs(
  programId,
  (logs, context) => {
    console.log("Program logs:", logs);
  },
  "confirmed",
);

// Slot subscription
const slotSubId = connection.onSlotChange((slotInfo) => {
  console.log("New slot:", slotInfo.slot);
});

// Cleanup
connection.removeAccountChangeListener(accountSubId);
connection.removeOnLogsListener(logSubId);
connection.removeSlotChangeListener(slotSubId);

RPC Node Economics

Running an RPC node:

Text
COSTS:
├── Hardware: $1,000 - $3,000/month
├── Bandwidth: $500 - $2,000/month
├── Operations: Variable
└── Total: $2,000 - $5,000/month

REVENUE OPTIONS:
├── Sell RPC access (metered)
├── Support your own dApps
├── White-label for others
└── Specialized data services

PROVIDERS (Alternative to self-hosting):
├── Helius
├── QuickNode
├── Alchemy
├── Triton
├── GetBlock
└── Many others

RPC Provider Comparison

When choosing an RPC provider:

TypeScript
// Rate limiting varies by provider
// Example: Checking provider capabilities

async function checkRpcProvider(endpoint: string) {
  const connection = new Connection(endpoint);

  const tests = {
    basic: async () => {
      return await connection.getSlot();
    },

    accountHistory: async () => {
      // Some providers limit historical queries
      return await connection.getSignaturesForAddress(publicKey, {
        limit: 1000,
      });
    },

    throughput: async () => {
      // Test request rate
      const start = Date.now();
      const promises = Array(100)
        .fill(null)
        .map(() => connection.getSlot());
      await Promise.all(promises);
      return Date.now() - start;
    },

    websockets: async () => {
      return new Promise((resolve) => {
        const subId = connection.onSlotChange((info) => {
          connection.removeSlotChangeListener(subId);
          resolve(true);
        });
        setTimeout(() => resolve(false), 5000);
      });
    },
  };

  return tests;
}

Commitment Levels

RPC queries accept commitment levels:

TypeScript
type Commitment =
  | "processed" // Node's most recent block
  | "confirmed" // Voted on by supermajority
  | "finalized"; // Confirmed + maximum lockout

// Usage
const balance = await connection.getBalance(
  publicKey,
  "confirmed", // Default for most queries
);

// Commitment comparison
/*
processed:
├── Speed: Fastest (~400ms)
├── Reliability: Can be rolled back
├── Use case: UI responsiveness

confirmed:
├── Speed: ~400ms + voting time
├── Reliability: Very unlikely to roll back
├── Use case: Most applications

finalized:
├── Speed: ~400ms + ~32 slots
├── Reliability: Cannot roll back
├── Use case: Exchanges, high-value transfers
*/

Validator vs RPC Decision Matrix

When should you run each type?

Text
RUN A VALIDATOR IF:
├── You have significant SOL to stake
├── You want to earn rewards
├── You want to participate in governance
├── You have hardware budget ($3,000+/month)
└── You have operational expertise

RUN AN RPC NODE IF:
├── Your dApp needs dedicated RPC
├── You need historical data access
├── You want to resell RPC access
├── Public RPCs are rate-limiting you
└── You need custom data indexing

USE A PROVIDER IF:
├── You're starting out
├── Cost is a concern
├── You don't want operational overhead
├── Your needs are standard
└── You prioritize development time

Gossip Network

Both validators and RPC nodes participate in gossip:

Text
GOSSIP PROTOCOL
═══════════════

Purpose:
├── Cluster discovery
├── Network health
├── Block propagation
└── Vote dissemination

Message types:
├── ContactInfo (node addresses)
├── Vote (consensus votes)
├── LowestSlot (pruning info)
├── Shred (block data)
└── CrdsValue (various metadata)

┌────────────────────────────────────────────────────┐
│                                                    │
│  Node A ──────┬──────────────┬────────── Node D   │
│       │       │              │              │      │
│       │       │   Gossip     │              │      │
│       │       │   Messages   │              │      │
│       │       │              │              │      │
│  Node B ──────┴──────────────┴────────── Node C   │
│                                                    │
│  Each node shares with random peers                │
│  Information propagates exponentially              │
│  Full network coverage in O(log n) rounds          │
└────────────────────────────────────────────────────┘

Connecting to RPC Nodes

TypeScript
import { Connection, clusterApiUrl } from "@solana/web3.js";

// Public endpoints (rate-limited)
const mainnet = new Connection(clusterApiUrl("mainnet-beta"));
const devnet = new Connection(clusterApiUrl("devnet"));
const testnet = new Connection(clusterApiUrl("testnet"));

// Custom endpoint
const custom = new Connection("https://your-rpc-node.com");

// With configuration
const configured = new Connection("https://api.mainnet-beta.solana.com", {
  commitment: "confirmed",
  confirmTransactionInitialTimeout: 60000,
  wsEndpoint: "wss://api.mainnet-beta.solana.com",
});

// Health check
async function checkHealth(connection: Connection) {
  try {
    const slot = await connection.getSlot();
    const health = await connection.getHealth();
    const version = await connection.getVersion();

    return {
      healthy: health === "ok",
      slot,
      version: version["solana-core"],
    };
  } catch (error) {
    return { healthy: false, error };
  }
}

Best Practices

For Application Developers

TypeScript
// 1. Use multiple RPC providers for reliability
const providers = [
  "https://primary-rpc.com",
  "https://backup-rpc-1.com",
  "https://backup-rpc-2.com",
];

async function resilientRequest<T>(
  fn: (connection: Connection) => Promise<T>,
): Promise<T> {
  for (const provider of providers) {
    try {
      const connection = new Connection(provider);
      return await fn(connection);
    } catch (error) {
      console.warn(`Provider ${provider} failed:`, error);
    }
  }
  throw new Error("All providers failed");
}

// 2. Implement proper retry logic
async function sendWithRetry(
  connection: Connection,
  transaction: Transaction,
  signers: Keypair[],
  maxRetries = 3,
): Promise<string> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const signature = await connection.sendTransaction(transaction, signers);
      await connection.confirmTransaction(signature, "confirmed");
      return signature;
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
    }
  }
  throw new Error("Should not reach here");
}

// 3. Use appropriate commitment levels
const userBalance = await connection.getBalance(wallet, "confirmed");
const forExchange = await connection.getBalance(wallet, "finalized");

For Node Operators

Bash
# Monitor validator health
solana-validator-status

# Check vote account
solana vote-account ~/vote-account-keypair.json

# Monitor catchup status
solana catchup ~/validator-keypair.json

# Important metrics to monitor:
# - Slot height vs cluster
# - Vote credits
# - Skip rate
# - RPC latency
# - Memory/CPU usage

Key Takeaways

  1. Validators secure the network through staking and voting
  2. RPC nodes provide API access without participating in consensus
  3. Hardware requirements are significant for both types
  4. Multiple RPC providers improve application reliability
  5. Commitment levels trade off speed for finality guarantees

Next: Slots & Epochs - Understanding Solana's time organization.