Introducción a WebSockets. Crear un simulador de minería de Bitcoin (Parte 3— Admin)
Si has seguido las otras dos partes de la serie, ya tienes el servidor y el cliente implementados. Ahora vamos a crear la aplicación que inicia el proceso de minado enviando el mensaje a todos los mineros conectados al servidor.
Cómo habrás observado, en la parte servidor hay eventos que no se usan en la parte de cliente, si no que son para el uso de esta parte que he llamado Admin.
¡Vamos a acabar el proyecto y empezar a minar!
Implementar Admin
Vamos a implementar la aplicación Admin, que no deja de ser un cliente que está suscrito a diferentes eventos que el cliente normal. Estos eventos son los que inician el proceso de minado, ya que para ser justo, se les envía a todos los mineros al mismo tiempo.
Para la ejecución es necesario también pasar algunos argumentos desde la línea de comandos, ya que debemos especificar la dificultad de minado, para recordar, es la cantidad de 0’s que tendrá el hash delante.
Así que especificamos la dificultad con el flag -d, seguido de un número entero de 0 a lo que quieras, ¡¡OJO!! Es muy fácil que tu pc se quede bloqueado o parezca un avión en el momento de despegar al activarse los ventiladores al máximo!!
Fichero: /client/admin/app.js
/*
ADMIN APP
*/
import { initAdmin } from "./admin.js";
const app = () => {
const args = {
timestamp: Date.now(),
transactions: 1,
previousHash: 1,
difficulty: ''
}
if(process.argv.length === 2) {
console.error('Se esperaba como mínimo 1 argumento: -d [número entero], debe introducirse');
process.exit(1);
}
const dIndex = process.argv.indexOf('-d');
if(dIndex) args.difficulty = process.argv[dIndex + 1];
const tIndex = process.argv.indexOf('-t');
if(tIndex) args.transactions = process.argv[tIndex + 1];
const pIndex = process.argv.indexOf('-p');
if(pIndex) args.previousHash = process.argv[pIndex + 1];
initAdmin(args);
}
app();
La estructura es la misma que en el cliente, así que este primer fichero es el control de los argumentos que se le pueden pasar a la aplicación por la línea de comandos.
Lo primero está la importación de la función initAdmin() desde el fichero de admin.js, que es la que inicia toda la parte de conexión a través WebSockets.
Después definimos un objeto llamado args que contiene todas las propiedades para enviarlo a los clientes. Estas propiedades son necesarias para que se ejecute el algoritmo PoW, para que le den aleatoriedad al hash que vamos a crear.
Las propiedades son:
- timestamp: La fecha actual, es básicamente el mismo instante: año, mes, día, hora, minuto, segundos y micro segundos, dependiendo del formato en el que lo crees. Nosotros lo conseguimos con el método .now() del objeto Date de JavaScript.
- transactions: Esta es el número de transacciones que se guardarían en el bloque. Para simular y dar un número, hemos puesto 1 de forma inicial. Esto en la realidad, sería diferente en cada uno de los bloques.
- previousHash: Este sería el hash del bloque anterior. Como ya comenté, los bloques nuevos, contienen el hash del anterior para darle más seguridad, y así está creado el concepto de la cadena.
- difficulty: Esta es la dificultad de minado. Es un número entero de entre 0 e infinito. Esta es la única propiedad que es necesaria definir, ya que no le hemos dado un valor inicial.
Después, comprobamos que el número de argumentos que se han pasado al ejecutar la aplicación. Ya lo he comentado, mínimo se tiene que pasar el argumento de difficulty, que se hace con el flag -d <NUMERO_ENTERO>.
Con esto es suficiente para que se ejecute todo el proceso, el resto de argumentos son opcionales, así que las transacciones las podemos definir con el flag -t <NUMERO_ENTERO>, y el hash previo, se define con el flag -p <NUMERO_ENTERO ó HASH>.
$ node app.js -d 2 -t 58 -p 11111
Este es un ejemplo de pasar todos los parámetros, si sólo quieres pasar la dificultad:
$ node app.js -d 2
Con esto ya es suficiente para iniciar el proceso con una dificultad de 2 para encontrar el hash.
Por último llamamos a la función initAdmin(args), pasándole como argumento el objeto args con las propiedades ya nombradas.
¡Ya tenemos la primera parte implementada, vamos a por la parte de WebSockets!
Conexión con el servidor
En este fichero, vamos a ver la función initAdmin() que ejecuta todas las conexiones y envío y recepción de eventos entre el servidor y este cliente.
Fichero: /client/admin/admin.js
import { io } from "socket.io-client";
export const initAdmin = (args) => {
const socket = io('ws://localhost:3000', {
autoConnect: false
});
socket.connect();
socket.on('connect', () => {
socket.emit('joinToMine', {id: socket.id});
console.log('connected', socket.id);
});
socket.emit('sendHash', {
timestamp: args.timestamp,
transactions: args.transactions,
previousHash: args.previousHash,
difficulty: args.difficulty
});
socket.on('hashFound', (miner) => {
console.log('winner: ', miner);
})
}
Lo primero es importar el objeto io donde tenemos las funciones de WebSockets.
Después se define la función initAdmin, recibiendo el objeto args que he definido en el otro fichero.
Lo primero es obtener una instancia del socket a través de io pasándole cómo argumento la url de conexión del servidor y el parámetro que ya hemos visto en los otros artículos.
Después, iniciamos la conexión del socket con el método .connect().
Lo siguiente ya es la suscripción a los eventos. El primero es el evento 'connect', al igual que el otro cliente, se une al array de mineros, esto lo hace al emitir el evento 'joinToMine' y luego muestra en consola, el id del socket. Pero, como vas a ver, no se suscribe al evento para iniciar el minado, por lo que no recibe el mensaje.
Seguido a esto se emite el evento 'sendHash', al cual está suscrito el servidor, como ya has visto. Como segundo argumento en este evento, se envían todas las propiedades del objeto args.
Por último se suscribe al evento 'hashFound' que recibe como argumento el nombre del primer minero que ha encontrado el hash creado con todas las propiedades de args, y lo imprime en consola.
Con esto ya tienes toda la estructura necesaria para que se ejecute todo conectado. El orden para iniciar bien el sistema:
- Servidor
- Clientes
- Admin
Conclusiones
Pues ahora ya tienes todos los elementos del simulador de minado usando WebSockets. Ya ves que el manejo de las conexiones y eventos de este protocolo es fácil, básicamente es habituarse a la estructura de suscribirse a los eventos y emitirlos. Esto lo puedes trasladar a cualquier otro proyecto, si quieres hacerte un chat para hablar con los amigos, pues con esta información ya suficiente para poder una versión simple del proyecto ;)
Si no lo has descargado todavía, y te interesa ver el código, te dejo mi repositorio de Github para que descargues todo el proyecto: WebSockets Simulador Minería Bitcoin.
¡Felicidades, ya has integrado las bases de WebSockets, a la vez que has realizado un "simulador de minado" de Bitcoin!