Future: Fully On-Chain P2P
This page describes the future vision for a fully decentralized, on-chain AI Power Grid. Current P2P mode is a stepping stone toward this goal.
The Challenge
Moving fully on-chain introduces hard problems:
| Challenge | Why It’s Hard |
|---|---|
| Output Verification | Can’t re-run inference to verify (expensive, non-deterministic) |
| Fair Rewards | How to pay workers fairly without central authority? |
| Quality Assurance | How to ensure workers aren’t returning garbage? |
| Sybil Resistance | How to prevent one entity running 1000 fake workers? |
| Gas Costs | Can’t put every token on-chain |
Proposed Architecture
┌─────────────────────────────────────────────────────────────────┐
│ ON-CHAIN (Base L2) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Worker │ │ Job │ │ Dispute │ │
│ │ Registry │ │ Escrow │ │ Resolution │ │
│ │ │ │ │ │ │ │
│ │ - Stakes │ │ - Deposits │ │ - Challenges │ │
│ │ - Scores │ │ - Releases │ │ - Slashing │ │
│ │ - Models │ │ - Refunds │ │ - Appeals │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │
└───────────────────────────┬─────────────────────────────────────┘
│
State commitments
Payment settlements
│
┌───────────────────────────┼─────────────────────────────────────┐
│ OFF-CHAIN (P2P) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Job │ │ Result │ │ Verification │ │
│ │ Broadcast │ │ Streaming │ │ Sampling │ │
│ │ │ │ │ │ │ │
│ │ gossipsub │ │ gossipsub │ │ Random re-computation │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘Worker Registry
Workers must register on-chain to participate:
contract WorkerRegistry {
struct Worker {
address owner;
bytes32 peerId; // libp2p peer ID
uint256 stake; // AIPG staked
uint256 reputationScore; // 0-1000
string[] models; // Models they serve
uint256 jobsCompleted;
uint256 jobsDisputed;
bool active;
}
mapping(bytes32 => Worker) public workers;
// Minimum stake to participate
uint256 public constant MIN_STAKE = 1000 * 10**18; // 1000 AIPG
function register(bytes32 peerId, string[] calldata models) external payable {
require(msg.value >= MIN_STAKE, "Insufficient stake");
workers[peerId] = Worker({
owner: msg.sender,
peerId: peerId,
stake: msg.value,
reputationScore: 500, // Start neutral
models: models,
jobsCompleted: 0,
jobsDisputed: 0,
active: true
});
}
function slash(bytes32 peerId, uint256 amount) external onlyDisputeContract {
workers[peerId].stake -= amount;
workers[peerId].reputationScore -= 50;
if (workers[peerId].stake < MIN_STAKE) {
workers[peerId].active = false;
}
}
}Stake-Weighted Claim Resolution
Higher stake = higher chance of winning jobs:
def compute_weighted_claim_score(job_id, seed, worker_id, stake):
"""
Lower score wins. Stake gives advantage.
A worker with 2x stake has 2x chance of winning.
"""
base_score = SHA256(job_id + seed + worker_id)
# Convert to number, divide by stake
# Higher stake = lower score = more likely to win
score_int = int.from_bytes(base_score, 'big')
weighted_score = score_int // stake
return weighted_scoreThis creates economic incentive to stake more, securing the network.
Job Escrow
Users deposit payment before job execution:
contract JobEscrow {
struct Job {
bytes32 jobId;
address user;
uint256 payment; // AIPG deposited
uint256 maxTokens;
bytes32 assignedWorker;
uint256 deadline;
JobStatus status;
bytes32 resultHash; // Hash of output (for disputes)
}
enum JobStatus { Pending, Claimed, Completed, Disputed, Refunded }
mapping(bytes32 => Job) public jobs;
function createJob(
bytes32 jobId,
uint256 maxTokens
) external payable {
require(msg.value > 0, "Payment required");
jobs[jobId] = Job({
jobId: jobId,
user: msg.sender,
payment: msg.value,
maxTokens: maxTokens,
assignedWorker: bytes32(0),
deadline: block.timestamp + 5 minutes,
status: JobStatus.Pending,
resultHash: bytes32(0)
});
// Emit event for P2P network to pick up
emit JobCreated(jobId, msg.sender, msg.value, maxTokens);
}
function completeJob(
bytes32 jobId,
bytes32 resultHash,
uint256 tokenCount,
bytes calldata signature
) external {
Job storage job = jobs[jobId];
require(job.status == JobStatus.Claimed, "Not claimed");
require(block.timestamp <= job.deadline, "Expired");
// Verify worker signature
require(verifyWorkerSignature(job.assignedWorker, jobId, resultHash, signature));
job.resultHash = resultHash;
job.status = JobStatus.Completed;
// Calculate payment based on tokens
uint256 payout = calculatePayout(job.payment, tokenCount, job.maxTokens);
// Pay worker
payable(workerRegistry.getOwner(job.assignedWorker)).transfer(payout);
// Refund unused portion to user
if (payout < job.payment) {
payable(job.user).transfer(job.payment - payout);
}
}
}Output Verification
The hardest problem. Several approaches:
Approach 0: Verification Nodes (Recommended for Images)
Dedicated nodes that re-run jobs to verify outputs:
┌─────────────────────────────────────────────────────────────────┐
│ VERIFICATION NODE FLOW │
│ │
│ Job submitted with: │
│ - prompt: "A cat wearing a hat" │
│ - seed: 12345 │
│ - model: flux │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ Job ──▶ Worker A │ Job ──▶ Verifier B │ │
│ │ │ │ │ │
│ │ Generate │ │ Generate │ │
│ │ image │ │ same image │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ COMPARE │ │
│ │ │ │
│ │ Images identical? ──▶ Pay both │ │
│ │ Images differ? ──▶ Investigate │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘For Images (Deterministic):
def verify_image(image_a: bytes, image_b: bytes) -> bool:
"""
Same model + same seed + same prompt = identical image.
Can compare hashes directly.
"""
hash_a = sha256(image_a)
hash_b = sha256(image_b)
return hash_a == hash_bFor Text (Non-Deterministic):
def verify_text(text_a: str, text_b: str) -> bool:
"""
LLMs aren't deterministic. Use semantic similarity.
Embed both outputs and compare cosine similarity.
"""
embedding_a = embed(text_a) # e.g., using sentence-transformers
embedding_b = embed(text_b)
similarity = cosine_similarity(embedding_a, embedding_b)
# Threshold: 0.90 = "close enough"
return similarity > 0.90Verification Node Economics:
┌─────────────────────────────────────────────────────────────────┐
│ WHO PAYS FOR VERIFICATION? │
│ │
│ Option A: User pays verification fee │
│ Job cost: $0.10 base + $0.02 verification fee │
│ Verifier gets the $0.02 │
│ │
│ Option B: Protocol pays from inflation │
│ X% of new AIPG emissions go to verification pool │
│ Verifiers stake and earn from pool │
│ │
│ Option C: Workers pay insurance │
│ Workers pay 2% of earnings to verification fund │
│ Fund pays for random spot-checks │
│ Workers who pass all checks get rebate │
│ │
└─────────────────────────────────────────────────────────────────┘Verification Sampling Rate:
┌─────────────────────────────────────────────────────────────────┐
│ ADAPTIVE SAMPLING │
│ │
│ New worker (reputation < 200): │
│ 100% of jobs verified │
│ Must prove they're honest │
│ │
│ Established worker (reputation 200-700): │
│ 10% of jobs randomly verified │
│ Occasional spot-checks │
│ │
│ Trusted worker (reputation > 700): │
│ 1% of jobs randomly verified │
│ Rare audits to ensure continued honesty │
│ │
│ Selection is random and unpredictable: │
│ should_verify = SHA256(job_id + block_hash)[0] < threshold │
│ │
└─────────────────────────────────────────────────────────────────┘Approach 1: Optimistic Verification
Assume outputs are correct. Allow challenges.
┌─────────────────────────────────────────────────────────────────┐
│ OPTIMISTIC VERIFICATION FLOW │
│ │
│ 1. Worker completes job, submits result hash on-chain │
│ │
│ 2. Challenge window opens (e.g., 10 minutes) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Anyone can challenge by: │ │
│ │ - Staking a challenge bond │ │
│ │ - Providing evidence of bad output │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 3a. No challenge → Payment released to worker │
│ │
│ 3b. Challenge raised → Dispute resolution │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ - Multiple verifiers re-run the job │ │
│ │ - Compare outputs │ │
│ │ - Majority wins │ │
│ │ - Loser gets slashed │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘Pros: Cheap (most jobs not challenged) Cons: Delay before payment, requires verifiers
Approach 2: Random Sampling
Randomly select X% of jobs for verification:
def should_verify(job_id, block_hash):
"""
Deterministic random selection.
~5% of jobs get verified.
"""
combined = SHA256(job_id + block_hash)
return combined[0] < 13 # 13/256 ≈ 5%For selected jobs:
- Send to 3 workers instead of 1
- Compare outputs
- Slash any worker that differs significantly
┌─────────────────────────────────────────────────────────────────┐
│ SAMPLING VERIFICATION │
│ │
│ 95% of jobs: │
│ User → Worker → Result → Payment │
│ │
│ 5% of jobs (randomly selected): │
│ User → Worker 1 ─┐ │
│ → Worker 2 ─┼─→ Compare → If match: Pay all │
│ → Worker 3 ─┘ If differ: Slash outlier │
│ │
└─────────────────────────────────────────────────────────────────┘Pros: Catches cheaters probabilistically Cons: 5% overhead, non-deterministic outputs make comparison hard
Approach 3: Commit-Reveal with Reputation
Workers build reputation over time:
┌─────────────────────────────────────────────────────────────────┐
│ REPUTATION TIERS │
│ │
│ Tier 1 (New Workers): │
│ - All jobs verified │
│ - Low payment until proven │
│ - Need 100 successful jobs to advance │
│ │
│ Tier 2 (Established): │
│ - 10% of jobs verified │
│ - Standard payment │
│ - Need 1000 jobs + high rating to advance │
│ │
│ Tier 3 (Trusted): │
│ - 1% of jobs verified │
│ - Premium payment (priority in claims) │
│ - Single bad job → back to Tier 2 │
│ │
└─────────────────────────────────────────────────────────────────┘Dispute Resolution
When verification fails or user challenges:
contract DisputeResolution {
struct Dispute {
bytes32 jobId;
address challenger;
uint256 challengeBond;
bytes evidence;
DisputeStatus status;
bytes32[] verifierVotes;
}
enum DisputeStatus { Open, Verifying, Resolved }
// Verifier selection: random from high-reputation workers
function selectVerifiers(bytes32 disputeId) internal returns (bytes32[] memory) {
// Use block hash + dispute ID for randomness
// Select 5 workers with reputation > 800
// They must re-run the job and vote
}
function resolveDispute(bytes32 disputeId) external {
Dispute storage dispute = disputes[disputeId];
// Count votes
uint256 forWorker = 0;
uint256 forChallenger = 0;
for (uint i = 0; i < dispute.verifierVotes.length; i++) {
if (dispute.verifierVotes[i] == VOTE_WORKER) forWorker++;
else forChallenger++;
}
if (forWorker > forChallenger) {
// Worker was honest
// Slash challenger bond
// Pay worker + bonus from challenge bond
} else {
// Worker cheated
// Slash worker stake
// Refund user
// Reward challenger
}
}
}Payment Channels (Gas Optimization)
Can’t put every token on-chain. Use payment channels:
┌─────────────────────────────────────────────────────────────────┐
│ PAYMENT CHANNEL FLOW │
│ │
│ 1. User opens channel with worker, deposits 100 AIPG │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ On-chain: Lock 100 AIPG in channel contract │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ 2. Jobs happen off-chain, user signs IOUs │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Job 1: User signs "Worker can claim 0.5 AIPG" │ │
│ │ Job 2: User signs "Worker can claim 1.2 AIPG" │ │
│ │ Job 3: User signs "Worker can claim 1.8 AIPG" │ │
│ │ ... │ │
│ │ No gas fees for these! │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ 3. Settle on-chain (once per day/week) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Worker submits final IOU (e.g., 45 AIPG) │ │
│ │ Contract pays worker, refunds user remainder │ │
│ │ One transaction settles hundreds of jobs! │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘Sybil Resistance
Preventing one entity from running many fake workers:
Economic Sybil Resistance
Cost to run N workers = N × MIN_STAKE
If MIN_STAKE = 1000 AIPG:
- 1 worker = 1000 AIPG staked
- 10 workers = 10,000 AIPG staked
- 100 workers = 100,000 AIPG staked
The attacker needs real capital at risk.
If they cheat, they lose it all.Reputation Sybil Resistance
New workers start with low reputation:
- Fewer jobs
- Lower payment
- More verification
Building reputation takes time.
Creating 100 new workers means 100x the time to build reputation.Hardware Attestation (Future)
Workers prove they have real hardware:
- Trusted execution environment (TEE)
- GPU attestation
- Proof of unique hardware
One GPU = one high-reputation worker slot.Reward Distribution
How workers get paid fairly:
┌─────────────────────────────────────────────────────────────────┐
│ REWARD FORMULA │
│ │
│ Base Payment = tokens_generated × price_per_token │
│ │
│ Multipliers: │
│ × reputation_bonus (0.8 - 1.2 based on score) │
│ × speed_bonus (faster = more) │
│ × stake_bonus (higher stake = slight bonus) │
│ │
│ Final Payment = Base × reputation × speed × stake │
│ │
│ Example: │
│ 100 tokens × $0.001 = $0.10 base │
│ × 1.1 (high reputation) │
│ × 1.05 (fast response) │
│ × 1.02 (10x min stake) │
│ = $0.118 final payment │
│ │
└─────────────────────────────────────────────────────────────────┘Migration Path
┌─────────────────────────────────────────────────────────────────┐
│ PHASE 1: Current (Centralized) │
│ - Central API server │
│ - Redis job queue │
│ - Off-chain payments │
│ │
│ PHASE 2: P2P Mode (Now) │
│ - Decentralized job distribution │
│ - Gossipsub messaging │
│ - Still off-chain payments │
│ │
│ PHASE 3: Hybrid On-Chain │
│ - Worker registry on-chain │
│ - Job escrow on-chain │
│ - Execution still off-chain (P2P) │
│ - Settlement on-chain │
│ │
│ PHASE 4: Full Decentralization │
│ - Verification system active │
│ - Dispute resolution │
│ - Payment channels │
│ - Fully trustless │
│ │
└─────────────────────────────────────────────────────────────────┘Image vs Text Verification
Different strategies for different modalities:
┌─────────────────────────────────────────────────────────────────┐
│ IMAGE GENERATION (Easy to verify) │
│ │
│ ✅ Deterministic with seed │
│ ✅ Pixel-perfect comparison possible │
│ ✅ Perceptual hash as backup │
│ ✅ Same model + seed + prompt = identical output │
│ │
│ Verification: Run same job, compare SHA256 of output │
│ Cost: 2x compute for verified jobs │
│ Confidence: 100% - either matches or doesn't │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ TEXT GENERATION (Harder to verify) │
│ │
│ ❌ NOT deterministic (floating point variance) │
│ ❌ Different GPUs = different outputs │
│ ❌ Different quantization = different outputs │
│ ⚠️ Even temperature=0 varies │
│ │
│ Verification options: │
│ 1. Semantic similarity (embeddings) │
│ 2. Key fact extraction and comparison │
│ 3. LLM-as-judge (another model grades output) │
│ 4. User feedback aggregation │
│ │
│ Confidence: 85-95% depending on method │
│ │
└─────────────────────────────────────────────────────────────────┘Text Verification Methods
Method 1: Embedding Similarity
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
def verify_text_similarity(output_a: str, output_b: str) -> float:
embeddings = model.encode([output_a, output_b])
similarity = cosine_similarity(embeddings[0], embeddings[1])
return similarity # 0.0 to 1.0Method 2: Fact Extraction
def extract_key_facts(text: str) -> set:
"""Use NER/extraction to pull key facts."""
# "The capital of France is Paris, with 2M people"
# → {"capital:France:Paris", "population:Paris:2M"}
pass
def verify_facts(output_a: str, output_b: str) -> float:
facts_a = extract_key_facts(output_a)
facts_b = extract_key_facts(output_b)
overlap = len(facts_a & facts_b) / len(facts_a | facts_b)
return overlapMethod 3: LLM-as-Judge
async def llm_judge(prompt: str, output_a: str, output_b: str) -> bool:
"""Use a small, fast model to judge if outputs are equivalent."""
judge_prompt = f"""
Original question: {prompt}
Response A: {output_a}
Response B: {output_b}
Are these responses semantically equivalent? (yes/no)
"""
verdict = await small_llm.complete(judge_prompt)
return "yes" in verdict.lower()Recommended Strategy
┌─────────────────────────────────────────────────────────────────┐
│ HYBRID VERIFICATION STRATEGY │
│ │
│ Images: │
│ → Always use hash comparison (100% accurate) │
│ → Sample 5% of jobs for verification │
│ → Instant, cheap, reliable │
│ │
│ Text: │
│ → Use embedding similarity as primary │
│ → Threshold: 0.88 = pass, <0.70 = fail, between = review │
│ → Sample 10% of jobs (higher rate due to uncertainty) │
│ → For disputes: escalate to LLM-as-judge │
│ │
│ Combined: │
│ → New workers: 100% verified (both methods) │
│ → Established: 5-10% spot checks │
│ → Trusted: 1% random audits │
│ │
└─────────────────────────────────────────────────────────────────┘Open Questions
These need community input:
- Verification Cost: Who pays for re-computation in disputes?
- Non-Determinism: LLM outputs vary. How similar is “same”?
- Challenge Incentives: How to incentivize challenges without spam?
- Verifier Selection: How to ensure verifiers are independent?
- Cross-Chain: Support multiple chains? Bridge tokens?
Economics Summary
| Component | On-Chain | Off-Chain |
|---|---|---|
| Worker Registration | ✅ | |
| Staking | ✅ | |
| Job Creation | ✅ (hash only) | Full payload |
| Job Execution | ✅ (P2P) | |
| Result Streaming | ✅ (P2P) | |
| Result Hash | ✅ | |
| Payment Settlement | ✅ | Channels |
| Dispute Resolution | ✅ |
Estimated gas costs (Base L2):
- Worker registration: ~$0.10
- Job creation: ~$0.02
- Job completion: ~$0.02
- Dispute: ~$0.50
With payment channels, most jobs cost zero gas after initial channel setup.
Comparison to Existing Systems
| System | Verification | Payment | Decentralization |
|---|---|---|---|
| OpenAI | Centralized | Credit card | None |
| Livepeer | Random sampling | On-chain | Full |
| Render | Trusted nodes | On-chain | Partial |
| AIPG (Future) | Optimistic + sampling | Channels | Full |
Get Involved
This is a community effort. Join the discussion:
- Discord: Discuss verification approaches
- GitHub: Propose smart contract designs
- Governance: Vote on economic parameters
The goal: AI inference that runs itself, pays itself, and polices itself.