Skip to content

Instantly share code, notes, and snippets.

@kelvysmoura
Last active May 26, 2022 00:00
Show Gist options
  • Save kelvysmoura/9c4c340da856bcbcb99de78573e444c4 to your computer and use it in GitHub Desktop.
Save kelvysmoura/9c4c340da856bcbcb99de78573e444c4 to your computer and use it in GitHub Desktop.
Hashcash / Proof of Work (POW)
/**
* Esse é um estudo sobre Hashcash que acabou se tornando Proof of Work (POW)
*
* As semelhança que eu identifiquei:
* - Ambos usam da capacidade computacional para resolver um desafio, que é encontrar um quantidade de zeros "0"
* em uma hash SHA-256.
*
* As diferenças que eu identifiquei:
* - Hashcash foi inicial feito para enviar spam de email. É um desafio enviado para pelo servidor, parecido com
* versão:timestamp_de_validade:nonce:SHA-256:SOLUÇÃO. A solução é o que o cliente (navegador) precisa descobrir.
*
* - PWO como é normalmente usada para transações de criptomoedas vai gerar um hash baseado nas informações da blockchain.
*
*
* COMO TESTAR?
* 1. Baixar esse codigo para um arquivo JS
* 2. importar esse JS para um HTML
* 3. Abir o HTML no navegador
* 4. Abrir o devtools do navegador e executar pow().
* 5. A função aceita um inteiro que por padrão é 2 e essa é a dificuldade da hash. Agora é so brincar um pouco com a
* dificuldade, ir aumentando pra ver o tanto de recurso usado a cada 0 exigido.
*
* Para validar a hash gerada por esse JS, dentro do PHP, basta pegar o serverData exibido no console
* e executar na função hash(), ficando assim
*
* $hash = hash('sha256', serverData);
*
* O valor da variavel PHP deve ser identica a hash mostrar no console do navegador
*
* :)
*/
/**
* Uma hash válida
*/
let hash = '';
/**
* Esse nonce é no contexto de POW.
* É usado como parte da string do hash e enquanto a hash não for o esperado pela dificuldade, é incrementado +1 no nonce
*/
let nonce = -1;
/**
* A dificuldade é o número de zeros que deve ser encontrado no início do hash
*/
async function pow(difficulty = 2) {
console.log(new Date());
/**
* A expressão Array(difficulty + 1).join(0) vai gerar uma string com a quatidade de zeros de difficulty+1
* Se a dificuldade for 2, então a string obitida será "00"
*
* A expressão hash.substr(0, difficulty) pega apenas um pedaço do inicio da hash e esse pedaço é do tamnho da dificuldade
* Se a hash gerada for 0078271db0176e77e3bcda9deaa8ee2e5bc4223b66ad1a35a15260aa32e7a1dd, substr vai pegar apenas "00" se a dificuldade for 2.
*
* Dessa forma enquando a while não for "00" === "00", vai continuar interando e gerando uma nova hash até
* encontrar uma hash que comece com essa quantidade de zeros
*/
while(hash.substr(0, difficulty) !== Array(difficulty + 1).join(0)){
nonce++;
hash = await hash256(serverData());
}
console.table({nonce, hash, serverData: serverData()});
console.log(new Date());
hash = '';
}
/**
* Aqui a o inicio da string está mais próximo do padrão Hashcash e nesse contexto o nonce é um código recebido do
* servidor e no final deve ser colocado a solução.
* Mas do jeito que ta abaixo a parte do nonce ja ta no padrão PWO, com algumas informações no começo e no final o nonce
*
* No final não importa muito que coloca aqui, qualquer informação da certo. Acho importante ter uma informação que possa
* verificado pelo servidor ou uma informação que tenha disponivel no servidor na hora de bater e conferir a hash
*/
function serverData() {
return `VERSAO:DATA:TIMERSTAMP:....:NONCE:${nonce}`;
}
/**
* Aqui é so um código que peguei pronto para gerar hash SHA-256
*/
function hash256(data) {
data = new TextEncoder().encode(data);
return crypto.subtle.digest('SHA-256', data).then(response => {
let hashArray = Array.from(new Uint8Array(response));
let hashHex = hashArray.map(hash => hash.toString(16).padStart(2, 0)).join('');
return hashHex
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment