#!/usr/bin/env node /** * Test Client for RIN Stratum Proxy * This simulates a miner connecting to the stratum server */ const net = require('net'); const readline = require('readline'); class StratumTestClient { constructor(host = '127.0.0.1', port = 3333) { this.host = host; this.port = port; this.socket = null; this.messageId = 1; this.subscribed = false; this.authorized = false; this.currentJob = null; console.log('๐Ÿงช RIN Stratum Test Client'); console.log(`๐Ÿ”— Connecting to ${host}:${port}`); } connect() { return new Promise((resolve, reject) => { this.socket = new net.Socket(); this.socket.connect(this.port, this.host, () => { console.log('โœ… Connected to stratum server'); resolve(); }); this.socket.on('data', (data) => { this.handleMessage(data.toString()); }); this.socket.on('close', () => { console.log('๐Ÿ”Œ Connection closed'); }); this.socket.on('error', (error) => { console.error(`โŒ Connection error: ${error.message}`); reject(error); }); }); } handleMessage(message) { try { const lines = message.trim().split('\n'); for (const line of lines) { if (line) { const data = JSON.parse(line); console.log('๐Ÿ“จ Received:', JSON.stringify(data, null, 2)); if (data.method === 'mining.notify') { this.handleMiningNotify(data.params); } else if (data.method === 'mining.set_difficulty') { this.handleSetDifficulty(data.params); } } } } catch (error) { console.error(`โŒ Message parsing error: ${error.message}`); } } handleMiningNotify(params) { if (params.length >= 8) { this.currentJob = { jobId: params[0], prevhash: params[1], coinb1: params[2], coinb2: params[3], merkleBranch: params[4], version: params[5], bits: params[6], ntime: params[7], cleanJobs: params[8] }; console.log(`๐Ÿ†• New job received: ${this.currentJob.jobId}`); console.log(` Height: ${this.currentJob.version} | Bits: ${this.currentJob.bits}`); // Simulate mining by submitting a test share setTimeout(() => { this.submitTestShare(); }, 1000); } } handleSetDifficulty(params) { if (params.length > 0) { console.log(`๐ŸŽฏ Difficulty set to: ${params[0]}`); } } sendMessage(method, params = [], id = null) { const message = { id: id || this.messageId++, method: method, params: params }; const jsonMessage = JSON.stringify(message) + '\n'; console.log('๐Ÿ“ค Sending:', JSON.stringify(message, null, 2)); this.socket.write(jsonMessage); } async subscribe() { console.log('๐Ÿ“ Subscribing to stratum server...'); this.sendMessage('mining.subscribe', ['TestMiner/1.0']); this.subscribed = true; } async authorize(username = 'testworker', password = 'x') { console.log(`๐Ÿ” Authorizing as ${username}...`); this.sendMessage('mining.authorize', [username, password]); this.authorized = true; } submitTestShare() { if (!this.currentJob) { console.log('โŒ No current job to submit share for'); return; } console.log('๐Ÿ“Š Submitting test share...'); // Generate test values const extranonce2 = Math.floor(Math.random() * 0xffffffff).toString(16).padStart(8, '0'); const ntime = this.currentJob.ntime; const nonce = Math.floor(Math.random() * 0xffffffff).toString(16).padStart(8, '0'); this.sendMessage('mining.submit', [ 'testworker', this.currentJob.jobId, extranonce2, ntime, nonce ]); } async run() { try { await this.connect(); await this.subscribe(); // Wait a bit for subscription response await new Promise(resolve => setTimeout(resolve, 1000)); await this.authorize(); // Keep connection alive console.log('๐Ÿ”„ Test client running. Press Ctrl+C to exit.'); // Simulate periodic share submissions setInterval(() => { if (this.currentJob && this.authorized) { this.submitTestShare(); } }, 5000); } catch (error) { console.error(`โŒ Test client error: ${error.message}`); process.exit(1); } } } // Handle graceful shutdown process.on('SIGINT', () => { console.log('\n๐Ÿ›‘ Shutting down test client...'); process.exit(0); }); // Start test client const client = new StratumTestClient(); client.run().catch(error => { console.error(`โŒ Failed to start test client: ${error.message}`); process.exit(1); });