original: https://www.savjee.be/2017/09/Implementing-proof-of-work-javascript-blockchain/
En el post anterior creamos una cadena de bloques simple en Javascript para demostrar cómo funciona una cadena de bloques. Sin embargo, muchas personas comentaron y dijeron que la implementación no estaba completa y que aún podían engañar al sistema. ¡Están en lo correcto!Nuestro blockchain necesita otro mecanismo para protegerse contra los ataques. ¡Veamos cómo podemos hacer eso!
Esta publicación de blog es parte de una serie completa:
El problema
Ahora mismo podemos crear bloques y agregarlos a nuestra cadena de bloques realmente rápido. Y esto crea 3 problemas:
- En primer lugar, las personas pueden crear bloques increíblemente rápido y enviar spam a nuestra cadena de bloques. Una inundación de bloques sobrecargaría nuestra cadena de bloques y la haría inutilizable.
- En segundo lugar, dado que es tan fácil crear un bloque válido, las personas podrían manipular uno de los bloques de nuestra cadena y luego simplemente recalcular todos los hashes de los bloques posteriores. Ellos terminarían con una cadena de bloques válida, aunque la hayan alterado.
- Y en tercer lugar, puede combinar los dos problemas anteriores para tomar el control efectivo de la cadena de bloques. Las cadenas de bloques se alimentan mediante una red de igual a igual en la que los nodos agregarán bloques a la cadena más larga disponible. Entonces puede manipular un bloque, recalcular todos los otros bloques y luego agregar tantos bloques como desee. Luego terminará con la cadena más larga y todos los pares lo aceptarán y comenzarán a agregarle sus propios bloques.
Claramente necesitamos una solución para estos problemas. Ingrese: prueba de trabajo.
¿Qué es una prueba de trabajo?
La prueba de trabajo es un mecanismo que existía antes de que se creara la primera cadena de bloques. Es una técnica simple que evita el abuso al requerir una cierta cantidad de trabajo de computación. Esa cantidad de trabajo es clave para evitar el spam y la manipulación. El spam ya no vale la pena si requiere mucha potencia informática.
Bitcoin implementa la prueba de trabajo al requerir que el hash de un bloque comience con un número específico de cero. Esto también se llama dificultad.
¡Pero espera un minuto! ¿Cómo puede cambiar el hash de un bloque? En el caso de Bitcoin, un bloque contiene detalles sobre una transacción financiera.¡Seguro que no queremos meternos con esa información solo para obtener un hash correcto!
Para solucionar este problema, blockchains agrega un valor
nonce
. Este es un número que se incrementa hasta que se encuentra un buen hash. Y como no puedes predecir el resultado de una función hash, simplemente tienes que probar muchas combinaciones antes de obtener un hash que satisfaga la dificultad. Buscar un hash válido (para crear un nuevo bloque) también se llama "minería" en cryptoworld.
En el caso de Bitcoin, el mecanismo de prueba de trabajo garantiza que solo se pueda agregar 1 bloque cada 10 minutos. Puede imaginar que los spammers tengan dificultades para engañar a la red si necesitan tanta potencia de cómputo solo para crear un nuevo bloque, y mucho menos alterar toda la cadena.
Implementación de prueba de trabajo
Entonces, ¿cómo lo implementas? Comencemos modificando nuestra clase de bloque y agregando la variable
nonce
en su constructor. Inicializaré su valor y lo estableceré 0. constructor ( index , timestamp , data , previousHash = '' ) { this . index = index ; this . previousHash = previousHash ; this . timestamp = timestamp ; this . data = data ; this . hash = this . calculateHash (); this . nonce = 0 ; }
También necesitamos un nuevo método que incrementará el nonce hasta que obtengamos un hash válido. De nuevo, esto viene dictado por la dificultad, por lo que recibiremos la dificultad como parámetro:
mineBlock ( difficulty ) { while ( this . hash . substring ( 0 , difficulty ) !== Array ( difficulty + 1 ). join ( "0" )) { this . nonce ++ ; this . hash = this . calculateHash (); } console . log ( "BLOCK MINED: " + this . hash ); }
Y finalmente también necesitamos cambiar la función
calculateHash()
porque en este momento no usa la variable nonce para calcular el hash. calculateHash () { return SHA256 ( this . index + this . previousHash + this . timestamp + JSON . stringify ( this . data ) + this . nonce ). toString (); }
Si lo arrojas todo junto, obtienes una clase Block que se ve así:
class Block { constructor ( index , timestamp , data , previousHash = '' ) { this . index = index ; this . previousHash = previousHash ; this . timestamp = timestamp ; this . data = data ; this . hash = this . calculateHash (); this . nonce = 0 ; } calculateHash () { return SHA256 ( this . index + this . previousHash + this . timestamp + JSON . stringify ( this . data ) + this . nonce ). toString (); } mineBlock ( difficulty ) { while ( this . hash . substring ( 0 , difficulty ) !== Array ( difficulty + 1 ). join ( "0" )) { this . nonce ++ ; this . hash = this . calculateHash (); } console . log ( "BLOCK MINED: " + this . hash ); } }
Modificaciones de Blockchain
Ahora que nuestros bloques tienen un nonce y se pueden extraer, debemos asegurarnos de que nuestro blockchain también sea compatible con este nuevo comportamiento. Comencemos agregando una nueva propiedad a nuestra cadena de bloques para hacer un seguimiento de la dificultad de la cadena. Empezaré por establecerlo en 2 (lo que significa que los hashes de los bloques deberían comenzar con 2 ceros).
constructor () { this . chain = [ this . createGenesisBlock ()]; this . difficulty = 2 ; }
Todo lo que queda ahora es cambiar el método
addBlock()
para que addBlock()
realmente el bloque antes de agregarlo a nuestra cadena. Aquí pasaremos la dificultad al bloque: addBlock ( newBlock ) { newBlock . previousHash = this . getLatestBlock (). hash ; newBlock . mineBlock ( this . difficulty ); this . chain . push ( newBlock ); }
¡Y eso es! Nuestro blockchain ahora tiene prueba de trabajo y protección contra el spam e intenta manipularlo.
Probándolo
Probemos ahora nuestra cadena de bloques y veamos qué efectos tiene la prueba de trabajo para agregar nuevos bloques a nuestra cadena. Usaré el mismo código que antes. Comenzaremos por crear una nueva instancia de nuestro blockchain y luego le agregaremos 2 bloques simples.
let savjeeCoin = new Blockchain (); console . log ( 'Mining block 1' ); savjeeCoin . addBlock ( new Block ( 1 , "20/07/2017" , { amount : 4 })); console . log ( 'Mining block 2' ); savjeeCoin . addBlock ( new Block ( 2 , "20/07/2017" , { amount : 8 }));
Si ejecuta esto, verá que agregar nuevos bloques sigue siendo muy rápido. Eso es porque la dificultad solo se establece en 2 (y porque las computadoras son realmente rápidas).
Si aumenta la dificultad a 5, verá que una computadora moderna tarda unos 10 segundos en explotar un bloque. Aumenta aún más y tendrás una gran protección de los atacantes.
Renuncia
Al igual que antes de una advertencia rápida: esto de ninguna manera es una implementación completa de la cadena de bloques. Todavía carece de muchas características (como una red P2P). Esto es solo para mostrar cómo funcionan las cadenas de bloques internamente.
Además, los bloques de minería en Javascript no son realmente rápidos porque solo usan un solo hilo.
Conclusión y código fuente
La prueba de trabajo es esencial para la seguridad e integridad de blockchains. Sin él, no podríamos confiar lo suficiente como para almacenar información.
El código fuente de este proyecto está disponible en Github