TARDÍGRADOS

Ciencia en español -ʟᴀ ʀᴀᴢóɴ ᴇsᴛá ᴀʜí ғᴜᴇʀᴀ-

Posts Tagged ‘EventEmitter’

Cómo programar tu propio minero de bitcoins, y no morir en el intento

Posted by Albert Zotkin on March 23, 2021

Reinventar la rueda puede tener varios contratiempos o inconvenientes. El primero de ellos es que la rueda ya está inventada, así que sería aparentemente una pérdida de tiempo y otros recursos el tratar de inventar algo que ya existe. Sin embargo, podemos probar a reinventar cosas con el sólo propósito de aprender, es decir, para adquirir conocimiento técnico sobre un tema específico. El segundo contratiempo o inconveniente de reinventar la rueda es que podría resultar que tu rueda no fuera tan perfecta y redonda como las ya existentes en el mercado, con lo cual la funcionalidad de tu creación sería más bien una chapuza que algo útil. El tercer inconveniente es que, aunque consiguieras crear una rueda bastante redonda y util, podría quedar descolgada, ya que no habría un coche o carro donde instalarla para probarla y ver cómo trabaja. Es decir, te quedarías colgado de la brocha, porque te habrían quitado la escalera de pronto. Si, a pesar de todos los inconvenientes y gasto inútil de recursos de todo tipo, aún sigues empeñado en reinventar la rueda, entonces tu única opción es seguir adelante.

Vamos a ello. Lo primero que vamos a programar es un servidor que pondremos a la escucha en nuestro localhost por el puerto 3000, y con el que gestionaremos todo los relacionado con los mineros de Bitcoins: Altas, bajas, envío de tareas, comprobaciones varias, gestión de cobros y pagos. Nuestro servidor lo programaremos en Node.JS . Y este será nuestro script inicial: Lo llamaremos

server-pool.js

  1. const Net = require('net');
  2. const url = require('url');
  3. const bitcore = require('bitcore-lib');
  4. const Hash = bitcore.crypto.Hash;
  5. const BufferUtil = bitcore.util.buffer;
  6. const _ = require('./util');
  7. const prettify = require('../prettify.js')();
  8. const stdin = process.openStdin();
  9. const fs = require('fs');
  10. const users_file ='./user.json';
  11. const DT = require('./datatypes');
  12. const $ = require('./util');
  13. const get = require('./manageGetRequest');
  14. const MBP = require('./make-block-project');
  15. var t_url = url.parse('http://127.0.0.1:3000/');
  16. var port = t_url.port;
  17.  
  18. /*
  19. const options = {
  20.   key: fs.readFileSync('./openssl/key.pem'),
  21.   cert: fs.readFileSync('./openssl/cert.pem')
  22. };
  23. */
  24.  
  25. const server = new Net.Server();
  26. var chats = [];
  27. var current_chat = null;
  28. var current_block_project = null;
  29. var ids = 0;
  30. var users = JSON.parse(fs.readFileSync(users_file, function(err){
  31. if(err) throw(err);
  32. }));
  33.  
  34. const MAX_HISTORY_SIZE = 40;
  35. const QUORUM_PERCENT = 74; // //percentage of minimum number of miners to proceed mining
  36. var jobready_count = 0;
  37.  
  38.  
  39.  
  40. server.listen(port, function() {
  41. console.log('\x1b[36m\x1b[2m[Server listening for connection requests on socket\x1b[0m:');
  42. //console.log(users);
  43.  
  44. });
  45. stdin.addListener("data", function(d) {
  46.  
  47. /*
  48.   var sentence = d.toString().trim();
  49.   talkTo(sentence);
  50.   sanitizeChats();
  51.   */
  52.  
  53. var sentence = d.toString().trim();
  54. var line = sentence.split('>');
  55. var header = line[0];
  56. var body =line[1];
  57. switch (header){
  58. case 'stop':
  59. broadcast2All(DT.StopMessage());
  60. break;
  61. case 'new'://'new-block-project':
  62. MBP.startNewBlockProject(false); //true =>get actual data from blockchain.info
  63. break;
  64. case 'ping':
  65. if(current_chat)
  66. current_chat.socket.write(DT.PingMessage());
  67. else console.error({error:'no clients connected'});
  68. break;
  69. case 'broadcast':
  70. broadcast2All(DT.testMessage());
  71. break;
  72. case 'exit':
  73. console.log("\x1b[33m[Server is about to be closed]\x1b[0m");
  74. server.destroy();
  75. setTimeout(function(){ process.exit(0)}, 1000); // kill client
  76. break;
  77. case 'exit':
  78.  
  79. break;
  80. default:
  81.  
  82. talkTo(sentence);
  83. sanitizeChats();
  84.  
  85. break;
  86. }
  87.  
  88. });
  89.  
  90. server.on('connection', function(socket) {
  91. console.log('\x1b[36mA new connection has been established\x1b[0m.');
  92. //current_chat = setChat(socket);
  93. //talkTo();
  94. socket.on('data', function(chunk) {
  95. current_chat = setChat(socket);
  96. var msg = DT.readMessage(chunk);
  97. if(msg)talkFromMessage(msg);
  98. else {
  99. var sentence = chunk.toString().split('\n');
  100. var w = chunk.toString().split(' ');
  101. var word = w[0];
  102. switch(word){
  103. case 'GET':
  104. /*
  105.   current_chat.socket.write([
  106. 'HTTP/1.1 200 OK',
  107. 'Content-Type: text/html; charset=UTF-8',
  108. 'Content-Encoding: UTF-8',
  109. 'Accept-Ranges: bytes',
  110. 'Connection: keep-alive',
  111. ].join('\n') + '\n\n');
  112. current_chat.socket.write('<h1> Example </h1> <img src ="../uploads/building.jpg"> ');
  113. // console.log(sentence[0].split(' ')[1]);
  114. //current_chat.socket.end();
  115. */
  116. get.ManageGetRequest(chunk,socket)
  117. console.log(chunk.toString());
  118. break;
  119. default:
  120. talkFrom(chunk.toString());
  121. break;
  122. }
  123.  
  124. }
  125. });
  126.  
  127. socket.on('end', function() {
  128. current_chat = setChat(socket);
  129. console.log('\x1b[36m\x1b[2mClosing connection with the client %s\x1b[0m.',current_chat.user);
  130. removeChat(socket);
  131. current_chat = null;
  132. });
  133.  
  134. socket.on('error', function(err) {
  135. current_chat = setChat(socket);
  136. console.log(err);
  137. });
  138. });
  139.  
  140. function talkTo(message){
  141. if(current_chat){
  142. var msg = message;
  143. if(current_chat.user === 'anonymous' ){
  144. msg = '\x1b[32mHello, \x1b[1mUSER:PWD\x1b[0m\x1b[32m, please\x1b[0m'
  145. }
  146. if(msg){
  147. current_chat.socket.write(msg);
  148. current_chat.history.push(msg);
  149. }
  150. }
  151. }
  152.  
  153.  
  154. function talkFrom(message){
  155.  
  156. if(current_chat){
  157.  
  158. if(current_chat.user === 'anonymous'){
  159. var msg = message.split(':');
  160. if(msg.length == 2) checkUserPwd(msg,current_chat);
  161. talkTo();
  162. }
  163.  
  164. console.log('%s: \x1b[33m%s\x1b[0m',current_chat.user, message);
  165. current_chat.history.push(message);
  166. }
  167. }
  168.  
  169. function talkFromMessage(m){
  170.  
  171. if(current_chat){
  172.  
  173. if(current_chat.user === 'anonymous'){
  174. var msg = m.payload.toString('utf8').split(':');
  175. if(msg.length == 2) checkUserPwd(msg,current_chat);
  176. talkTo();
  177. }
  178. onCommand(m);
  179. var obj = {};obj[current_chat.user]= m;
  180. console.log(obj);
  181. current_chat.history.push(JSON.stringify(m));
  182. }
  183. }
  184. /*
  185. function checkUserPwd(msg, chat){
  186.  
  187.   for(var e in users){
  188.   if(msg[0] === e && msg[1] === users[e].pwd ){
  189.   for(var f in chats){
  190.   if(chats[f].socket == chat.socket){
  191.   chats[f].user = msg[0];
  192.   chat.socket.write('Thanks ' + chats[f].user);
  193.   return true;
  194.   }
  195.   }
  196.   }
  197.   }
  198. return false;
  199. }
  200. */
  201. function checkUserPwd(msg, chat){
  202.  
  203. for(var e in users){
  204. if(msg[0] === e && msg[1] === users[e].pwd){
  205. for(var f in chats){
  206. if(chats[f].user == msg[0]) return false;
  207. if(chats[f].socket == chat.socket){
  208. chats[f].user = msg[0];
  209. chat.socket.write('Welcome ' + chats[f].user);
  210. return true;
  211. }
  212. }
  213. }
  214. }
  215. return false;
  216. }
  217.  
  218. function sanitizeChats(){
  219.  
  220. for(var e in chats){
  221. if(chats[e].history.length > MAX_HISTORY_SIZE)
  222. chats[e].history.shift();
  223. }
  224. }
  225.  
  226. function setChat(socket){
  227.  
  228. var chat = {user:'anonymous',id :-1,socket:socket,history:[]};
  229.  
  230. for(var e in chats){
  231. if(chat.socket == chats[e].socket)
  232. return chats[e];
  233. }
  234. chat.id =ids++;
  235. chats.push(chat);
  236. return chat;
  237. }
  238. function resumeChat(user, socket){
  239.  
  240. var chat = {user:user, id:-1, socket:socket, history:[]};
  241. for(var e in chats){
  242. if(user == chats[e].user){
  243. chats[e].socket = socket;
  244. return chats[e];
  245. }
  246. }
  247. chat.id =ids++;
  248. chats.push(chat);
  249. return chat;
  250. }
  251. function removeChat(socket){
  252.  
  253. for(var e in chats){
  254. if(chats[e].socket == socket){
  255. chats.splice(e, 1);
  256. }
  257. }
  258.  
  259. }
  260.  
  261. function broadcast2All(message){
  262. if(chats)for(var e in chats){
  263. chats[e].socket.write(message);
  264. }
  265. }
  266.  
  267. function sendNoncePartition2All(){
  268. var size = chats.length;
  269. var a = getNoncePartition();
  270. var buf = new Buffer.alloc(8);
  271. if(chats)for(var e=0;e<size;e++){
  272. buf.writeUInt32BE(a[e],0); buf.writeUInt32BE(a[e+1],4);
  273. var nonce_p ={
  274. command: 'nonce_p',
  275. networkMagic: new Buffer.from('FF00FF00', 'hex'),
  276. payload: buf
  277. };
  278. var msg = DT.makeMessage(nonce_p);
  279.  
  280.  
  281. /*
  282. var msg2 = DT.readMessage(msg);
  283. var p = [
  284. msg2.payload.readUInt32BE(0),
  285. msg2.payload.readUInt32BE(4)
  286. ];
  287. console.log(msg2);
  288. */
  289. chats[e].socket.write(msg);
  290.  
  291. }
  292.  
  293. }
  294.  
  295. function getNoncePartition(){
  296.  
  297. var size = chats.length;
  298. const nonce = 0xffffffff;
  299. var p = Math.floor(parseFloat(nonce)/size);
  300. var b = [];
  301. for(var i = 0;i<size;i++){
  302. b.push(i*p);
  303. }
  304. b.push(nonce);
  305. return b;
  306.  
  307. //return_.SplitInterval([0,45676543],6));
  308. }
  309.  
  310.  
  311. function onCommand(msg){
  312. var command = msg.command;
  313. switch(command){
  314. case 'ping':
  315. current_chat.socket.write(DT.PongMessage(msg));
  316. break;
  317.  
  318. case 'joback':
  319. jobready_count++;
  320. if(jobready_count >= chats.length){
  321. jobready_count=0;
  322. sendNoncePartition2All();
  323. }
  324. break;
  325.  
  326. case 'block_solved':
  327. try{
  328. var file = 'solved-'+ Date.now()+'.hex';
  329. fs.writeFileSync(file,msg.payload.toString('hex'),'utf8');
  330. broadcast2All(DT.StopMessage());
  331. }catch(e){
  332. console.error(e);
  333. }
  334. break;
  335. /*
  336. case:
  337. break;
  338. case:
  339. break;
  340. case:
  341. break;
  342. */
  343.  
  344. }
  345. }
  346.  
  347.  
  348. MBP.emitter.on('new-block-project-ready', () => {
  349.  
  350. console.log('\x1b[33mnew-\x1b[1mblock-project\x1b[0m\x1b[33m-ready:\x1b[0m');
  351. current_block_project = new bitcore.Block.fromBuffer(MBP.get_block_project());
  352. console.log(current_block_project.header.toObject());
  353. broadcast2All(DT.JobMessage(current_block_project.header.toBuffer()));
  354.  
  355.  
  356.  
  357. });
  358.  
  359.  
Los módulos requeridos net, bitcore-lib y url, los instalaremos mediante npm . Los restantes módulos requeridos son scripts que deberemos escribir nosotros, y son los siguientes:

util.js

  1. 'use strict';
  2. const bitcore = require('bitcore-lib');
  3. const Random = bitcore.crypto.Random;
  4. var $ = bitcore.util.preconditions;
  5. const BufferReader = bitcore.encoding.BufferReader;
  6. const BufferWriter = bitcore.encoding.BufferWriter;
  7. const OUTPUT = 1;
  8. const INPUT = 0;
  9. /*
  10. Can parse a JSON object even if there are comment tags between its items
  11. */
  12. var parseW = function(str){
  13. return JSON.parse(str.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1'));
  14. }
  15.  
  16.  
  17. function variantToBufferFromNum(n){
  18. return new BufferWriter(new Buffer.alloc(0))
  19. .writeVarintNum(n)
  20. .toBuffer();
  21. }
  22.  
  23. function SplitInterval(partition,n){
  24.  
  25. if(typeof partition !== 'object') return null;
  26. if(typeof partition[0] !== 'number' ||
  27. typeof partition[1] !== 'number') return null;
  28. if( partition[1] - partition[0] <= 0) return null;
  29. var d = Math.floor((partition[1]-partition[0])/n);
  30.  
  31. var b = [];
  32.  
  33. for(var j = 0;j<n;j++){
  34. var c = [];
  35. c.push(partition[0] + d*j);
  36. c.push(partition[0] + d*(j+1)-1);
  37. b.push(c);
  38. }
  39.  
  40. b[n-1][1] = partition[1];
  41. return b;
  42. }
  43.  
  44. function format_date(timestamp){
  45. var date = new Date(timestamp * 1000);
  46. var day = date.getDate();
  47. //var month = months_arr[date.getMonth()];
  48. var month = date.getMonth()+1;
  49. var year = date.getFullYear();
  50.  
  51. var hours = date.getHours();
  52. var minutes = "0" + date.getMinutes();
  53. var seconds = "0" + date.getSeconds();
  54. // Will display time in 10:30:23 format
  55. var formattedTime = month + '/' + day + '/' +year +
  56. ' ' + hours + ':' + minutes.substr(-2) + ':' + seconds.substr(-2);
  57. return formattedTime;
  58. }
  59.  
  60. /*
  61. split buffer buf into an array of subbuffers,
  62. separator must be a string of bytes in hexadecimal mode.
  63. */
  64. function Split(buf, separator){
  65. var occurr =[];
  66. var sp =[];
  67. var b = new Buffer.from(buf);
  68. var pos = 0;
  69. var i = 0;
  70. const magic = new Buffer.from(separator, 'hex');
  71. while(1){
  72. var index = b.indexOf(magic);
  73. if(index > -1){
  74. pos += index;
  75. occurr.push(pos+magic.length*i);i++;
  76. b = b.slice(index+magic.length);
  77. }else break;
  78. }
  79. if(occurr.length){
  80. if(occurr[0]>0)
  81. sp.push(buf.slice(0,occurr[0]));
  82. for(var i = 0;i<occurr.length;i++)
  83. sp.push(buf.slice(occurr[i],occurr[i+1]));
  84. /*if(occurr[occurr.length-1]< buf.length-1)
  85. sp.push(buf.slice(occurr[occurr.length-1]));*/
  86. } else sp.push(buf);
  87.  
  88. return sp;
  89. }
  90.  
  91. Buffer['split'] = Split; //let Global Object, Buffer, manages this.
  92. JSON['parseW'] = parseW; //let Global Object, JSON, manages this.
  93.  
  94.  
  95. function WM_Encrypt(buffer, mode){
  96. if(buffer.length){
  97. var puntero = 0;
  98. var size = buffer.length;
  99. while(puntero < size/2)
  100. {
  101. Traspuesta(puntero,size-puntero-1,mode,buffer);
  102. puntero++;
  103. }
  104. }
  105.  
  106. return buffer;
  107. }
  108. function Traspuesta(i, j, modo, buffer){
  109.  
  110. var nparam1,nparam2;
  111. nparam1 = buffer.readUInt8(i);
  112. if(modo)nparam1 = nparam1 - 10;
  113. else nparam1 = nparam1 + 10;
  114. nparam1 = nparam1<0?256+nparam1:nparam1;
  115. nparam1 = nparam1>0xff?nparam1-256:nparam1;
  116. nparam2 = buffer.readUInt8(j);//flujofichero.charCodeAt(j);
  117. if(modo)nparam2 = nparam2 - 10;
  118. else nparam2 = nparam2 + 10;
  119.  
  120. nparam2 = nparam2<0?256+nparam2:nparam2;
  121. nparam2 = nparam2>0xff?nparam2-256:nparam2;
  122. buffer.writeUInt8(nparam2, i);
  123. buffer.writeUInt8(nparam1, j);
  124. }
  125.  
  126. function wm_encode(buffer){
  127. return WM_Encrypt(buffer, OUTPUT);
  128. }
  129. function wm_decode(buffer){
  130. return WM_Encrypt(buffer, INPUT);
  131. }
  132.  
  133. function stochash256(buffer){
  134. if(buffer.length !== 256) return null;
  135. var pt =0;
  136. var hash = []
  137. while(1){
  138. var byte = buffer.readUInt8(pt);
  139. var repunit = getRepunit(byte);
  140. var rnd = Random.getRandomBuffer(1).readUInt8(0);
  141. hash.push(repunit[rnd]);
  142. pt++;
  143. if(pt == buffer.length)break;
  144. }
  145. var result = new bitcore.crypto.BN.fromString(hash.join(''),2);
  146. return result.toBuffer();
  147. }
  148. function getRepunit(byte){
  149.  
  150. const two = new bitcore.crypto.BN(2);
  151. const one = new bitcore.crypto.BN(1);
  152. var b = new bitcore.crypto.BN(byte);
  153. var repunit = two.pow(b);
  154. repunit = repunit.sub(one);
  155. return repunit.toString(2,256);
  156.  
  157.  
  158. }
  159.  
  160. module.exports =
  161. {
  162. SplitInterval:SplitInterval,
  163. variantToBufferFromNum:variantToBufferFromNum,
  164. format_date:format_date,
  165. WM_Encrypt:WM_Encrypt,
  166. wm_encode:wm_encode,
  167. wm_decode:wm_decode,
  168. stochash256:stochash256,
  169. getRepunit:getRepunit
  170. };
  171.  
¿Aún no has perdido la ilusión por reinventar la rueda?. Ok, sigamos pues escribiendo los scripts para nuestros módulos requeridos

Posted in Bitcoin, criptografía, Criptomonedas, curiosidades y analogías, informática, Satoshi | Tagged: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a Comment »