Skip to content

Commit 7a9ecec

Browse files
authored
feat(p2p): add decentralized P2P mode with Bittensor block linking (#2)
* feat(p2p-consensus): add bootstrap nodes config and peer count methods - Add DEFAULT_BOOTSTRAP_NODES constant for production bootstrap peers - Update production() config to use DEFAULT_BOOTSTRAP_NODES - Export DEFAULT_BOOTSTRAP_NODES from lib.rs - Add len() and is_empty() methods to PeerMapping - Add connected_peer_count() and has_min_peers() to P2PNetwork * feat(p2p): add Bittensor block linking to ChainState - Add bittensor_block and bittensor_block_hash fields to ChainState - Add link_to_bittensor_block() and linked_block() methods - Update Default impl with zero-initialized fields - Link state to Bittensor block on NewBlock events * feat(challenge-sdk): add P2P submission storage and evaluation status support - Add StoreSubmission, RequestEvaluationStatus, EvaluationStatusResponse message types - Add ValidatorEvaluationResult struct for tracking validator evaluations - Add store_submission() method to P2PChallengeClient for distributed storage - Add get_evaluation_status() method to query submission evaluation progress - Update decentralized runner to handle new message types - Add comprehensive tests for new message types and methods - Export ValidatorEvaluationResult from lib.rs and prelude * refactor: make centralized server optional, add deprecation notices for P2P migration * docs: add P2P decentralized mode documentation * feat: add Docker configs for decentralized P2P mode * style: format code with cargo fmt
1 parent 8c89a10 commit 7a9ecec

File tree

16 files changed

+751
-13
lines changed

16 files changed

+751
-13
lines changed

AGENTS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,11 @@ Challenge Repositories (separate)
184184

185185
## Links
186186

187-
- [Platform Server](https://chain.platform.network) - Central coordination
187+
- [Platform Server](https://chain.platform.network) - Central coordination (centralized mode)
188188
- [Bittensor Docs](https://docs.bittensor.com) - Network documentation
189189
- [Validator Guide](docs/validator.md) - Running a validator
190190

191+
**Note:** For P2P mode, validators communicate directly without the central server.
192+
See the main README for decentralized deployment instructions.
193+
191194
For challenge-specific questions, please refer to the appropriate challenge repository.

Dockerfile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# =============================================================================
2-
# Platform Network - Unified Docker Image
2+
# Platform Network - Unified Docker Image (Centralized Mode)
33
# =============================================================================
4+
# NOTE: This Dockerfile builds the centralized validator which requires
5+
# chain.platform.network. For decentralized P2P mode, use:
6+
# validator-decentralized --data-dir /data --listen-addr /ip4/0.0.0.0/tcp/9000
7+
#
48
# Single image that can run as either server or validator mode:
59
# docker run platform server [OPTIONS]
610
# docker run platform validator --secret-key <KEY> [OPTIONS]
@@ -82,8 +86,9 @@ RUN mkdir -p /data && chmod 777 /data
8286

8387
# Default: run validator-node (reads VALIDATOR_SECRET_KEY from env)
8488
# Validators can use their existing docker-compose without changes
89+
# Note: In P2P mode, use validator-decentralized instead
8590
ENTRYPOINT ["validator-node"]
86-
CMD ["--data-dir", "/data", "--platform-server", "https://chain.platform.network"]
91+
CMD ["--data-dir", "/data"]
8792

8893
# Labels
8994
LABEL org.opencontainers.image.source="https://github.com/PlatformNetwork/platform"

Dockerfile.decentralized

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# =============================================================================
2+
# Platform Network - Decentralized Validator Docker Image
3+
# =============================================================================
4+
# Fully P2P mode - no dependency on chain.platform.network
5+
#
6+
# Build:
7+
# docker build -f Dockerfile.decentralized -t platform:decentralized .
8+
# =============================================================================
9+
10+
# Build stage
11+
FROM rust:1.92-bookworm AS builder
12+
13+
# Install dependencies
14+
RUN apt-get update && apt-get install -y \
15+
pkg-config \
16+
libssl-dev \
17+
protobuf-compiler \
18+
cmake \
19+
clang \
20+
libclang-dev \
21+
&& rm -rf /var/lib/apt/lists/*
22+
23+
# Set up cargo-chef for caching
24+
RUN cargo install cargo-chef --locked
25+
26+
WORKDIR /app
27+
28+
# Prepare recipe for caching dependencies
29+
COPY . .
30+
RUN cargo chef prepare --recipe-path recipe.json
31+
32+
# Cache dependencies
33+
FROM rust:1.92-bookworm AS cacher
34+
RUN apt-get update && apt-get install -y \
35+
pkg-config \
36+
libssl-dev \
37+
protobuf-compiler \
38+
cmake \
39+
clang \
40+
libclang-dev \
41+
&& rm -rf /var/lib/apt/lists/*
42+
RUN cargo install cargo-chef --locked
43+
WORKDIR /app
44+
COPY --from=builder /app/recipe.json recipe.json
45+
RUN cargo chef cook --release --recipe-path recipe.json
46+
47+
# Build stage
48+
FROM rust:1.92-bookworm AS final-builder
49+
RUN apt-get update && apt-get install -y \
50+
pkg-config \
51+
libssl-dev \
52+
protobuf-compiler \
53+
cmake \
54+
clang \
55+
libclang-dev \
56+
&& rm -rf /var/lib/apt/lists/*
57+
58+
WORKDIR /app
59+
COPY --from=cacher /app/target target
60+
COPY --from=cacher /usr/local/cargo /usr/local/cargo
61+
COPY . .
62+
63+
# Build only the decentralized validator
64+
RUN cargo build --release -p validator-decentralized
65+
66+
# Runtime stage (Ubuntu 24.04 for glibc 2.39 compatibility)
67+
FROM ubuntu:24.04
68+
69+
# Install runtime dependencies
70+
RUN apt-get update && apt-get install -y \
71+
ca-certificates \
72+
libssl3t64 \
73+
curl \
74+
&& rm -rf /var/lib/apt/lists/*
75+
76+
# Copy binary
77+
COPY --from=final-builder /app/target/release/validator-decentralized /usr/local/bin/validator-decentralized
78+
79+
# Create data directory
80+
RUN mkdir -p /data && chmod 777 /data
81+
82+
# Environment defaults
83+
ENV RUST_LOG=info,validator_decentralized=debug,platform_p2p_consensus=info
84+
ENV DATA_DIR=/data
85+
ENV SUBTENSOR_ENDPOINT=wss://entrypoint-finney.opentensor.ai:443
86+
ENV NETUID=100
87+
88+
# Expose P2P port
89+
EXPOSE 9000
90+
91+
# Health check
92+
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
93+
CMD test -f /data/distributed.db || exit 1
94+
95+
# Default entrypoint
96+
ENTRYPOINT ["validator-decentralized"]
97+
CMD ["--data-dir", "/data", "--listen-addr", "/ip4/0.0.0.0/tcp/9000"]
98+
99+
# Labels
100+
LABEL org.opencontainers.image.source="https://github.com/PlatformNetwork/platform"
101+
LABEL org.opencontainers.image.description="Platform Validator - Decentralized P2P Mode"
102+
LABEL org.opencontainers.image.licenses="Apache-2.0"

README.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838

3939
## System Overview
4040

41+
> **Note:** Platform now supports two operating modes: **Centralized Mode** (default, using platform-server) and **P2P Mode** (recommended, fully decentralized). See the [Decentralized P2P Mode](#decentralized-p2p-mode-recommended) section for the peer-to-peer architecture.
42+
4143
Platform involves three main participants:
4244

4345
- **Miners**: Submit code/models to challenges for evaluation
@@ -368,6 +370,54 @@ docker compose up -d
368370

369371
The validator will auto-connect to platform-server at `chain.platform.network` and sync.
370372

373+
## Decentralized P2P Mode (Recommended)
374+
375+
Platform supports a fully decentralized architecture where validators communicate via P2P without requiring a central server.
376+
377+
### Benefits
378+
- **No single point of failure** - No dependency on chain.platform.network
379+
- **Fully trustless** - Validators reach consensus via PBFT
380+
- **Bittensor-linked state** - State changes are linked to Subtensor block numbers
381+
- **DHT storage** - Submissions and evaluations stored across the network
382+
383+
### Quick Start (P2P Mode)
384+
385+
```bash
386+
git clone https://github.com/PlatformNetwork/platform.git
387+
cd platform
388+
cp .env.example .env
389+
# Edit .env: add your VALIDATOR_SECRET_KEY (BIP39 mnemonic)
390+
docker compose -f docker-compose.decentralized.yml up -d
391+
```
392+
393+
### Architecture (P2P Mode)
394+
395+
```
396+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
397+
│ VALIDATOR 1 │◄───►│ VALIDATOR 2 │◄───►│ VALIDATOR N │
398+
│ (libp2p) │ │ (libp2p) │ │ (libp2p) │
399+
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
400+
│ │ │
401+
└───────────────────────┼───────────────────────┘
402+
403+
404+
┌─────────────────────────┐
405+
│ BITTENSOR CHAIN │
406+
│ (Weight Commits) │
407+
│ State linked to blocks │
408+
└─────────────────────────┘
409+
```
410+
411+
### P2P Configuration
412+
413+
| Variable | Description | Default |
414+
|----------|-------------|---------|
415+
| `VALIDATOR_SECRET_KEY` | BIP39 mnemonic or hex key | - |
416+
| `SUBTENSOR_ENDPOINT` | Bittensor RPC endpoint | `wss://entrypoint-finney.opentensor.ai:443` |
417+
| `NETUID` | Subnet UID | `100` |
418+
| `P2P_LISTEN_ADDR` | libp2p listen address | `/ip4/0.0.0.0/tcp/9000` |
419+
| `BOOTSTRAP_PEERS` | Bootstrap peers (comma-separated) | - |
420+
371421
## Hardware Requirements
372422

373423
| Resource | Minimum | Recommended |
@@ -413,7 +463,7 @@ Standard REST endpoints for submissions, evaluations, and challenges.
413463
| `SUBTENSOR_ENDPOINT` | Bittensor RPC endpoint | `wss://entrypoint-finney.opentensor.ai:443` | No |
414464
| `NETUID` | Subnet UID | `100` | No |
415465
| `RUST_LOG` | Log level | `info` | No |
416-
| `PLATFORM_SERVER_URL` | Platform server URL | `https://chain.platform.network` | No |
466+
| `PLATFORM_SERVER_URL` | Platform server URL | `https://chain.platform.network` | No (centralized mode only) |
417467
| `PLATFORM_PUBLIC_URL` | Public URL for challenge containers | - | Yes |
418468
| `DATABASE_URL` | PostgreSQL connection (server only) | - | Yes (server) |
419469
| `OWNER_HOTKEY` | Subnet owner hotkey | - | Yes (server) |

bins/csudo/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use platform_core::Keypair;
1717
use reqwest::Client;
1818
use serde::{Deserialize, Serialize};
1919

20+
// Default server for centralized mode (optional in P2P mode)
21+
// In decentralized mode, sudo operations are broadcast via P2P consensus
2022
const DEFAULT_SERVER: &str = "https://chain.platform.network";
2123

2224
#[derive(Parser, Debug)]

bins/validator-decentralized/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,10 @@ async fn handle_block_event(
545545
match event {
546546
BlockSyncEvent::NewBlock { block_number, .. } => {
547547
debug!("Block {}", block_number);
548+
// Link state to Bittensor block (block hash not available in event, use zeros)
549+
state_manager.apply(|state| {
550+
state.link_to_bittensor_block(block_number, [0u8; 32]);
551+
});
548552
}
549553
BlockSyncEvent::EpochTransition {
550554
old_epoch,

bins/validator-node/src/main.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
//! Validator Node - Centralized Architecture
1+
//! # DEPRECATED - Use validator-decentralized Instead
2+
//!
3+
//! This centralized validator is deprecated. For new deployments, use:
4+
//! ```
5+
//! validator-decentralized --data-dir /data --listen-addr /ip4/0.0.0.0/tcp/9000
6+
//! ```
7+
//!
8+
//! The decentralized validator operates in P2P mode without requiring
9+
//! chain.platform.network. It uses libp2p gossipsub for consensus and
10+
//! Kademlia DHT for peer discovery.
11+
//!
12+
//! ---
13+
//!
14+
//! # Validator Node - Centralized Architecture (Legacy)
215
//!
316
//! All communication via platform-server (chain.platform.network).
417
//! No P2P networking. Weights submitted via Subtensor (handles CRv4 automatically).

crates/challenge-orchestrator/src/docker.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -883,11 +883,12 @@ impl DockerClient {
883883
})
884884
});
885885

886-
// Pass Platform URL for metagraph verification and API calls
887-
// Default to public platform-server URL so validators don't need extra config
888-
let platform_url = std::env::var("PLATFORM_PUBLIC_URL")
889-
.unwrap_or_else(|_| "https://chain.platform.network".to_string());
890-
env.push(format!("PLATFORM_URL={}", platform_url));
886+
// In P2P mode, challenges communicate via libp2p, not HTTP
887+
// PLATFORM_PUBLIC_URL is only needed for centralized/hybrid mode
888+
let platform_url = std::env::var("PLATFORM_PUBLIC_URL").unwrap_or_else(|_| String::new());
889+
if !platform_url.is_empty() {
890+
env.push(format!("PLATFORM_URL={}", platform_url));
891+
}
891892

892893
// Pass Container Broker WebSocket URL for secure container spawning
893894
// Challenges connect to this broker instead of using Docker socket directly

crates/challenge-sdk/src/decentralized.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,35 @@ pub async fn run_decentralized<C: ServerChallenge + Send + Sync + 'static>(
144144
// Weights response, may be used for verification
145145
debug!("Received weights response in runner");
146146
}
147+
P2PChallengeMessage::StoreSubmission {
148+
submission,
149+
challenge_id: msg_challenge_id,
150+
} => {
151+
if msg_challenge_id == challenge_id {
152+
debug!(
153+
challenge_id = %challenge_id,
154+
submission_hash = %submission.submission_hash,
155+
miner_hotkey = %submission.miner_hotkey,
156+
"Received submission to store - forwarding to evaluation queue"
157+
);
158+
// Submissions are auto-queued for evaluation when stored via P2P
159+
// The P2P layer handles distributed storage and replication
160+
} else {
161+
warn!(
162+
expected = %challenge_id,
163+
received = %msg_challenge_id,
164+
"Received StoreSubmission for wrong challenge, ignoring"
165+
);
166+
}
167+
}
168+
P2PChallengeMessage::RequestEvaluationStatus { .. } => {
169+
// Request for evaluation status is handled by the P2P layer
170+
debug!("Ignoring RequestEvaluationStatus message in runner - handled by P2P layer");
171+
}
172+
P2PChallengeMessage::EvaluationStatusResponse { .. } => {
173+
// Evaluation status responses are handled by the client layer
174+
debug!("Ignoring EvaluationStatusResponse message in runner");
175+
}
147176
}
148177
}
149178

crates/challenge-sdk/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ pub mod weights;
111111
pub use decentralized::run_decentralized;
112112
pub use p2p_client::{
113113
P2PChallengeClient, P2PChallengeConfig, P2PChallengeMessage, PendingSubmission,
114+
ValidatorEvaluationResult,
114115
};
115116
pub use platform_client::{
116117
run_as_client, ChallengeMessage, ConnectionState, PlatformClient, PlatformClientConfig,
@@ -145,6 +146,7 @@ pub mod prelude {
145146
pub use super::decentralized::run_decentralized;
146147
pub use super::p2p_client::{
147148
P2PChallengeClient, P2PChallengeConfig, P2PChallengeMessage, PendingSubmission,
149+
ValidatorEvaluationResult,
148150
};
149151

150152
// Common utilities

0 commit comments

Comments
 (0)