TARDÍGRADOS

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

Archive for the ‘Bitcoin’ Category

Criptomonedas: Bitcoin

Posted by Albert Zotkin on November 21, 2021

Posted in Bitcoin, criptografía, Criptomonedas | Tagged: , , , , , , , , , , , , , , , | Leave a Comment »

Cómo “robar” bitcoins con una modesta tarjeta gráfica NVIDIA

Posted by Albert Zotkin on July 15, 2021

Hola amigos de Tardígrados. El propósito principal del siguiente documento informático, no es enseñar a robar Bitcoins, eso estaría muy feo sino demostrar la robustez de la protección criptográfica de Bitcoin, y mostrar de paso, con un caso práctico, la casi inexpugnable muralla que ofrece esa criptografía contra posibles ataques, que los hay. ¿Es tan segura esa protección criptográfica?. Sí. Estamos hablando de cadenas de 256 bits. Esas contraseñas serán las semillas para generar las llaves privadas y las direcciones públicas de Bitcoin. O sea, estamos hablando de un número monstruosamente grande de combinaciones. Ese número entero es:

\displaystyle N =2^{2^{2^{3}}}=1157920892373161954235709850086879078532699846656405640394575840079131\ 29639936

que, como vemos, tiene 78 cifras decimales. Para intentar hacernos una idea de lo que ese número representa, supongamos que tenemos una lista de 135 direcciones de Bitcoins, que queremos atacar. Atacar, aquí quiere decir descubrir la clave privada desde la dirección publica. Entonces la probabilidad de dar con una de esas 135 claves sería de:

\displaystyle P =\frac{135}{ 2^{2^{2^{3}}} } = 1.1658827549377500244271575014780539421006600491989 \text{ x } 10^{-75}

Es decir, aun suponiendo que nuestra computadora fuera capaz de procesar 1 millón de direcciones por segundo, tendríamos por delante aún unos

\displaystyle t =\frac{ 2^{2^{2^{3}}} }{135 \times 1000000} = 8.5771917953567552165608137043472524335755544196771 \text{ x } 10^{68} \text{ segundos}

para encontrar la primera de esas 135 direcciones de la lista que queremos atacar. Creo que ni Matusalén conseguiría vivir tanto para ver como se consigue ese primer hallazgo 🙂

Mi tarjeta gráfica es una NVIDIA GeForce GT 430. Escribiremos un pequeño programa para Node.JS , en el que aceleraremos la computación mediante tarjeta gráfica, es decir, usaremos la GPU de la tarjeta gráfica, en lugar de la CPU de la computadora. Para la computación con tarjeta gráfica usaremos el módulo gpu.js, el cual posee un javascript de aceleración muy interesante. Para trabajar con bitcoins, yo uso el módulo bitcore-lib, donde además de las herramientas para bitcoin también trae otras interesantes utilidades. Empecemos abriendo un editor de texto cualquiera, y escribamos las primeras instrucciones de nuestro programilla, que como sabemos deberá ser escrito en lenguaje javascript, ya que es para Node.JS .

  1. const {GPU} = require(‘./gpu.js’);
  2. const bitcore = require(‘bitcore-lib’);
  3. const crypto = require(‘crypto’);
  4. const fs = require(‘fs’);
  5. const gpu = new GPU();
  6. var argv = require(‘yargs’)
  7. .usage(‘Usage: $0 –range [num] –samples [num]’)
  8. /*.demand([‘range’,’samples’])*/
  9. .argv;
  10. const jsonutil = require(‘jsonutil’);
  11. const JSONStream = require(‘JSONStream’);
Los módulos gpu.js, yargs, jsonutil y bitcore-lib, los puedes decargar desde npm. Los módulos crypto y fs, no, porque ya vienen incluidos en el propio Node.js.
Nuestro programilla, que trabajará desde una consola DOS de windows mediante comandos, tiene el módulo yargs, con el que definimos dos argumentos para pasar en la linea de comandos al ser ejecutado. Nuestro primer argumento será –range [num], y en él especificaremos el número de coincidencias que deseamos (obviamente, desearíamos un número de coincidencias pleno, pero como esta es sólo una herramienta demostrativa, tenemos que dar juego a las coincidencias. Las direcciones públicas de bitcoin poseen 34 caracteres, porque están codificadas en Base58, por lo tanto el juego podrá ir desde 1 coincidencias hasta el pleno de 34. ¿Por qué 34?. Porque son números hexadecimales de 50 dígitos, que están codificados en Base58. Después usaremos en nuestra linea de comando el argumento –samples [num], que será el número de direcciones que queremos en nuestra lista de resultados, Obviamente si nuestra lista de direcciones objetivo es de 135, el número num que sigue a –samples no podrá ser nunca mayor a esos 135.
Sigamos escribiendo nuestro código en nuestro editor de textos favorito:

  1. const range = argv.range?parseInt(argv.range):4;
  2. var max_limit = argv.samples?parseInt(argv.samples):10;
  3. const file = “keys-“+range+“.json”;
  4. var results_file = “keys-“+range+“.json”;
  5. var jsonwriter = JSONStream.stringify();
  6. var jsonwriter_file = fs.createWriteStream(results_file);
  7. jsonwriter.pipe(jsonwriter_file);
  8. const objetive_addrs =[// list of bitcoin public addreses, can’t be empty
  9. //…TODO
  10. ];
  11. const objetive_size = 100;
  12. const m = Fill(objetive_size);
  13. const render = gpu.createKernel(function(a,m) {
  14. var h = a[this.thread.y][this.thread.x];
  15. var c = m[this.thread.y][this.thread.x];
  16. if(h == c) return h;
  17. return 1;
  18. }).setOutput([42, objetive_size]);
  19. var n=0;
  20. var time0 = Date.now();
  21. var total=0;
  22. var stop = 8;
  23. var keys = {
  24. phrase:null,
  25. private_key:null,
  26. wif:null,
  27. public_address:null,
  28. objetive_addrs:null
  29. };
En las lineas 14 y 15 pasamos los argumentos –range y –samples a sus respectivas variables range y max_limit. En las lineas 16 y 17 nombramos 2 archivos con el mismo nombre.. En las lineas 18 a 20 creamos los streams para escribir el archivo results_file.

El resto del programilla es como sigue:

  1. /***********************************************************************************************************/
  2. console.log(\x1b[32m***************************************************\x1b[0m”);
  3. console.log(\x1b[32m*******************\x1b[0m\x1b[92m THE IMPOSSIBLE\x1b[0m\x1b[32m ****************\x1b[0m”);
  4. console.log(\x1b[32m*******\x1b[0m\x1b[92mIt’s kind of fun to do the impossible\x1b[0m\x1b[32m*******\x1b[0m”);
  5. console.log(\x1b[32m—————RANGE \x1b[1m%s\x1b[0m \x1b[35mwith samples\x1b[0m \x1b[1m%s\x1b[0m \x1b[35mSTARTED—————\x1b[0m”,range,max_limit);
  6. console.log({miners_size:objetive_adrs.length});
  7. while(n < max_limit){
  8. var ar = FillArray(miners_size);
  9. var a = ar[0];
  10. var b = ar[1];
  11. var p = ar[2];
  12. var w = ar[3];
  13. var r = render(a,m);
  14. var t = filterArray(r, a, b, p, w, m, range);
  15. if(t.length){
  16. for(var i=0;i<t.length;i++){
  17. keys.phrase = t[i][0];
  18. keys.private_key = t[i][1];
  19. keys.wif = t[i][2];
  20. keys.public_address = t[i][3];
  21. keys.miner__address = t[i][4];
  22. jsonwriter.write(keys);
  23. var pair = highlight(t[i]);
  24. keys.phrase = pair[0];
  25. keys.private_key = pair[1];
  26. keys.wif = pair[2];
  27. keys.public_address = pair[3];
  28. keys.miner__address = pair[4];
  29. consola(keys);
  30. var time = Date.now()time0;
  31. total += time;
  32. time0 = Date.now();
  33. console.log(\x1b[33mNew keys has been appended to file \x1b[1m%s\x1b[0m. \x1b[33mDuration of this sample: \x1b[31m\x1b[1m%s\x1b[0m \x1b[33mseconds\x1b[0m”,results_file, time/1000);
  34. }
  35. n++;
  36. if(n==max_limit) {
  37. jsonwriter.end();
  38. console.log(“max-limit of %s samples reached. Job terminated.”,max_limit);
  39. console.log(\x1b[33m – Total \x1b[1mduration\x1b[0m \x1b[33mof job: \x1b[31m\x1b[1m%s\x1b[0m \x1b[33mseconds – \x1b[0m”,total/1000);
  40. break;
  41. }
  42. }
  43. }
  44. /******************************************************/
  45. function FillArray(size){
  46. var a = [];
  47. var b = [];
  48. var p = [];
  49. var w = [];
  50. for(var j = 0;j < size;j++){
  51. var t = [];
  52. var hash = crypto.randomBytes(32);
  53. //var hash = bitcore.crypto.Hash.sha256(crypto.randomBytes(32));
  54. var bn = bitcore.crypto.BN.fromBuffer(hash);
  55. var privateKey = new bitcore.PrivateKey(bn);
  56. var address = privateKey.toAddress().toString(‘hex’);
  57. b.push(hash.toString(‘hex’));
  58. p.push(privateKey.toString(‘hex’));
  59. w.push(privateKey.toWIF().toString(‘hex’));
  60. for(var i = 0;i < address.length;i++){
  61. t.push(address.charCodeAt(i));
  62. }
  63. a.push(t);
  64. }
  65. return [a,b,p,w];
  66. }
  67. function FillMiners(size){
  68. var a = [];
  69. var k = 0;
  70. for(var j = 0;j < size;j++){
  71. if(k == objetive_adrs.length)k=0;
  72. var address = objetive_adrs[k];
  73. k++;
  74. var t = [];
  75. for(var i = 0;i < 42;i++){
  76. if(i<address.length)
  77. t.push(address.charCodeAt(i));
  78. else
  79. t.push(0);
  80. }
  81. a.push(t);
  82. }
  83. return a;
  84. }
  85. function FillMiners2(size){
  86. var a = [];
  87. var k = 0;
  88. for(var j = 0;j < size;j++){
  89. if(k == objetive_addrs.length)k=0;
  90. var address = objetive_addrs[k];
  91. k++;
  92. var t = [];
  93. for(var i = 0;i < 42;i++){
  94. if(i<address.length)
  95. t.push(address.charCodeAt(i));
  96. else
  97. t.push(0);
  98. }
  99. a.push(t);
  100. }
  101. return a;
  102. }
  103. function check(a) {
  104. return a != 1;
  105. }
  106. function filterArray(r, a, b, p, w, m, range){
  107. var f = [];
  108. for(i in r){
  109. var s = r[i].filter(check);
  110. if(s.length >= range){
  111. var a1 = new Buffer.from(a[i]).toString();
  112. var b1 = b[i];
  113. var p1 = p[i];
  114. var w1 = w[i];
  115. var m1 = new Buffer.from(objetive_adrs[i]).toString();
  116. f.push([b1,p1,w1,a1,m1]);
  117. }
  118. }
  119. return f;
  120. }
  121. // funciones para imprimir resultados en la consols DOS de Winsdows
  122. function highlight(keys){
  123. var pa = keys[3].split();
  124. var ma = keys[4].split();
  125. const h =\x1b[1m’;
  126. const t =\x1b[0m\x1b[32m’;
  127. const y =\x1b[33m’;
  128. const g =\x1b[32m’;
  129. for(id in pa){
  130. if(pa[id] == ma[id]){
  131. pa[id] = ma[id] = h+ma[id]+t;
  132. }
  133. }
  134. var b = y+keys[0]+\x1b[0m’;
  135. var p = y+keys[1]+\x1b[0m’;
  136. var w = y+keys[2]+\x1b[0m’;
  137. var pa2 = g+pa.join()+\x1b[0m’;
  138. var ma2 = g+ma.join()+\x1b[0m’;
  139. return [b,p,w,pa2,ma2];
  140. }
  141. function consola(json){
  142. var a = JSON.stringify(json,null,8);
  143. a = a.replace(/”/g,);
  144. a = a.replace(/\\u001b/g,\x1b’);
  145. process.stdout.write(a);
  146. }
  147. /******************************************************/

Saludos

Posted in Bitcoin, criptografía, Criptomonedas, informática | Tagged: , , , , , , , , , , , | Leave a Comment »

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 »

STOCHASHES: ¿Qué es un hash estocástico, y para qué sirve?

Posted by Albert Zotkin on June 1, 2020

Hola amigos de Tardígrados. Hoy me voy a inventar un nuevo concepto de hash, que podremos aplicar en el campo de la criptografía, y en el de las criptomonedas. Se trata del concepto de hash estocástico. En el mundo de las criptodivisas, estamos acostumbrados a trabajar como todo tipo de hashes, y demás cadenas y scripts de todo tipo. Pero. creo que lo que voy a definir a continuación no existe, como tal, en el mundo de la criptografía.
Podemos definir un hash estocástico como una función tal que tiene como input una cadena constante de N bytes, y cada vez que la aplicamos, genera una cadena casi-aleatoria de N bits. Decímos, casi-aleatoria, porque en realidad lo que cada byte del input de entrada define es una probabilidad. Al ser N bytes de entrada existirán N bits de salida, y cada uno de esos bits es generado de acuerdo a la probabilidad definida por su correspondiente byte de entrada. Eso significa, que cada vez que aplicamos la función, obtenemos una cadena de N bits distinta (aunque podría darse el improbable caso de que alguna cadena se repitiera), y estadisticamente se podría comprobar cómo para cualquier muestra, se cumple siempre la distribución definida en las probabilidades codificadas en los bytes de entrada. O sea, un hash estocástico de N bytes genera hashes estándar de N bits.

Pongamos un ejemplo: Definamos una función stochash256 que tomará 256 bytes como input, y “escupirá”, 256 bits de salida, cada vez que se ejecute. Para la realización de este proyecto, usaré la librería de node.js llamada bitcore-lib, la cual posee bonitas funciones y utilidades varias. Crearé un script en node.js para que podamos ver la idea, y cómo trabaja esa función, que he llamado stochash256.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
			Stochashes
*/
const bitcore = require('bitcore-lib');
const Random = bitcore.crypto.Random;

var stochash = Random.getRandomBuffer(256);
for(var i=0;i<100;i++){
		var hash = stochash256(stochash);
		console.log(hash.toString('hex'));
}

function stochash256(buffer){
	if(buffer.length !== 256) return null;
	var pt  = 0;
	var hash = []
	while(1){
			var byte = buffer.readUInt8(pt);
	 		var repunit = getRepunit(byte);
	 		var rnd = Random.getRandomBuffer(1).readUInt8(0);
	 		hash.push(repunit[rnd]);
	 pt++;
	 if(pt == buffer.length)break;
	}
	 var result = new bitcore.crypto.BN.fromString(hash.join(''),2);
	 return result.toBuffer();
}
function getRepunit(byte){
	
	const two = new bitcore.crypto.BN(2);
	const one = new bitcore.crypto.BN(1);
	var b = new bitcore.crypto.BN(byte);
	var repunit = two.pow(b);
	repunit = repunit.sub(one);
	return repunit.toString(2,256);
	
	
}
La librería bitcore-lib nos ofrece la función const Random = bitcore.crypto.Random, con la cual podemos generar buffers aleatorios de arbitraria longitud. En nuestro caso generamos uno de 256 bytes, para empezar

var stochash = Random.getRandomBuffer(256);
Una vez que tenemos ese buffer de 256 bytes aleatorios, con cada byte definiendo una probabilidad que será aplicada a su correspondiente bit de salida, generamos una muestra de 100 hashes estándar, aplicando la función stochash256 con el mismo buffer constante stochash. En seguida vemos, que uso una función auxiliar llamada getRepunit. ¿qué hace?. Crea un número repunit en base 2 según el valor del byte con el que estamos trabjando. Es decir, esa función getRepunit nos da un numero en base 2 (ceros y unos), que contiene tantos unos seguidos como especifica el valor del byte. rellenamos con ceros a la izquierda para completar hasta 256. Una vez que tenemos nuestro número repunit, solo hay que escoger al azar uno cualquiera de sus ceros y unos, para obtener así nuestro bit casi-aleatorio (estocástico). la idea de usar números repunit en base 2 como recurso para realizar la probabilidad definida en el valor del byte, se me ocurrió ayer mientras echaba una siestecilla de 2 horas 😛
Apliquemos nuestra función stochash256 para obtener una muestra de 100 hashes estándar de 256 bits cada uno, que expresaremos en base hexadecimal:
1. Primero generamos el buffer aleatorio de 256 bytes de entrada stochash. Y después aplicamos 100 veces stochash256, con ese stochash constante:
{
        "stochash": 
"4f92bc84fd5126a6a65f31bf33b5ad112644e7a55f22e134a4ccba1800323  						 fb1b2ac0d9c31c0b8001cd4edfb9447be44e9cf04987d0f5a26936b9463e1					 96ba76b38aa35068a1e960447d1850a20d8a10d9b723b9ba7178551f19988
3bcd41ec6f9c813135d585b173b1047dc98a23f83a84a3ce8fde269ed9f7d
bb2f5fa6a9f8f9395b8a4cb96961d50a752069800eb5ee75435138210d724						 20b751be5480042997f42c2966ffdfa117e68bc746fd98f27665853aff3cd						 081d9a1c2dd702e74982f50255c9afbfefe7e8a6158d1ed75258afd772e03  						 00cba1c8d3c76d7a969e90f7f3a8b61eb3cd649114e346a923ad32fe4b354
9d8f2e15d47d6ef8fed69570",
        "hashes": [
                "1b343045ce7898ea771ad17dcc1f9d23379190083f560403cdc3ea5d0c274cfd",
                "38d63263c67fdabeae5cda15cc121f21a4acf1289f13060bd5f3381745096bdc",
                "69142ae56c72d1a3d29a326d823d18bfa4b1a02ca35b4e4ac7c0290d0d311cff",
                "29b4ba65d4f9ce9cf242fc2dcc10dfe3a409c10cd766ac684fe3791b65076c7d",
                "78c232c0ce7489fd5609dd3d80bd9debff85c28ceba70c8acfe04989348348dc",
                "6a8682614e7280afc648cc1dc23e992d9ca8899d17413e49dd6a783b1500e0f8",
                "fb0833219e74c0bab2da982dc095dfe786a1f129db316c6ac5ead97f4fad783f",
                "7982e2c2b47b48b39a4acf1ec095996fb4a18c288b1a9e7a5fe9790d4d7954be",
                "f9542e21d07fc32b9beb9b76ce1dfbc78c88f52c975b460adee36d5f758d4cdc",
                "d89222e1f67ad26fd6d09db7cc11cfa7838081199f4b1e6a6fe37869192c509f",
                "ec927641c678506fa70ace2dc82cff83b2a18128db3724cacbcbb91d0d0574da",
                "ebbeb263c65ec068a6888c05c7979ffb848cf8809f22246ac9e269cd1d0de4be",
                "19d6bbc1d67ad828b30a413dc2159f639105b0e83f1a5e4259cafd2d410460be",
                "6d9c7ac5d052dba7d68a989de21ecd23b4a1400cfb72ae0acfe9e8431580745d",
                "fb5e7ae196b390fbf212de14c892cc67b590c0288f1f060aceea28491501689f",
                "e99073609e7ad0dff7628831843abfebb7809129d3610e4ad5e1394954206c9d",
                "6d1422e19672c01a86c8df2dc0559feb96a94088df3b2e08cfe6690d7d1554dd",
                "ecf602e3d272ca2eeb4ac82dc8349bbf90048008571f0e4a57eae95d0f055cde",
                "a99e32819e73d8b8a748d73dcc1e5dc3950890259b707e2a47c2fc5d5c8c443f",
                "edc26280b47bc09f2688fb258535dd8392a9800897274e4a5dc2f0ff49806afe",
                "39127a619e5bdaebb3d0f41d8018ddcfa6af8c08db3a66c3cbe370ed1d2be27e",
                "b8d62022d072cb8bc45ad325c1169fe134b4fc2893af3e4247eb615d150864df",
                "69567a6150628aadb61ad80fca0dfda7d5a5e08877758e0bddeacd5d718a7cfe",
                "edd7baf0c23b92fd8a2cd81dc02d1d25c0a9d98817be6e8a49e2f00d548c62fe",
                "39ac72e1e472c2eb7618de3dc087de6f9ce1c1b8bbc17c4a47e3391dd50d488f",
                "bb943ee4d678d29e2fdad82dd01f1da7e599880d1b081c0b4fca790f5c0154fe",
                "699a3a7144761b3e6e48de8dc6399de5d6acc029931d2e5ac3c2e90b0db56cfe",
                "5a903260d4fac84af740f219c01f1de5b4b5c0016f16dec2c9e6e84f5509ccde",
                "2f823ac38670d1cf0642940dcb591ca5b60d81a8ff5d2e3b4fc2691d0b016c9a",
                "580622e15673d37e339ce4bcc0341d6b8ca1a0091b0f4e62c08a044c2d356cba",
                "399732e31672da9ae748f83dc011dd659581c12893443e2a4feae94916356e5b",
                "7c96fa40d678ca5b63dac7358e1cbda790a0c1089b171e9bc5caed7d1d05629a",
                "2b9678e1f67ad8defec8982988959d4fa9e1b8289b5a5e4a5bca391c678d60de",
                "385e3b66de7ad02f2658c125c0523d239ca5c129db8c1e41dfa2e1cd6c04acbd",
                "ef9738e74077c9ee965a8885c895dda394a08518c3260c7adff3e94d1681d85d",
                "789622a0543adc0cf6cac3b8c29d9d27bca9e4083f6f170b47faf97d45246e1e",
                "28163ac5027bda3e1648dc34d891bd7b9e81a808bb53064acfe6397957847cde",
                "d91e62c1d6d39064b282c96340188d8f848c91285f1e0e0a5de2ecdd25814c9c",
                "29dc76c21676d0583750c016ca2cbda5d388e908936ac65acfe9785d0d21e81e",
                "29b27a60c67e92ff3e52c51ec01d9d87ada1812cf7557e4bdbcb71090f916cde",
                "7e843aa18072d0decb0acb19c28e1dbff68dc23053262e5acbc2f95d1555709e",
                "3e0620831672c0ae8e42de05c2961e83d4819029df7746cac9eaf94b5fc57a9f",
                "3f9432c144b7daeffa48d82542149daf878ca080df5f1e4acfa23949661168ac",
                "2c8422f0d67ec0acdf48d02fc4f29ba3dc8488295f4e2e0ac9c2694317056cde",
                "8f323f61c672c0b6be68c044c4119f27d18dd00c97364c4ad9ee294b4d11629e",
                "58d63aa14e72c0a8ae50f83dca3d1b2fd381a1688f424e1aceea3de74723c499",
                "f9572ee0de7ad0edee62db2dcb141b6f84bda0889b124ecadfe3d8ff07a17c99",
                "7dd67ae1c6b896cfc64a9034c19d9dc79425d108db380e5b41ca797d050cc8df",
                "98fa2241867a0a3e2f4ab635c0144dcb8181810891507e4ac8e25d4d1d4d2cd5",
                "7814b6e5c6509299d7125b0dc02d3d579f899008db37064bc3c76959472652fe",
                "791836e3567eda6a8688dd68c0113de39e80882903121e5a4fe2d9595f27649a",
                "79d023a6ce77d88ce740e20dc817defba089842c9b740e0acdea7d6f3510619e",
                "78da32a2de70d29c265a9a7dca15dde5db88a519d7350f424bc8291c07216c7e",
                "382236e0447bc00cea98f805d4159ee7b08991a0b3d94a1a4ffab90d5d17469f",
                "ac987bc16670d2eca2884a25c33eff8b97240128fb6f0e9b57c21f1d450468dd",
                "f91263014c72c2a38652de3d821c9f6f8089e90147070e1a45cbb90fd50102df",
                "39c61aa0d21281387682d1adc81d7f25e3a581a8c17a4e1acde2694d0d04629a",
                "e9d622e3c67458bdc298d90ce0913de39e85e808df4e0e237be3793d260548ad",
                "6d9262e09673ca492a185919c5989d0df62dc0a94f1bae4b4fe039791f05609c",
                "49866260e47cc028ab51ce0bc2189fa19c8de1b997eb4e4acbc67859450d401b",
                "399e2ee6dc7b9820e742c811d9359da9b49508081f9f2e4a4fe3e967d7217cbc",
                "5db773e5de7b99ea8321dc1cc21fddabe48c942093c72e0a5de3b84d75016cbe",
                "79f2b220d27a407aa6b8da0cc035dd75bca49b085f5a4e8adfebc84914897cde",
                "1d0632679672c04e2c01c83dd91cd90586b0802cd6680e2ac6c0a12b070164fc",
                "7987326544f0d14d9218170dc0101daf920bb1259357c44a4be0195915096d5d",
                "191e7061e4fd90ac7b18da2dc4159ca1a084b0b843579e4ac9c0690d1c03089f",
                "e99232e4c47cd8ecc648ca04cc059d2385a909195717764ad9ea7c5b0b0165ff",
                "bc9c92c1467ad00fea1a8804848c9fe79d29c8281b3aa608dd8b780f6d3c6c9e",
                "7cba70c1d673d08f6e48c13482309be399a5c90d9f0e8e4b4f6b698d1d25609c",
                "6d123aa3d6f9c0ab060ad5a3c69c99e5a4808038533a641bd3ee617904014cde",
                "991e30e18679d00e645afa95d714fbaf960989807b560e4bcbf0795d0715681f",
                "798623c20e72dcb0bf42da1dc89c9fc39ea58019937f0e4b4fe3b81d5d016a9e",
                "9e162a4100f8c82e56089b7dc81e9fefad359689d37f064bcfe9294445017c5e",
                "49c630c2c270d2db6e08d929c65fdfebbda0900c0b5f8e434fc2e81d160061dd",
                "883637e1063fda18ea02d61de019df8f9f8f99a89f04ce025fc1213e0c2d68b8",
                "c9163ac1d67bc2bba75288bdc6139f39900984b95f5aae43dfea294f3d1d7c8d",
                "28dc3ae1d673c0673a5ad83dc0a11f2794a589a87f38cc0adfc261099f03749d",
                "089c6af1867ac85bef009c0fc0019fe39b84c4e9e3324e4b4f4a785d412de09f",
                "19de32e1107fc83512489315ce1ddd879081c0008f135e8a8fda617b5d85e8de",
                "e8902a83c673d00ae6085035828e3ba39e81f008335b444ac7e2b95d922964dd",
                "98bc6ae1527288b8c248d83bc21359cf80098c04975f3e0ac7ffe84e95036edc",
                "2d5216f55672d8fec20adaacc61fbc3185a0a0189b557c0addc27f3b778de29d",
                "588f3660d472c82e7a88d23dc841dd87b68580a83f0b5e4b43a9ed1f441d60df",
                "399032f3de72c8dbe702882dc890ddef99a9a82c97388e0bcb68f95f5d0f69d1",
                "79537ae1467adb9aae9ad4b5c21d1f7f9589c00c93222e4a57e2fb7975cde0de",
                "65de0ae18233c0e1aac9d20dcc151def9480a048d3124e4acfcb295d55c47ebe",
                "5d36ba255e72d08bd658ec0dc01c1dff34a9c108137b2e4a4bea7919040d54fe",
                "48c062a4d6b2c2be8512a00f881f9fe58724a0089b3a0ecacfcbb91f050d3edd",
                "691712c1dcba01ab034c4e3d865cdf859b8991b89f3b2e4ac9e2282f4d0d7cbe",
                "2dd63082c672d186ee40d829c41abf85d921d128bf071ec9c9c968015d217edf",
                "799232e1f47fc05db6c8d63dca149de185e5c1a9475deec05be9b899c505fc9c",
                "0bb036a41e7bd83af60a98bdc094c9a5a72dc02c5b3b0b4a0fe6790b4391681f",
                "7b1402e5c27b88ae7b0ad84cc71b5d9786a48000933a1e624fc8285f5d8776b4",
                "b93612c11276d89f9418583cc4599d27dc818128bb32ee4bcda2211f578178dc",
                "39166a41467cd2aeffe8d93dc00e5fefa9888118935a6614cdc8e04d54a94898",
                "387130e1d67b90e72208de2cc03fbfcf86bdc02c53530e085fe0391b15305e1f",
                "3d1632c154729abf83c8d82d881b1dc78ea5a0091f1e960ac7ec611e151564dd",
                "4f467600c21bc2ce890a5c2dc21d5fafa5a1c1a99b5a66db5bea79df45206cdd",
                "5c963a62c470d8ce4e00d62dc61e0f2e8635082853364e4a5fe2780f4565e4fd",
                "599632e1d67298bf4a1adc1082959d2da6ad802dd75a460a4deadd0c5516409f"
        ]
}

se puede comprobar fácilmente como la muestra de hashes cae toda dentro de una distribución normal, según las probabilidades definidas en cada byte de stochash. Los stochashes nos servirán para optimizar la velocidad de resolución de bloques en minería de bitcoins u otras criptodivisas. Pero, ese es otro capítulo.

Saludos estocásticos a todos.

Posted in Bitcoin, criptografía, Criptomonedas | Tagged: , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a Comment »

Iconos para bloques en la blockchain de bitcoin

Posted by Albert Zotkin on February 27, 2020

Ayer, mientras ganaba algunos miles de euros minando bitcoins, se me ocurrió una idea gráfica, para identificar mediante una imagen a cada uno de los bloques en la blockchain.
Como cada bloque posee tres hashes principales de 32 bytes cada uno: el hash propio del bloque, el del bloque inmediato anterior, y el de la raíz del árbol Merkle de las transacciones., se me ocurrió lo siguiente: cada dígito de cada uno de esos tres hashes podría ser un código de color de una terna RGB, rojo, verde y azul, para cada pixel de la imagen. Pongamos un ejemplo. Elijamos el Bloque Génesis, es decir, el primer bloque de bitcoin que fue minado por el propio Satoshi Nakamoto. El objeto JSON de este histórico bloque es:

{
        "0": {
                "hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
                "version": 1,
                "previous_block": "0000000000000000000000000000000000000000000000000000000000000000",
                "merkle_root": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
                "time": 1231006505,
                "bits": 486604799,
                "fee": "0.00000000",
                "nonce": 2083236893,
                "n_tx": 1,
                "size": 285,
                "block_index": 0,
                "main_chain": true,
                "height": 0,
                "received_time": null,
                "relayed_by": null,
                "transactions": [
                        {
                                "double_spend": false,
                                "block_height": null,
                                "time": 1231006505,
                                "lock_time": 0,
                                "relayed_by": "0.0.0.0",
                                "hash": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
                                "tx_index": 0,
                                "version": 1,
                                "size": 204,
                                "inputs": [
                                        {
                                                "sequence": 4294967295,
                                                "script_sig": "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73",
                                                "coinbase": true
                                        }
                                ],
                                "outputs": [
                                        {
                                                "n": 0,
                                                "value": "50.00000000",
                                                "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
                                                "tx_index": 0,
                                                "script": "4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac",
                                                "spent": false
                                        }
                                ]
                        }
                ]
        }
}
Por lo tanto vemos que los tres hashes principales son:

                "hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
                "previous_block": "0000000000000000000000000000000000000000000000000000000000000000",
                "merkle_root": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
los cuales son números de 64 dígitos en base hexadecimal, es decir, 32 bytes cada uno. Podemos diseñar una imagen en formato PNG de 64 x 64 pixels. Para ello vamos seleccionado ternas de dígitos para componer el RGB de cada pixel. Así tendremos que el primer pixel de nuestra imagen (posición superior izquierda será el RGB(4,0,0), porque 4 es el primer dígito en el hash de la Merkle root, y 0 para los otros dos. El siguiente pixel sería RGB(a,0,0), y los cuatro siguientes son RGB(5,0,0), RGB(e,0,0), RGB(1,0,0). Pero como esos dígitos son hexadecimales, sólo van desde el 0 al 15, en lugar de ir del 0 al 255, lo cual produciría un color bastante oscuro. Por lo tanto, le vamos a dar brillo a cada pixel duplicando cada componente RGB hasta completar el byte, es decir, sería como multiplicar por 17 cada componente (dígito hexadecimal). Así nuestros pixeles, con brillo serían: RGB(44,0,0), RGB(aa,0,0), RGB(55,0,0), RGB(ee,0,0), RGB(11,0,0), y el ultimo pixel de la imagen PNG sería el RGB(bb, 0, ff). Es decir, nuestra imagen PNG para el Bloque Génesis sería esta:

ID imagen del Bloque Génesis de Satoshi Nakamoto

O sea, podemos crear la imagen dinámicamente mediante lenguaje PHP, creando una función que tome como argumentos esos tres hashes del bloque en un array. Yo escribí el código para una función que haga eso. Esa función es la siguiente:

function ImageFromHash3($hash){
if(strlen($hash[0])!=64 && strlen($hash[1])!=64 && strlen($hash[2])!=64) return '';
// Create a 64 x 64 image
$image = imagecreatetruecolor(64, 64);
$n=0;
$r = str_split($hash[0],1);
$g = str_split($hash[1],1);
$b = str_split($hash[2],1);
				
for ($y = 0; $y < 64; $y+=8){
for ($x = 0; $x < 64; $x+=8) {						
imagefilledrectangle($image, $x, $y, $x+8, $y+8, 
imagecolorallocate($image, hexdec($r[$n])*17, hexdec($g[$n])*17, hexdec($b[$n])*17 ));			   		
$n ++;			
}				 
ob_start();
imagepng($image);
$im = ob_get_contents();
ob_end_clean();
imagedestroy($image);
return '<img src="data:image/png;base64,'.base64_encode($im).'" />';					
}
Y así, la podemos presentar en nuestra página web. Pongamos un ejemplo más. El ultimo bloque validado por la minería de bitcoin, hasta la fecha, tiene una altura de 619122 (la altura n de un bloque significa el orden, o sea, que existen n bloques antes que él en la blockchain). Este bloque de fecha 26/2/2020 22:16:32, posee los siguientes tres hashes:

                "hash": "0000000000000000001159ec899185ec8f056b7655bf7898fef186454efde94f",
                "previous_block": "00000000000000000008e901529a41ef9350ff42166c8abeea80f6e708a46463",
                "merkle_root": "1adeb8334f75e32a335b19005871b2be3ac5555c661ce17b81131b0c43579a61",

y ha sido minado por la pool ViaBTC. La imagen generada con nuestra función sería esta:

ID imagen del bloque con altura = 619122

Como vemos la imagen ha sido creada dinámicamente en formato PNG con el lenguaje de programación PHP, y los bytes del rawdata de la imagen están codificados en base64, mediante la función base64_encode. Y el rawdata está en la variable $im. Es decir:

data:image/png;base64,iVBORw0KGg
oAAAANSUhEUgAAAEAAAABACAIAAAAlC+
aJAAAACXBIWXMAAA7EAAAOxAGVKw4bAA
ABWklEQVRoge2awY2DMBRE8SoNeEvwlk
ALUAKUACUkJUAJSwlQApSwtOASQgnZBm
YOOU0szTsOlqOnL32bH0KsML8kr0nO9j
nfzDeSM77eXP9xWECNBdRYQE1oyIOd5A
+SZ5Kz86Ql+UXyieTFV8ACaiygxgJqAr
vfMxK5+Q+kUfcDPmmmDefj2OGNIj5piq
+ABdRYQI0F1NxShxt4feJ+f93xRn3Ebw
prj0+a8Txgvj/xOVCTwVPxFbCAGguosY
CaWyaDoS4PME9HgHnecP9eXvgeX1d4/6
WdYX7PeGJUfAUsoMYCaiygJgyRzGFIvJ
H+vZP5Tzrwe8JPIvOliPOm9jnwmVhAjQ
XUFC8QpucLPpgP8o9txn06Pb7x8hceJK
0jfk9oJ5z/zfh3i6+ABdRYQI0F1ISmwv
f4a1phnsg9Pi24f7P1MeMvRI+E8+3EXz
AVXwELqLGAGguo+Qen00MzJKmE8gAAAA
BJRU5ErkJggg==
Saludos, me siento como bloqueado ya 😛

Posted in Bitcoin, Criptomonedas, informática, Matemáticas | Tagged: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a Comment »

Minería de bitcoins explicada con lápiz y papel

Posted by Albert Zotkin on January 30, 2020

Hola amigos de tardígrados. Hoy vamos a aprender a hacernos ricos en poco tiempo 🙂 En este pequeño tutorial aprenderemos cómo minar bitcoins desde cero, burlando así a las grandes granjas chinas de minería de criptomonedas.

Quizás yo no sea muy bueno en muchas cosas, pero con los años aprendí a conocerme a mi mismo bien, y descubrí que tengo ciertos talentos ocultos. Una de esas destrezas, que tenía oculta, es que soy capaz de adivinar cualquier contraseña en menos de un minuto sólo con el poder de mi mente 😛 ¿No te lo crees?. Mándame una dirección de correo electrónico tuya, y verás en tu sección de correos enviados, cómo, en poco tiempo (dependiendo del número de solicitudes de incrédulos), “me habrás enviado” un correo electrónico a albertzotkin@yahoo.com, en el que me dirás lo siguiente: “Esto prueba que sé tu contraseña y usé tu dirección de correo para enviarme esto”. Sí, amigo de Tardígrados, yo soy un superhéroe los días pares, y los impares un villano. A parte de esa destreza de adivinar mentalmente contraseñas, la cual resulta bastante sorprendente, teniendo en cuanto que soy un humano, también tengo otras cualidades ocultas, más sorprendentes aún que esa, si cabe, pero no te las voy a decir de momento, para que sigan siendo ocultas.

Hoy en día existen muchas criptomonedas, la mayoria están basadas en la tecnología del blockchain. Hasta la tostadora IoT que tengo en mi casa sería capaz de crear una criptomoneda. Sólo hay que ver que hasta Nicolás Maduro fue capaz de crear la criptomoneda Petro, con eso está dicho todo.

En este pequeño artículo que estoy escribiendo, me voy a centrar solo en el bitcoin. Hoy en día es posible comprar y vender bitcoins fácilmente, con lo que tú solito puedes llegar a enriquecerte, o a arruinarte a placer, especulando con esa moneda electrónica tan volátil. Las ventajas que tiene el bitcoin y otras monedas electrónicas, son que es un sistema descentralizado, y puede escapar fácilmente del fisco, pero a cambio, estás a expensas de fraudes, robos, y demás actividades encaminadas por terceros a dejarte sin dinero en tu billetera a cambio de nada más que disgustos. Pero, como vamos a partir de cero, es decir, de cero euros, nuestro disgusto de que nos roben cero euros sería menos del que sufriríamos si partiéramos de nuestros ahorros de toda la vida, o del dinero de la cesta de la compra.

Pero vayamos ya sin más tardar, al ajo. Para poder hacernos ricos con el bitcoin, lo primero que tenemos que hacer es conseguir una billetera (no digo monedero, porque hay ciertas personas con ciertos apellidos que me caen mal). Para ganar nuestro primer bitcoin, que a día de hoy esta a 8212.96 € en el mercado cambiario, tendremos nuestra billetera preparada para recibir moneditas calenticas recién minadas. La mejor billetera que puedes conseguir es la electrum. Una vez que hayas creado tu billetera de bitcoin electrum, guardarás tus claves offline, fuera del sistema, para que nadie que no seas tú, pueda hacerse con ellas y desplumarte. La electrum crea una clave muy peculiar, desde la que puedes acceder a tu billetera desde cualquier parte que posea conexión a internet, o con una única contraseña si la usas localmente desde tu dispositivo habitual. Dicha clave consiste en una secuencia ordenada de 12 palabras. Esa secuencia ordenada la puede memorizar, o la puedes guardar donde te plazca, pero siempre fuera del alcance de curiosos. Yo, por ejemplo, la clave de mi billetera electrum la tengo memorizada, pero también me hice un pictograma con esas 12 palabras de mi clave, porque a veces mi memoria me falla. El pictograma mnemotécnico de mi clave electrum, donde guardo algunos bitcoins, es este:

Si sientes el deseo que robarme mis criptomonedas, sólo tienes que adivinar a qué palabra corresponde cada uno de esos iconos, y voila!. Pero, si lo que deseas es enviarme algunos Satoshis, puedes hacerlo a mi clave publica:

un Satoshi es la cien millonésima parte de un bitcoin = 0.00000001 BTC. O sea que si un bitcoin se cambia actualmente por 8212.96 €, entonces 1 euro serían 12175.87 Satoshis. Lo de comprar y vender bitcoins, o con bitcoins, no tiene mayor misterio. Como habrás comprobado ya, incluso a la hora de recibir o enviar bitcoins con electrum, lo podemos hacer mediante los famosos códigos QR desde nuestros teléfonos móviles. Entonces, te estarás preguntando, cómo y cuándo vamos a empezar ya de una vez a ganar nuestros miles de Satoshis. La respuesta está en la minería de bitcoins.

La minería de bitcoins es una actividad mediante la cual van agrupando las nuevas transacciones en bloques, y estos bloques se unen a la gran cadena de bloques o blockchain. Los mineros usan potentes maquinaria de hardware a gran escala, para conseguir algún provecho, de lo contrario, esa actividad no sería rentable. Los mineros se comunican unos con otros mediante una red de nodos P2P, peer-to-peer. Usualmente lo hacen con el protocolo TCP estándar, el de los servidores web, pero no por el puerto 80 habitual, sino por los puertos 8333 o el 8332. Estos mineros de criptomonedas usan factorías llenas de cientos de procesadores fabricados y dedicados expresamente a realizar una única y sencilla operación matemática, pero han de hacerlo a gran velocidad para tener algo de probabilidad de encontrar la solución en un tiempo razonable para la rentabilidad económica. El problema con esos dispositivos es que, cada uno de ellos, es un monstruito muy ruidoso, bastante tragón de energía eléctrica, y se calienta bastante. Si juntamos en una nave industrial cientos de esos monstruitos computando a la vez sin parar las 24 horas del día, lo que obtenemos es una actividad super contaminante de ruido y calor, con energía desaprovechada por un tubo. La factura de la luz de un sólo día puede llegar a alcanzar miles de euros. La recompensa por resolver un bloque de transacciones de bitcoins es de algunas monedas, suficiente para conseguir rentabilidad.

¿En qué consiste esa sencilla operación matemática que tienen que realizar a toda velocidad esos procesadores para conseguir hacer negocio?. La solución de esa rutina operacional se llama Prueba de Trabajo. Los mineros, recolectan un conjunto de últimas transacciones del día, que están por ser confirmadas (validadas), y con ese conjunto de transacciones construyen un proyecto de bloque para ser añadido e integrado en la cadena blockchain. Junto a esas transacciones redactan la suya propia (transacción coinbase) para ganar la recompensa en el momento de dar con la solución, y todo irá incluido en el mismo proyecto de bloque. Pongamos un ejemplo concreto. Hace unos momentos, a las 17:55 de la tarde (hora de Benidorm 🙂 ), unos mineros chinos del pool AntPool han conseguido cuadrar (resolver) un nuevo bloque. Exactamente uno con el hash = 000000000000000000044b6c80648ead63c9ab7cad89f7841d6c1a420d34ba43. ¿Que qué es un hash?. Un hash es simplemente algo muy parecido a la huella de una cadena de caracteres. Existe una función hash256 que se aplica, para nuestro caso concreto, dos veces (iteración) sobre una cadena de 80 caracteres, correspondiente a la cabecera del bloque proyecto que proponen los mineros. Este bloque hallado es el número 614930 dentro del blockchain. Estos mineros chinos, en su granja con sus miles de “gallinas cibernéticas”, cacareando calientes sin parar, han puesto un huevo, por el que se están llevando al bolsillo 12.5 bitcoins, más una comisión de 0.2, es decir, un total de 12.7 bitcoins = 104304.6 euros (y eso lo hacen cada día, unas cuantas veces). La cabecera de 80 bytes, codificada en binario, de ese bloque es la siguiente:

0000000h : 00 00 00 20 9d 88 e1 40 67 f7 f0 10 db 58 5a 2f ; ... ...@g....XZ/ 0000010h : d7 28 bb c2 d8 1e e3 09 64 b7 0b 00 00 00 00 00 ; .(......d....... 0000020h : 00 00 00 00 82 e4 1c d2 3e c9 9f 27 d0 66 27 99 ; ........>..'.f'. 0000030h : c0 cc 88 11 35 1e 33 ed d0 33 6c bd 3c b5 11 79 ; ....5.3..3l.<..y 0000040h : 68 a5 e4 5e 9b 67 30 5e ff 32 12 17 74 c7 f9 01 ; h..^.g0^.2..t...
Si desglosamos la estructura de esa cabecera de 80 bytes, veremos qué parámetros la forman:

$version = 536870912
$previous_block = 0000000000000000000bb76409e31ed8c2bb28d72f5a58db10f0f76740e1889d
$merkle_root = 5ee4a5687911b53cbd6c33d0ed331e351188ccc0992766d0279fc93ed21ce482
$time = 1580230555
$nbits = 387068671
$nonce = 33146740

Si ahora aplicamos la función hash256 dos veces sobre la cadena en binario de esos 80 bytes de la cabecera, obtenemos lo siguiente:
hash256(hash256(cabecera))= 000000000000000000044b6c80648ead63c9ab7cad89f7841d6c1a420d34ba43
que coincide exactamente con el hash del bloque encontrado por los mineros del Antpool. Esto significa que hemos comprobado por nosotros mismos que ese block es válido. Ya de entrada, notamos algo raro en el hash de ese bloque. Los hash256 son todos cadenas alfanuméricas (aparentemente aleatorias, porque no hay manera de saber a qué cadena de caracteres corresponde un hash determinado) de exactamente 64 caracteres, o lo que es lo mismo, de 32 bytes (independientemente del número de caracteres de la cadena sobre la que se aplica). En realidad, vemos que todo hash es un número expresado en base hexadecimal. Lo raro que notamos, es pues que existan tantos ceros seguidos a la izquierda de ese hash, de hecho es muy raro que un hash256 empiece siquiera por un único cero. En realidad, esa rareza de muchos ceros al inicio del hash es precisamente lo que se busca con tanta codicia por los mineros. Los dos parámetros clave para los mineros son el nbits y el nonce. Este ultimo es un valor que el minero está autorizado a cambiar, para ir probando hasta hallar un hash que tenga muchos ceros al inicio y que cumpla una condición. La condición la dicta el parámetro nbits. Mediante una sencilla operación el nbits se transforma en un número en base hexadecimal llamado target. Para el caso concreto del ejemplo de bloque que estamos analizando, como nbits = 387068671, el target, con esa sencilla operación, resultará ser

target=1232ff0000000000000000000000000000000000000000
Como tanto el hash del bloque como el target son números enteros (expresados en base hexadecimal, pero números), la condición, que exige Prueba de Trabajo es una simple pregunta: ¿Es el hash del bloque menor que el target?. Para contestar afirmativamente a esa pregunta los mineros empiezan a calcular el doble hash256 de la cabecera, desde nonce = 0. Y la palabra Bingo! suena bien alto en la sala cuando se halla un hash que es menor que el target.

En nuestro caso, para un nonce = 0, obtendríamos un doble hash256 de:
hash256(hash256(cabecera))= a625bbfaf77163b53d1e30c30e8d01a7b0e00fb396b08f475d1c3eda7ecaf133
Es fácil ver que ese hash es mayor que el target. Por lo tanto, un bloque con ese nonce = 0 no sería un bloque válido. Es imposible saber qué valor de nonce cumpliría la condición. En nuestro ejemplo el nonce = 33146740 hace que se cumpla la condición, y por la tanto el bloque queda validado.Todo es puro azar, una lotería, vamos. Cuanto más juegues a la lotería más probabilidad tienes de que te toque. En eso consiste la minería de criptomonedas, con trastos ASIC, tragando hashes a velocidades endiabladas. Ludopatía pura y dura.

La velocidad de los procesadores mineros debe ser muy alta si quieren conseguir rentabilidad. Actualmente se usan en las granjas de minería de bitcoins esos chips tipo ASIC. En cada granja puede haber instalados cientos de esos ruidosos aparatos. Compremos uno de esos aparatos y pongámonos a minar bitcoins por nuestra cuenta. Podemos adquirir uno baratito por 100 euros, el AntMiner S7

Este aparatito tiene una velocidad de cálculo de 4.73 TH/s, es decir, 4.73 x 1012 hashes por segundo, pero chupa electricidad por un tubo, ya que consume con una potencia eléctrica de 1210 vatios. Teniendo en cuenta que en España el precio del kilovatio-hora es aproximadamente de unos 0.13 euros, ese aparatito, por sí solo, durante 1 día consume 29040 kilovatios-hora, es decir, un coste por día de 3.7 euros.

¿Cuál es la probabilidad de que a la primera obtengamos un doble hash256 que cumpla esa condición de ser menor que el parámetro target?. Es como tirar un dado y ver si obtenemos un 6 a la primera tirada, pero en el caso de los hashes existirían muchos más lados que los seis de un dado normal. ¿Cuántos exactamente?. El máximo número entero de 64 cifras en representación de base hexadecimal es este:

\displaystyle  m = \text{ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff},

y si lo expresamos en base decimal obtendremos el número de 81 cifras

\displaystyle  n = 115792089237316195423570985008687907853269984665640564039457584007913129639935.

Esto significa que el número de caras de ese supuesto dado gigantesto sería de n+1. El target que se usó en el hallazgo de la solución para ese bloque era, como he dicho antes,

\displaystyle  \text{target}=1232\text{ff}0000000000000000000000000000000000000000,

que expresado en base decimal es

\displaystyle  \text{target}_{10}= 1743137387349479903250289511035208906392689711805104128,

Esto significa que la probabilidad de que un hash256 sea menor o igual a ese target es precisamente

\displaystyle  p = \frac{\text{target}_{10}}{n+1} = 1.5054028291837063 \; \times  \; 10^{-23}

Nuestro aparatito para minar (el AntMiner S7) tiene una velocidad de v = 4.73 TH/s, por lo tanto, en un día, la probabilidad de resolver ese bloque sería de

\displaystyle  p_d = p \times v \times 24  \times 60 \times 60 = 6.15216 \; \times  \; 10^{-6}

Si no me he equivocado en los cálculos, resulta ser una probabilidad demasiado baja, se necesitaría un promedio de 445 años para hallar la solución. ¿Entonces, cómo podemos ganar dinero nosotros si no nos unimos a uno de esos pool de minería?. Parece obvio que, por nuestra cuenta, las probabilidades de obtener ganancias están muy próximas a cero. De hecho los mineros en pools incluso, a veces, agotan todos los nonces, y necesitan incluir extra nonces en los proyectos de bloque para seguir probando suerte.

Y hasta aquí puedo escribir … 😛 … de momento.

Posted in Bitcoin, Criptomonedas, informática, Matemáticas | Tagged: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , | Leave a Comment »