HIP-95: QoS Challenge System. Status Draft. Hanzo's own standard — read this before implementing against it.
This document specifies the Quality of Service (QoS) Challenge System for Hanzo Network's decentralized compute marketplace. The system ensures compute providers maintain advertised service levels through cryptographic challenges, verifiable proofs, and economic incentives.
In a decentralized compute marketplace, providers may:
The QoS Challenge System addresses these issues by continuously verifying provider capabilities and performance through cryptographic proofs.
QoS Challenge System Architecture
+================================================================================+
| |
| +------------------+ +-------------------+ +--------------------+ |
| | Challenge | | Verification | | Scoring | |
| | Generator |---->| Engine |---->| System | |
| +------------------+ +-------------------+ +--------------------+ |
| | | | |
| v v v |
| +------------------+ +-------------------+ +--------------------+ |
| | Challenge Types | | Proof Types | | Score Factors | |
| | - Compute | | - Hash Proof | | - Latency | |
| | - Latency | | - TEE Attest | | - Throughput | |
| | - Bandwidth | | - Merkle Proof | | - Availability | |
| | - Availability | | - ZK Proof | | - Consistency | |
| +------------------+ +-------------------+ +--------------------+ |
| | |
| v |
| +------------------------------------------------------------------+ |
| | On-Chain Settlement | |
| | +-------------------+ +-------------------+ +-----------------+ | |
| | | Stake Management | | Reward/Slash | | Reputation | | |
| | | (hanzo-mining) | | Distribution | | Registry | | |
| | +-------------------+ +-------------------+ +-----------------+ | |
| +------------------------------------------------------------------+ |
| |
+================================================================================+
The QoS Challenge System integrates with:
| Component | Integration Point | Purpose | | --------------- | ---------------------------- | ------------------------------------- | | hanzo-compute | ComputeSwarm, Peer | Challenge issuance, result collection | | hanzo-mining | MiningManager, Consensus | On-chain settlement, stake management | | hanzo-libp2p | P2P messaging | Challenge delivery, proof collection | | hanzo-pqc | Cryptographic primitives | Quantum-safe signatures for proofs |
Verifies actual compute capability (TFLOPS/GFLOPS)
pub struct ComputeChallenge {
/// Unique challenge identifier
pub id: ChallengeId,
/// Challenge type marker
pub challenge_type: ChallengeType,
/// Random seed for deterministic computation
pub seed: [u8; 32],
/// Difficulty level (determines workload size)
pub difficulty: u8,
/// Expected compute metric being tested
pub metric: ComputeMetric,
/// Maximum time allowed (milliseconds)
pub deadline_ms: u64,
/// Challenger's signature
pub signature: Vec<u8>,
/// Timestamp of challenge issuance
pub issued_at: u64,
}
#[derive(Clone, Copy)]
pub enum ComputeMetric {
/// GPU FP32 TFLOPS
GpuFp32,
/// GPU FP16 TFLOPS
GpuFp16,
/// GPU INT8 TOPS
GpuInt8,
/// CPU GFLOPS
CpuGflops,
/// Memory bandwidth GB/s
MemoryBandwidth,
}
#[derive(Clone, Copy)]
pub enum ChallengeType {
Compute(ComputeMetric),
Latency,
Bandwidth,
Availability,
Model(ModelCapability),
}
Verification Method: Provider executes a deterministic computation kernel (e.g., matrix multiplication with specific seed). Result hash and execution time are submitted.
Difficulty Scaling:
# Difficulty determines problem size
def compute_problem_size(difficulty: int, metric: str) -> int:
"""
Returns dimensions for matrix operations
difficulty 1-10 maps to increasingly large workloads
"""
base_sizes = {
'gpu_fp32': 1024, # Starting matrix size
'gpu_fp16': 2048,
'cpu_gflops': 512,
'memory_bandwidth': 1073741824, # 1GB base
}
return base_sizes[metric] * (2 ** (difficulty - 1))
Verifies network latency and response time
pub struct LatencyChallenge {
pub id: ChallengeId,
/// Nonce to prevent replay
pub nonce: [u8; 16],
/// Target percentile (p50, p95, p99)
pub target_percentile: u8,
/// Expected max latency in milliseconds
pub max_latency_ms: u32,
/// Number of round trips required
pub rounds: u8,
pub signature: Vec<u8>,
}
Verification Method: Echo-response pattern with cryptographic binding. Provider must respond within deadline with signed nonce.
Verifies data transfer capabilities
pub struct BandwidthChallenge {
pub id: ChallengeId,
/// Random data to transfer (merkle root)
pub data_root: [u8; 32],
/// Size of transfer in bytes
pub transfer_size: u64,
/// Direction of test
pub direction: TransferDirection,
/// Expected minimum throughput Mbps
pub min_throughput_mbps: u32,
pub signature: Vec<u8>,
}
pub enum TransferDirection {
Upload,
Download,
Bidirectional,
}
Verifies provider is online and ready to serve
pub struct AvailabilityChallenge {
pub id: ChallengeId,
/// Random nonce
pub nonce: [u8; 32],
/// Challenge window (must respond within)
pub window_ms: u32,
/// Current epoch
pub epoch: u64,
pub signature: Vec<u8>,
}
Verification Method: Heartbeat-style challenges issued at random intervals. Missing responses count against availability score.
Verifies provider can run specific AI models
pub struct ModelChallenge {
pub id: ChallengeId,
/// Model identifier to test
pub model: String,
/// Deterministic prompt seed
pub prompt_seed: [u8; 32],
/// Expected completion hash prefix (first N bytes)
pub expected_prefix_len: u8,
/// Max generation time
pub deadline_ms: u64,
pub signature: Vec<u8>,
}
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Challenge │ │ Provider │ │ Verifier │ │ On-Chain │
│ Committee │ │ Node │ │ Network │ │ Contract │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │ │
│ 1. SELECT RANDOM PROVIDERS │ │
├───────────────────────────────────────┤ │
│ │ │ │
│ 2. ISSUE CHALLENGE │ │
├──────────────────>│ │ │
│ │ │ │
│ 3. EXECUTE CHALLENGE │ │
│ ├───┐ │ │
│ │ │ │ │
│ │<──┘ │ │
│ │ │ │
│ 4. SUBMIT PROOF │ │
│<──────────────────┤ │ │
│ │ │ │
│ 5. FORWARD FOR VERIFICATION │ │
├──────────────────────────────────────>│ │
│ │ │ │
│ 6. VERIFY PROOF │ │
│ │ ├───┐ │
│ │ │ │ │
│ │ │<──┘ │
│ │ │ │
│ 7. SUBMIT RESULT (if passing) │ │
│ │ ├──────────────────>│
│ │ │ │
│ 8. UPDATE SCORE & DISTRIBUTE REWARDS │ │
│ │<─────────────────────────────────────│
│ │ │ │
| Provider Tier | Challenge Frequency | Challenge Mix | | -------------------- | ------------------- | ---------------------------------------------- | | New (< 10 jobs) | Every 10 minutes | 40% Compute, 30% Avail, 20% Latency, 10% Model | | Established (10-100) | Every 30 minutes | 30% Compute, 30% Avail, 25% Latency, 15% Model | | Trusted (100+) | Every 60 minutes | 25% Compute, 35% Avail, 20% Latency, 20% Model | | Elite (1000+, TEE) | Every 2 hours | 20% Compute, 40% Avail, 20% Latency, 20% Model |
pub struct ChallengeProof {
/// Challenge being responded to
pub challenge_id: ChallengeId,
/// Provider submitting proof
pub provider_id: PeerId,
/// Proof data (varies by challenge type)
pub proof_data: ProofData,
/// Execution timestamp
pub executed_at: u64,
/// Execution duration in milliseconds
pub duration_ms: u64,
/// ML-DSA signature over proof
pub signature: Vec<u8>,
/// Optional TEE attestation
pub tee_attestation: Option<TeeAttestation>,
}
pub enum ProofData {
/// Compute proof: result hash + intermediate hashes
Compute {
result_hash: [u8; 32],
intermediate_hashes: Vec<[u8; 32]>,
actual_flops: f64,
},
/// Latency proof: signed responses per round
Latency {
round_results: Vec<LatencyRound>,
p50_ms: u32,
p95_ms: u32,
p99_ms: u32,
},
/// Bandwidth proof: merkle proof of transferred data
Bandwidth {
merkle_proofs: Vec<MerkleProof>,
achieved_mbps: u32,
bytes_transferred: u64,
},
/// Availability proof: signed nonce response
Availability {
signed_nonce: Vec<u8>,
response_time_ms: u32,
},
/// Model proof: completion hash and token count
Model {
completion_hash: [u8; 32],
token_count: u32,
tokens_per_second: f64,
},
}
For compute challenges, verifiers execute the same deterministic computation:
impl ComputeChallengeVerifier {
pub fn verify(&self, challenge: &ComputeChallenge, proof: &ProofData) -> VerificationResult {
match proof {
ProofData::Compute { result_hash, intermediate_hashes, actual_flops } => {
// Recreate the computation with same seed
let expected = self.execute_reference(challenge.seed, challenge.difficulty);
// Compare hashes
if expected.result_hash != *result_hash {
return VerificationResult::Failed(FailureReason::HashMismatch);
}
// Verify intermediate hashes (spot check)
let sample_indices = self.select_sample_indices(challenge.seed, 5);
for idx in sample_indices {
if expected.intermediate_hashes[idx] != intermediate_hashes[idx] {
return VerificationResult::Failed(FailureReason::IntermediateHashMismatch);
}
}
// Verify performance claim
let expected_min_flops = self.difficulty_to_min_flops(challenge.difficulty);
let performance_score = actual_flops / expected_min_flops;
VerificationResult::Passed {
score: performance_score.min(1.0),
bonus: if performance_score > 1.2 { 0.1 } else { 0.0 },
}
}
_ => VerificationResult::Failed(FailureReason::WrongProofType),
}
}
}
For efficiency, most verifications are optimistic:
pub struct OptimisticVerification {
/// Challenge ID
pub challenge_id: ChallengeId,
/// Provider's submitted proof
pub proof: ChallengeProof,
/// Submission timestamp
pub submitted_at: u64,
/// Dispute window (blocks)
pub dispute_window: u64,
/// Current status
pub status: VerificationStatus,
}
pub enum VerificationStatus {
/// Pending - within dispute window
Pending,
/// Accepted - dispute window passed
Accepted,
/// Disputed - fraud proof submitted
Disputed { challenger: PeerId, fraud_proof: FraudProof },
/// Rejected - fraud proof verified
Rejected { slash_amount: u64 },
}
pub struct FraudProof {
/// Reference computation result
pub expected_result: ProofData,
/// Evidence of mismatch
pub evidence: FraudEvidence,
/// Challenger's stake
pub stake: u64,
/// Challenger's signature
pub signature: Vec<u8>,
}
For TEE-enabled providers:
pub trait TeeAttestationVerifier {
/// Verify remote attestation quote
fn verify_attestation(&self, quote: &[u8], expected_mrenclave: &[u8; 32]) -> bool;
/// Verify signed report from enclave
fn verify_report(&self, report: &TeeReport) -> bool;
/// Get supported TEE types
fn supported_types(&self) -> Vec<TeeType>;
}
pub enum TeeType {
IntelSgx,
AmdSev,
ArmTrustZone,
NvidiaConfidentialCompute,
NvidiaBlackwellTeeIo,
}
The QoS score is a composite metric:
pub struct QoSScore {
/// Overall composite score (0-1000)
pub composite: u32,
/// Individual component scores
pub components: QoSComponents,
/// Score confidence (based on sample size)
pub confidence: f64,
/// Last updated timestamp
pub updated_at: u64,
/// Historical trend (positive = improving)
pub trend: f64,
}
pub struct QoSComponents {
/// Compute performance vs claimed (0-1000)
pub compute_score: u32,
/// Latency vs SLA (0-1000)
pub latency_score: u32,
/// Bandwidth vs claimed (0-1000)
pub bandwidth_score: u32,
/// Uptime percentage * 10 (0-1000)
pub availability_score: u32,
/// Consistency of performance (0-1000)
pub consistency_score: u32,
}
impl QoSScore {
pub fn calculate(components: &QoSComponents, weights: &ScoreWeights) -> Self {
// Weighted geometric mean for composite score
let composite = (
(components.compute_score as f64).powf(weights.compute) *
(components.latency_score as f64).powf(weights.latency) *
(components.bandwidth_score as f64).powf(weights.bandwidth) *
(components.availability_score as f64).powf(weights.availability) *
(components.consistency_score as f64).powf(weights.consistency)
).powf(1.0 / (weights.total())) as u32;
Self {
composite,
components: components.clone(),
confidence: 0.0, // Set based on sample count
updated_at: now_ms(),
trend: 0.0, // Set based on historical data
}
}
}
pub struct ScoreWeights {
pub compute: f64, // Default: 0.30
pub latency: f64, // Default: 0.20
pub bandwidth: f64, // Default: 0.15
pub availability: f64, // Default: 0.25
pub consistency: f64, // Default: 0.10
}
impl ConsistencyCalculator {
/// Calculate consistency score from recent challenge results
pub fn calculate(&self, results: &[ChallengeResult]) -> u32 {
if results.len() < 5 {
return 500; // Default neutral score for insufficient data
}
// Group by challenge type
let by_type = self.group_by_type(results);
let mut consistency_scores = Vec::new();
for (challenge_type, type_results) in by_type {
// Calculate coefficient of variation (CV = std_dev / mean)
let scores: Vec<f64> = type_results.iter()
.map(|r| r.performance_score)
.collect();
let mean = scores.iter().sum::<f64>() / scores.len() as f64;
let variance = scores.iter()
.map(|s| (s - mean).powi(2))
.sum::<f64>() / scores.len() as f64;
let std_dev = variance.sqrt();
let cv = std_dev / mean;
// Lower CV = higher consistency
// CV of 0.05 = perfect (1000), CV of 0.5 = poor (0)
let type_score = ((1.0 - (cv / 0.5).min(1.0)) * 1000.0) as u32;
consistency_scores.push(type_score);
}
// Average across challenge types
consistency_scores.iter().sum::<u32>() / consistency_scores.len() as u32
}
}
Economic Flow Diagram
+============================================================================+
| |
| Provider Stake Pool Challenge Rewards Pool |
| ┌─────────────────┐ ┌─────────────────┐ |
| │ │ │ │ |
| │ AI Tokens │ │ AI Tokens │ |
| │ (Locked) │ │ (Available) │ |
| │ │ │ │ |
| └────────┬────────┘ └────────┬────────┘ |
| │ │ |
| │ ┌──────────────────────────────┘ |
| │ │ |
| v v |
| ┌─────────────────────────────────────────────────────────┐ |
| │ QoS Challenge Engine │ |
| │ │ |
| │ Challenge Passed? │ |
| │ │ │ |
| │ ├── YES ──> Reward Distribution │ |
| │ │ │ │ |
| │ │ ├── Base Reward (from pool) │ |
| │ │ ├── Performance Bonus │ |
| │ │ └── Reputation Increase │ |
| │ │ │ |
| │ └── NO ───> Penalty Application │ |
| │ │ │ |
| │ ├── Stake Slash (to pool) │ |
| │ ├── Reputation Decrease │ |
| │ └── Potential Ban │ |
| │ │ |
| └─────────────────────────────────────────────────────────┘ |
| |
+============================================================================+
pub struct RewardConfig {
/// Base reward per successful challenge (AI tokens)
pub base_reward: u64,
/// Performance bonus multiplier (max 2x)
pub max_performance_bonus: f64,
/// Streak bonus for consecutive passes
pub streak_bonus_per_pass: f64,
/// Maximum streak bonus
pub max_streak_bonus: f64,
/// TEE attestation bonus
pub tee_bonus: f64,
/// Elite provider bonus threshold
pub elite_threshold_score: u32,
/// Elite provider bonus
pub elite_bonus: f64,
}
impl Default for RewardConfig {
fn default() -> Self {
Self {
base_reward: 100_000_000_000_000_000, // 0.1 AI tokens (18 decimals)
max_performance_bonus: 2.0,
streak_bonus_per_pass: 0.01,
max_streak_bonus: 0.5,
tee_bonus: 0.25,
elite_threshold_score: 900,
elite_bonus: 0.15,
}
}
}
pub struct RewardCalculator {
config: RewardConfig,
}
impl RewardCalculator {
pub fn calculate_reward(
&self,
challenge_result: &ChallengeResult,
provider_state: &ProviderState,
) -> u64 {
if !challenge_result.passed {
return 0;
}
let mut reward = self.config.base_reward as f64;
// Performance bonus (0-100% additional)
let performance_bonus = (challenge_result.performance_score - 1.0)
.max(0.0)
.min(1.0)
* self.config.max_performance_bonus;
reward *= 1.0 + performance_bonus;
// Streak bonus
let streak_bonus = (provider_state.challenge_streak as f64
* self.config.streak_bonus_per_pass)
.min(self.config.max_streak_bonus);
reward *= 1.0 + streak_bonus;
// TEE bonus
if challenge_result.tee_verified {
reward *= 1.0 + self.config.tee_bonus;
}
// Elite bonus
if provider_state.qos_score.composite >= self.config.elite_threshold_score {
reward *= 1.0 + self.config.elite_bonus;
}
reward as u64
}
}
pub struct PenaltyConfig {
/// Base slash percentage for failed challenge
pub base_slash_percent: f64,
/// Increased slash for consecutive failures
pub consecutive_failure_multiplier: f64,
/// Maximum slash percentage per challenge
pub max_slash_percent: f64,
/// Reputation penalty per failure
pub reputation_penalty: f64,
/// Failures before temporary ban
pub failures_before_temp_ban: u32,
/// Temp ban duration (seconds)
pub temp_ban_duration: u64,
/// Failures before permanent ban
pub failures_before_perm_ban: u32,
/// Grace period for new providers
pub grace_period_challenges: u32,
}
impl Default for PenaltyConfig {
fn default() -> Self {
Self {
base_slash_percent: 0.01, // 1%
consecutive_failure_multiplier: 1.5,
max_slash_percent: 0.10, // 10% max per challenge
reputation_penalty: 10.0,
failures_before_temp_ban: 5,
temp_ban_duration: 86400, // 24 hours
failures_before_perm_ban: 20,
grace_period_challenges: 3,
}
}
}
pub struct PenaltyCalculator {
config: PenaltyConfig,
}
impl PenaltyCalculator {
pub fn calculate_penalty(
&self,
challenge_result: &ChallengeResult,
provider_state: &ProviderState,
) -> Penalty {
// Grace period for new providers
if provider_state.total_challenges < self.config.grace_period_challenges {
return Penalty::Warning;
}
// Calculate slash amount
let consecutive_failures = provider_state.consecutive_failures;
let slash_multiplier = self.config.consecutive_failure_multiplier
.powi(consecutive_failures as i32);
let slash_percent = (self.config.base_slash_percent * slash_multiplier)
.min(self.config.max_slash_percent);
let slash_amount = (provider_state.stake as f64 * slash_percent) as u64;
// Calculate reputation penalty
let rep_penalty = self.config.reputation_penalty
* (1.0 + 0.1 * consecutive_failures as f64);
// Check for ban conditions
if consecutive_failures >= self.config.failures_before_perm_ban {
return Penalty::PermanentBan {
slash_amount: provider_state.stake, // Slash all
reason: BanReason::ExcessiveFailures,
};
}
if consecutive_failures >= self.config.failures_before_temp_ban {
return Penalty::TemporaryBan {
slash_amount,
reputation_penalty: rep_penalty,
duration: self.config.temp_ban_duration,
};
}
Penalty::Slash {
amount: slash_amount,
reputation_penalty: rep_penalty,
}
}
}
pub enum Penalty {
/// Warning only (grace period)
Warning,
/// Slash stake and reduce reputation
Slash {
amount: u64,
reputation_penalty: f64,
},
/// Temporary ban with slash
TemporaryBan {
slash_amount: u64,
reputation_penalty: f64,
duration: u64,
},
/// Permanent ban with full slash
PermanentBan {
slash_amount: u64,
reason: BanReason,
},
}
pub enum BanReason {
ExcessiveFailures,
FraudDetected,
TeeAttestationRevoked,
ManualReview,
}
pub struct ReputationManager {
/// Minimum reputation to participate
min_reputation: f64,
/// Starting reputation for new providers
starting_reputation: f64,
/// Maximum reputation
max_reputation: f64,
/// Decay rate per day of inactivity
decay_rate: f64,
}
impl ReputationManager {
pub fn update_reputation(
&self,
provider: &mut ProviderState,
result: &ChallengeResult,
penalty: &Penalty,
reward: u64,
) {
match penalty {
Penalty::Warning => {
// Slight decrease for warning
provider.reputation = (provider.reputation - 1.0).max(self.min_reputation);
}
Penalty::Slash { reputation_penalty, .. } |
Penalty::TemporaryBan { reputation_penalty, .. } => {
provider.reputation = (provider.reputation - reputation_penalty)
.max(self.min_reputation);
}
Penalty::PermanentBan { .. } => {
provider.reputation = 0.0;
}
}
// Reward case - increase reputation
if result.passed {
// Asymptotic increase toward max
let increase_factor = (self.max_reputation - provider.reputation) / self.max_reputation;
let increase = result.performance_score * 2.0 * increase_factor;
provider.reputation = (provider.reputation + increase).min(self.max_reputation);
}
// Track metrics
provider.total_rewards += reward;
if result.passed {
provider.challenges_passed += 1;
provider.consecutive_failures = 0;
provider.challenge_streak += 1;
} else {
provider.challenges_failed += 1;
provider.consecutive_failures += 1;
provider.challenge_streak = 0;
}
}
/// Apply daily reputation decay for inactive providers
pub fn apply_decay(&self, provider: &mut ProviderState, days_inactive: f64) {
if days_inactive > 0.0 {
let decay = self.decay_rate.powf(days_inactive);
provider.reputation *= decay;
provider.reputation = provider.reputation.max(self.min_reputation);
}
}
}
pub struct ProviderState {
pub id: PeerId,
pub stake: u64,
pub reputation: f64,
pub qos_score: QoSScore,
pub challenges_passed: u64,
pub challenges_failed: u64,
pub consecutive_failures: u32,
pub challenge_streak: u32,
pub total_challenges: u32,
pub total_rewards: u64,
pub total_slashed: u64,
pub last_active: u64,
pub status: ProviderStatus,
}
pub enum ProviderStatus {
Active,
TemporarilyBanned { until: u64 },
PermanentlyBanned { reason: BanReason },
Suspended { reason: String },
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
/**
* @title QoSChallengeSettlement
* @notice On-chain settlement for QoS challenges
*/
contract QoSChallengeSettlement is ReentrancyGuard {
IERC20 public immutable aiToken;
struct Provider {
uint256 stake;
uint256 reputation;
uint256 totalRewards;
uint256 totalSlashed;
uint32 consecutiveFailures;
uint64 lastChallengeTime;
ProviderStatus status;
}
enum ProviderStatus { Active, TempBanned, PermBanned }
struct ChallengeResult {
bytes32 challengeId;
address provider;
bool passed;
uint256 performanceScore; // 18 decimals, 1e18 = 100%
bool teeVerified;
uint256 timestamp;
}
mapping(address => Provider) public providers;
mapping(bytes32 => ChallengeResult) public results;
uint256 public constant MIN_STAKE = 100 ether; // 100 AI tokens
uint256 public constant BASE_REWARD = 0.1 ether;
uint256 public constant BASE_SLASH_PERCENT = 1; // 1%
event ChallengeSettled(
bytes32 indexed challengeId,
address indexed provider,
bool passed,
uint256 reward,
uint256 slash
);
event ProviderSlashed(
address indexed provider,
uint256 amount,
string reason
);
event ProviderBanned(
address indexed provider,
bool permanent,
string reason
);
constructor(address _aiToken) {
aiToken = IERC20(_aiToken);
}
function settleChallengeResult(
bytes32 challengeId,
address providerAddr,
bool passed,
uint256 performanceScore,
bool teeVerified,
bytes calldata signature
) external nonReentrant {
// Verify signature from challenge committee
require(verifySettlementSignature(
challengeId, providerAddr, passed, performanceScore, teeVerified, signature
), "Invalid signature");
Provider storage provider = providers[providerAddr];
require(provider.status == ProviderStatus.Active, "Provider not active");
uint256 reward = 0;
uint256 slash = 0;
if (passed) {
reward = calculateReward(provider, performanceScore, teeVerified);
provider.consecutiveFailures = 0;
provider.reputation += performanceScore / 1e17; // Scaled increase
} else {
(slash, bool tempBan, bool permBan) = calculatePenalty(provider);
if (slash > 0) {
provider.stake -= slash;
provider.totalSlashed += slash;
emit ProviderSlashed(providerAddr, slash, "Challenge failed");
}
provider.consecutiveFailures++;
provider.reputation = provider.reputation > 10 ?
provider.reputation - 10 : 0;
if (permBan) {
provider.status = ProviderStatus.PermBanned;
emit ProviderBanned(providerAddr, true, "Excessive failures");
} else if (tempBan) {
provider.status = ProviderStatus.TempBanned;
emit ProviderBanned(providerAddr, false, "Multiple failures");
}
}
provider.lastChallengeTime = uint64(block.timestamp);
// Store result
results[challengeId] = ChallengeResult({
challengeId: challengeId,
provider: providerAddr,
passed: passed,
performanceScore: performanceScore,
teeVerified: teeVerified,
timestamp: block.timestamp
});
// Transfer reward
if (reward > 0) {
provider.totalRewards += reward;
require(aiToken.transfer(providerAddr, reward), "Reward transfer failed");
}
emit ChallengeSettled(challengeId, providerAddr, passed, reward, slash);
}
function calculateReward(
Provider storage provider,
uint256 performanceScore,
bool teeVerified
) internal view returns (uint256) {
uint256 reward = BASE_REWARD;
// Performance bonus (up to 2x)
if (performanceScore > 1e18) {
uint256 bonus = (performanceScore - 1e18) * reward / 1e18;
reward += bonus > reward ? reward : bonus;
}
// TEE bonus (25%)
if (teeVerified) {
reward = reward * 125 / 100;
}
// Elite bonus (15%) for high reputation
if (provider.reputation >= 900) {
reward = reward * 115 / 100;
}
return reward;
}
function calculatePenalty(
Provider storage provider
) internal view returns (uint256 slash, bool tempBan, bool permBan) {
// Slash increases with consecutive failures
uint256 slashPercent = BASE_SLASH_PERCENT *
(150 ** provider.consecutiveFailures) / (100 ** provider.consecutiveFailures);
// Cap at 10%
if (slashPercent > 10) slashPercent = 10;
slash = provider.stake * slashPercent / 100;
// Ban conditions
tempBan = provider.consecutiveFailures >= 4;
permBan = provider.consecutiveFailures >= 19;
return (slash, tempBan, permBan);
}
function verifySettlementSignature(
bytes32 challengeId,
address provider,
bool passed,
uint256 performanceScore,
bool teeVerified,
bytes calldata signature
) internal view returns (bool) {
// Implementation: verify ECDSA/ML-DSA signature from challenge committee
// This would verify against a multisig or threshold signature
return true; // Placeholder
}
}
| Threat | Impact | Mitigation | | --------------------------- | -------------------------------------------- | ----------------------------------------------------------- | | Sybil Attack | Multiple fake identities to game reputation | Minimum stake requirement, unique hardware attestation | | Computation Outsourcing | Provider outsources challenge to faster node | Strict deadlines, random challenge timing, TEE verification | | Selective Participation | Only respond to easy challenges | Random assignment, penalty for missed challenges | | Collusion | Verifiers and providers collude | Multiple independent verifiers, fraud proofs, slashing | | Replay Attacks | Reuse old challenge responses | Unique nonces, timestamp validation, challenge expiry | | Eclipse Attack | Isolate provider from network | Multiple bootstrap nodes, diverse verifier set | | TEE Compromise | Fake attestation quotes | Quote freshness checks, revocation lists, multi-vendor TEE |
use vrf::VRF;
pub struct ChallengeSelector {
vrf: VRF,
last_beacon: [u8; 32],
}
impl ChallengeSelector {
/// Select providers for next challenge round
pub fn select_providers(
&mut self,
all_providers: &[PeerId],
count: usize,
block_hash: &[u8; 32],
) -> Vec<PeerId> {
// Update beacon with latest block hash for unpredictability
let mut input = Vec::new();
input.extend_from_slice(&self.last_beacon);
input.extend_from_slice(block_hash);
let (output, proof) = self.vrf.prove(&input);
self.last_beacon = output;
// Use VRF output to deterministically select providers
let mut selected = Vec::new();
let mut rng = ChaCha20Rng::from_seed(output);
let mut indices: Vec<usize> = (0..all_providers.len()).collect();
indices.shuffle(&mut rng);
for idx in indices.into_iter().take(count) {
selected.push(all_providers[idx].clone());
}
selected
}
/// Select challenge type for a provider
pub fn select_challenge_type(
&self,
provider: &ProviderState,
beacon: &[u8; 32],
) -> ChallengeType {
let mut hasher = blake3::Hasher::new();
hasher.update(beacon);
hasher.update(provider.id.as_bytes());
let hash = hasher.finalize();
// Use first byte to select type based on provider tier weights
let weights = provider.challenge_weights();
let roll = hash.as_bytes()[0] as u32 * 100 / 256;
weights.select(roll)
}
}
| Milestone | Deliverable | Date | | --------- | ------------------------------------------ | ------- | | M1 | Challenge protocol specification finalized | Week 2 | | M2 | Testnet deployment with basic challenges | Week 6 | | M3 | Full verification engine operational | Week 8 | | M4 | Economic settlement on testnet | Week 10 | | M5 | Security audit complete | Week 11 | | M6 | Mainnet launch | Week 12 |
| Metric | Target | Measurement | | ------------------------- | ------------ | --------------------------------------------- | | Challenge completion rate | > 98% | Successful responses / total challenges | | False positive rate | < 0.1% | Incorrect failures / total failures | | Verification latency | < 5 seconds | Time from proof submission to verification | | Provider churn | < 5% monthly | Providers leaving due to failed challenges | | Fraud detection rate | > 99% | Detected fraudulent proofs / total fraudulent |
syntax = "proto3";
package hanzo.qos.v1;
service QoSChallengeService {
// Issue a new challenge to a provider
rpc IssueChallenge(IssueChallengeRequest) returns (Challenge);
// Submit proof for a challenge
rpc SubmitProof(SubmitProofRequest) returns (SubmitProofResponse);
// Get challenge status
rpc GetChallengeStatus(GetChallengeStatusRequest) returns (ChallengeStatus);
// Get provider QoS score
rpc GetQoSScore(GetQoSScoreRequest) returns (QoSScore);
// Stream challenge events
rpc StreamChallenges(StreamChallengesRequest) returns (stream ChallengeEvent);
}
message Challenge {
bytes id = 1;
ChallengeType type = 2;
bytes seed = 3;
uint32 difficulty = 4;
uint64 deadline_ms = 5;
uint64 issued_at = 6;
bytes signature = 7;
}
enum ChallengeType {
CHALLENGE_TYPE_UNSPECIFIED = 0;
CHALLENGE_TYPE_COMPUTE = 1;
CHALLENGE_TYPE_LATENCY = 2;
CHALLENGE_TYPE_BANDWIDTH = 3;
CHALLENGE_TYPE_AVAILABILITY = 4;
CHALLENGE_TYPE_MODEL = 5;
}
message ChallengeProof {
bytes challenge_id = 1;
string provider_id = 2;
bytes proof_data = 3;
uint64 executed_at = 4;
uint64 duration_ms = 5;
bytes signature = 6;
optional bytes tee_attestation = 7;
}
message QoSScore {
uint32 composite = 1;
uint32 compute_score = 2;
uint32 latency_score = 3;
uint32 bandwidth_score = 4;
uint32 availability_score = 5;
uint32 consistency_score = 6;
double confidence = 7;
uint64 updated_at = 8;
}
openapi: 3.0.0
info:
title: QoS Challenge API
version: 1.0.0
paths:
/v1/challenges:
get:
summary: List active challenges for provider
parameters:
- name: provider_id
in: query
required: true
schema:
type: string
responses:
"200":
description: Active challenges
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Challenge"
/v1/challenges/{challenge_id}/proof:
post:
summary: Submit proof for challenge
parameters:
- name: challenge_id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ChallengeProof"
responses:
"200":
description: Proof accepted
"400":
description: Invalid proof
/v1/providers/{provider_id}/qos:
get:
summary: Get provider QoS score
parameters:
- name: provider_id
in: path
required: true
schema:
type: string
responses:
"200":
description: QoS score
content:
application/json:
schema:
$ref: "#/components/schemas/QoSScore"
/v1/providers/{provider_id}/history:
get:
summary: Get challenge history
parameters:
- name: provider_id
in: path
required: true
schema:
type: string
- name: limit
in: query
schema:
type: integer
default: 100
responses:
"200":
description: Challenge history
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ChallengeResult"
components:
schemas:
Challenge:
type: object
properties:
id:
type: string
format: byte
type:
type: string
enum: [compute, latency, bandwidth, availability, model]
seed:
type: string
format: byte
difficulty:
type: integer
deadline_ms:
type: integer
format: int64
issued_at:
type: integer
format: int64
ChallengeProof:
type: object
properties:
challenge_id:
type: string
format: byte
provider_id:
type: string
proof_data:
type: object
duration_ms:
type: integer
format: int64
signature:
type: string
format: byte
tee_attestation:
type: string
format: byte
QoSScore:
type: object
properties:
composite:
type: integer
compute_score:
type: integer
latency_score:
type: integer
bandwidth_score:
type: integer
availability_score:
type: integer
consistency_score:
type: integer
confidence:
type: number
format: double
updated_at:
type: integer
format: int64
ChallengeResult:
type: object
properties:
challenge_id:
type: string
passed:
type: boolean
performance_score:
type: number
reward:
type: integer
format: int64
slash:
type: integer
format: int64
timestamp:
type: integer
format: int64
// Reference CUDA kernel for compute challenges
// Uses deterministic matrix multiplication with seed-based initialization
__global__ void compute_challenge_kernel(
float* A, float* B, float* C,
int N, uint32_t seed
) {
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
if (row < N && col < N) {
float sum = 0.0f;
for (int k = 0; k < N; k++) {
sum += A[row * N + k] * B[k * N + col];
}
C[row * N + col] = sum;
}
}
// Initialize matrices deterministically from seed
void init_challenge_matrices(float* A, float* B, int N, uint32_t seed) {
// Use PCG random generator seeded with challenge seed
pcg32_random_t rng;
pcg32_srandom_r(&rng, seed, seed ^ 0xDEADBEEF);
for (int i = 0; i < N * N; i++) {
// Generate deterministic values in [-1, 1]
A[i] = (pcg32_random_r(&rng) / (float)UINT32_MAX) * 2.0f - 1.0f;
B[i] = (pcg32_random_r(&rng) / (float)UINT32_MAX) * 2.0f - 1.0f;
}
}
// Compute challenge proof
ChallengeProof execute_compute_challenge(ComputeChallenge* challenge) {
int N = difficulty_to_size(challenge->difficulty);
float *h_A, *h_B, *h_C;
float *d_A, *d_B, *d_C;
// Allocate and initialize
h_A = (float*)malloc(N * N * sizeof(float));
h_B = (float*)malloc(N * N * sizeof(float));
h_C = (float*)malloc(N * N * sizeof(float));
init_challenge_matrices(h_A, h_B, N, *(uint32_t*)challenge->seed);
cudaMalloc(&d_A, N * N * sizeof(float));
cudaMalloc(&d_B, N * N * sizeof(float));
cudaMalloc(&d_C, N * N * sizeof(float));
cudaMemcpy(d_A, h_A, N * N * sizeof(float), cudaMemcpyHostToDevice);
cudaMemcpy(d_B, h_B, N * N * sizeof(float), cudaMemcpyHostToDevice);
// Execute timed kernel
dim3 threads(16, 16);
dim3 blocks((N + 15) / 16, (N + 15) / 16);
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start);
compute_challenge_kernel<<<blocks, threads>>>(d_A, d_B, d_C, N,
*(uint32_t*)challenge->seed);
cudaEventRecord(stop);
cudaEventSynchronize(stop);
float milliseconds = 0;
cudaEventElapsedTime(&milliseconds, start, stop);
// Copy result back
cudaMemcpy(h_C, d_C, N * N * sizeof(float), cudaMemcpyDeviceToHost);
// Compute result hash
uint8_t result_hash[32];
blake3_hasher hasher;
blake3_hasher_init(&hasher);
blake3_hasher_update(&hasher, h_C, N * N * sizeof(float));
blake3_hasher_finalize(&hasher, result_hash, 32);
// Calculate TFLOPS
double flops = 2.0 * N * N * N; // 2 ops per multiply-add
double tflops = flops / (milliseconds * 1e9);
// Build proof
ChallengeProof proof = {
.challenge_id = challenge->id,
.duration_ms = (uint64_t)milliseconds,
.actual_flops = tflops,
};
memcpy(proof.result_hash, result_hash, 32);
// Cleanup
free(h_A); free(h_B); free(h_C);
cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);
return proof;
}
| Version | Date | Author | Changes | | ------- | ---------- | ----------------- | ----------------------- | | 1.0 | 2026-01-24 | Architecture Team | Initial design document |