Skip to content

Instantly share code, notes, and snippets.

@Timonchegs
Created December 18, 2025 16:43
Show Gist options
  • Select an option

  • Save Timonchegs/a76d2eb2f65ad1a55211d4281b39eb14 to your computer and use it in GitHub Desktop.

Select an option

Save Timonchegs/a76d2eb2f65ad1a55211d4281b39eb14 to your computer and use it in GitHub Desktop.
last test
const net = require('net');
const https = require('https');
class AbelianStratumProxy {
constructor() {
this.currentJob = null;
// Конфигурация из логов пула
this.difficulty = 0.5;
this.target = '00000001ffff0000000000000000000000000000000000000000000000000000'; // Для diff 0.5
this.algo = 'abelhash';
this.epoch = '100'; // Будет обновляться из ноды
this.extranonce = '0';
this.extra_nonce_bits_num = '16';
}
async rpcRequest(method, params = ['']) {
return new Promise((resolve) => {
const postData = JSON.stringify({
jsonrpc: '1.0',
id: 'proxy',
method: method,
params: params
});
const auth = Buffer.from('tteFTlJ7YOfGDA2KBMHKqnDnXeE=:SOkvF8sxay8ViOxpgbraHmqJmSU=').toString('base64');
const options = {
hostname: '127.0.0.1',
port: 8667,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${auth}`,
'Content-Length': Buffer.byteLength(postData)
},
rejectUnauthorized: false
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
const parsed = JSON.parse(data);
resolve(parsed.result);
} catch (e) {
console.error('RPC Parse Error:', e.message);
resolve(null);
}
});
});
req.on('error', (error) => {
console.error('RPC Request Error:', error.message);
resolve(null);
});
req.write(postData);
req.end();
});
}
async getWork() {
console.log('[PROXY] Requesting work from node...');
const work = await this.rpcRequest('getwork', ['']);
if (work && work.jobid) {
this.currentJob = work;
// Обновляем параметры из ответа ноды
this.epoch = work.epoch?.toString() || '100';
this.extranonce = work.extranonce?.toString() || '0';
this.extra_nonce_bits_num = work.extranoncebitsnum?.toString() || '16';
console.log(`[PROXY] Work received: ${work.jobid.substring(0, 16)}...`);
return true;
}
console.log('[PROXY] Failed to get work');
return false;
}
handleMinerConnection(socket) {
const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
console.log(`[PROXY] New connection: ${clientId}`);
const send = (data) => {
if (socket.writable) {
const str = JSON.stringify(data);
console.log(`[PROXY -> MINER] ${str.substring(0, 100)}...`);
socket.write(str + '\n');
}
};
socket.on('data', async (data) => {
const str = data.toString().trim();
console.log(`[MINER -> PROXY] ${str}`);
try {
const msg = JSON.parse(str);
// 1. Ответ на mining.hello
if (msg.method === 'mining.hello') {
console.log(`[PROXY] Hello from: ${msg.params[0]}`);
send({
id: msg.id,
result: {
proto: "AbelianStratum",
encoding: "plain",
resume: "0",
timeout: "258",
maxerrors: "10",
node: "Abec-v2.0.2/Proxy-v1.0"
},
error: null
});
}
// 2. Ответ на mining.subscribe
else if (msg.method === 'mining.subscribe') {
console.log('[PROXY] Subscribe');
send({
id: msg.id,
result: [
["mining.set_difficulty", "mining.set"], // Обрати внимание: mining.set, а не notify
"08089999",
4
],
error: null
});
}
// 3. Ответ на mining.authorize
else if (msg.method === 'mining.authorize') {
console.log(`[PROXY] Authorize: ${msg.params[0]}`);
send({
id: msg.id,
result: {
session_id: `s-${Date.now()}`
},
error: null
});
// 3.1 Отправляем результат авторизации (как в логах пула)
setTimeout(() => {
send({
id: null,
result: {
worker: msg.params[0].split('.')[0], // Кошелек без воркера
registered: "0"
},
error: null
});
}, 50);
// 3.2 Устанавливаем сложность
setTimeout(() => {
send({
id: null,
method: "mining.set_difficulty",
params: [this.difficulty]
});
console.log(`[PROXY] Set difficulty: ${this.difficulty}`);
}, 100);
// 3.3 ОТПРАВЛЯЕМ РАБОТУ в формате mining.set (КАК НА ОФИЦИАЛЬНОМ ПУЛЕ)
setTimeout(async () => {
if (!this.currentJob) await this.getWork();
if (this.currentJob) {
console.log('[PROXY] Sending work (mining.set)...');
// Ключевой момент: формат как в логах официального пула
send({
method: "mining.set",
params: {
epoch: this.epoch,
target: this.target,
algo: this.algo,
extranonce: this.extranonce,
extra_nonce_bits_num: this.extra_nonce_bits_num
},
id: null
});
console.log(`[PROXY] Job sent. Epoch: ${this.epoch}, Target: ${this.target.substring(0, 16)}...`);
}
}, 150);
}
// 4. Ответ на mining.noop
else if (msg.method === 'mining.noop') {
console.log('[PROXY] Noop received');
send({
id: msg.id,
result: true,
error: null
});
}
} catch (e) {
console.error(`[PROXY] JSON Parse Error: ${e.message}`);
}
});
socket.on('close', () => {
console.log(`[PROXY] Connection closed: ${clientId}`);
});
}
async start(port = 3333) {
console.log('[PROXY] Starting Abelian Stratum Proxy...');
console.log(`[PROXY] Target difficulty: ${this.difficulty}`);
console.log(`[PROXY] Algorithm: ${this.algo}`);
await this.getWork();
const server = net.createServer((socket) => {
this.handleMinerConnection(socket);
});
server.listen(port, '0.0.0.0', () => {
console.log(`[PROXY] Listening on 0.0.0.0:${port}`);
console.log('[PROXY] Ready for connections.');
});
}
}
const proxy = new AbelianStratumProxy();
proxy.start().catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment