Construye con Javascript una blockchain basica. Parte 1

red p2p

En este post, descubriremos cómo construir su red p2p para tu cadena de bloques La cadena de bloques que construiremos aqui sera muy básica pero tocaremos varios temas fundamentales que no se suelen ver a nivel de código como las redes p2p, el registro de mineros, el almacenamiento de bloques en una base de datos como level-db, creacion de wallets, creacion de APIs o la creacion de una CLI-API (Command line Interface API). Los conocimientos que se exponen aquí están destinados a fines de aprendizaje. Le darán una mejor comprensión de las blockchain y los elementos que son necesarios para lograr un prototipo completamente funcional de una cadena de bloques. No es factible de entrada crear una cadena de bloques de grado de producción completa desde cero y creandola una sola persona, sobre todo a nivel de seguridad, sin embargo, se otorgan los fundamentos para crear una.

creando una red p2p basica

El primer paso para crear su blockchain es crear una red P2P. La red P2P fue la clave para la creación de una blockchain como Bitcoin. En las criptomonedas, la red P2P puede ayudar a prevenir la problema de doble gasto gracias a PoW y también es la arquitectura central detrás de PoS (Proof of stake). En una cadena de bloques, le permite sincronizar cualquier dato necesario en una red.

Peer-to-peer (P2P) es un tipo de red informática que utiliza una arquitectura distribuida. Cada par o nodo comparte la carga de trabajo y es igual a los otros pares, lo que significa que no debe haber ningún par privilegiado

“We have proposed a system for electronic transactions without relying on trust. We started with the usual framework of

coins made from digital signatures, which provides strong

control of ownership, but is incomplete without a way to prevent double-spending. To solve this, we proposed a peer-topeer network using proof-of-work to record a public history of

transactions that quickly becomes computationally impractical for an attacker to change if honest nodes control a majority

of CPU proof-of-worker.”

—Bitcoin: A Peer-to-Peer Electronic Cash System

Vamos a mostrar cómo crear tu blockchain con Node.js, pero puedes hacer esto con cualquier otro lenguaje de programación porque los principios son los mismos. Configuremos el proyecto y crearemos una red P2P básica para enviar y recibir mensajes. Una vez que puedas enviar y recibir mensajes, podrás crear una clase de block y una lista encadenada de bloques para vincular varios bloques juntos para crear una cadena de bloques. Necesitarás Node.js instalado en tu máquina; hay muchas formas de instalarlo. Una forma fácil es a través del administrador de instaladores prediseñado; encuentre uno que se adapte a su plataforma aquí: https: //nodejs.org/en/download/

Crea una carpeta y asígnele el nombre Blockchain. Luego crea un archivo y un nombre como p2p.js y escriba el siguiente código:

const crypto = require('crypto'),
 Swarm = require('discovery-swarm'),
 defaults = require('dat-swarm-defaults'),
 getPort = require('get-port');
const peers = {};
let connSeq = 0;
let channel = 'myBlockchain';
const myPeerId = crypto.randomBytes(32);
console.log('myPeerId: ' + myPeerId.toString('hex'));
const config = defaults({
 id: myPeerId,
});
const swarm = Swarm(config);
(async () => {
 const port = await getPort();
 swarm.listen(port);
 console.log('Listening port: ' + port);
 swarm.join(channel);
 swarm.on('connection', (conn, info) => {
 const seq = connSeq;
 const peerId = info.id.toString('hex');
 console.log(`Connected #${seq} to peer: ${peerId}`);
 if (info.initiator) {
 try {
 conn.setKeepAlive(true, 600);
 } catch (exception) {
 console.log('exception', exception);
 }
 }
 conn.on('data', data => {
 let message = JSON.parse(data);
 console.log('----------- Received Message start ----
---------');
 console.log(
 'from: ' + peerId.toString('hex'),
 'to: ' + peerId.toString(message.to),
 'my: ' + myPeerId.toString('hex'),
 'type: ' + JSON.stringify(message.type)
 );
 console.log('----------- Received Message end -----
--------');
 });
 conn.on('close', () => {
 console.log(`Connection ${seq} closed, peerId:
${peerId}`);
 if (peers[peerId].seq === seq) {
 delete peers[peerId]
 }
 });
 if (!peers[peerId]) {
 peers[peerId] = {}
 }
 peers[peerId].conn = conn;
 peers[peerId].seq = seq;
 connSeq++
 })
})();
setTimeout(function(){
 writeMessageToPeers('hello', null);
}, 10000);
writeMessageToPeers = (type, data) => {
 for (let id in peers) {
 console.log('-------- writeMessageToPeers start -------- ');
 console.log('type: ' + type + ', to: ' + id);
 console.log('-------- writeMessageToPeers end ----------- ');
 sendMessage(id, type, data);
 }
};
writeMessageToPeerToId = (toId, type, data) => {
 for (let id in peers) {
 if (id === toId) {
 console.log('-------- writeMessageToPeerToId start
-------- ');
 console.log('type: ' + type + ', to: ' + toId);
 console.log('-------- writeMessageToPeerToId end ---
-------- ');
 sendMessage(id, type, data);
 }
 }
};
sendMessage = (id, type, data) => {
 peers[id].conn.write(JSON.stringify(
 {
 to: id,
 from: myPeerId,
 type: type,
 data: data
 }
 ));
};

Para que este ejemplo funcione, debe ejecutar dos instancias de este código. Tú
puede ejecutarlas desde dos máquinas separadas como se haría en la vida real, o poderias ejecutar dos instancias desde la misma máquina a través de la terminal.
Su código necesita encontrar y conectar pares, implementar servidores que están acostumbrados a descubrir otros pares y obtener un puerto TCP disponible. Eso se hace utilizando estas tres bibliotecas:

  1. discovery-swarm: Se utiliza para crear una red swarm que utiliza
    Discovery-channel para encontrar y conectar peers
  2. dat-swarm-defaults: Despliega los servidores que son usados para descubrir otros peers
  3. get-port: Obtiene puertos TCP disponibles

Instala estas librerias:

> npm install crypto discovery-swarm dat-swarm-defaults get-port
 --save

Ahora que las bibliotecas están instaladas, abra dos instancias de Terminal y navegue a la ubicación del proyecto. Ejecute el siguiente comando:

> node p2p.js

Aqui se muestra la salida por consolaen el nodo 1:

Salida por consola en el nodo 1

Y la salida del nodo 2:

Salida por consola en el nodo 2

Como puedes ver, la red generó una ID aleatoria para su máquina y eligió un puerto aleatorio utilizando las bibliotecas de descubrimiento que hemos instalado. Luego, el código pudo descubrir otros pares en la red y enviar y recibir mensajes desde y hacia estos pares. Ahora estas conectado en una red P2P con otros usuarios.

Repasemos el código para comprender mejor cómo funciona todo. Las primeras líneas de código son una declaración de importación para las bibliotecas de código abierto que estás usando en tu código.

const crypto = require('crypto'),
 Swarm = require('discovery-swarm'),
 defaults = require('dat-swarm-defaults'),
 getPort = require('get-port');
const peers = {};
let connSeq = 0;
let channel = 'myBlockchain';
const myPeerId = crypto.randomBytes(32);
console.log('myPeerId: ' + myPeerId.toString('hex'));

A continuación, generamos un objeto de configuración que contiene su ID de par. Entonces usas el config para inicializar la libreria discovery-swarm. Esta libreria se puede encontrar aquí: https://github.com/mafintosh/discovery-swarm. Lo que hace es crear una red swarm que usa la libreria discovery-channel para encontrar y conectarse a los peers en una red UCP / TCP.

const config = defaults({
 id: myPeerId,
});
const swarm = Swarm(config);

Ahora que todo está configurado y listo, estaremos creando una función async Node.js para monitorear continuamente los mensajes de eventos swarm.on.

Escuchas en el puerto aleatorio seleccionado, y una vez que se establece una conexión al peer, usas setKeepAlive para garantizar que la conexión de red se mantenga con otros peers.

const port = await getPort();
 swarm.listen(port);
 console.log('Listening port: ' + port);
 swarm.join(channel);
 swarm.on('connection', (conn, info) => {
 const seq = connSeq;
 const peerId = info.id.toString('hex');
 console.log(`Connected #${seq} to peer: ${peerId}`);
 if (info.initiator) {
 try {
 conn.setKeepAlive(true, 600);
 } catch (exception) {
 console.log('exception', exception);
 }
 }

Una vez que recibe un mensaje de datos en la red P2P, analizas los datos
usando JSON.parse, que es un comando nativo de Node.js, por lo que no necesita
para incluir la declaración de importación. Este comando decodifica su mensaje de vuelta en un objeto, y el comando toString convierte bytes en un tipo de datos de cadena legible.

 conn.on('data', data => {
 let message = JSON.parse(data);
 console.log('----------- Received Message start ----
---------');
 console.log(
 'from: ' + peerId.toString('hex'),
 'to: ' + peerId.toString(message.to),
 'my: ' + myPeerId.toString('hex'),
 'type: ' + JSON.stringify(message.type)
 );
 console.log('----------- Received Message end -----
--------');
 });

También escuchas un evento de tipo close , que te indicará que perdiste la conexión con los peers, para que pueda tomar medidas, como eliminar a los peer de tu objeto de lista peers:

 conn.on('close', () => {
 console.log(`Connection ${seq} closed, peerId: ${peerId}`);
 if (peers[peerId].seq === seq) {
 delete peers[peerId]
 }
 });
 if (!peers[peerId]) {
 peers[peerId] = {}
 }
 peers[peerId].conn = conn;
 peers[peerId].seq = seq;
 connSeq++
 })
})();

Aquí, utilizarás una función nativa setTimeout para enviar un mensaje después de diez segundos a los peers disponibles. El primer mensaje que enviará es solo un mensaje de “hello”. Creas métodos llamados writeMessageToPeers y writeMessageToPeerToId para manejar su objeto, por lo que tiene el formato de los datos que desea transmitir y a quién desea para enviarlo.

setTimeout(function(){
 writeMessageToPeers('hello', null);
}, 10000);

El método writeMessageToPeers enviará mensajes a todos los peers conectados.

writeMessageToPeers = (type, data) => {
 for (let id in peers) {
 console.log('-------- writeMessageToPeers start ------
-- ');
 console.log('type: ' + type + ', to: ' + id);
 console.log('-------- writeMessageToPeers end ---------
-- ');
 sendMessage(id, type, data);
 }
};

Además, crearás otro método, writeMessageToPeerToId, que enviará el mensaje a un peer ID, en caso de que desee comunicarse con un solo peer específico.

writeMessageToPeerToId = (toId, type, data) => {
 for (let id in peers) {
 if (id === toId) {
 console.log('-------- writeMessageToPeerToId start
-------- ');
 console.log('type: ' + type + ', to: ' + toId);
 console.log('-------- writeMessageToPeerToId end ---
-------- ');
 sendMessage(id, type, data);
 }
 }
};

Por último, sendMessage es un método genérico que utilizará para enviar un
mensaje formateado con los parámetros que le gustaría pasar e incluye lo siguiente:

  • to/from: El peer Id que se esta enviando el mensaje desde/hacia (from/to)
  • type: El tipo de mensaje
  • data: Cualquier dato que se quiera compartir en la red peer to peer

Estos parámetros serán útiles una vez que comparta su bloque en la blockchain. Te en cuenta que el mensaje que pasa debe ser una cadena y no puede ser un objeto, por lo que está utilizando una función nativa JSON.stringify para codificar sus mensajes antes de compartirlos a través de la red P2P.

sendMessage = (id, type, data) => {
 peers[id].conn.write(JSON.stringify(
 {
 to: id,
 from: myPeerId,
 type: type,
 data: data
 }
 ));
};

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s