Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
205
vendor/ruvector/examples/edge-net/sim/src/cell.ts
vendored
Normal file
205
vendor/ruvector/examples/edge-net/sim/src/cell.ts
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Cell (Node) Simulation
|
||||
* Represents a single node in the edge-net network
|
||||
*/
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export enum CellType {
|
||||
GENESIS = 'genesis',
|
||||
REGULAR = 'regular',
|
||||
}
|
||||
|
||||
export enum CellState {
|
||||
ACTIVE = 'active',
|
||||
READ_ONLY = 'read_only',
|
||||
RETIRED = 'retired',
|
||||
}
|
||||
|
||||
export interface CellCapabilities {
|
||||
computePower: number; // 0.1 - 1.0 (relative)
|
||||
bandwidth: number; // 0.1 - 1.0 (relative)
|
||||
reliability: number; // 0.5 - 1.0 (uptime probability)
|
||||
storage: number; // 0.1 - 1.0 (relative)
|
||||
}
|
||||
|
||||
export interface CellMetrics {
|
||||
tasksCompleted: number;
|
||||
energyEarned: number;
|
||||
energySpent: number;
|
||||
connections: number;
|
||||
uptime: number; // ticks alive
|
||||
successRate: number; // task success rate
|
||||
}
|
||||
|
||||
export class Cell {
|
||||
public readonly id: string;
|
||||
public readonly type: CellType;
|
||||
public readonly joinedAtTick: number;
|
||||
public state: CellState;
|
||||
public capabilities: CellCapabilities;
|
||||
public energy: number; // rUv balance
|
||||
public metrics: CellMetrics;
|
||||
public connectedCells: Set<string>;
|
||||
public genesisMultiplier: number; // 10x for genesis nodes initially
|
||||
|
||||
constructor(
|
||||
type: CellType,
|
||||
joinedAtTick: number,
|
||||
capabilities?: Partial<CellCapabilities>
|
||||
) {
|
||||
this.id = uuidv4();
|
||||
this.type = type;
|
||||
this.joinedAtTick = joinedAtTick;
|
||||
this.state = CellState.ACTIVE;
|
||||
this.energy = type === CellType.GENESIS ? 1000 : 10; // Genesis starts with more
|
||||
this.connectedCells = new Set();
|
||||
this.genesisMultiplier = type === CellType.GENESIS ? 10 : 1;
|
||||
|
||||
// Random capabilities or provided ones
|
||||
this.capabilities = {
|
||||
computePower: capabilities?.computePower ?? this.randomCapability(0.1, 1.0),
|
||||
bandwidth: capabilities?.bandwidth ?? this.randomCapability(0.1, 1.0),
|
||||
reliability: capabilities?.reliability ?? this.randomCapability(0.5, 1.0),
|
||||
storage: capabilities?.storage ?? this.randomCapability(0.1, 1.0),
|
||||
};
|
||||
|
||||
this.metrics = {
|
||||
tasksCompleted: 0,
|
||||
energyEarned: 0,
|
||||
energySpent: 0,
|
||||
connections: 0,
|
||||
uptime: 0,
|
||||
successRate: 1.0,
|
||||
};
|
||||
}
|
||||
|
||||
private randomCapability(min: number, max: number): number {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a task and earn energy
|
||||
*/
|
||||
public processTask(taskComplexity: number, baseReward: number): boolean {
|
||||
// Check if cell is alive (reliability check)
|
||||
if (Math.random() > this.capabilities.reliability) {
|
||||
return false; // Cell failed this tick
|
||||
}
|
||||
|
||||
// Check if cell has enough compute power
|
||||
if (this.capabilities.computePower < taskComplexity * 0.5) {
|
||||
return false; // Task too complex
|
||||
}
|
||||
|
||||
// Success - earn energy with genesis multiplier
|
||||
const reward = baseReward * this.genesisMultiplier;
|
||||
this.energy += reward;
|
||||
this.metrics.energyEarned += reward;
|
||||
this.metrics.tasksCompleted++;
|
||||
|
||||
// Update success rate
|
||||
this.updateSuccessRate(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spend energy (for network operations, connections, etc.)
|
||||
*/
|
||||
public spendEnergy(amount: number): boolean {
|
||||
if (this.energy >= amount) {
|
||||
this.energy -= amount;
|
||||
this.metrics.energySpent += amount;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to another cell
|
||||
*/
|
||||
public connectTo(cellId: string): void {
|
||||
if (!this.connectedCells.has(cellId)) {
|
||||
this.connectedCells.add(cellId);
|
||||
this.metrics.connections = this.connectedCells.size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from a cell
|
||||
*/
|
||||
public disconnectFrom(cellId: string): void {
|
||||
this.connectedCells.delete(cellId);
|
||||
this.metrics.connections = this.connectedCells.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cell state based on network phase
|
||||
*/
|
||||
public updateState(networkSize: number): void {
|
||||
if (this.type === CellType.GENESIS) {
|
||||
if (networkSize >= 50000) {
|
||||
// Phase 3: Maturation - Genesis goes read-only
|
||||
this.state = CellState.READ_ONLY;
|
||||
this.genesisMultiplier = 1; // No more bonus
|
||||
} else if (networkSize >= 10000) {
|
||||
// Phase 2: Growth - Genesis reduces multiplier
|
||||
this.genesisMultiplier = Math.max(1, 10 * (1 - (networkSize - 10000) / 40000));
|
||||
}
|
||||
|
||||
if (networkSize >= 100000) {
|
||||
// Phase 4: Independence - Genesis retires
|
||||
this.state = CellState.RETIRED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate one tick of operation
|
||||
*/
|
||||
public tick(): void {
|
||||
this.metrics.uptime++;
|
||||
|
||||
// Passive energy decay (network costs)
|
||||
const decayCost = 0.1 * this.connectedCells.size;
|
||||
this.spendEnergy(decayCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update success rate with exponential moving average
|
||||
*/
|
||||
private updateSuccessRate(success: boolean): void {
|
||||
const alpha = 0.1; // Smoothing factor
|
||||
this.metrics.successRate = alpha * (success ? 1 : 0) + (1 - alpha) * this.metrics.successRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cell's overall fitness score
|
||||
*/
|
||||
public getFitnessScore(): number {
|
||||
const { computePower, bandwidth, reliability, storage } = this.capabilities;
|
||||
return (computePower * 0.3 + bandwidth * 0.2 + reliability * 0.3 + storage * 0.2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize cell state for reporting
|
||||
*/
|
||||
public toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
state: this.state,
|
||||
joinedAtTick: this.joinedAtTick,
|
||||
energy: this.energy,
|
||||
genesisMultiplier: this.genesisMultiplier,
|
||||
capabilities: this.capabilities,
|
||||
metrics: {
|
||||
...this.metrics,
|
||||
netEnergy: this.metrics.energyEarned - this.metrics.energySpent,
|
||||
},
|
||||
connections: this.connectedCells.size,
|
||||
fitnessScore: this.getFitnessScore(),
|
||||
};
|
||||
}
|
||||
}
|
||||
190
vendor/ruvector/examples/edge-net/sim/src/economics.js
vendored
Normal file
190
vendor/ruvector/examples/edge-net/sim/src/economics.js
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Economic Tracking and Analysis
|
||||
* Monitors economic health, sustainability, and distribution
|
||||
*/
|
||||
|
||||
export class EconomicTracker {
|
||||
constructor() {
|
||||
this.totalSupply = 0;
|
||||
this.treasury = 0;
|
||||
this.contributorPool = 0;
|
||||
this.protocolFund = 0;
|
||||
this.founderPool = 0;
|
||||
|
||||
// Distribution ratios
|
||||
this.distribution = {
|
||||
contributors: 0.70,
|
||||
treasury: 0.15,
|
||||
protocol: 0.10,
|
||||
founders: 0.05,
|
||||
};
|
||||
|
||||
// Health metrics
|
||||
this.velocity = 0;
|
||||
this.utilization = 0;
|
||||
this.growthRate = 0;
|
||||
this.stability = 1.0;
|
||||
|
||||
// Historical data
|
||||
this.history = [];
|
||||
this.epochCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a simulation tick
|
||||
*/
|
||||
tick(nodes, metrics) {
|
||||
// Calculate new rUv minted this tick
|
||||
const totalEarned = nodes.reduce((sum, n) => sum + n.ruvEarned, 0);
|
||||
const totalSpent = nodes.reduce((sum, n) => sum + n.ruvSpent, 0);
|
||||
|
||||
const newSupply = totalEarned - this.totalSupply;
|
||||
this.totalSupply = totalEarned;
|
||||
|
||||
if (newSupply > 0) {
|
||||
// Distribute according to ratios
|
||||
this.contributorPool += Math.floor(newSupply * this.distribution.contributors);
|
||||
this.treasury += Math.floor(newSupply * this.distribution.treasury);
|
||||
this.protocolFund += Math.floor(newSupply * this.distribution.protocol);
|
||||
this.founderPool += Math.floor(newSupply * this.distribution.founders);
|
||||
}
|
||||
|
||||
// Update health metrics
|
||||
this.updateHealthMetrics(nodes, metrics, totalSpent);
|
||||
|
||||
// Record snapshot periodically
|
||||
if (this.epochCount % 10 === 0) {
|
||||
this.recordSnapshot(nodes.length, metrics);
|
||||
}
|
||||
|
||||
this.epochCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update economic health metrics
|
||||
*/
|
||||
updateHealthMetrics(nodes, metrics, totalSpent) {
|
||||
// Velocity: how fast rUv circulates (spent / supply)
|
||||
this.velocity = this.totalSupply > 0
|
||||
? totalSpent / this.totalSupply
|
||||
: 0;
|
||||
|
||||
// Utilization: active nodes / total supply capacity
|
||||
const activeNodes = nodes.filter(n => n.active).length;
|
||||
this.utilization = activeNodes > 0
|
||||
? Math.min(1.0, metrics.totalTasksCompleted / (activeNodes * 100))
|
||||
: 0;
|
||||
|
||||
// Growth rate: change in supply (simplified)
|
||||
this.growthRate = this.totalSupply > 0
|
||||
? 0.01 // Simplified constant growth
|
||||
: 0;
|
||||
|
||||
// Stability: balance across pools
|
||||
this.stability = this.calculateStability();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate stability index based on pool distribution
|
||||
*/
|
||||
calculateStability() {
|
||||
const totalPools = this.treasury + this.contributorPool + this.protocolFund;
|
||||
if (totalPools === 0) return 1.0;
|
||||
|
||||
const treasuryRatio = this.treasury / totalPools;
|
||||
const contributorRatio = this.contributorPool / totalPools;
|
||||
const protocolRatio = this.protocolFund / totalPools;
|
||||
|
||||
// Ideal is 33% each
|
||||
const ideal = 0.33;
|
||||
const variance = Math.pow(treasuryRatio - ideal, 2) +
|
||||
Math.pow(contributorRatio - ideal, 2) +
|
||||
Math.pow(protocolRatio - ideal, 2);
|
||||
|
||||
return Math.max(0, Math.min(1.0, 1.0 - Math.sqrt(variance)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if network is economically self-sustaining
|
||||
*/
|
||||
isSelfSustaining(activeNodes, dailyTasks) {
|
||||
const minNodes = 100;
|
||||
const minDailyTasks = 1000;
|
||||
const treasuryRunwayDays = 90;
|
||||
const estimatedDailyCost = activeNodes * 10; // 10 rUv per node per day
|
||||
|
||||
return (
|
||||
activeNodes >= minNodes &&
|
||||
dailyTasks >= minDailyTasks &&
|
||||
this.treasury >= estimatedDailyCost * treasuryRunwayDays &&
|
||||
this.growthRate >= 0.0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get economic velocity (transactions per period)
|
||||
*/
|
||||
getVelocity() {
|
||||
return this.velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record economic snapshot
|
||||
*/
|
||||
recordSnapshot(nodeCount, metrics) {
|
||||
this.history.push({
|
||||
epoch: this.epochCount,
|
||||
timestamp: Date.now(),
|
||||
totalSupply: this.totalSupply,
|
||||
treasury: this.treasury,
|
||||
contributorPool: this.contributorPool,
|
||||
protocolFund: this.protocolFund,
|
||||
founderPool: this.founderPool,
|
||||
velocity: this.velocity,
|
||||
utilization: this.utilization,
|
||||
growthRate: this.growthRate,
|
||||
stability: this.stability,
|
||||
nodeCount,
|
||||
health: this.getHealthScore(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get overall economic health score (0-1)
|
||||
*/
|
||||
getHealthScore() {
|
||||
// Weighted combination of metrics
|
||||
return (
|
||||
this.velocity * 0.3 +
|
||||
this.utilization * 0.3 +
|
||||
this.stability * 0.4
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate economic report
|
||||
*/
|
||||
getReport() {
|
||||
return {
|
||||
supply: {
|
||||
total: this.totalSupply,
|
||||
treasury: this.treasury,
|
||||
contributors: this.contributorPool,
|
||||
protocol: this.protocolFund,
|
||||
founders: this.founderPool,
|
||||
},
|
||||
health: {
|
||||
velocity: this.velocity,
|
||||
utilization: this.utilization,
|
||||
growthRate: this.growthRate,
|
||||
stability: this.stability,
|
||||
overall: this.getHealthScore(),
|
||||
},
|
||||
sustainability: {
|
||||
selfSustaining: this.isSelfSustaining(1000, 10000), // Example values
|
||||
treasuryRunway: Math.floor(this.treasury / 100), // Days
|
||||
},
|
||||
history: this.history,
|
||||
};
|
||||
}
|
||||
}
|
||||
290
vendor/ruvector/examples/edge-net/sim/src/metrics.ts
vendored
Normal file
290
vendor/ruvector/examples/edge-net/sim/src/metrics.ts
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
/**
|
||||
* Metrics Collection and Aggregation
|
||||
* Tracks network performance across all phases
|
||||
*/
|
||||
|
||||
import { Network, NetworkPhase } from './network.js';
|
||||
|
||||
export interface PhaseMetrics {
|
||||
phase: NetworkPhase;
|
||||
startTick: number;
|
||||
endTick: number;
|
||||
duration: number;
|
||||
nodeCount: {
|
||||
start: number;
|
||||
end: number;
|
||||
peak: number;
|
||||
};
|
||||
energy: {
|
||||
totalEarned: number;
|
||||
totalSpent: number;
|
||||
netEnergy: number;
|
||||
avgPerNode: number;
|
||||
sustainability: number; // earned / spent ratio
|
||||
};
|
||||
genesis: {
|
||||
avgMultiplier: number;
|
||||
activeCount: number;
|
||||
readOnlyCount: number;
|
||||
retiredCount: number;
|
||||
};
|
||||
network: {
|
||||
avgConnections: number;
|
||||
avgSuccessRate: number;
|
||||
taskThroughput: number;
|
||||
tasksCompleted: number;
|
||||
};
|
||||
validation: {
|
||||
passed: boolean;
|
||||
reasons: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export class MetricsCollector {
|
||||
private network: Network;
|
||||
private phaseMetrics: Map<NetworkPhase, PhaseMetrics>;
|
||||
private currentPhaseStart: number;
|
||||
private currentPhaseNodeCount: number;
|
||||
private peakNodeCount: number;
|
||||
|
||||
constructor(network: Network) {
|
||||
this.network = network;
|
||||
this.phaseMetrics = new Map();
|
||||
this.currentPhaseStart = 0;
|
||||
this.currentPhaseNodeCount = 0;
|
||||
this.peakNodeCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize metrics collection
|
||||
*/
|
||||
public initialize(): void {
|
||||
this.currentPhaseStart = this.network.currentTick;
|
||||
this.currentPhaseNodeCount = this.network.cells.size;
|
||||
this.peakNodeCount = this.network.cells.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect metrics for the current tick
|
||||
*/
|
||||
public collect(): void {
|
||||
const stats = this.network.getStats();
|
||||
|
||||
// Update peak node count
|
||||
this.peakNodeCount = Math.max(this.peakNodeCount, stats.nodeCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle phase transition
|
||||
*/
|
||||
public onPhaseTransition(oldPhase: NetworkPhase, newPhase: NetworkPhase): void {
|
||||
// Finalize metrics for old phase
|
||||
this.finalizePhase(oldPhase);
|
||||
|
||||
// Start tracking new phase
|
||||
this.currentPhaseStart = this.network.currentTick;
|
||||
this.currentPhaseNodeCount = this.network.cells.size;
|
||||
this.peakNodeCount = this.network.cells.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize metrics for a completed phase
|
||||
*/
|
||||
private finalizePhase(phase: NetworkPhase): void {
|
||||
const stats = this.network.getStats();
|
||||
const endTick = this.network.currentTick;
|
||||
const duration = endTick - this.currentPhaseStart;
|
||||
|
||||
const cells = Array.from(this.network.cells.values());
|
||||
const totalEarned = cells.reduce((sum, c) => sum + c.metrics.energyEarned, 0);
|
||||
const totalSpent = cells.reduce((sum, c) => sum + c.metrics.energySpent, 0);
|
||||
const totalTasks = cells.reduce((sum, c) => sum + c.metrics.tasksCompleted, 0);
|
||||
|
||||
const metrics: PhaseMetrics = {
|
||||
phase,
|
||||
startTick: this.currentPhaseStart,
|
||||
endTick,
|
||||
duration,
|
||||
nodeCount: {
|
||||
start: this.currentPhaseNodeCount,
|
||||
end: stats.nodeCount,
|
||||
peak: this.peakNodeCount,
|
||||
},
|
||||
energy: {
|
||||
totalEarned,
|
||||
totalSpent,
|
||||
netEnergy: totalEarned - totalSpent,
|
||||
avgPerNode: stats.economy.avgEnergyPerNode,
|
||||
sustainability: totalSpent > 0 ? totalEarned / totalSpent : 0,
|
||||
},
|
||||
genesis: {
|
||||
avgMultiplier: stats.genesisNodes.avgMultiplier,
|
||||
activeCount: stats.genesisNodes.active,
|
||||
readOnlyCount: stats.genesisNodes.readOnly,
|
||||
retiredCount: stats.genesisNodes.retired,
|
||||
},
|
||||
network: {
|
||||
avgConnections: stats.network.avgConnections,
|
||||
avgSuccessRate: stats.network.avgSuccessRate,
|
||||
taskThroughput: duration > 0 ? totalTasks / duration : 0,
|
||||
tasksCompleted: totalTasks,
|
||||
},
|
||||
validation: this.validatePhase(phase, stats),
|
||||
};
|
||||
|
||||
this.phaseMetrics.set(phase, metrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate phase completion criteria
|
||||
*/
|
||||
private validatePhase(phase: NetworkPhase, stats: any): { passed: boolean; reasons: string[] } {
|
||||
const reasons: string[] = [];
|
||||
let passed = true;
|
||||
|
||||
switch (phase) {
|
||||
case NetworkPhase.GENESIS:
|
||||
// Verify 10x multiplier is active
|
||||
if (stats.genesisNodes.avgMultiplier < 9.0) {
|
||||
passed = false;
|
||||
reasons.push(`Genesis multiplier too low: ${stats.genesisNodes.avgMultiplier.toFixed(2)} (expected ~10.0)`);
|
||||
} else {
|
||||
reasons.push(`✓ Genesis multiplier active: ${stats.genesisNodes.avgMultiplier.toFixed(2)}x`);
|
||||
}
|
||||
|
||||
// Verify energy accumulation
|
||||
if (stats.economy.totalEarned < 1000) {
|
||||
passed = false;
|
||||
reasons.push(`Insufficient energy accumulation: ${stats.economy.totalEarned.toFixed(2)}`);
|
||||
} else {
|
||||
reasons.push(`✓ Energy accumulated: ${stats.economy.totalEarned.toFixed(2)} rUv`);
|
||||
}
|
||||
|
||||
// Verify network formation
|
||||
if (stats.network.avgConnections < 5) {
|
||||
passed = false;
|
||||
reasons.push(`Network poorly connected: ${stats.network.avgConnections.toFixed(2)} avg connections`);
|
||||
} else {
|
||||
reasons.push(`✓ Network connected: ${stats.network.avgConnections.toFixed(2)} avg connections`);
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkPhase.GROWTH:
|
||||
// Verify genesis nodes stop accepting connections
|
||||
if (stats.genesisNodes.active > stats.genesisNodes.count * 0.1) {
|
||||
passed = false;
|
||||
reasons.push(`Too many genesis nodes still active: ${stats.genesisNodes.active}`);
|
||||
} else {
|
||||
reasons.push(`✓ Genesis nodes reducing activity: ${stats.genesisNodes.active} active`);
|
||||
}
|
||||
|
||||
// Verify multiplier decay
|
||||
if (stats.genesisNodes.avgMultiplier > 5.0) {
|
||||
passed = false;
|
||||
reasons.push(`Genesis multiplier decay insufficient: ${stats.genesisNodes.avgMultiplier.toFixed(2)}`);
|
||||
} else {
|
||||
reasons.push(`✓ Multiplier decaying: ${stats.genesisNodes.avgMultiplier.toFixed(2)}x`);
|
||||
}
|
||||
|
||||
// Verify task routing optimization
|
||||
if (stats.network.avgSuccessRate < 0.7) {
|
||||
passed = false;
|
||||
reasons.push(`Task success rate too low: ${(stats.network.avgSuccessRate * 100).toFixed(1)}%`);
|
||||
} else {
|
||||
reasons.push(`✓ Task routing optimized: ${(stats.network.avgSuccessRate * 100).toFixed(1)}% success`);
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkPhase.MATURATION:
|
||||
// Verify genesis nodes are read-only
|
||||
if (stats.genesisNodes.readOnly < stats.genesisNodes.count * 0.8) {
|
||||
passed = false;
|
||||
reasons.push(`Genesis nodes not read-only: ${stats.genesisNodes.readOnly}/${stats.genesisNodes.count}`);
|
||||
} else {
|
||||
reasons.push(`✓ Genesis nodes read-only: ${stats.genesisNodes.readOnly}/${stats.genesisNodes.count}`);
|
||||
}
|
||||
|
||||
// Verify economic sustainability
|
||||
const sustainability = stats.economy.totalEarned / Math.max(stats.economy.totalSpent, 1);
|
||||
if (sustainability < 1.0) {
|
||||
passed = false;
|
||||
reasons.push(`Network not sustainable: ${sustainability.toFixed(2)} earned/spent ratio`);
|
||||
} else {
|
||||
reasons.push(`✓ Economically sustainable: ${sustainability.toFixed(2)} ratio`);
|
||||
}
|
||||
|
||||
// Verify network independence
|
||||
if (stats.network.avgConnections < 10) {
|
||||
passed = false;
|
||||
reasons.push(`Network connectivity too low for independence: ${stats.network.avgConnections.toFixed(2)}`);
|
||||
} else {
|
||||
reasons.push(`✓ Network ready for independence: ${stats.network.avgConnections.toFixed(2)} avg connections`);
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkPhase.INDEPENDENCE:
|
||||
// Verify genesis nodes retired
|
||||
if (stats.genesisNodes.retired < stats.genesisNodes.count * 0.9) {
|
||||
passed = false;
|
||||
reasons.push(`Genesis nodes not fully retired: ${stats.genesisNodes.retired}/${stats.genesisNodes.count}`);
|
||||
} else {
|
||||
reasons.push(`✓ Genesis nodes retired: ${stats.genesisNodes.retired}/${stats.genesisNodes.count}`);
|
||||
}
|
||||
|
||||
// Verify pure P2P operation
|
||||
if (stats.genesisNodes.avgMultiplier > 1.1) {
|
||||
passed = false;
|
||||
reasons.push(`Genesis multiplier still active: ${stats.genesisNodes.avgMultiplier.toFixed(2)}`);
|
||||
} else {
|
||||
reasons.push(`✓ Pure P2P operation: ${stats.genesisNodes.avgMultiplier.toFixed(2)}x multiplier`);
|
||||
}
|
||||
|
||||
// Verify long-term stability
|
||||
if (stats.economy.netEnergy < 0) {
|
||||
passed = false;
|
||||
reasons.push(`Network losing energy: ${stats.economy.netEnergy.toFixed(2)}`);
|
||||
} else {
|
||||
reasons.push(`✓ Network stable: +${stats.economy.netEnergy.toFixed(2)} rUv net energy`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return { passed, reasons };
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize current phase (for end of simulation)
|
||||
*/
|
||||
public finalizeCurrent(): void {
|
||||
this.finalizePhase(this.network.currentPhase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all collected metrics
|
||||
*/
|
||||
public getAllMetrics(): PhaseMetrics[] {
|
||||
return Array.from(this.phaseMetrics.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metrics for a specific phase
|
||||
*/
|
||||
public getPhaseMetrics(phase: NetworkPhase): PhaseMetrics | undefined {
|
||||
return this.phaseMetrics.get(phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get overall success rate
|
||||
*/
|
||||
public getOverallSuccess(): { passed: boolean; totalPassed: number; totalPhases: number } {
|
||||
const metrics = this.getAllMetrics();
|
||||
const totalPassed = metrics.filter(m => m.validation.passed).length;
|
||||
const totalPhases = metrics.length;
|
||||
|
||||
return {
|
||||
passed: totalPassed === totalPhases,
|
||||
totalPassed,
|
||||
totalPhases,
|
||||
};
|
||||
}
|
||||
}
|
||||
394
vendor/ruvector/examples/edge-net/sim/src/network.js
vendored
Normal file
394
vendor/ruvector/examples/edge-net/sim/src/network.js
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
/**
|
||||
* Network Simulation Engine
|
||||
* Manages the overall network state and lifecycle phases
|
||||
*/
|
||||
|
||||
import { SimNode } from './node.js';
|
||||
import { EconomicTracker } from './economics.js';
|
||||
import { PhaseManager } from './phases.js';
|
||||
|
||||
export class NetworkSimulation {
|
||||
constructor(config = {}) {
|
||||
this.config = {
|
||||
genesisNodes: config.genesisNodes || 10,
|
||||
targetNodes: config.targetNodes || 100000,
|
||||
tickInterval: config.tickInterval || 1000, // ms
|
||||
accelerationFactor: config.accelerationFactor || 1000, // Simulate faster
|
||||
...config
|
||||
};
|
||||
|
||||
this.nodes = new Map();
|
||||
this.currentTick = 0;
|
||||
this.startTime = Date.now();
|
||||
this.totalComputeHours = 0;
|
||||
|
||||
this.economics = new EconomicTracker();
|
||||
this.phases = new PhaseManager();
|
||||
|
||||
this.metrics = {
|
||||
totalTasksCompleted: 0,
|
||||
totalTasksSubmitted: 0,
|
||||
totalRuvCirculating: 0,
|
||||
networkHealth: 1.0,
|
||||
averageLatency: 0,
|
||||
averageSuccessRate: 0,
|
||||
};
|
||||
|
||||
this.events = [];
|
||||
this.phaseTransitions = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the network with genesis nodes
|
||||
*/
|
||||
async initialize() {
|
||||
console.log(`🌱 Initializing network with ${this.config.genesisNodes} genesis nodes...`);
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
// Create genesis nodes
|
||||
for (let i = 0; i < this.config.genesisNodes; i++) {
|
||||
const node = new SimNode(`genesis-${i}`, now, true);
|
||||
this.nodes.set(node.id, node);
|
||||
}
|
||||
|
||||
// Connect genesis nodes to each other
|
||||
const genesisNodes = Array.from(this.nodes.values());
|
||||
for (let i = 0; i < genesisNodes.length; i++) {
|
||||
for (let j = i + 1; j < genesisNodes.length; j++) {
|
||||
genesisNodes[i].connectTo(genesisNodes[j].id);
|
||||
genesisNodes[j].connectTo(genesisNodes[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
this.logEvent('network_initialized', {
|
||||
genesisNodes: this.config.genesisNodes,
|
||||
timestamp: now
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run simulation for a specific phase or all phases
|
||||
*/
|
||||
async run(targetPhase = 'all') {
|
||||
console.log(`🚀 Starting simulation (target: ${targetPhase})...`);
|
||||
|
||||
const phaseTargets = {
|
||||
genesis: 10000,
|
||||
transition: 50000,
|
||||
maturity: 100000,
|
||||
'post-genesis': 150000,
|
||||
all: this.config.targetNodes
|
||||
};
|
||||
|
||||
const targetNodeCount = phaseTargets[targetPhase] || this.config.targetNodes;
|
||||
|
||||
while (this.nodes.size < targetNodeCount) {
|
||||
await this.tick();
|
||||
|
||||
// Add new nodes at varying rates based on phase
|
||||
const currentPhase = this.getCurrentPhase();
|
||||
const joinRate = this.getNodeJoinRate(currentPhase);
|
||||
|
||||
if (Math.random() < joinRate) {
|
||||
this.addNode();
|
||||
}
|
||||
|
||||
// Some nodes leave (churn)
|
||||
if (Math.random() < 0.001 && this.nodes.size > this.config.genesisNodes) {
|
||||
this.removeRandomNode();
|
||||
}
|
||||
|
||||
// Log progress periodically
|
||||
if (this.currentTick % 100 === 0) {
|
||||
this.logProgress();
|
||||
}
|
||||
|
||||
// Check for phase transitions
|
||||
this.checkPhaseTransition();
|
||||
}
|
||||
|
||||
console.log('✅ Simulation complete!');
|
||||
return this.generateReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single simulation tick
|
||||
*/
|
||||
async tick() {
|
||||
this.currentTick++;
|
||||
|
||||
// Accelerated time delta (ms)
|
||||
const deltaTime = this.config.tickInterval * this.config.accelerationFactor;
|
||||
|
||||
// Update all active nodes
|
||||
const currentPhase = this.getCurrentPhase();
|
||||
let totalCompute = 0;
|
||||
|
||||
for (const node of this.nodes.values()) {
|
||||
node.tick(deltaTime, this.totalComputeHours, currentPhase);
|
||||
totalCompute += node.totalComputeHours;
|
||||
}
|
||||
|
||||
this.totalComputeHours = totalCompute;
|
||||
|
||||
// Update network metrics
|
||||
this.updateMetrics();
|
||||
|
||||
// Update economic state
|
||||
this.economics.tick(this.getActiveNodes(), this.metrics);
|
||||
|
||||
// Small delay for visualization (optional)
|
||||
if (this.config.visualDelay) {
|
||||
await new Promise(resolve => setTimeout(resolve, this.config.visualDelay));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new node to the network
|
||||
*/
|
||||
addNode() {
|
||||
const nodeId = `node-${this.nodes.size}`;
|
||||
const node = new SimNode(nodeId, Date.now(), false);
|
||||
this.nodes.set(nodeId, node);
|
||||
|
||||
// Connect to random existing nodes
|
||||
const existingNodes = Array.from(this.nodes.values())
|
||||
.filter(n => n.id !== nodeId && n.canAcceptConnections());
|
||||
|
||||
const connectionsToMake = Math.min(5, existingNodes.length);
|
||||
for (let i = 0; i < connectionsToMake; i++) {
|
||||
const randomNode = existingNodes[Math.floor(Math.random() * existingNodes.length)];
|
||||
node.connectTo(randomNode.id);
|
||||
randomNode.connectTo(nodeId);
|
||||
}
|
||||
|
||||
// Prefer connecting to genesis nodes initially
|
||||
const currentPhase = this.getCurrentPhase();
|
||||
if (currentPhase === 'genesis') {
|
||||
const genesisNodes = existingNodes.filter(n => n.isGenesis && n.canAcceptConnections());
|
||||
for (const gNode of genesisNodes.slice(0, 3)) {
|
||||
node.connectTo(gNode.id);
|
||||
gNode.connectTo(nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a random non-genesis node (network churn)
|
||||
*/
|
||||
removeRandomNode() {
|
||||
const regularNodes = Array.from(this.nodes.values()).filter(n => !n.isGenesis);
|
||||
if (regularNodes.length === 0) return;
|
||||
|
||||
const nodeToRemove = regularNodes[Math.floor(Math.random() * regularNodes.length)];
|
||||
|
||||
// Disconnect from all peers
|
||||
for (const node of this.nodes.values()) {
|
||||
node.disconnect(nodeToRemove.id);
|
||||
}
|
||||
|
||||
this.nodes.delete(nodeToRemove.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current network phase based on node count
|
||||
*/
|
||||
getCurrentPhase() {
|
||||
const count = this.nodes.size;
|
||||
|
||||
if (count < 10000) return 'genesis';
|
||||
if (count < 50000) return 'transition';
|
||||
if (count < 100000) return 'maturity';
|
||||
return 'post-genesis';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get node join rate for current phase
|
||||
*/
|
||||
getNodeJoinRate(phase) {
|
||||
const rates = {
|
||||
genesis: 0.3, // Slow initial growth
|
||||
transition: 0.5, // Accelerating growth
|
||||
maturity: 0.7, // Peak growth
|
||||
'post-genesis': 0.4 // Stable growth
|
||||
};
|
||||
|
||||
return rates[phase] || 0.3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a phase transition occurred
|
||||
*/
|
||||
checkPhaseTransition() {
|
||||
const count = this.nodes.size;
|
||||
const previousPhase = this.phases.currentPhase;
|
||||
const currentPhase = this.getCurrentPhase();
|
||||
|
||||
if (previousPhase !== currentPhase) {
|
||||
this.phases.transition(currentPhase);
|
||||
|
||||
this.phaseTransitions.push({
|
||||
from: previousPhase,
|
||||
to: currentPhase,
|
||||
tick: this.currentTick,
|
||||
nodeCount: count,
|
||||
totalCompute: this.totalComputeHours,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
this.logEvent('phase_transition', {
|
||||
from: previousPhase,
|
||||
to: currentPhase,
|
||||
nodeCount: count
|
||||
});
|
||||
|
||||
console.log(`\n🔄 Phase Transition: ${previousPhase} → ${currentPhase} (${count} nodes)`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update network-wide metrics
|
||||
*/
|
||||
updateMetrics() {
|
||||
const activeNodes = this.getActiveNodes();
|
||||
const nodeCount = activeNodes.length;
|
||||
|
||||
if (nodeCount === 0) return;
|
||||
|
||||
let totalTasks = 0;
|
||||
let totalSubmitted = 0;
|
||||
let totalRuv = 0;
|
||||
let totalLatency = 0;
|
||||
let totalSuccess = 0;
|
||||
|
||||
for (const node of activeNodes) {
|
||||
totalTasks += node.tasksCompleted;
|
||||
totalSubmitted += node.tasksSubmitted;
|
||||
totalRuv += node.ruvEarned;
|
||||
totalLatency += node.avgLatency;
|
||||
totalSuccess += node.successRate;
|
||||
}
|
||||
|
||||
this.metrics = {
|
||||
totalTasksCompleted: totalTasks,
|
||||
totalTasksSubmitted: totalSubmitted,
|
||||
totalRuvCirculating: totalRuv,
|
||||
averageLatency: totalLatency / nodeCount,
|
||||
averageSuccessRate: totalSuccess / nodeCount,
|
||||
activeNodeCount: nodeCount,
|
||||
genesisNodeCount: activeNodes.filter(n => n.isGenesis).length,
|
||||
networkHealth: this.calculateNetworkHealth(activeNodes),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate overall network health score (0-1)
|
||||
*/
|
||||
calculateNetworkHealth(nodes) {
|
||||
if (nodes.length === 0) return 0;
|
||||
|
||||
// Factors: connectivity, success rate, economic velocity
|
||||
const avgConnections = nodes.reduce((sum, n) => sum + n.connections.size, 0) / nodes.length;
|
||||
const avgSuccess = nodes.reduce((sum, n) => sum + n.successRate, 0) / nodes.length;
|
||||
const economicVelocity = this.economics.getVelocity();
|
||||
|
||||
const connectivityScore = Math.min(1.0, avgConnections / 20); // Target 20 connections
|
||||
const reliabilityScore = avgSuccess;
|
||||
const economicScore = Math.min(1.0, economicVelocity / 0.5); // Target 0.5 velocity
|
||||
|
||||
return (connectivityScore * 0.3 + reliabilityScore * 0.4 + economicScore * 0.3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active nodes
|
||||
*/
|
||||
getActiveNodes() {
|
||||
return Array.from(this.nodes.values()).filter(n => n.active);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an event
|
||||
*/
|
||||
logEvent(type, data) {
|
||||
this.events.push({
|
||||
type,
|
||||
tick: this.currentTick,
|
||||
timestamp: Date.now(),
|
||||
...data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log progress to console
|
||||
*/
|
||||
logProgress() {
|
||||
const phase = this.getCurrentPhase();
|
||||
const activeNodes = this.getActiveNodes();
|
||||
const genesisActive = activeNodes.filter(n => n.isGenesis).length;
|
||||
|
||||
console.log(
|
||||
`📊 Tick ${this.currentTick} | ` +
|
||||
`Phase: ${phase.toUpperCase()} | ` +
|
||||
`Nodes: ${activeNodes.length} (${genesisActive} genesis) | ` +
|
||||
`Compute: ${Math.floor(this.totalComputeHours)}h | ` +
|
||||
`Health: ${(this.metrics.networkHealth * 100).toFixed(1)}%`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate final simulation report
|
||||
*/
|
||||
generateReport() {
|
||||
const report = {
|
||||
summary: {
|
||||
totalTicks: this.currentTick,
|
||||
totalNodes: this.nodes.size,
|
||||
activeNodes: this.getActiveNodes().length,
|
||||
totalComputeHours: this.totalComputeHours,
|
||||
finalPhase: this.getCurrentPhase(),
|
||||
simulationDuration: Date.now() - this.startTime,
|
||||
},
|
||||
metrics: this.metrics,
|
||||
economics: this.economics.getReport(),
|
||||
phases: {
|
||||
transitions: this.phaseTransitions,
|
||||
current: this.getCurrentPhase(),
|
||||
},
|
||||
nodes: {
|
||||
genesis: Array.from(this.nodes.values())
|
||||
.filter(n => n.isGenesis)
|
||||
.map(n => n.getStats()),
|
||||
regular: Array.from(this.nodes.values())
|
||||
.filter(n => !n.isGenesis)
|
||||
.slice(0, 100) // Sample of regular nodes
|
||||
.map(n => n.getStats()),
|
||||
},
|
||||
events: this.events,
|
||||
};
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export metrics as time series
|
||||
*/
|
||||
exportTimeSeries() {
|
||||
// This would be populated during simulation
|
||||
// For now, return current snapshot
|
||||
return {
|
||||
timestamp: Date.now(),
|
||||
tick: this.currentTick,
|
||||
nodeCount: this.nodes.size,
|
||||
activeNodes: this.getActiveNodes().length,
|
||||
totalCompute: this.totalComputeHours,
|
||||
phase: this.getCurrentPhase(),
|
||||
health: this.metrics.networkHealth,
|
||||
...this.metrics,
|
||||
};
|
||||
}
|
||||
}
|
||||
314
vendor/ruvector/examples/edge-net/sim/src/network.ts
vendored
Normal file
314
vendor/ruvector/examples/edge-net/sim/src/network.ts
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* Network State Management
|
||||
* Manages the P2P network state and phase transitions
|
||||
*/
|
||||
|
||||
import { Cell, CellType, CellState } from './cell.js';
|
||||
|
||||
export enum NetworkPhase {
|
||||
GENESIS = 'genesis', // 0 - 10K nodes
|
||||
GROWTH = 'growth', // 10K - 50K nodes
|
||||
MATURATION = 'maturation', // 50K - 100K nodes
|
||||
INDEPENDENCE = 'independence', // 100K+ nodes
|
||||
}
|
||||
|
||||
export interface NetworkConfig {
|
||||
genesisNodeCount: number;
|
||||
targetNodeCount: number;
|
||||
nodesPerTick: number;
|
||||
taskGenerationRate: number;
|
||||
baseTaskReward: number;
|
||||
connectionCost: number;
|
||||
maxConnectionsPerNode: number;
|
||||
}
|
||||
|
||||
export class Network {
|
||||
public cells: Map<string, Cell>;
|
||||
public currentPhase: NetworkPhase;
|
||||
public currentTick: number;
|
||||
public config: NetworkConfig;
|
||||
public genesisCells: Set<string>;
|
||||
private taskQueue: number[];
|
||||
|
||||
constructor(config?: Partial<NetworkConfig>) {
|
||||
this.cells = new Map();
|
||||
this.currentPhase = NetworkPhase.GENESIS;
|
||||
this.currentTick = 0;
|
||||
this.genesisCells = new Set();
|
||||
this.taskQueue = [];
|
||||
|
||||
this.config = {
|
||||
genesisNodeCount: config?.genesisNodeCount ?? 100,
|
||||
targetNodeCount: config?.targetNodeCount ?? 120000,
|
||||
nodesPerTick: config?.nodesPerTick ?? 10,
|
||||
taskGenerationRate: config?.taskGenerationRate ?? 5,
|
||||
baseTaskReward: config?.baseTaskReward ?? 1.0,
|
||||
connectionCost: config?.connectionCost ?? 0.5,
|
||||
maxConnectionsPerNode: config?.maxConnectionsPerNode ?? 50,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize network with genesis nodes
|
||||
*/
|
||||
public initialize(): void {
|
||||
console.log(`Initializing network with ${this.config.genesisNodeCount} genesis nodes...`);
|
||||
|
||||
for (let i = 0; i < this.config.genesisNodeCount; i++) {
|
||||
const cell = new Cell(CellType.GENESIS, this.currentTick, {
|
||||
computePower: 0.8 + Math.random() * 0.2, // Genesis nodes are powerful
|
||||
bandwidth: 0.8 + Math.random() * 0.2,
|
||||
reliability: 0.9 + Math.random() * 0.1,
|
||||
storage: 0.8 + Math.random() * 0.2,
|
||||
});
|
||||
|
||||
this.cells.set(cell.id, cell);
|
||||
this.genesisCells.add(cell.id);
|
||||
}
|
||||
|
||||
// Connect genesis nodes to each other (mesh topology)
|
||||
this.connectGenesisNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect all genesis nodes to each other
|
||||
*/
|
||||
private connectGenesisNodes(): void {
|
||||
const genesisArray = Array.from(this.genesisCells);
|
||||
for (let i = 0; i < genesisArray.length; i++) {
|
||||
for (let j = i + 1; j < genesisArray.length; j++) {
|
||||
const cell1 = this.cells.get(genesisArray[i])!;
|
||||
const cell2 = this.cells.get(genesisArray[j])!;
|
||||
|
||||
cell1.connectTo(cell2.id);
|
||||
cell2.connectTo(cell1.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new regular nodes to the network
|
||||
*/
|
||||
public spawnNodes(count: number): void {
|
||||
for (let i = 0; i < count; i++) {
|
||||
const cell = new Cell(CellType.REGULAR, this.currentTick);
|
||||
this.cells.set(cell.id, cell);
|
||||
|
||||
// Connect to random existing nodes (preferential attachment)
|
||||
this.connectNewNode(cell);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect a new node to the network
|
||||
*/
|
||||
private connectNewNode(newCell: Cell): void {
|
||||
const connectionCount = Math.min(
|
||||
5 + Math.floor(Math.random() * 5),
|
||||
this.config.maxConnectionsPerNode
|
||||
);
|
||||
|
||||
const potentialTargets = Array.from(this.cells.values())
|
||||
.filter(c => c.id !== newCell.id)
|
||||
.filter(c => {
|
||||
// In Phase 2+, genesis nodes don't accept new connections
|
||||
if (this.currentPhase !== NetworkPhase.GENESIS && c.type === CellType.GENESIS) {
|
||||
return false;
|
||||
}
|
||||
return c.state === CellState.ACTIVE && c.connectedCells.size < this.config.maxConnectionsPerNode;
|
||||
});
|
||||
|
||||
// Preferential attachment: higher fitness = more likely to connect
|
||||
const selectedTargets = this.selectPreferentialTargets(potentialTargets, connectionCount);
|
||||
|
||||
for (const target of selectedTargets) {
|
||||
newCell.connectTo(target.id);
|
||||
target.connectTo(newCell.id);
|
||||
|
||||
// Connection costs energy
|
||||
newCell.spendEnergy(this.config.connectionCost);
|
||||
target.spendEnergy(this.config.connectionCost);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select targets using preferential attachment
|
||||
*/
|
||||
private selectPreferentialTargets(candidates: Cell[], count: number): Cell[] {
|
||||
if (candidates.length <= count) {
|
||||
return candidates;
|
||||
}
|
||||
|
||||
const selected: Cell[] = [];
|
||||
const weights = candidates.map(c => c.getFitnessScore() * (1 + c.connectedCells.size));
|
||||
const totalWeight = weights.reduce((sum, w) => sum + w, 0);
|
||||
|
||||
for (let i = 0; i < count && candidates.length > 0; i++) {
|
||||
let random = Math.random() * totalWeight;
|
||||
let selectedIndex = 0;
|
||||
|
||||
for (let j = 0; j < weights.length; j++) {
|
||||
random -= weights[j];
|
||||
if (random <= 0) {
|
||||
selectedIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
selected.push(candidates[selectedIndex]);
|
||||
candidates.splice(selectedIndex, 1);
|
||||
weights.splice(selectedIndex, 1);
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tasks for the network
|
||||
*/
|
||||
private generateTasks(): void {
|
||||
const tasksToGenerate = Math.floor(
|
||||
this.cells.size * this.config.taskGenerationRate * Math.random()
|
||||
);
|
||||
|
||||
for (let i = 0; i < tasksToGenerate; i++) {
|
||||
// Task complexity between 0.1 and 1.0
|
||||
this.taskQueue.push(0.1 + Math.random() * 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribute tasks to capable cells
|
||||
*/
|
||||
private distributeTasks(): void {
|
||||
const activeCells = Array.from(this.cells.values())
|
||||
.filter(c => c.state === CellState.ACTIVE);
|
||||
|
||||
while (this.taskQueue.length > 0 && activeCells.length > 0) {
|
||||
const task = this.taskQueue.shift()!;
|
||||
|
||||
// Select cell based on fitness and availability
|
||||
const selectedCell = activeCells[Math.floor(Math.random() * activeCells.length)];
|
||||
selectedCell.processTask(task, this.config.baseTaskReward);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update network phase based on node count
|
||||
*/
|
||||
private updatePhase(): void {
|
||||
const nodeCount = this.cells.size;
|
||||
const oldPhase = this.currentPhase;
|
||||
|
||||
if (nodeCount >= 100000) {
|
||||
this.currentPhase = NetworkPhase.INDEPENDENCE;
|
||||
} else if (nodeCount >= 50000) {
|
||||
this.currentPhase = NetworkPhase.MATURATION;
|
||||
} else if (nodeCount >= 10000) {
|
||||
this.currentPhase = NetworkPhase.GROWTH;
|
||||
} else {
|
||||
this.currentPhase = NetworkPhase.GENESIS;
|
||||
}
|
||||
|
||||
if (oldPhase !== this.currentPhase) {
|
||||
console.log(`\n🔄 PHASE TRANSITION: ${oldPhase} → ${this.currentPhase} (${nodeCount} nodes)`);
|
||||
this.onPhaseTransition();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle phase transition events
|
||||
*/
|
||||
private onPhaseTransition(): void {
|
||||
// Update all cells based on new phase
|
||||
this.cells.forEach(cell => cell.updateState(this.cells.size));
|
||||
|
||||
// Phase-specific actions
|
||||
switch (this.currentPhase) {
|
||||
case NetworkPhase.GROWTH:
|
||||
console.log(' → Genesis nodes reducing 10x multiplier...');
|
||||
break;
|
||||
case NetworkPhase.MATURATION:
|
||||
console.log(' → Genesis nodes entering READ-ONLY mode...');
|
||||
break;
|
||||
case NetworkPhase.INDEPENDENCE:
|
||||
console.log(' → Genesis nodes RETIRED. Network is independent!');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate one tick of the network
|
||||
*/
|
||||
public tick(): void {
|
||||
this.currentTick++;
|
||||
|
||||
// Spawn new nodes (if not at target)
|
||||
if (this.cells.size < this.config.targetNodeCount) {
|
||||
const nodesToSpawn = Math.min(
|
||||
this.config.nodesPerTick,
|
||||
this.config.targetNodeCount - this.cells.size
|
||||
);
|
||||
this.spawnNodes(nodesToSpawn);
|
||||
}
|
||||
|
||||
// Generate and distribute tasks
|
||||
this.generateTasks();
|
||||
this.distributeTasks();
|
||||
|
||||
// Update all cells
|
||||
this.cells.forEach(cell => {
|
||||
cell.tick();
|
||||
cell.updateState(this.cells.size);
|
||||
});
|
||||
|
||||
// Check for phase transitions
|
||||
this.updatePhase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get network statistics
|
||||
*/
|
||||
public getStats() {
|
||||
const cells = Array.from(this.cells.values());
|
||||
const genesisCells = cells.filter(c => c.type === CellType.GENESIS);
|
||||
const regularCells = cells.filter(c => c.type === CellType.REGULAR);
|
||||
|
||||
const totalEnergy = cells.reduce((sum, c) => sum + c.energy, 0);
|
||||
const totalEarned = cells.reduce((sum, c) => sum + c.metrics.energyEarned, 0);
|
||||
const totalSpent = cells.reduce((sum, c) => sum + c.metrics.energySpent, 0);
|
||||
const totalTasks = cells.reduce((sum, c) => sum + c.metrics.tasksCompleted, 0);
|
||||
|
||||
return {
|
||||
tick: this.currentTick,
|
||||
phase: this.currentPhase,
|
||||
nodeCount: this.cells.size,
|
||||
genesisNodes: {
|
||||
count: genesisCells.length,
|
||||
active: genesisCells.filter(c => c.state === CellState.ACTIVE).length,
|
||||
readOnly: genesisCells.filter(c => c.state === CellState.READ_ONLY).length,
|
||||
retired: genesisCells.filter(c => c.state === CellState.RETIRED).length,
|
||||
avgMultiplier: genesisCells.reduce((sum, c) => sum + c.genesisMultiplier, 0) / genesisCells.length,
|
||||
},
|
||||
regularNodes: {
|
||||
count: regularCells.length,
|
||||
},
|
||||
economy: {
|
||||
totalEnergy,
|
||||
totalEarned,
|
||||
totalSpent,
|
||||
netEnergy: totalEarned - totalSpent,
|
||||
avgEnergyPerNode: totalEnergy / this.cells.size,
|
||||
},
|
||||
tasks: {
|
||||
completed: totalTasks,
|
||||
queued: this.taskQueue.length,
|
||||
avgPerNode: totalTasks / this.cells.size,
|
||||
},
|
||||
network: {
|
||||
avgConnections: cells.reduce((sum, c) => sum + c.connectedCells.size, 0) / this.cells.size,
|
||||
avgSuccessRate: cells.reduce((sum, c) => sum + c.metrics.successRate, 0) / this.cells.size,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
171
vendor/ruvector/examples/edge-net/sim/src/node.js
vendored
Normal file
171
vendor/ruvector/examples/edge-net/sim/src/node.js
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Simulated Edge-Net Node
|
||||
* Represents a single node in the distributed network
|
||||
*/
|
||||
|
||||
export class SimNode {
|
||||
constructor(id, joinedAt, isGenesis = false) {
|
||||
this.id = id;
|
||||
this.joinedAt = joinedAt;
|
||||
this.isGenesis = isGenesis;
|
||||
|
||||
// Node state
|
||||
this.active = true;
|
||||
this.uptime = 0;
|
||||
this.lastSeen = joinedAt;
|
||||
|
||||
// Economic state
|
||||
this.ruvEarned = 0;
|
||||
this.ruvSpent = 0;
|
||||
this.ruvStaked = 0;
|
||||
|
||||
// Performance metrics
|
||||
this.tasksCompleted = 0;
|
||||
this.tasksSubmitted = 0;
|
||||
this.successRate = 0.95;
|
||||
this.avgLatency = 100 + Math.random() * 200; // ms
|
||||
|
||||
// Network state
|
||||
this.connections = new Set();
|
||||
this.maxConnections = isGenesis ? 1000 : 50;
|
||||
this.reputation = 1.0;
|
||||
|
||||
// Contribution metrics
|
||||
this.cpuContribution = 0.2 + Math.random() * 0.3; // 20-50%
|
||||
this.totalComputeHours = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update node state for a time step
|
||||
*/
|
||||
tick(deltaTime, networkCompute, currentPhase) {
|
||||
if (!this.active) return;
|
||||
|
||||
this.uptime += deltaTime;
|
||||
this.lastSeen = Date.now();
|
||||
|
||||
// Calculate contribution for this tick
|
||||
const hoursThisTick = deltaTime / 3600000; // ms to hours
|
||||
const contribution = this.cpuContribution * hoursThisTick;
|
||||
this.totalComputeHours += contribution;
|
||||
|
||||
// Simulate task completion
|
||||
const tasksThisTick = Math.floor(Math.random() * 3);
|
||||
if (tasksThisTick > 0) {
|
||||
this.tasksCompleted += tasksThisTick;
|
||||
|
||||
// Calculate rewards with multiplier
|
||||
const baseReward = tasksThisTick * 10; // 10 rUv per task
|
||||
const multiplier = this.calculateMultiplier(networkCompute, currentPhase);
|
||||
const reward = Math.floor(baseReward * multiplier);
|
||||
|
||||
this.ruvEarned += reward;
|
||||
}
|
||||
|
||||
// Simulate task submission (nodes also consume)
|
||||
if (Math.random() < 0.1) { // 10% chance per tick
|
||||
this.tasksSubmitted += 1;
|
||||
const cost = 5 + Math.floor(Math.random() * 15); // 5-20 rUv
|
||||
|
||||
if (this.getBalance() >= cost) {
|
||||
this.ruvSpent += cost;
|
||||
}
|
||||
}
|
||||
|
||||
// Update success rate (small random walk)
|
||||
this.successRate = Math.max(0.7, Math.min(0.99,
|
||||
this.successRate + (Math.random() - 0.5) * 0.01
|
||||
));
|
||||
|
||||
// Genesis nodes in transition phase have connection limits
|
||||
if (this.isGenesis && currentPhase === 'transition') {
|
||||
this.maxConnections = Math.max(100, this.maxConnections - 1);
|
||||
}
|
||||
|
||||
// Genesis nodes become read-only in maturity phase
|
||||
if (this.isGenesis && currentPhase === 'maturity') {
|
||||
this.maxConnections = 0; // No new connections
|
||||
}
|
||||
|
||||
// Genesis nodes retire in post-genesis
|
||||
if (this.isGenesis && currentPhase === 'post-genesis') {
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate contribution multiplier based on network state
|
||||
*/
|
||||
calculateMultiplier(networkCompute, phase) {
|
||||
// Base multiplier from contribution curve
|
||||
const MAX_BONUS = 10.0;
|
||||
const DECAY_CONSTANT = 1000000.0;
|
||||
const decay = Math.exp(-networkCompute / DECAY_CONSTANT);
|
||||
const baseMultiplier = 1.0 + (MAX_BONUS - 1.0) * decay;
|
||||
|
||||
// Early adopter bonus for genesis nodes
|
||||
let earlyBonus = 1.0;
|
||||
if (this.isGenesis && phase === 'genesis') {
|
||||
earlyBonus = 10.0; // 10x for genesis contributors
|
||||
} else if (this.isGenesis && phase === 'transition') {
|
||||
earlyBonus = 5.0 - (networkCompute / 1000000.0) * 4.0; // Decay from 5x to 1x
|
||||
earlyBonus = Math.max(1.0, earlyBonus);
|
||||
}
|
||||
|
||||
return baseMultiplier * earlyBonus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current rUv balance
|
||||
*/
|
||||
getBalance() {
|
||||
return Math.max(0, this.ruvEarned - this.ruvSpent - this.ruvStaked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to another node
|
||||
*/
|
||||
connectTo(nodeId) {
|
||||
if (this.connections.size < this.maxConnections) {
|
||||
this.connections.add(nodeId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from a node
|
||||
*/
|
||||
disconnect(nodeId) {
|
||||
this.connections.delete(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if node can accept connections
|
||||
*/
|
||||
canAcceptConnections() {
|
||||
return this.active && this.connections.size < this.maxConnections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get node statistics
|
||||
*/
|
||||
getStats() {
|
||||
return {
|
||||
id: this.id,
|
||||
isGenesis: this.isGenesis,
|
||||
active: this.active,
|
||||
uptime: this.uptime,
|
||||
ruvBalance: this.getBalance(),
|
||||
ruvEarned: this.ruvEarned,
|
||||
ruvSpent: this.ruvSpent,
|
||||
tasksCompleted: this.tasksCompleted,
|
||||
tasksSubmitted: this.tasksSubmitted,
|
||||
successRate: this.successRate,
|
||||
reputation: this.reputation,
|
||||
connections: this.connections.size,
|
||||
maxConnections: this.maxConnections,
|
||||
totalComputeHours: this.totalComputeHours,
|
||||
};
|
||||
}
|
||||
}
|
||||
193
vendor/ruvector/examples/edge-net/sim/src/phases.js
vendored
Normal file
193
vendor/ruvector/examples/edge-net/sim/src/phases.js
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Phase Management for Network Lifecycle
|
||||
* Tracks and validates phase transitions
|
||||
*/
|
||||
|
||||
export class PhaseManager {
|
||||
constructor() {
|
||||
this.currentPhase = 'genesis';
|
||||
this.phaseHistory = [];
|
||||
this.phaseMetrics = new Map();
|
||||
|
||||
this.initializePhases();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize phase definitions
|
||||
*/
|
||||
initializePhases() {
|
||||
this.phases = {
|
||||
genesis: {
|
||||
name: 'Genesis Phase',
|
||||
nodeRange: [0, 10000],
|
||||
description: 'Network bootstrap with genesis nodes',
|
||||
features: [
|
||||
'Genesis node initialization',
|
||||
'Early adopter multiplier (10x)',
|
||||
'Network bootstrap',
|
||||
'Initial task distribution',
|
||||
'Security learning initialization',
|
||||
],
|
||||
validations: [
|
||||
{ metric: 'genesisNodesActive', min: 1, description: 'At least 1 genesis node active' },
|
||||
{ metric: 'earlyMultiplier', min: 5.0, description: 'High early adopter multiplier' },
|
||||
],
|
||||
},
|
||||
transition: {
|
||||
name: 'Transition Phase',
|
||||
nodeRange: [10000, 50000],
|
||||
description: 'Genesis sunset preparation',
|
||||
features: [
|
||||
'Genesis node connection limiting',
|
||||
'Network resilience testing',
|
||||
'Task routing optimization',
|
||||
'Economic sustainability threshold',
|
||||
'Topology self-organization',
|
||||
],
|
||||
validations: [
|
||||
{ metric: 'genesisConnectionLimit', max: 500, description: 'Genesis connections limited' },
|
||||
{ metric: 'networkResilience', min: 0.7, description: 'Network resilient without full genesis' },
|
||||
{ metric: 'taskRoutingSuccess', min: 0.85, description: 'Efficient task routing' },
|
||||
],
|
||||
},
|
||||
maturity: {
|
||||
name: 'Maturity Phase',
|
||||
nodeRange: [50000, 100000],
|
||||
description: 'Genesis read-only mode',
|
||||
features: [
|
||||
'Genesis nodes read-only',
|
||||
'Full network self-sustenance',
|
||||
'Economic health monitoring',
|
||||
'Security threat response',
|
||||
'Founder tribute distribution',
|
||||
],
|
||||
validations: [
|
||||
{ metric: 'genesisReadOnly', exact: true, description: 'Genesis nodes read-only' },
|
||||
{ metric: 'economicHealth', min: 0.75, description: 'Healthy economic metrics' },
|
||||
{ metric: 'selfSustaining', exact: true, description: 'Network self-sustaining' },
|
||||
],
|
||||
},
|
||||
'post-genesis': {
|
||||
name: 'Post-Genesis Phase',
|
||||
nodeRange: [100000, Infinity],
|
||||
description: 'Full decentralization',
|
||||
features: [
|
||||
'Genesis retirement complete',
|
||||
'Independent network operation',
|
||||
'Long-term stability',
|
||||
'Economic equilibrium',
|
||||
'Community governance',
|
||||
],
|
||||
validations: [
|
||||
{ metric: 'genesisRetired', exact: true, description: 'All genesis nodes retired' },
|
||||
{ metric: 'networkStability', min: 0.8, description: 'Stable network operation' },
|
||||
{ metric: 'economicEquilibrium', min: 0.7, description: 'Economic equilibrium reached' },
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition to a new phase
|
||||
*/
|
||||
transition(newPhase) {
|
||||
if (this.currentPhase === newPhase) return;
|
||||
|
||||
const previousPhase = this.currentPhase;
|
||||
this.currentPhase = newPhase;
|
||||
|
||||
this.phaseHistory.push({
|
||||
from: previousPhase,
|
||||
to: newPhase,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log(`🔄 PHASE TRANSITION: ${previousPhase} → ${newPhase}`);
|
||||
console.log(`${'='.repeat(60)}`);
|
||||
console.log(`\n${this.phases[newPhase].description}\n`);
|
||||
console.log('Features:');
|
||||
this.phases[newPhase].features.forEach(f => console.log(` ✓ ${f}`));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current phase definition
|
||||
*/
|
||||
getCurrentPhaseInfo() {
|
||||
return this.phases[this.currentPhase];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate phase metrics
|
||||
*/
|
||||
validatePhase(metrics) {
|
||||
const phase = this.phases[this.currentPhase];
|
||||
if (!phase) return { valid: false, errors: ['Unknown phase'] };
|
||||
|
||||
const errors = [];
|
||||
const validations = phase.validations || [];
|
||||
|
||||
for (const validation of validations) {
|
||||
const value = metrics[validation.metric];
|
||||
|
||||
if (validation.min !== undefined && value < validation.min) {
|
||||
errors.push(`${validation.description}: ${value} < ${validation.min}`);
|
||||
}
|
||||
|
||||
if (validation.max !== undefined && value > validation.max) {
|
||||
errors.push(`${validation.description}: ${value} > ${validation.max}`);
|
||||
}
|
||||
|
||||
if (validation.exact !== undefined && value !== validation.exact) {
|
||||
errors.push(`${validation.description}: ${value} !== ${validation.exact}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors,
|
||||
phase: this.currentPhase,
|
||||
validations,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Record phase metrics
|
||||
*/
|
||||
recordMetrics(phase, metrics) {
|
||||
if (!this.phaseMetrics.has(phase)) {
|
||||
this.phaseMetrics.set(phase, []);
|
||||
}
|
||||
|
||||
this.phaseMetrics.get(phase).push({
|
||||
timestamp: Date.now(),
|
||||
...metrics,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get phase report
|
||||
*/
|
||||
getReport() {
|
||||
return {
|
||||
currentPhase: this.currentPhase,
|
||||
phaseInfo: this.getCurrentPhaseInfo(),
|
||||
history: this.phaseHistory,
|
||||
metrics: Object.fromEntries(this.phaseMetrics),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expected phase for node count
|
||||
*/
|
||||
getExpectedPhase(nodeCount) {
|
||||
for (const [phaseName, phase] of Object.entries(this.phases)) {
|
||||
const [min, max] = phase.nodeRange;
|
||||
if (nodeCount >= min && nodeCount < max) {
|
||||
return phaseName;
|
||||
}
|
||||
}
|
||||
return 'post-genesis';
|
||||
}
|
||||
}
|
||||
202
vendor/ruvector/examples/edge-net/sim/src/phases.ts
vendored
Normal file
202
vendor/ruvector/examples/edge-net/sim/src/phases.ts
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Phase Transition Logic
|
||||
* Manages lifecycle phases and transition conditions
|
||||
*/
|
||||
|
||||
import { Network, NetworkPhase } from './network.js';
|
||||
import { MetricsCollector } from './metrics.js';
|
||||
import { Cell, CellType, CellState } from './cell.js';
|
||||
|
||||
export interface PhaseTransitionCondition {
|
||||
minNodes: number;
|
||||
maxNodes: number;
|
||||
requiredDuration?: number;
|
||||
customCheck?: (network: Network) => boolean;
|
||||
}
|
||||
|
||||
export class PhaseManager {
|
||||
private network: Network;
|
||||
private metrics: MetricsCollector;
|
||||
private conditions: Map<NetworkPhase, PhaseTransitionCondition>;
|
||||
private lastPhase: NetworkPhase;
|
||||
|
||||
constructor(network: Network, metrics: MetricsCollector) {
|
||||
this.network = network;
|
||||
this.metrics = metrics;
|
||||
this.lastPhase = NetworkPhase.GENESIS;
|
||||
|
||||
this.conditions = new Map<NetworkPhase, PhaseTransitionCondition>([
|
||||
[NetworkPhase.GENESIS, {
|
||||
minNodes: 0,
|
||||
maxNodes: 10000,
|
||||
}],
|
||||
[NetworkPhase.GROWTH, {
|
||||
minNodes: 10000,
|
||||
maxNodes: 50000,
|
||||
customCheck: (net: Network) => {
|
||||
// Verify genesis nodes are still active but reducing multiplier
|
||||
const genesisCells = Array.from(net.cells.values())
|
||||
.filter((c: Cell) => c.type === CellType.GENESIS);
|
||||
const avgMultiplier = genesisCells.reduce((sum, c) => sum + c.genesisMultiplier, 0) / genesisCells.length;
|
||||
return avgMultiplier < 10 && avgMultiplier > 1;
|
||||
},
|
||||
}],
|
||||
[NetworkPhase.MATURATION, {
|
||||
minNodes: 50000,
|
||||
maxNodes: 100000,
|
||||
customCheck: (net: Network) => {
|
||||
// Verify genesis nodes are entering read-only mode
|
||||
const genesisCells = Array.from(net.cells.values())
|
||||
.filter((c: Cell) => c.type === CellType.GENESIS);
|
||||
const readOnlyCount = genesisCells.filter(c => c.state === CellState.READ_ONLY).length;
|
||||
return readOnlyCount >= genesisCells.length * 0.5; // At least 50% read-only
|
||||
},
|
||||
}],
|
||||
[NetworkPhase.INDEPENDENCE, {
|
||||
minNodes: 100000,
|
||||
maxNodes: Infinity,
|
||||
customCheck: (net: Network) => {
|
||||
// Verify genesis nodes are retired
|
||||
const genesisCells = Array.from(net.cells.values())
|
||||
.filter((c: Cell) => c.type === CellType.GENESIS);
|
||||
const retiredCount = genesisCells.filter(c => c.state === CellState.RETIRED).length;
|
||||
return retiredCount >= genesisCells.length * 0.8; // At least 80% retired
|
||||
},
|
||||
}],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if network should transition to next phase
|
||||
*/
|
||||
public checkTransition(): boolean {
|
||||
const currentPhase = this.network.currentPhase;
|
||||
const nodeCount = this.network.cells.size;
|
||||
|
||||
// Determine target phase based on node count
|
||||
let targetPhase = NetworkPhase.GENESIS;
|
||||
if (nodeCount >= 100000) {
|
||||
targetPhase = NetworkPhase.INDEPENDENCE;
|
||||
} else if (nodeCount >= 50000) {
|
||||
targetPhase = NetworkPhase.MATURATION;
|
||||
} else if (nodeCount >= 10000) {
|
||||
targetPhase = NetworkPhase.GROWTH;
|
||||
}
|
||||
|
||||
// If phase changed, validate transition
|
||||
if (targetPhase !== currentPhase) {
|
||||
const condition = this.conditions.get(targetPhase);
|
||||
|
||||
if (condition) {
|
||||
// Check node count bounds
|
||||
if (nodeCount < condition.minNodes || nodeCount >= condition.maxNodes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check custom conditions
|
||||
if (condition.customCheck && !condition.customCheck(this.network)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid transition
|
||||
this.onTransition(currentPhase, targetPhase);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle phase transition
|
||||
*/
|
||||
private onTransition(fromPhase: NetworkPhase, toPhase: NetworkPhase): void {
|
||||
console.log(`\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
||||
console.log(`🔄 PHASE TRANSITION: ${fromPhase.toUpperCase()} → ${toPhase.toUpperCase()}`);
|
||||
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
||||
|
||||
// Notify metrics collector
|
||||
this.metrics.onPhaseTransition(fromPhase, toPhase);
|
||||
|
||||
// Log phase-specific information
|
||||
this.logPhaseInfo(toPhase);
|
||||
|
||||
this.lastPhase = toPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log phase-specific information
|
||||
*/
|
||||
private logPhaseInfo(phase: NetworkPhase): void {
|
||||
const stats = this.network.getStats();
|
||||
|
||||
console.log(`📊 Network Status:`);
|
||||
console.log(` Nodes: ${stats.nodeCount.toLocaleString()}`);
|
||||
console.log(` Genesis Nodes: ${stats.genesisNodes.count}`);
|
||||
console.log(` Avg Connections: ${stats.network.avgConnections.toFixed(2)}`);
|
||||
console.log(` Total Energy: ${stats.economy.totalEnergy.toFixed(2)} rUv`);
|
||||
|
||||
switch (phase) {
|
||||
case NetworkPhase.GENESIS:
|
||||
console.log(`\n🌱 Genesis Phase:`);
|
||||
console.log(` - Genesis nodes establishing network`);
|
||||
console.log(` - 10x energy multiplier active`);
|
||||
console.log(` - Target: 10,000 nodes`);
|
||||
break;
|
||||
|
||||
case NetworkPhase.GROWTH:
|
||||
console.log(`\n🌿 Growth Phase:`);
|
||||
console.log(` - Genesis multiplier: ${stats.genesisNodes.avgMultiplier.toFixed(2)}x`);
|
||||
console.log(` - Genesis nodes reducing connections`);
|
||||
console.log(` - Network self-organizing`);
|
||||
console.log(` - Target: 50,000 nodes`);
|
||||
break;
|
||||
|
||||
case NetworkPhase.MATURATION:
|
||||
console.log(`\n🌳 Maturation Phase:`);
|
||||
console.log(` - Genesis nodes: ${stats.genesisNodes.readOnly} read-only`);
|
||||
console.log(` - Network operating independently`);
|
||||
console.log(` - Economic sustainability: ${(stats.economy.totalEarned / Math.max(stats.economy.totalSpent, 1)).toFixed(2)}x`);
|
||||
console.log(` - Target: 100,000 nodes`);
|
||||
break;
|
||||
|
||||
case NetworkPhase.INDEPENDENCE:
|
||||
console.log(`\n🚀 Independence Phase:`);
|
||||
console.log(` - Genesis nodes: ${stats.genesisNodes.retired} retired`);
|
||||
console.log(` - Pure P2P operation`);
|
||||
console.log(` - Network fully autonomous`);
|
||||
console.log(` - Target: Long-term stability`);
|
||||
break;
|
||||
}
|
||||
|
||||
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get phase progress (0-1)
|
||||
*/
|
||||
public getPhaseProgress(): number {
|
||||
const condition = this.conditions.get(this.network.currentPhase);
|
||||
if (!condition) return 0;
|
||||
|
||||
const nodeCount = this.network.cells.size;
|
||||
const range = condition.maxNodes - condition.minNodes;
|
||||
const progress = (nodeCount - condition.minNodes) / range;
|
||||
|
||||
return Math.max(0, Math.min(1, progress));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get estimated ticks to next phase
|
||||
*/
|
||||
public getTicksToNextPhase(): number {
|
||||
const condition = this.conditions.get(this.network.currentPhase);
|
||||
if (!condition || condition.maxNodes === Infinity) return -1;
|
||||
|
||||
const nodeCount = this.network.cells.size;
|
||||
const nodesNeeded = condition.maxNodes - nodeCount;
|
||||
const ticksNeeded = Math.ceil(nodesNeeded / this.network.config.nodesPerTick);
|
||||
|
||||
return Math.max(0, ticksNeeded);
|
||||
}
|
||||
}
|
||||
246
vendor/ruvector/examples/edge-net/sim/src/report.ts
vendored
Normal file
246
vendor/ruvector/examples/edge-net/sim/src/report.ts
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* Report Generation
|
||||
* Generates comprehensive JSON reports of simulation results
|
||||
*/
|
||||
|
||||
import { writeFileSync } from 'fs';
|
||||
import { Network } from './network.js';
|
||||
import { MetricsCollector, PhaseMetrics } from './metrics.js';
|
||||
|
||||
export interface SimulationReport {
|
||||
metadata: {
|
||||
timestamp: string;
|
||||
simulationVersion: string;
|
||||
duration: number;
|
||||
totalTicks: number;
|
||||
};
|
||||
configuration: {
|
||||
genesisNodeCount: number;
|
||||
targetNodeCount: number;
|
||||
nodesPerTick: number;
|
||||
taskGenerationRate: number;
|
||||
baseTaskReward: number;
|
||||
};
|
||||
summary: {
|
||||
phasesCompleted: number;
|
||||
totalPassed: boolean;
|
||||
phasesPassed: number;
|
||||
phasesTotal: number;
|
||||
finalNodeCount: number;
|
||||
finalPhase: string;
|
||||
};
|
||||
phases: {
|
||||
[key: string]: PhaseMetrics;
|
||||
};
|
||||
finalState: {
|
||||
nodeCount: number;
|
||||
genesisNodes: any;
|
||||
economy: any;
|
||||
network: any;
|
||||
topPerformers: any[];
|
||||
};
|
||||
validation: {
|
||||
overallPassed: boolean;
|
||||
criticalIssues: string[];
|
||||
warnings: string[];
|
||||
successes: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export class ReportGenerator {
|
||||
private network: Network;
|
||||
private metrics: MetricsCollector;
|
||||
private startTime: number;
|
||||
|
||||
constructor(network: Network, metrics: MetricsCollector) {
|
||||
this.network = network;
|
||||
this.metrics = metrics;
|
||||
this.startTime = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate comprehensive simulation report
|
||||
*/
|
||||
public generateReport(): SimulationReport {
|
||||
const endTime = Date.now();
|
||||
const stats = this.network.getStats();
|
||||
const allMetrics = this.metrics.getAllMetrics();
|
||||
const overallSuccess = this.metrics.getOverallSuccess();
|
||||
|
||||
// Organize metrics by phase
|
||||
const phaseMetrics: { [key: string]: PhaseMetrics } = {};
|
||||
allMetrics.forEach(m => {
|
||||
phaseMetrics[m.phase] = m;
|
||||
});
|
||||
|
||||
// Get top performing nodes
|
||||
const topPerformers = this.getTopPerformers(10);
|
||||
|
||||
// Collect validation issues
|
||||
const validation = this.collectValidation(allMetrics);
|
||||
|
||||
const report: SimulationReport = {
|
||||
metadata: {
|
||||
timestamp: new Date().toISOString(),
|
||||
simulationVersion: '1.0.0',
|
||||
duration: endTime - this.startTime,
|
||||
totalTicks: this.network.currentTick,
|
||||
},
|
||||
configuration: {
|
||||
genesisNodeCount: this.network.config.genesisNodeCount,
|
||||
targetNodeCount: this.network.config.targetNodeCount,
|
||||
nodesPerTick: this.network.config.nodesPerTick,
|
||||
taskGenerationRate: this.network.config.taskGenerationRate,
|
||||
baseTaskReward: this.network.config.baseTaskReward,
|
||||
},
|
||||
summary: {
|
||||
phasesCompleted: allMetrics.length,
|
||||
totalPassed: overallSuccess.passed,
|
||||
phasesPassed: overallSuccess.totalPassed,
|
||||
phasesTotal: overallSuccess.totalPhases,
|
||||
finalNodeCount: stats.nodeCount,
|
||||
finalPhase: this.network.currentPhase,
|
||||
},
|
||||
phases: phaseMetrics,
|
||||
finalState: {
|
||||
nodeCount: stats.nodeCount,
|
||||
genesisNodes: stats.genesisNodes,
|
||||
economy: stats.economy,
|
||||
network: stats.network,
|
||||
topPerformers,
|
||||
},
|
||||
validation,
|
||||
};
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get top performing nodes
|
||||
*/
|
||||
private getTopPerformers(count: number): any[] {
|
||||
const cells = Array.from(this.network.cells.values());
|
||||
|
||||
return cells
|
||||
.sort((a, b) => {
|
||||
const scoreA = a.metrics.energyEarned - a.metrics.energySpent;
|
||||
const scoreB = b.metrics.energyEarned - b.metrics.energySpent;
|
||||
return scoreB - scoreA;
|
||||
})
|
||||
.slice(0, count)
|
||||
.map(cell => ({
|
||||
id: cell.id.substring(0, 8),
|
||||
type: cell.type,
|
||||
netEnergy: cell.metrics.energyEarned - cell.metrics.energySpent,
|
||||
tasksCompleted: cell.metrics.tasksCompleted,
|
||||
successRate: (cell.metrics.successRate * 100).toFixed(1) + '%',
|
||||
connections: cell.connectedCells.size,
|
||||
fitnessScore: cell.getFitnessScore().toFixed(3),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all validation issues
|
||||
*/
|
||||
private collectValidation(allMetrics: PhaseMetrics[]): {
|
||||
overallPassed: boolean;
|
||||
criticalIssues: string[];
|
||||
warnings: string[];
|
||||
successes: string[];
|
||||
} {
|
||||
const criticalIssues: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
const successes: string[] = [];
|
||||
|
||||
allMetrics.forEach(metrics => {
|
||||
if (!metrics.validation.passed) {
|
||||
criticalIssues.push(`${metrics.phase.toUpperCase()} phase failed validation`);
|
||||
}
|
||||
|
||||
metrics.validation.reasons.forEach(reason => {
|
||||
if (reason.startsWith('✓')) {
|
||||
successes.push(`${metrics.phase}: ${reason}`);
|
||||
} else if (reason.includes('too low') || reason.includes('insufficient')) {
|
||||
warnings.push(`${metrics.phase}: ${reason}`);
|
||||
} else {
|
||||
criticalIssues.push(`${metrics.phase}: ${reason}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
overallPassed: criticalIssues.length === 0,
|
||||
criticalIssues,
|
||||
warnings,
|
||||
successes,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Save report to file
|
||||
*/
|
||||
public saveReport(filepath: string): void {
|
||||
const report = this.generateReport();
|
||||
writeFileSync(filepath, JSON.stringify(report, null, 2), 'utf-8');
|
||||
console.log(`\n📄 Report saved to: ${filepath}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print summary to console
|
||||
*/
|
||||
public printSummary(): void {
|
||||
const report = this.generateReport();
|
||||
|
||||
console.log('\n╔════════════════════════════════════════════════════════════╗');
|
||||
console.log('║ EDGE-NET LIFECYCLE SIMULATION REPORT ║');
|
||||
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
console.log('📊 SUMMARY:');
|
||||
console.log(` Duration: ${(report.metadata.duration / 1000).toFixed(2)}s`);
|
||||
console.log(` Total Ticks: ${report.metadata.totalTicks.toLocaleString()}`);
|
||||
console.log(` Final Nodes: ${report.summary.finalNodeCount.toLocaleString()}`);
|
||||
console.log(` Final Phase: ${report.summary.finalPhase.toUpperCase()}`);
|
||||
console.log(` Phases Passed: ${report.summary.phasesPassed}/${report.summary.phasesTotal}`);
|
||||
console.log(` Overall Result: ${report.summary.totalPassed ? '✅ PASSED' : '❌ FAILED'}\n`);
|
||||
|
||||
console.log('📈 PHASE RESULTS:');
|
||||
Object.entries(report.phases).forEach(([phase, metrics]) => {
|
||||
const icon = metrics.validation.passed ? '✅' : '❌';
|
||||
console.log(` ${icon} ${phase.toUpperCase()}:`);
|
||||
console.log(` Nodes: ${metrics.nodeCount.start.toLocaleString()} → ${metrics.nodeCount.end.toLocaleString()}`);
|
||||
console.log(` Energy: ${metrics.energy.netEnergy.toFixed(2)} rUv (${metrics.energy.sustainability.toFixed(2)}x sustainable)`);
|
||||
console.log(` Tasks: ${metrics.network.tasksCompleted.toLocaleString()} completed`);
|
||||
console.log(` Success Rate: ${(metrics.network.avgSuccessRate * 100).toFixed(1)}%`);
|
||||
});
|
||||
|
||||
console.log('\n🏆 TOP PERFORMERS:');
|
||||
report.finalState.topPerformers.slice(0, 5).forEach((node, i) => {
|
||||
console.log(` ${i + 1}. ${node.id} (${node.type})`);
|
||||
console.log(` Net Energy: ${node.netEnergy.toFixed(2)} rUv | Tasks: ${node.tasksCompleted} | Success: ${node.successRate}`);
|
||||
});
|
||||
|
||||
if (report.validation.criticalIssues.length > 0) {
|
||||
console.log('\n🚨 CRITICAL ISSUES:');
|
||||
report.validation.criticalIssues.forEach(issue => {
|
||||
console.log(` ❌ ${issue}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (report.validation.warnings.length > 0) {
|
||||
console.log('\n⚠️ WARNINGS:');
|
||||
report.validation.warnings.slice(0, 5).forEach(warning => {
|
||||
console.log(` ⚠️ ${warning}`);
|
||||
});
|
||||
if (report.validation.warnings.length > 5) {
|
||||
console.log(` ... and ${report.validation.warnings.length - 5} more warnings`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ SUCCESSES:');
|
||||
report.validation.successes.slice(0, 10).forEach(success => {
|
||||
console.log(` ${success}`);
|
||||
});
|
||||
|
||||
console.log('\n╚════════════════════════════════════════════════════════════╝\n');
|
||||
}
|
||||
}
|
||||
163
vendor/ruvector/examples/edge-net/sim/src/simulator.ts
vendored
Normal file
163
vendor/ruvector/examples/edge-net/sim/src/simulator.ts
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Main Simulation Engine
|
||||
* Orchestrates the complete edge-net lifecycle simulation
|
||||
*/
|
||||
|
||||
import { Network, NetworkPhase } from './network.js';
|
||||
import { MetricsCollector } from './metrics.js';
|
||||
import { PhaseManager } from './phases.js';
|
||||
import { ReportGenerator } from './report.js';
|
||||
|
||||
interface SimulationConfig {
|
||||
verbose: boolean;
|
||||
fast: boolean;
|
||||
outputFile: string;
|
||||
}
|
||||
|
||||
class EdgeNetSimulator {
|
||||
private network: Network;
|
||||
private metrics: MetricsCollector;
|
||||
private phaseManager: PhaseManager;
|
||||
private reportGenerator: ReportGenerator;
|
||||
private config: SimulationConfig;
|
||||
private progressInterval: number;
|
||||
|
||||
constructor(config: SimulationConfig) {
|
||||
this.config = config;
|
||||
this.progressInterval = config.fast ? 1000 : 100;
|
||||
|
||||
// Initialize components
|
||||
this.network = new Network({
|
||||
genesisNodeCount: 100,
|
||||
targetNodeCount: 120000,
|
||||
nodesPerTick: config.fast ? 100 : 10, // Faster node spawning in fast mode
|
||||
taskGenerationRate: 5,
|
||||
baseTaskReward: 1.0,
|
||||
connectionCost: 0.5,
|
||||
maxConnectionsPerNode: 50,
|
||||
});
|
||||
|
||||
this.metrics = new MetricsCollector(this.network);
|
||||
this.phaseManager = new PhaseManager(this.network, this.metrics);
|
||||
this.reportGenerator = new ReportGenerator(this.network, this.metrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the complete simulation
|
||||
*/
|
||||
public async run(): Promise<void> {
|
||||
console.log('╔════════════════════════════════════════════════════════════╗');
|
||||
console.log('║ EDGE-NET LIFECYCLE SIMULATION - Starting... ║');
|
||||
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
console.log('⚙️ Configuration:');
|
||||
console.log(` Genesis Nodes: ${this.network.config.genesisNodeCount}`);
|
||||
console.log(` Target Nodes: ${this.network.config.targetNodeCount.toLocaleString()}`);
|
||||
console.log(` Nodes/Tick: ${this.network.config.nodesPerTick}`);
|
||||
console.log(` Mode: ${this.config.fast ? 'FAST' : 'NORMAL'}`);
|
||||
console.log('');
|
||||
|
||||
// Initialize network with genesis nodes
|
||||
this.network.initialize();
|
||||
this.metrics.initialize();
|
||||
|
||||
console.log('🌱 Genesis nodes deployed. Starting simulation...\n');
|
||||
|
||||
let lastProgressUpdate = 0;
|
||||
const startTime = Date.now();
|
||||
|
||||
// Main simulation loop
|
||||
while (this.network.currentPhase !== NetworkPhase.INDEPENDENCE ||
|
||||
this.network.cells.size < this.network.config.targetNodeCount) {
|
||||
|
||||
// Simulate one tick
|
||||
this.network.tick();
|
||||
this.metrics.collect();
|
||||
this.phaseManager.checkTransition();
|
||||
|
||||
// Progress updates
|
||||
if (this.network.currentTick - lastProgressUpdate >= this.progressInterval) {
|
||||
this.printProgress();
|
||||
lastProgressUpdate = this.network.currentTick;
|
||||
}
|
||||
|
||||
// Safety check - don't run forever
|
||||
if (this.network.currentTick > 50000) {
|
||||
console.log('\n⚠️ Simulation timeout reached (50,000 ticks)');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const endTime = Date.now();
|
||||
const duration = (endTime - startTime) / 1000;
|
||||
|
||||
console.log('\n✨ Simulation complete!\n');
|
||||
console.log(` Total Ticks: ${this.network.currentTick.toLocaleString()}`);
|
||||
console.log(` Duration: ${duration.toFixed(2)}s`);
|
||||
console.log(` Final Nodes: ${this.network.cells.size.toLocaleString()}`);
|
||||
console.log(` Final Phase: ${this.network.currentPhase.toUpperCase()}\n`);
|
||||
|
||||
// Finalize metrics
|
||||
this.metrics.finalizeCurrent();
|
||||
|
||||
// Generate and save report
|
||||
this.reportGenerator.printSummary();
|
||||
this.reportGenerator.saveReport(this.config.outputFile);
|
||||
|
||||
// Exit with appropriate code
|
||||
const report = this.reportGenerator.generateReport();
|
||||
process.exit(report.summary.totalPassed ? 0 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print simulation progress
|
||||
*/
|
||||
private printProgress(): void {
|
||||
const stats = this.network.getStats();
|
||||
const progress = this.phaseManager.getPhaseProgress();
|
||||
const ticksToNext = this.phaseManager.getTicksToNextPhase();
|
||||
|
||||
if (this.config.verbose) {
|
||||
console.log(`[Tick ${this.network.currentTick}] ${this.network.currentPhase.toUpperCase()}`);
|
||||
console.log(` Nodes: ${stats.nodeCount.toLocaleString()} | Energy: ${stats.economy.totalEnergy.toFixed(2)} rUv`);
|
||||
console.log(` Tasks: ${stats.tasks.completed.toLocaleString()} | Success: ${(stats.network.avgSuccessRate * 100).toFixed(1)}%`);
|
||||
console.log(` Genesis: ${stats.genesisNodes.active} active, ${stats.genesisNodes.readOnly} read-only, ${stats.genesisNodes.retired} retired`);
|
||||
console.log(` Progress: ${(progress * 100).toFixed(1)}% | Next phase: ${ticksToNext >= 0 ? `~${ticksToNext} ticks` : 'N/A'}`);
|
||||
console.log('');
|
||||
} else {
|
||||
// Compact progress bar
|
||||
const barLength = 40;
|
||||
const filled = Math.floor(progress * barLength);
|
||||
const bar = '█'.repeat(filled) + '░'.repeat(barLength - filled);
|
||||
|
||||
process.stdout.write(
|
||||
`\r[${bar}] ${this.network.currentPhase.padEnd(12)} | ` +
|
||||
`${stats.nodeCount.toLocaleString().padStart(7)} nodes | ` +
|
||||
`${stats.tasks.completed.toLocaleString().padStart(8)} tasks | ` +
|
||||
`Genesis: ${stats.genesisNodes.retired}/${stats.genesisNodes.count} retired`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse command line arguments
|
||||
function parseArgs(): SimulationConfig {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
return {
|
||||
verbose: args.includes('--verbose') || args.includes('-v'),
|
||||
fast: args.includes('--fast') || args.includes('-f'),
|
||||
outputFile: args.find(arg => arg.startsWith('--output='))?.split('=')[1] ||
|
||||
'/workspaces/ruvector/examples/edge-net/sim/simulation-report.json',
|
||||
};
|
||||
}
|
||||
|
||||
// Run simulation
|
||||
const config = parseArgs();
|
||||
const simulator = new EdgeNetSimulator(config);
|
||||
|
||||
simulator.run().catch(error => {
|
||||
console.error('❌ Simulation failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user