Skip to main content

Run a Validator Node

This guide walks you through building, configuring, and running a Mersennet validator node from source.

Prerequisites​

Hardware Requirements​

ResourceMinimumRecommendedNotes
CPU4 cores8+ coresBlock production and EVM execution are CPU-bound
RAM8 GB16 GBState trie and mempool reside in memory
Storage100 GB SSD500 GB NVMe SSDState grows over time; NVMe recommended for I/O
Network100 Mbps1 GbpsLow latency matters for consensus round-trips
OSUbuntu 22.04+Ubuntu 24.04 LTSAny modern Linux; macOS for development only
caution

Running on HDD (spinning disk) is not recommended. The sled storage backend performs frequent random reads/writes that require SSD-class IOPS.

Software Requirements​

RequirementVersionPurpose
Rust1.75 or laterCompiler for building from source
GitLatestClone the repository
build-essentialLatestC linker and system libraries
pkg-configLatestLibrary discovery for native dependencies
libssl-devLatestTLS support for networking

Install all dependencies on Ubuntu/Debian:

sudo apt update && sudo apt install -y build-essential pkg-config libssl-dev git

Install Rust via rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustc --version # Should be 1.75+

Build from Source​

Clone the Mersennet repository and build the release binary:

git clone https://github.com/PrimeNumbersLabs/prime-chain.git
cd prime-chain
cargo build --release

The binary will be at target/release/prime-chain (or prime-chain-node depending on the crate name).

Configuration​

Mersennet uses a JSON configuration file. Create config.json with the sections relevant to your deployment.

Minimal Validator Configuration​

{
"engine": {
"chain_id": 7919,
"state_path": "/var/lib/prime-chain/state",
"storage_backend": "sled"
},
"p2p": {
"node_key_path": "/var/lib/prime-chain/state/node_key.json",
"listen": "0.0.0.0:30303",
"peers": ["46.225.30.187:30303"],
"block_time_ms": 1000
},
"rpc": {
"enabled": true,
"addr": "0.0.0.0:8545"
},
"ws": {
"enabled": true,
"addr": "0.0.0.0:9945"
}
}

Full Configuration Reference​

engine β€” Core Engine​

ParameterTypeDefaultDescription
chain_idu647919Chain identifier β€” must match genesis and network
state_pathstring"state"Directory for state database and metadata
gas_limit_per_blocku6430000000Maximum gas per block (30M)
fee_elasticity_multiplieru642EIP-1559 elasticity (target = limit / multiplier)
fee_max_change_denominatoru648Max base fee change per block (1/8 = 12.5%)
storage_backendstring"sled""sled", "redb", or "memory"

mempool β€” Transaction Pool​

ParameterTypeDefaultDescription
max_totalusize10000Maximum transactions in the mempool
max_per_senderusize1000Max pending txs per sender address
bump_bpsu641000Min gas price bump for replacement (10%)

p2p β€” Peer-to-Peer Networking​

ParameterTypeDefaultDescription
node_key_pathstring"state/node_key.json"Path to node identity keypair
peer_store_pathstring"state/peers.json"Path to persistent peer list
listenstring"0.0.0.0:30303"P2P listen address
peersstring[][]Seed peers for bootstrap
block_time_msu641000Block production interval (ms)
noise_enabledboolfalseEncrypt P2P with Noise Protocol

rpc β€” JSON-RPC Server​

ParameterTypeDefaultDescription
enabledboolfalseEnable the HTTP JSON-RPC server
addrstring"127.0.0.1:8545"RPC listen address

ws β€” WebSocket Server​

ParameterTypeDefaultDescription
enabledboolfalseEnable the WebSocket server
addrstring"127.0.0.1:9945"WebSocket listen address

slashing β€” Slashing Parameters​

ParameterTypeDefaultDescription
double_sign_bpsu64500Double-sign penalty (5%)
timeout_bpsu64100Timeout penalty (1%)
escalation_step_bpsu6425Penalty increase per offense (0.25%)
escalation_max_bpsu641000Maximum penalty cap (10%)
round_timeout_msu64500Consensus round timeout (ms)
unbonding_periodu642Epochs before unbonded stake withdrawable

token_economics β€” Rewards & Supply​

ParameterTypeDefaultDescription
max_supplystring"1000000000000000000000000000"Max PRIM supply (1B Γ— 10¹⁸)
initial_reward_per_blockstring"10000000000000000000"Block reward (10 PRIM)
halving_intervalu6435000000Blocks between reward halvings

prime_orders β€” PrimeOrders Precompile​

ParameterTypeDefaultDescription
initial_margin_bpsu640Initial margin requirement (bps)
maintenance_margin_bpsu640Maintenance margin (bps)

bridge β€” Cross-Domain Bridge​

ParameterTypeDefaultDescription
max_queue_lenusize10000Max pending bridge messages

zk β€” Zero-Knowledge Proofs​

ParameterTypeDefaultDescription
enabledboolfalseEnable ZK proof generation
checkpoint_intervalu64100Blocks between ZK checkpoints
tip

Check the Network Information page for current seed peer addresses.

Genesis Setup​

For mainnet or testnet, you need the correct genesis file. Genesis defines the initial state: chain ID, validators, allocations, and parameters.

  • Chain ID: 7919 (testnet)
  • Genesis is typically distributed with the network launch. Place it at genesis.json and reference it in your startup command or config.

Example genesis structure:

{
"chain_id": "prime-chain-7919",
"initial_height": "1",
"validators": [
{
"address": "0x...",
"pub_key": "...",
"power": "1000000"
}
],
"app_state": { ... }
}

Starting the Node​

Run the node with your config and genesis:

./target/release/prime-chain \
--config config.json \
--genesis genesis.json

For a non-validator (sentinel/full node), omit the validator key or set engine.validator_key_path to empty. The node will sync and serve RPC but will not propose blocks.

Registering as a Validator​

To join the validator set:

  1. Stake PRIM β€” Send a staking transaction to register your validator address with your desired stake amount. See Staking Guide.

  2. Ensure your node is synced β€” Wait until your node has caught up to the latest block height.

  3. Validator key β€” Your validator_key_path must point to a key file that corresponds to the address you staked from. The node will automatically begin participating in consensus once registered and synced.

Validator registration happens through the genesis configuration (for initial validators) or via staking transactions. There is no separate RPC method for registrationβ€”once you stake PRIM and your node is synced with the correct validator key, you join the active set.

Systemd Service (Production)​

For production deployments, run the node as a systemd service with proper resource limits and security hardening.

Create a Dedicated User​

sudo useradd --system --home-dir /var/lib/prime-chain --shell /usr/sbin/nologin prime
sudo mkdir -p /var/lib/prime-chain/state
sudo chown -R prime:prime /var/lib/prime-chain

Service File​

Create /etc/systemd/system/prime-chain.service:

[Unit]
Description=Mersennet Validator Node
Documentation=https://docs.mersennet.io
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=prime
Group=prime
WorkingDirectory=/var/lib/prime-chain

ExecStart=/usr/local/bin/prime-chain \
--config /etc/prime-chain/config.json

Restart=always
RestartSec=10
StartLimitInterval=200
StartLimitBurst=5

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=prime-chain

# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/prime-chain
PrivateTmp=yes

# Resource limits
LimitNOFILE=65535
LimitNPROC=4096
MemoryMax=12G

# Environment
Environment="RUST_LOG=info"
Environment="RUST_BACKTRACE=1"

[Install]
WantedBy=multi-user.target

Install and Start​

# Copy binary
sudo cp target/release/prime-chain /usr/local/bin/
sudo chmod +x /usr/local/bin/prime-chain

# Copy config
sudo mkdir -p /etc/prime-chain
sudo cp config.json /etc/prime-chain/

# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable prime-chain
sudo systemctl start prime-chain

# Check status
sudo systemctl status prime-chain
sudo journalctl -u prime-chain -f

Log Levels​

Control verbosity via the RUST_LOG environment variable:

# Default β€” info level
Environment="RUST_LOG=info"

# Debug networking issues
Environment="RUST_LOG=info,prime_chain_network=debug"

# Trace EVM execution (very verbose)
Environment="RUST_LOG=info,prime_chain::engine=trace"

# Quiet mode β€” warnings and errors only
Environment="RUST_LOG=warn"

Log Interpretation Guide​

Understanding node log messages helps diagnose issues quickly.

Normal Operation​

Log MessageMeaning
node identity loaded address=0x...Node keypair loaded successfully
registered genesis validator address=0x...Validator registered from genesis config
RPC server listening on 0.0.0.0:8545JSON-RPC server started
P2P listening on 0.0.0.0:30303Peer-to-peer networking active
block produced number=N txs=M gas_used=GBlock successfully produced
block finalized number=N hash=0x...Block reached 2/3+ consensus
peer connected addr=1.2.3.4:30303New peer connection established

Warning Signs​

Log MessageMeaningAction
consensus timeout round=NFailed to get 2/3+ votes in timeCheck peer connectivity
mempool full, rejecting txMempool at capacity (10K default)Increase mempool.max_total or reduce load
peer disconnected addr=...Lost connection to a peerCheck network; peer may restart automatically
slashing evidence kind=timeoutThis node missed a consensus roundEnsure clock sync and network stability

Error Messages​

Log MessageMeaningAction
failed to read config fileConfig path invalid or unreadableVerify --config path and file permissions
invalid address hexMalformed address in genesis configFix the hex address in config.json
signing failedNode key corrupted or missingRestore from backup or regenerate identity
state lock poisonedInternal state corruption (panic recovery)Restart the node; report if persistent

Monitoring Setup​

Mersennet exposes Prometheus-compatible metrics at the /metrics endpoint on the RPC port.

Available Metrics​

MetricTypeDescription
prime_chain_upGaugeNode liveness (always 1 while running)
prime_chain_heightGaugeLatest committed block height
prime_chain_blocks_produced_totalCounterTotal blocks produced
prime_chain_block_gas_usedGaugeGas consumed in latest block
prime_chain_block_tx_countGaugeTransaction count in latest block
prime_chain_block_execution_secondsHistogramBlock execution duration
prime_chain_base_fee_weiGaugeCurrent EIP-1559 base fee
prime_chain_consensus_roundsCounterTotal consensus rounds
prime_chain_consensus_finalizedCounterTotal blocks finalized
prime_chain_validators_activeGaugeActive validator count
prime_chain_total_stakeGaugeTotal staked PRIM
prime_chain_slashing_eventsCounterSlashing events by kind
prime_chain_mempool_sizeGaugeCurrent mempool transaction count
prime_chain_mempool_rejectedCounterRejected transactions by reason
prime_chain_rpc_requestsCounterRPC requests by method
prime_chain_rpc_errorsCounterRPC errors by method and code
prime_chain_rpc_duration_secondsHistogramRPC request duration
prime_chain_orders_submittedCounterOrders submitted to order book
prime_chain_orders_filledCounterOrders fully filled
prime_chain_trades_executedCounterTrade fills executed
prime_chain_markets_activeGaugeActive markets count

Prometheus Configuration​

Add the following scrape target to your prometheus.yml:

scrape_configs:
- job_name: 'prime-chain'
scrape_interval: 15s
metrics_path: '/metrics'
static_configs:
- targets: ['localhost:8545']
labels:
chain: 'prime-chain-testnet'
node: 'validator-01'

Grafana Dashboard​

Connect Grafana to your Prometheus instance and create dashboards for:

  • Block Production β€” Height over time, block execution duration, gas usage trends
  • Consensus Health β€” Finalization rate, consensus rounds, active validators
  • Mempool β€” Pool size, rejection rate, gas price distribution
  • Network β€” Peer count, P2P message rates
  • PrimeOrders β€” Order submission rate, fill rate, active markets

The public Grafana dashboard is available at http://46.225.30.187:3000.

Alerting Rules​

Recommended Prometheus alert rules:

groups:
- name: prime-chain
rules:
- alert: NodeDown
expr: up{job="prime-chain"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Mersennet node is down"

- alert: BlockProductionStalled
expr: increase(prime_chain_blocks_produced_total[5m]) == 0
for: 5m
labels:
severity: critical
annotations:
summary: "No blocks produced in 5 minutes"

- alert: HighMempoolSize
expr: prime_chain_mempool_size > 8000
for: 2m
labels:
severity: warning
annotations:
summary: "Mempool approaching capacity"

- alert: SlashingEvent
expr: increase(prime_chain_slashing_events[5m]) > 0
labels:
severity: critical
annotations:
summary: "Validator slashing event detected"

- alert: ConsensusTimeouts
expr: increase(prime_chain_consensus_rounds[5m]) - increase(prime_chain_consensus_finalized[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "High number of consensus timeouts"

Backup and Restore​

What to Back Up​

PathContentsCritical?
state/node_key.jsonNode identity keypair (validator signing key)Yes β€” loss means new identity
state/peers.jsonKnown peer addressesNo β€” peers rediscovered on restart
state/sled_db/Full chain state (accounts, storage, blocks)Yes β€” loss requires full resync
config.jsonNode configurationYes β€” keep in version control

Backup Procedure​

# Stop the node first to ensure consistent state
sudo systemctl stop prime-chain

# Create timestamped backup
BACKUP_DIR="/backups/prime-chain/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp -r /var/lib/prime-chain/state "$BACKUP_DIR/"
cp /etc/prime-chain/config.json "$BACKUP_DIR/"

# Restart the node
sudo systemctl start prime-chain

Restore Procedure​

# Stop the node
sudo systemctl stop prime-chain

# Restore from backup
BACKUP_DIR="/backups/prime-chain/20260101-120000"
rm -rf /var/lib/prime-chain/state
cp -r "$BACKUP_DIR/state" /var/lib/prime-chain/
chown -R prime:prime /var/lib/prime-chain

# Start the node β€” it will catch up from the restored height
sudo systemctl start prime-chain
warning

Never run two nodes with the same node_key.json simultaneously β€” this may trigger double-sign slashing.

Troubleshooting​

Common Issues​

Node won't start​

SymptomCauseSolution
failed to read config fileConfig path wrong or missingCheck --config path; ensure file exists and is valid JSON
address already in usePort conflict (8545, 30303, or 9945)Stop conflicting process or change port in config
permission deniedFile/directory permissionschown -R prime:prime /var/lib/prime-chain
failed to install Prometheus metrics exporterDuplicate recorder initializationEnsure only one node instance is running

Node not producing blocks​

SymptomCauseSolution
No block produced logsNot registered as validatorStake PRIM and verify your node key matches
Only importing blocksNode is syncingWait for sync to complete to chain tip
consensus timeout repeatedNetwork partition or clock driftCheck peers list; sync system clock with NTP

Peer connection issues​

SymptomCauseSolution
0 connected peersFirewall blocking port 30303Open TCP/UDP 30303 in firewall
Peers connect then disconnectChain ID mismatchVerify engine.chain_id matches network (7919)
Slow block propagationHigh network latencyUse peers geographically closer; check bandwidth

High resource usage​

SymptomCauseSolution
Memory >12GB and growingState trie growthNormal for long-running nodes; increase RAM or add swap
CPU constantly at 100%EVM execution or block productionCheck for spam transactions; consider gas_limit_per_block adjustment
Disk filling upState database growthIncrease storage; consider pruning old data

Useful Commands​

# Check node status
sudo systemctl status prime-chain

# Follow logs in real time
sudo journalctl -u prime-chain -f

# Check block height via RPC
curl -s http://localhost:8545 -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' | jq

# Check connected peer count
curl -s http://localhost:8545 -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}' | jq

# Check node sync status
curl -s http://localhost:8545 -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' | jq

# View Prometheus metrics
curl -s http://localhost:8545/metrics | grep prime_chain_height

# Check disk usage
du -sh /var/lib/prime-chain/state/

Next Steps​