| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| const { Blockchain, Network, Storage } = require('decentralized-internet');
|
| const Web3 = require('web3');
|
| const EventEmitter = require('events');
|
| const fs = require('fs').promises;
|
| const path = require('path');
|
|
|
| |
| |
| |
|
|
| class DecentralizedCoordinator extends EventEmitter {
|
| constructor(config = {}) {
|
| super();
|
|
|
| this.config = {
|
| blockchainProvider: config.blockchainProvider || 'http://localhost:8545',
|
| networkPort: config.networkPort || 8080,
|
| nodeId: config.nodeId || this.generateNodeId(),
|
| storageDir: config.storageDir || './storage',
|
| ...config
|
| };
|
|
|
| this.blockchain = null;
|
| this.network = null;
|
| this.web3 = null;
|
| this.taskRegistry = new Map();
|
| this.nodeRegistry = new Map();
|
| this.isInitialized = false;
|
| }
|
|
|
| |
| |
| |
|
|
| async initialize() {
|
| try {
|
| console.log('Initializing Decentralized Coordinator (Localhost)...');
|
| console.log(`Node ID: ${this.config.nodeId}`);
|
|
|
|
|
| await fs.mkdir(this.config.storageDir, { recursive: true });
|
|
|
|
|
| this.web3 = new Web3(this.config.blockchainProvider);
|
|
|
|
|
| this.blockchain = new Blockchain({
|
| nodeId: this.config.nodeId,
|
| difficulty: 2
|
| });
|
|
|
| this.network = new Network({
|
| port: this.config.networkPort,
|
| nodeId: this.config.nodeId
|
| });
|
|
|
|
|
| this.setupEventListeners();
|
|
|
|
|
| await this.network.start();
|
|
|
| this.isInitialized = true;
|
| console.log('Decentralized Coordinator initialized successfully');
|
|
|
| this.emit('initialized', { nodeId: this.config.nodeId });
|
|
|
| return true;
|
| } catch (error) {
|
| console.error('Failed to initialize Decentralized Coordinator:', error);
|
| return false;
|
| }
|
| }
|
|
|
| |
| |
| |
| |
|
|
| async registerTask(task) {
|
| if (!this.isInitialized) {
|
| throw new Error('Coordinator not initialized');
|
| }
|
|
|
| try {
|
| const taskId = this.generateTaskId();
|
|
|
|
|
| const taskData = {
|
| id: taskId,
|
| ligandFile: task.ligandFile,
|
| receptorFile: task.receptorFile,
|
| parameters: task.parameters,
|
| status: 'pending',
|
| submittedBy: this.config.nodeId,
|
| timestamp: Date.now(),
|
| requiredCompute: task.requiredCompute || 1,
|
| priority: task.priority || 'normal'
|
| };
|
|
|
|
|
| const ligandPath = await this.storeLocally(task.ligandContent, `ligand_${taskId}.pdbqt`);
|
| const receptorPath = await this.storeLocally(task.receptorContent, `receptor_${taskId}.pdbqt`);
|
|
|
| taskData.ligandPath = ligandPath;
|
| taskData.receptorPath = receptorPath;
|
|
|
|
|
| const block = {
|
| type: 'TASK_REGISTRATION',
|
| data: taskData,
|
| timestamp: Date.now(),
|
| nodeId: this.config.nodeId
|
| };
|
|
|
| this.blockchain.addBlock(block);
|
|
|
|
|
| this.taskRegistry.set(taskId, taskData);
|
|
|
|
|
| await this.network.broadcast({
|
| type: 'NEW_TASK',
|
| taskId: taskId,
|
| task: taskData
|
| });
|
|
|
| console.log(`Task registered: ${taskId}`);
|
| this.emit('taskRegistered', taskData);
|
|
|
| return taskId;
|
| } catch (error) {
|
| console.error('Failed to register task:', error);
|
| throw error;
|
| }
|
| }
|
|
|
| |
| |
| |
| |
|
|
| async claimTask(taskId) {
|
| if (!this.isInitialized) {
|
| throw new Error('Coordinator not initialized');
|
| }
|
|
|
| try {
|
| const task = this.taskRegistry.get(taskId);
|
|
|
| if (!task) {
|
| throw new Error(`Task not found: ${taskId}`);
|
| }
|
|
|
| if (task.status !== 'pending') {
|
| throw new Error(`Task already claimed: ${taskId}`);
|
| }
|
|
|
|
|
| task.status = 'processing';
|
| task.claimedBy = this.config.nodeId;
|
| task.claimedAt = Date.now();
|
|
|
|
|
| const block = {
|
| type: 'TASK_CLAIM',
|
| data: {
|
| taskId: taskId,
|
| nodeId: this.config.nodeId,
|
| timestamp: Date.now()
|
| }
|
| };
|
|
|
| this.blockchain.addBlock(block);
|
|
|
|
|
| await this.network.broadcast({
|
| type: 'TASK_CLAIMED',
|
| taskId: taskId,
|
| nodeId: this.config.nodeId
|
| });
|
|
|
| console.log(`Task claimed: ${taskId}`);
|
| this.emit('taskClaimed', task);
|
|
|
|
|
| const ligandContent = await this.retrieveLocally(task.ligandPath);
|
| const receptorContent = await this.retrieveLocally(task.receptorPath);
|
|
|
| return {
|
| ...task,
|
| ligandContent,
|
| receptorContent
|
| };
|
| } catch (error) {
|
| console.error('Failed to claim task:', error);
|
| throw error;
|
| }
|
| }
|
|
|
| |
| |
| |
| |
| |
|
|
| async submitResults(taskId, results) {
|
| if (!this.isInitialized) {
|
| throw new Error('Coordinator not initialized');
|
| }
|
|
|
| try {
|
| const task = this.taskRegistry.get(taskId);
|
|
|
| if (!task) {
|
| throw new Error(`Task not found: ${taskId}`);
|
| }
|
|
|
|
|
| const resultsPath = await this.storeLocally(JSON.stringify(results), `results_${taskId}.json`);
|
|
|
|
|
| task.status = 'completed';
|
| task.resultsPath = resultsPath;
|
| task.completedAt = Date.now();
|
| task.computationTime = results.computationTime;
|
|
|
|
|
| const block = {
|
| type: 'TASK_COMPLETION',
|
| data: {
|
| taskId: taskId,
|
| nodeId: this.config.nodeId,
|
| resultsPath: resultsPath,
|
| timestamp: Date.now()
|
| }
|
| };
|
|
|
| this.blockchain.addBlock(block);
|
|
|
|
|
| await this.network.broadcast({
|
| type: 'TASK_COMPLETED',
|
| taskId: taskId,
|
| resultsPath: resultsPath,
|
| nodeId: this.config.nodeId
|
| });
|
|
|
| console.log(`Results submitted for task: ${taskId}`);
|
| this.emit('resultsSubmitted', { taskId, resultsPath });
|
|
|
| return true;
|
| } catch (error) {
|
| console.error('Failed to submit results:', error);
|
| throw error;
|
| }
|
| }
|
|
|
| |
| |
| |
| |
|
|
| async getResults(taskId) {
|
| try {
|
| const task = this.taskRegistry.get(taskId);
|
|
|
| if (!task) {
|
| throw new Error(`Task not found: ${taskId}`);
|
| }
|
|
|
| if (task.status !== 'completed') {
|
| throw new Error(`Task not completed: ${taskId}`);
|
| }
|
|
|
|
|
| const resultsContent = await this.retrieveLocally(task.resultsPath);
|
| const results = JSON.parse(resultsContent);
|
|
|
| return {
|
| taskId: taskId,
|
| results: results,
|
| completedAt: task.completedAt,
|
| processedBy: task.claimedBy
|
| };
|
| } catch (error) {
|
| console.error('Failed to retrieve results:', error);
|
| throw error;
|
| }
|
| }
|
|
|
| |
| |
| |
|
|
| getBlockchainStatus() {
|
| if (!this.blockchain) {
|
| return null;
|
| }
|
|
|
| return {
|
| chainLength: this.blockchain.chain.length,
|
| difficulty: this.blockchain.difficulty,
|
| isValid: this.blockchain.isChainValid(),
|
| lastBlock: this.blockchain.getLatestBlock()
|
| };
|
| }
|
|
|
| |
| |
| |
|
|
| getNetworkStatus() {
|
| return {
|
| nodeId: this.config.nodeId,
|
| isInitialized: this.isInitialized,
|
| connectedPeers: this.nodeRegistry.size,
|
| pendingTasks: Array.from(this.taskRegistry.values()).filter(t => t.status === 'pending').length,
|
| processingTasks: Array.from(this.taskRegistry.values()).filter(t => t.status === 'processing').length,
|
| completedTasks: Array.from(this.taskRegistry.values()).filter(t => t.status === 'completed').length
|
| };
|
| }
|
|
|
| |
| |
| |
|
|
| async storeLocally(content, filename) {
|
| try {
|
| const filePath = path.join(this.config.storageDir, filename);
|
| await fs.writeFile(filePath, content);
|
| return filePath;
|
| } catch (error) {
|
| console.error('Failed to store locally:', error);
|
| throw error;
|
| }
|
| }
|
|
|
| |
| |
| |
|
|
| async retrieveLocally(filePath) {
|
| try {
|
| const content = await fs.readFile(filePath, 'utf8');
|
| return content;
|
| } catch (error) {
|
| console.error('Failed to retrieve locally:', error);
|
| throw error;
|
| }
|
| }
|
|
|
| |
| |
| |
|
|
| setupEventListeners() {
|
|
|
| this.network.on('message', (message) => {
|
| this.handleNetworkMessage(message);
|
| });
|
|
|
|
|
| this.network.on('peer:connected', (peerId) => {
|
| console.log(`Peer connected: ${peerId}`);
|
| this.nodeRegistry.set(peerId, { id: peerId, connectedAt: Date.now() });
|
| this.emit('peerConnected', peerId);
|
| });
|
|
|
| this.network.on('peer:disconnected', (peerId) => {
|
| console.log(`Peer disconnected: ${peerId}`);
|
| this.nodeRegistry.delete(peerId);
|
| this.emit('peerDisconnected', peerId);
|
| });
|
| }
|
|
|
| |
| |
| |
|
|
| handleNetworkMessage(message) {
|
| switch (message.type) {
|
| case 'NEW_TASK':
|
| if (!this.taskRegistry.has(message.taskId)) {
|
| this.taskRegistry.set(message.taskId, message.task);
|
| this.emit('newTask', message.task);
|
| }
|
| break;
|
|
|
| case 'TASK_CLAIMED':
|
| const task = this.taskRegistry.get(message.taskId);
|
| if (task) {
|
| task.status = 'processing';
|
| task.claimedBy = message.nodeId;
|
| }
|
| break;
|
|
|
| case 'TASK_COMPLETED':
|
| const completedTask = this.taskRegistry.get(message.taskId);
|
| if (completedTask) {
|
| completedTask.status = 'completed';
|
| completedTask.resultsPath = message.resultsPath;
|
| this.emit('taskCompleted', { taskId: message.taskId, resultsPath: message.resultsPath });
|
| }
|
| break;
|
| }
|
| }
|
|
|
| |
| |
| |
|
|
| generateNodeId() {
|
| return `NODE_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
| }
|
|
|
| |
| |
| |
|
|
| generateTaskId() {
|
| return `TASK_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
| }
|
|
|
| |
| |
|
|
| async shutdown() {
|
| if (this.network) {
|
| await this.network.stop();
|
| }
|
| this.isInitialized = false;
|
| console.log('Decentralized Coordinator shut down');
|
| }
|
| }
|
|
|
| module.exports = DecentralizedCoordinator;
|
|
|