19
Linguagens de Programação
UVV
24
Linguagens de Programação
UVV
15
Linguagens de Programação
UVV
22
Linguagens de Programação
UVV
19
Linguagens de Programação
UVV
25
Linguagens de Programação
UVV
1
Linguagens de Programação
UVV
33
Linguagens de Programação
UVV
Texto de pré-visualização
4 Blockchain uma aplicação a sistemas distribuídos Resumo Do conceito à implementação aqui iniciamos nossa jornada para entender a tecnolo gia Blockchain por meio da construção de uma implementação de sistemas distribuídos A tecnologia blockchain revolucionou a maneira como pensamos sobre dados segurança e confiança Ela é a es pinha dorsal de criptomoedas como o Bitcoin e tem aplicações potenciais em diversos setores desde finanças até saúde Mas o que exatamente é um blockchain e como ele funciona Nesta unidade exploraremos os conceitos fundamentais por trás da tecnologia blockchain explicando o processo de construção de um blockchain simples em Python e relacionando suas aplicações em sistemas distri buídos Primeiros passos Em essência um blockchain é um livrorazão distribuído que registra transações em vários compu tadores logo um sistema distribuído Ele foi projetado para ser seguro transparente e resistente a modificações Cada bloco no blockchain contém uma lista de transações um registro de data e hora e uma referência ao bloco anterior na cadeia Essa estrutura cria um histórico contínuo e imutável de transações garantindo que os dados permaneçam precisos e à prova de adulteração O primeiro passo na construção de uma blockchain é criar um bloco Um bloco é uma unidade fundamental em uma blockchain que armazena os dados por exemplo transações e se conecta ao bloco anterior usando uma referência conhecida como hash do bloco anterior Vamos começar criando uma classe Block em Python Essa classe representará cada bloco em nossa blockchain class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfhash Vamos analisar com cuidado o código acima o método init este é o método construtor em Python que inicializa o bloco com atributos específicos 83 84 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos index representa a posição do bloco no blockchain timestamp considere esse atributo como sendo um carimbo de datahora ou seja ele registra a hora em que o bloco foi criado data pode ser qualquer informação ou transação que desejamos armazenar no bloco priorhash uma string que contém o hash do bloco anterior no blockchain Esse atributo garante que os blocos estejam interligados hash atributo que armazenará o hash do bloco atual após o cálculo Por enquanto ele é inicializado como uma string vazia Esta classe será a base do nosso blockchain ainda que venhamos a realizar algumos modificações à frente durante o nosso desenvolvimento Nas próximas etapas precisamos calcular o hash para cada bloco criar uma cadeia de blocos e garantir a integridade do nosso blockchain Compreendendo Criptografia e Hashing Antes de nos aprofundarmos em blockchain é crucial entender a diferença entre criptografia e hashing pois esses conceitos são vitais para a segurança dos dados Criptografia é um algoritmo bidirecional projetado para compartilhar dados com segurança durante o transporte Se um hacker obtiver a chave ele poderá descriptografar os dados de volta à sua forma original Isso é ótimo para alguns casos de uso mas não para outros como senhas Já um Hashing é um algoritmo unidirecional Ele recebe dados de entrada e retorna uma sequência única de comprimento fixo conhecida como hash Um hash é projetado para ser irrever sível o que significa que não é possível retornar do hash aos dados originais1 É por isso que os hashes são cruciais para armazenar senhas com segurança e garantir a integridade dos dados em um blockchain Os programadores usam o hashing para transformar os dados de entrada em um valor de tamanho fixo Esse valor representa os dados de forma exclusiva e a técnica de hashing facilita a transmissão e o armazenamento seguro de várias formas de dados O hashing protege os dados contra acesso não autorizado e adulteração É um ingrediente essencial nos casos de uso de integridade e segurança de dados Nesta parte vamos explora tudo o que você precisa saber sobre hashing em Python O que é Hashing em Python Hashing converte dados de entrada como uma string arquivo ou objeto em uma string de bytes de tamanho fixo O hash ou digest representa a entrada de uma maneira única e reproduzível O hashing desempenha um papel significativo na detecção de manipulação de dados e no au mento da segurança Ele pode calcular um valor de hash para um arquivo mensagem ou outro tipo de dado Um aplicativo armazena o hash de forma segura para verificar posteriormente que os dados não foram adulterados Um dos usos mais comuns do hashing na segurança é o armazenamento de senhas O hashing é uma alternativa viável ao armazenamento de senhas de texto simples em um banco de dados Quando 1Bem pretendo assustar vocês em relação a essa informação Tabelas Rainbow são listas précomputadas de senhas comumente usadas e seus hashes correspondentes Essas tabelas podem facilitar a descoberta de senhas por invasores Acesse crackstationnet e insira um hash que você criou para verificar se ele realmente está seguro 85 um usuário digita sua senha o sistema faz o hash antes de armazenála no banco de dados Se um hacker acessar o banco de dados ele descobrirá que a senha é difícil de ser roubada As funções de hashing do Python tornam tudo isso possível Essas funções matemáticas per mitem que um aplicativo manipule dados em valores de hash Como criar uma função Hashing eficaz Uma função de hash deve atender aos seguintes critérios para ser eficaz e segura Determinística dada a mesma entrada a função deve sempre retornar a mesma saída Eficiente deve ser computacionalmente eficiente ao calcular o valor de hash de uma determi nada entrada Resistente a colisões a função deve minimizar a chance de duas entradas gerarem o mesmo valor de hash Uniforme as saídas da função devem ser distribuídas uniformemente no intervalo de valores de hash possíveis Não inversível deve ser improvável que um computador calcule o valor de entrada da função com base no valor de hash Não previsível prever os resultados da função deve ser um desafio dado um conjunto de entradas Sensível a alterações de entrada a função deve ser sensível a pequenas diferenças na entrada Pequenas alterações devem causar uma grande diferença no valor de hash resultante Casos de uso de Hashing Quando você tiver uma função de hashing adequada com todas essas características poderá aplicála a vários casos de uso As funções de hashing funcionam bem para Armazenamento de senhas o hashing é uma das melhores maneiras de armazenar senhas de usuários em sistemas modernos O Python combina vários módulos para fazer hash e proteger as senhas antes de armazenálas em um banco de dados Armazenamento em cache o hashing armazena a saída de uma função para economizar tempo ao chamála posteriormente Recuperação de dados o Python usa uma tabela de hash com uma estrutura de dados de dicionário integrada para recuperar rapidamente os valores por chave Assinaturas digitais o hashing pode verificar a autenticidade das mensagens que têm assi naturas digitais Verificações de integridade de arquivos o hashing pode verificar a integridade de um arquivo durante sua transferência e download 86 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos Função de Hashing integrada do Python A função de hashing integrada do Python hash retorna um valor inteiro que representa o objeto de entrada Em seguida o código usa o valor de hash resultante para determinar o local do objeto na tabela de hash Essa tabela de hash é uma estrutura de dados que implementa dicionários e conjuntos O código abaixo demonstra como a função hash funciona mystring Ola Mundo Calculate the hash value of the string hashvalue hashmystring Print the string and its hash value printString mystring printValor Hash hashvalue cuja saída é String Ola Mundo Valor Hash 2532871366394334124 Observe que ao rodar novamente o código iremos obter um novo valor para o hash da mensa gem String Ola Mundo Valor Hash 6006117206146337754 O valor do hash é diferente quando invocado uma segunda vez porque as versões recentes do Python versões 33 e posteriores por padrão aplicam uma semente de hash aleatória para essa função A semente muda em cada invocação do Python Em uma única instância os resultados serão idênticos Por exemplo vamos colocar esse código em nosso arquivo exemplo02py mystring Ola Mundo Calculando 2 valores hash da string hashvalue1 hashmystring hashvalue2 hashmystring Imprimindo a string e seus respectivos hashs printString mystring printValor Hash 1 hashvalue1 printValor Hash 2 hashvalue2 Obtemos a seguinte saída String Ola Mundo Valor Hash 1 5459162928360796302 Valor Hash 2 5459162928360796302 87 Limitações do Hashing Embora a função hash do Python seja promissora para vários casos de uso suas limitações a tornam inadequada para fins de segurança Veja como Ataques de colisão uma colisão ocorre quando duas entradas diferentes produzem o mesmo valor de hash Um invasor pode usar o mesmo método de criação de entrada para contornar medidas de segurança que dependem de valores de hash para autenticação ou verificações de integridade de dados Tamanho de entrada limitado como as funções de hash produzem uma saída de tamanho fixo independentemente do tamanho da entrada uma entrada de tamanho maior do que a saída da função de hash pode causar uma colisão Previsibilidade uma função de hash deve ser determinística fornecendo o mesmo resultado sempre que você fornecer a mesma entrada Os invasores podem tirar proveito desse ponto fraco précompilando valores de hash para muitas entradas e em seguida comparandoos com hashes de valoresalvo para encontrar uma correspondência Esse processo é chamado de ataque de tabela arcoíris Para evitar ataques e manter seus dados seguros use algoritmos de hash seguros projetados para resistir a essas vulnerabilidades Uso do hashlib para hashing seguro em Python Em vez de usar a função hash nativa do Python utilize hashlib para um hashing mais seguro Este módulo do Python oferece uma variedade de algoritmos de hash para criptografar dados de forma segura Estes algoritmos incluem MD5 SHA1 e a família SHA2 mais segura que engloba SHA256 SHA384 SHA512 entre outros MD5 O algoritmo criptográfico md5 amplamente utilizado revela um valor de hash de 128 bits Use o código abaixo para gerar um hash md5 usando o construtor da hashlib import hashlib text Ola Mundo hashobject hashlibmd5textencode printhashobjecthexdigest A saída do código acima será consistente em todas as execuções 973de02327c22cb78f9c1d525fdbd039 Observação o método hexdigest no código acima retorna o hash em um formato hexade cimal seguro para qualquer apresentação não binária como email 88 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos SHA1 A função de hash SHA1 protege os dados criando um valor de hash de 160 bits Use o código abaixo com o construtor sha1 para o hash SHA1 do módulo hashlib import hashlib text Ola Mundo hashobject hashlibsha1textencode printhashobjecthexdigest A saída do código acima cbdf6ff8ea93c229d8e019563c538a0838387f58 SHA256 Há várias opções de hash na família SHA2 O construtor hashlib SHA256 gera uma versão mais segura dessa família com um valor de hash de 256 bits Os programadores costumam usar o SHA256 para criptografia como assinaturas digitais ou códigos de autenticação de mensagens O código abaixo demonstra como gerar um hash SHA256 import hashlib text Ola Mundo hashobject hashlibsha256textencode printhashobjecthexdigest A saída do código acima af3724df163a19e336eb9f2c0b12fdcf9c206dd353077a7cc6c41755fae5d0b6 SHA384 SHA384 é um valor de hash de 384 bits Os programadores costumam usar a função SHA384 em aplicativos que precisam de mais segurança de dados Com base nos exemplos anteriores você provavelmente pode adivinhar que esta é uma instrução que gerará um hash SHA384 hashobject hashlibsha384textencode SHA512 O SHA512 é o membro mais seguro da família SHA2 Ele gera um valor de hash de 512 bits Os programadores o utilizam para aplicativos de alto rendimento como a verificação da integridade dos dados O código abaixo mostra como gerar um hash SHA512 com o módulo hashlib no Python hashobject hashlibsha512textencode 89 Como escolher um algoritmo de Hashing Como esses algoritmos são diferentes selecione o algoritmo de hashing com base no caso de uso e nos requisitos de segurança Aqui estão algumas etapas que você deve seguir Entenda o caso de uso o caso de uso determina o tipo de algoritmo a ser usado Por exemplo ao armazenar dados confidenciais como senhas o algoritmo de hash deve proteger contra ataques de força bruta Considere suas necessidades de segurança os requisitos de segurança do seu caso de uso dependem do tipo de dados que você pretende armazenar e eles determinam qual algoritmo escolher Por exemplo um algoritmo de hashing robusto é mais adequado para armazenar informações altamente sensíveis Pesquise os algoritmos de hashing disponíveis explore cada tipo de hashing para enten der seus pontos fortes e fracos Essas informações ajudam você a selecionar a melhor opção para o seu caso de uso Avalie o algoritmo de hashing selecionado depois que você escolher um algoritmo de hashing avalie se ele atende aos seus requisitos de segurança Esse processo pode envolver testes contra ataques ou vulnerabilidades conhecidas Implemente e teste o algoritmo de hashing por fim implemente e teste o algoritmo minuciosamente para garantir que ele funcione de forma correta e segura Como usar o Hashing para armazenamento de senhas O hashing tem excelente potencial para armazenar senhas um componente essencial da segurança cibernética O ideal é que o aplicativo faça hash e armazene as senhas em um banco de dados seguro para evitar acesso não autorizado e violações de dados No entanto o hash sozinho pode não ser suficiente para proteger as informações As senhas com hash ainda são suscetíveis a ataques de força bruta e de dicionário Os hackers geralmente usam essas práticas para adivinhar senhas e obter acesso não autorizado às contas Uma maneira mais segura de usar hashing para o armazenamento de senhas envolve a técnica de salting2 Salting adiciona strings ou caracteres únicos e aleatórios a cada senha antes de transformá la em hash O salt é único para cada senha e o aplicativo o armazena junto com a senha em hash no banco de dados Sempre que um usuário faz login o aplicativo recupera o salt do banco de dados adicionao à senha inserida e em seguida faz o hash do salt e da senha combinados Se um invasor ganhar acesso ao banco de dados ele terá que calcular o hash para cada senha e cada possível valor de salt Salting torna esses ataques mais complexos sendo uma técnica útil para desencorajar ataques de dicionário O módulo secrets do Python facilita o salting Esse módulo gera salts aleatórios armazenando senhas de forma segura e gerenciando tokens e chaves criptográficas O código abaixo usa a biblioteca hashlib e o módulo secrets para proteger ainda mais as senhas dos usuários 2Em traduções livres podemos considerar literalmente que estamos salgando falsificando ou imunizando a nossa codificação ou seja deixandoa mais aleatória 90 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos import hashlib import secrets Gera um salt aleatorio usando o modulo secrets salt secretstokenhex16 printsalt Define a senha do usuario password bolinhas Hash para a senha usando o salt e o algoritmo SHA256 hashobject hashlibsha256password saltencode Transforma a saida hash em uma representacao hexadecimal hashhex hashobjecthexdigest printhashhex As saídas são as seguintes 57bb950e7be8a47f02a0b1583ecc9afa e492515d1b26203aef1fcb579f9ef3d553df3577ceb0f18bfeaf3d791894669b Como usar o Hashing para verificação de integridade de dados O hashing também ajuda a verificar a integridade dos dados e a proteger os dados transmitidos contra modificações e adulterações Essa técnica de quatro etapas usa uma função de hash criptográfico para dar ao arquivo um valor de hash exclusivo Primeiro selecione a função de hash apropriada e use para gerar um valor de hash para os dados de entrada Armazene esse valor de hash e use para comparação quando necessário Sempre que você precisar verificar a integridade dos dados o aplicativo gerará o valor de hash dos dados atuais usando a mesma função de hash Em seguida o aplicativo compara o novo valor de hash com o valor armazenado para garantir que eles sejam idênticos Em caso afirmativo os dados não serão corrompidos O valor de hash é exclusivo e até mesmo uma pequena alteração nos dados de entrada aciona um valor de hash significativamente diferente Isso facilita a detecção de alterações ou modificações não autorizadas nos dados transmitidos As etapas abaixo demonstram o uso de uma função de hash para verificações de integridade de dados ETAPA 1 importe o módulo hashlib import hashlib ETAPA 2 use um algoritmo de hash hashlib def generatehashfilepath Abra o arquivo em modo binario with openfilepath rb as f Leia o conteudo do arquivo contents fread Gere o hash SHA256 do conteudo hashobject hashlibsha256contents Retorne a representacao hexadecimal do hash return hashobjecthexdigest ETAPA 3 chame a função e passe o caminho do arquivo filepath pathtomyfiletxt hashvalue generatehashfilepath printhashvalue ETAPA 4 gere hashes para o arquivo original e para o arquivo transmitido ou modificado Gernerando hash do arquivo original originalfilepath pathtomyfiletxt originalfilehash generatehashoriginalfilepath Transmitir ou modificar o arquivo por exemplo copiando para uma diferente localizao transmittedfilepath pathtotransmittedfiletxt Gerar o hash do arquivo transmitido transmittedfilehash generatehashtransmittedfilepath ETAPA 5 Compare os dos hashes if originalfilehash transmittedfilehash printO arquivo nao foi adulterado else printO arquivo foi adulterado O hashing é inestimável para a integridade de dados e a segurança de senhas Você aproveita ao máximo uma função de hashing quando implementa técnicas de hashing seguras como o uso do módulo hashlib e salting Essas técnicas ajudam a prevenir ataques do tipo rainbow colisões e outras vulnerabilidades de segurança que afetam o hashing Programadores frequentemente utilizam essas técnicas com funções de hashing em Python para garantir a integridade dos dados de arquivos e armazenar senhas de forma segura BLUF Bottom Line Up Front Resumo Inicial Uma blockchain é uma cadeia de blocos com dados em cada bloco onde 92 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos Os dados nos blocos são à prova de violação graças à criptografia Utilizam Tecnologia de Livrorazão Distribuído É pública completamente aberta a qualquer pessoa garantindo transparência Vamos começar analisando um único bloco em uma blockchain Cada bloco contém Dados Um identificador único gerado usando um algoritmo de hash Hash do Bloco Anterior o que corresponde a uma Liga os blocos Os dados armazenados em uma blockchain dependem do tipo de blockchain Por exemplo Bitcoin e Litecoin os dados podem incluir o remetente o destinatário e a quantidade de criptomoeda Blockchains de Geração 2 podem incluir dados personalizados além da criptomoeda Blockchains de Geração 3 podem armazenar quaisquer dados personalizados sem necessa riamente envolver criptomoeda Nas etapas a seguir desenvolveremos essa base calculando hashes ligando blocos e garantindo a integridade da nossa blockchain Criando nossa gênese do blockchain Já estudamos como criar hashs Também já definimos uma classe para a estrutura básica de um bloco em nossa blockchain Agora é hora de gerar um identificador único para cada bloco usando um algoritmo de hash Esse hash servirá como a impressão digital do bloco garantindo que qualquer al teração em seu conteúdo resulte em um hash completamente diferente mantendo assim a integridade da blockchain Como discutido um hash é uma função unidirecional que converte dados de entrada em uma sequência de caracteres de comprimento fixo geralmente uma combinação de números e letras No contexto da blockchain o hashing é usado para conectar blocos e proteger os dados dentro de cada bloco Se um único caractere no bloco mudar o hash mudará drasticamente alertando o sistema sobre uma possível violação Primeiro precisamos adicionar um método à nossa classe Block que irá gerar o hash com base nos dados do bloco import hashlib import json class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfhash selfcreatehash def createhashself blockstring fselfindexselfpriorhashselftimestampselfdata encode return hashlibsha256blockstringhexdigest class StudentBlockChain def initself selfchain selfcreategenesisblock def creategenesisblockself return Block0 29111979MyFirstBlockChainUVVbr0 if name main studentcoin StudentBlockChain firstblock studentcoincreategenesisblock printfirstblockdict printjsondumpsstudentcoinchain defaultlambda o odict indent 4 A saída do código acima é a seguinte index 0 timestamp 29111979 data MyFirstBlockChainUVVbr priorhash 0 hash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e Uma vez que a saída foi gerada vamos entender o que fizemos O primeiro bloco em qualquer blockchain é conhecido como bloco gênese Esse bloco é único porque não possui nenhum bloco anterior ao qual se referir portanto seu priorhash normalmente é definido como um valor padrão A classe de criação do bloco se chama StudentBlockChain mas podemos nomeála como quisermos O importante aqui é lembrar que ela é que gerenciará o blockchain O construtor desta classe inicializará o blockchain Inicialmente ele estará vazio Por fim compreenda que o primeiro bloco da blockchain conhecido como bloco genesis é especial porque não tem predecessor Sendo assim criamos um método chamado creategenesisblock que retornará este bloco Portanto o bloco gênese terá parâmetros fixos índice 0 carimbo de datahora uma data fixa por exemplo 29111979 dados MyFirstBlockChainUVVbr priorhash 0 Esses parâmetros garantem que o bloco gênese seja único e sirva como ponto de partida para a blockchain Com o bloco de gênese definido nossa blockchain está pronta para crescer Na próxima etapa exploraremos como adicionar novos blocos à blockchain e garantir que cada bloco esteja vinculado com segurança ao seu antecessor mantendo a integridade da cadeia Adicionando e gerenciando blocos Agora que nossa blockchain foi inicializada com o bloco genesis é hora de adicionar mais blocos à cadeia Nesta etapa criaremos métodos para recuperar o último bloco da cadeia e adicionar novos blocos garantindo que cada novo bloco faça referência ao anterior mantendo a integridade da blockchain ou seja continuaremos construindo a classe StudentBlockChain adicionando dois novos métodos getlastblock e addblock O objetivo do método getlastblock é retornar o último bloco no blockchain o que é essencial para vincular novos blocos à cadeia Quanto ao método addblock este cuidará da adição de novos blocos à blockchain Este método irá Definir o priorhash do novo bloco como o hash do último bloco na cadeia Calcular o hash do novo bloco Adicionar o novo bloco à blockchain Vamos à implementação import hashlib import json class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfhash selfcreatehash def createhashself blockstring fselfindexselfpriorhashselftimestampselfdata encode return hashlibsha256blockstringhexdigest class StudentBlockChain def initself selfchain selfcreategenesisblock def creategenesisblockself return Block0 29111979MyFirstBlockChainUVVbr0 def getlastblockself return selfchain1 def addblockself newblock newblockpriorhash selfgetlastblockhash newblockhash newblockcreatehash selfchainappendnewblock if name main studentcoin StudentBlockChain firstblock studentcoincreategenesisblock printfirstblockdict studentcoinaddblockBlock1 12121979 amount 27 studentcoinaddblockBlock2 01011980 amount 13 studentcoinaddblockBlock3 04021980 amount 75 printjsondumpsstudentcoinchain defaultlambda o odict indent4 O código apresenta então a seguinte saída index 0 timestamp 29111979 data MyFirstBlockChainUVVbr priorhash 0 hash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e index 1 timestamp 12121979 data amount 27 priorhash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e hash 8f3dbb1621eaa7c73f5ca4efd85706cd6d8d721e2c61701d32827ab7acefdefb index 2 timestamp 01011980 data amount 13 priorhash 8f3dbb1621eaa7c73f5ca4efd85706cd6d8d721e2c61701d32827ab7acefdefb hash e719478517ffc2e64881a9683c6b4acfc1f26c257f26181b01358aa4a209b35f index 3 timestamp 04021980 data amount 75 priorhash e719478517ffc2e64881a9683c6b4acfc1f26c257f26181b01358aa4a209b35f hash 72024f5e3b673f79b9363c3beeac24f1a259f985b036cbb7f5279ad08a3ba8e8 Com a capacidade de adicionar e gerenciar blocos nossa blockchain agora está funcional No entanto blockchains do mundo real envolvem mais complexidade como validação de transações garantia de consenso entre nós e muito mais Nas próximas etapas exploraremos recursos adicionais que podem ser adicionados para tornar nossa blockchain mais robusta e segura Validando o BlockChain Essa etapa é crítica para garantir que estamos lidando com um blockchain íntegro Para cumprila implementaremos um método para validar a blockchain garantindo que nenhum dado tenha sido adulterado Este é um aspecto crucial da tecnologia blockchain pois garante a confiabilidade de toda a cadeia Adicionaremos um método chamado validatingblockchain à classe StudentBlockChain O objetivo deste método será percorrer o blockchain a fim de verificar se o hash de cada bloco está correto e se cada bloco aponta para o bloco anterior correto Ou seja o método validatingblockchain fará um loop no blockchain começando pelo bloco após o bloco de gênese comparando o hash armazenado com o hash calculado e verificando se o priorhash do bloco atual corresponde ao hash do bloco anterior Vamos a implementação import hashlib import json class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfhash selfcreatehash def createhashself blockstring fselfindexselfpriorhashselftimestampselfdataencode return hashlibsha256blockstringhexdigest class StudentBlockChain def initself selfchain selfcreategenesisblock def creategenesisblockself return Block0 29111979MyFirstBlockChainUVVbr0 def getlastblockself return selfchain1 def addblockself newblock newblockpriorhash selfgetlastblockhash newblockhash newblockcreatehash selfchainappendnewblock def validatingblockchainself for i in range1 lenselfchain currentblock selfchaini previousblock selfchaini1 Check if has for the current block if currentblockhash currentblockcreatehash return False Check current block points to the previous blocks if currentblockpriorhash previousblockhash return False return True if name main studentcoin StudentBlockChain firstblock studentcoincreategenesisblock printfirstblockdict studentcoinaddblockBlock1 12121979 amount 27 studentcoinaddblockBlock2 01011980 amount 13 studentcoinaddblockBlock3 04021980 amount 75 printjsondumpsstudentcoinchain defaultlambda o odict indent4 Check if has for the current block printIs studentcoin Valid strstudentcoinvalidatingblockchain Changing the blockchain studentcoinchain1data amount 28 Tampering with the data printIs studentcoin Valid after change strstudentcoinvalidatingblockchain Update the hash to match the change data studentcoinchain1hash studentcoinchain1createhash printIs studentcoin Valid after updating the hash strstudentcoinvalidatingblockchain Temos a seguinte saída 98 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos index 0 timestamp 29111979 data MyFirstBlockChainUVVbr priorhash 0 hash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e index 1 timestamp 12121979 data amount 27 priorhash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e hash 8f3dbb1621eaa7c73f5ca4efd85706cd6d8d721e2c61701d32827ab7acefdefb index 2 timestamp 01011980 data amount 13 priorhash 8f3dbb1621eaa7c73f5ca4efd85706cd6d8d721e2c61701d32827ab7acefdefb hash e719478517ffc2e64881a9683c6b4acfc1f26c257f26181b01358aa4a209b35f index 3 timestamp 04021980 data amount 75 priorhash e719478517ffc2e64881a9683c6b4acfc1f26c257f26181b01358aa4a209b35f hash 72024f5e3b673f79b9363c3beeac24f1a259f985b036cbb7f5279ad08a3ba8e8 Is studentcoin Valid True Is studentcoin Valid after change False Is studentcoin Valid after updating the hash False Esta etapa demonstrou a importância de validar uma blockchain para garantir sua integridade Embora nossa blockchain simples não inclua recursos avançados como prova de trabalho ou uma rede distribuída por enquanto ela nos serviu para ilustrar os princípios fundamentais da tecnologia blockchain Em cenários reais camadas adicionais de segurança e validação são necessárias para proteger contra adulterações e fraudes Note que a saída da função de hash pode ser usada como uma assinatura digital Imagine que você tem uma string com 10 milhões de caracteres digamos que você esteja escrevendo um romance e para garantir que esse romance não seja adulterado você diz a todos os seus leitores em potencial que eles precisam contar os 10 milhões de caracteres para garantir que o romance não seja corrompido Ninguém faria isso Mas com o hash você pode publicar a validação de saída com apenas 64 caracteres por email por exemplo e seus leitores em potencial podem fazer o hash do romance que comprambaixam e comparálos para garantir que o romance deles seja legítimo Então o que fizemos até aqui foi adicionar o hash do bloco pai na classe do bloco Dessa forma mantemos a assinatura digital do bloco pai em nosso bloco mais atual Isso significa que se nós por pura maldade alterarmos o conteúdo de qualquer bloco o hash do bloco pai em qualquer bloco filho será inválido e você será pego em flagrante Mas não poderíamos alterar o hash do bloco pai do bloco filho se quisermos alterar o conteúdo de qualquer bloco Sim podemos No entanto o processo 99 de alteração do conteúdo se torna mais difícil Nesse caso precisaríamos seguir dois passos Agora imagine que você tem 10 blocos e quer alterar o conteúdo do primeiro bloco Nesse caso você deve alterar o hash do bloco pai no bloco filho imediato Mas infelizmente existem ramificações invisíveis nisso Tecnicamente falando o hash do bloco pai em seu filho imediato é parte do conteúdo desse bloco Isso significaria que o hash do bloco pai em seu filho o neto do primeiro bloco seria inválido Portanto para seguirmos com essa alteração fraude precisaríamos alterar o hash do pai desse neto mas isso afeta o bloco subsequente E agora devemos alterar os hashes dos pais de todos os blocos Para isso são necessários 10 passos Ou seja usar o hash de um pai torna a adulteração muito mais difícil Entendendo as conexões que se realizam em cada novo bloco em função dos hashs é hora de aprendermos a inovação construída sobre ele Isso nos leva à prova de trabalho Na sequência iremos implementar uma prova de trabalho nessa arquitetura para demonstrar esse fundamento de segurança antes de avançarmos para a parte distribuída Prova de Trabalho Palavras são baratas ações são valiosas Portanto devemos ter em mente que embora todos possam fazer hashing nem todos estão dispostos a fazêlo muitas vezes Para garantir que alguém seja digno de proteger dados no blockchain ele deve estar disposto a realizar muitos processos de hash Esta é a base da tecnologia blockchain Na terminologia da blockchain aquele que escreve os hashs é chamado de minerador Para inserir a transação na blockchain o minerador precisa realizar algum trabalho primeiro Introduziremos uma variável nonce3 na classe Block para randomizar o conteúdo do bloco alterando assim o hash resultante Em seguida implementamos o método mineblock para garantir que o hash de cada bloco comece com um número específico de zeros representando o nível de dificuldade A variável nonce será inicializada como zero e incrementada sempre que tentarmos criar um hash válido O objetivo do nonce é proteger as transações em blockchain Veremos mais tarde que para criar uma transação deveremos obter o nonce de quantas transações foram criadas no blockchain Por exemplo se foram criadas três transações e desejamos criar uma nova transação deve usar 3 como valor para o nonce No Bitcoin por exemplo o algoritmo de prova de trabalho tem nome chamase Hashcash E não é muito diferente do exemplo básico que estamos tratando aqui É o algoritmo que os mineradores correm para resolver a fim de criar um novo bloco Em geral a dificuldade é determinada pelo número de zeros no início de uma sequência os mineradores são então recompensados por sua solução recebendo um número definido de Bitcoins No nosso código também vamos inserir essa variável difficulty para ajustar a dificuldade da mineração modificando assim o número de zeros à esquerda O ajustamos para quatro Perceba que a adição de um único zero à esquerda faz uma diferença exponencial no tempo necessário para encontrarmos uma solução No Bitcoin a dificuldade é ajustada a cada 2016 blocos por consenso na rede A dificuldade é aumentada para evitar a inevitável aceleração do hardware Isso porque a dificuldade da prova de trabalho é determinada por uma média móvel que visa um número médio de blocos por hora a serem processados Se eles forem gerados muito rápido a dificuldade deve aumentar a fim de se manter o controle do processamento Vamos à implementação 3Em tradução literal nenhum import hashlib import json class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfnonce 0 selfhash selfcreatehash def createhashself blockstring fselfindexselfpriorhashselftimestampselfdataselfnonceencode return hashlibsha256blockstringhexdigest def mineblockself difficulty while selfhashdifficulty 0 difficulty selfnonce 1 selfhash selfcreatehash printBlock Hash selfhash class StudentBlockChain def initself selfchain selfcreategenesisblock selfdifficulty 4 def creategenesisblockself return Block0 29111979MyFirstBlockChainUVWbr0 def getlastblockself return selfchain1 def addblockself newblock newblockpriorhash selfgetlastblockhash newblockhash newblockcreatehash newblockmineblockselfdifficulty selfchainappendnewblock def validatingblockchainself for i in range1 lenselfchain currentblock selfchaini previousblock selfchaini1 Check if has for the current block if currentblockhash currentblockcreatehash return False Check current block points to the previous blocks if currentblockpriorhash previousblockhash return False return True if name main studentcoin StudentBlockChain firstblock studentcoincreategenesisblock studentcoinaddblockBlock1 12121979 amount 27 studentcoinaddblockBlock2 01011980 amount 13 studentcoinaddblockBlock3 04021980 amount 75 printjsondumpsstudentcoinchain defaultlambda o odict indent4 102 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos index 1 timestamp 12121979 data amount 27 priorhash f10deec5d0033d56c0566a301fdfca632dc2f427442a87685ab5618156697817 nonce 67376 hash 0000a4266a75769d998f8a676657bfc8d684d623b8a2c533f28ea53ace44b2a6 index 2 timestamp 01011980 data amount 13 priorhash 0000a4266a75769d998f8a676657bfc8d684d623b8a2c533f28ea53ace44b2a6 nonce 99729 hash 0000aa4676e56b9c606ef178e5fbf50a5291789f766ad72923629b62eb914dea index 3 timestamp 04021980 data amount 75 priorhash 0000aa4676e56b9c606ef178e5fbf50a5291789f766ad72923629b62eb914dea nonce 36748 hash 00009e4c832667bffca97ef317693a88f3af4bf937fedf68cc67096f9b1736ec Para compreender melhor a nova saída do nosso código vamos destacar os pontos chaves Variável nonce é um número usado apenas uma vez no registro Ele é incrementado até que o hash atenda aos critérios de dificuldade Serve como um randomizador no sistema de Prova de Trabalho PoW do blockchain O nonce é incrementado continuamente até que o hash do bloco atenda a uma condição específica normalmente que comece com um certo número de zeros conforme determinado pelo nível de dificuldade Método mineblock este método garante que a criação de blocos seja computacionalmente intensiva exigindo que o hash comece com um determinado número de zeros A consequência desse métoda é a mineração processo de encontrar um hash de bloco válido O objetivo da mineração é encontrar um nonce que quando combinado com o conteúdo do bloco produza um hash que atenda ao nível de dificuldade exigido por exemplo comece com quatro zeros no nosso exemplo Variável difficulty o nível de dificuldade controla quantos zeros à esquerda são necessários no hash impactando diretamente a dificuldade de minerar um bloco O nível de dificuldade nesta blockchain é definido como 4 o que significa que um hash de bloco válido deve começar com pelo menos quatro zeros 0000 Isso garante que a criação de um bloco válido seja computacionalmente intensiva e não algo que possa ser feito instantaneamente aumentando a segurança quanto a modificações não autorizadas Sendo assim logo no início da saída do código podemos ver uma série de Hash de Bloco cada uma representando uma tentativa de encontrar um hash válido incrementando o nonce O nonce começa em 0 e é incrementado até que o hash atenda ao requisito de dificuldade 103 Assim que um hash válido é encontrado iniciando com quatro zeros ele é armazenado como o hash do bloco e o valor do nonce que atingiu esse hash é armazenado junto com ele Isso garante que cada bloco tenha um identificador único computacionalmente difícil de alterar Compreenda que o nonce e o nível de dificuldade tornam extremamente difícil alterar qualquer bloco na blockchain Se alguém tentar adulterar os dados de um bloco o hash será alterado e a cadeia deixará de ser válida a menos que o bloco seja reminerado encontrando um novo nonce para cada bloco subsequente uma tarefa computacionalmente proibitiva Ou seja Este processo garante que a criação de cada bloco exija esforço computacional o que protege o blockchain de adulterações Com a implementação da Prova de Trabalho nossa blockchain agora está mais segura e re sistente a adulterações Em uma blockchain real recursos de segurança adicionais como consenso distribuído e assinaturas criptográficas seriam necessários Nas próximas etapas revisitaremos o processo de validação e discutiremos como lidar com casos em que a blockchain for considerada inválida 104 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos Atividade Análise e Extensão de um Código de Mineração Blockchain Uma vez que concluímos a parte introdutória sobre blockchain expomos códigos em Python que simulam e mineram os blocos com prova de trabalho simples usando hashlib e um nonce vamos agora analisar modificar e estender os código para criar uma aplicação distribuída e concorrente básica Os objetivos desta atividade são os seguintes Compreender os mecanismos de mineração por prova de trabalho PoW Explorar concorrência no processo de mineração uso de threads ou multiprocessing Simular uma rede blockchain distribuída com múltiplos nós minerando blocos Identificar desafios reais de consenso propagação e sincronização Vocês deverão cumprir as seguintes etapas 1 Análise e Explicação individual ou em dupla estudar o código fornecido no material introdutório de aula apresentar comentários explicativos nas seguintes partes Estrutura do bloco campos encadeamento via hash Algoritmo de prova de trabalho Verificação da validade do bloco Responda brevemente O que impede dois blocos de serem minerados simultaneamente Como o hash e o nonce garantem a dificuldade da mineração 2 Modificação e Concorrência Altere o código para executar mineração com múltiplas threads ou processos competindo para encontrar um nonce válido Quando uma thread encontrar o nonce as outras devem parar imediatamente Meça o tempo de mineração com 1 2 4 8 threads e registre os resultados em um gráfico Exemplo de Resultado Esperado Minerando com 1 thread Tempo 123s Minerando com 2 threads Tempo 68s Minerando com 4 threads Tempo 35s A partir do padrão de saída acima fornecer gráfico threads vs tempo de mineração Entrega Código com comentários Gráfico de desempenho Respostas conceituais Avaliação threadminerpy import hashlib import time import threading Classe que representa um bloco da blockchain class Block def initself index previoushash timestamp data nonce0 selfindex index selfprevioushash previoushash selftimestamp timestamp selfdata data selfnonce nonce selfhash selfcalculatehash Funcao que calcula o hash do bloco def calculatehashself value strselfindex selfprevioushash strselftimestamp selfdata strselfnonce return hashlibsha256valueencodehexdigest Funcao que executa a prova de trabalho mineracao def mineblockself difficulty stopevent prefix 0 difficulty while not selfhashstartswithprefix if stopeventisset return selfnonce 1 selfhash selfcalculatehash Avisar que um bloco foi minerado stopeventset printfBloco minerado com nonce selfnonce selfhash Blockchain basica com lista de blocos class Blockchain def initself difficulty4 selfchain selfcreategenesisblock selfdifficulty difficulty def creategenesisblockself return Block0 0 timetime Genesis Block def getlatestblockself return selfchain1 def addblockself newblock newblockprevioushash selfgetlatestblockhash newblockmineblockselfdifficulty threadingEvent selfchainappendnewblock Funcao para mineracao concorrente com multiplas threads def concurrentminingnumthreads difficulty latestblock Block1 0 timetime Bloco concorrente stopevent threadingEvent def mine blockcopy Blocklatestblockindex latestblockprevioushash latestblocktimestamp latestblockdata blockcopymineblockdifficulty stopevent threads starttime timetime for in rangenumthreads t threadingThreadtargetmine tstart threadsappendt for t in threads tjoin elapsedtime timetime starttime printf Mineracao com numthreads threads concluida em elapsedtime2f segundos Ponto de entrada principal if name main Altere os parametros para testar diferentes numeros de threads concurrentminingnumthreads4 difficulty4
19
Linguagens de Programação
UVV
24
Linguagens de Programação
UVV
15
Linguagens de Programação
UVV
22
Linguagens de Programação
UVV
19
Linguagens de Programação
UVV
25
Linguagens de Programação
UVV
1
Linguagens de Programação
UVV
33
Linguagens de Programação
UVV
Texto de pré-visualização
4 Blockchain uma aplicação a sistemas distribuídos Resumo Do conceito à implementação aqui iniciamos nossa jornada para entender a tecnolo gia Blockchain por meio da construção de uma implementação de sistemas distribuídos A tecnologia blockchain revolucionou a maneira como pensamos sobre dados segurança e confiança Ela é a es pinha dorsal de criptomoedas como o Bitcoin e tem aplicações potenciais em diversos setores desde finanças até saúde Mas o que exatamente é um blockchain e como ele funciona Nesta unidade exploraremos os conceitos fundamentais por trás da tecnologia blockchain explicando o processo de construção de um blockchain simples em Python e relacionando suas aplicações em sistemas distri buídos Primeiros passos Em essência um blockchain é um livrorazão distribuído que registra transações em vários compu tadores logo um sistema distribuído Ele foi projetado para ser seguro transparente e resistente a modificações Cada bloco no blockchain contém uma lista de transações um registro de data e hora e uma referência ao bloco anterior na cadeia Essa estrutura cria um histórico contínuo e imutável de transações garantindo que os dados permaneçam precisos e à prova de adulteração O primeiro passo na construção de uma blockchain é criar um bloco Um bloco é uma unidade fundamental em uma blockchain que armazena os dados por exemplo transações e se conecta ao bloco anterior usando uma referência conhecida como hash do bloco anterior Vamos começar criando uma classe Block em Python Essa classe representará cada bloco em nossa blockchain class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfhash Vamos analisar com cuidado o código acima o método init este é o método construtor em Python que inicializa o bloco com atributos específicos 83 84 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos index representa a posição do bloco no blockchain timestamp considere esse atributo como sendo um carimbo de datahora ou seja ele registra a hora em que o bloco foi criado data pode ser qualquer informação ou transação que desejamos armazenar no bloco priorhash uma string que contém o hash do bloco anterior no blockchain Esse atributo garante que os blocos estejam interligados hash atributo que armazenará o hash do bloco atual após o cálculo Por enquanto ele é inicializado como uma string vazia Esta classe será a base do nosso blockchain ainda que venhamos a realizar algumos modificações à frente durante o nosso desenvolvimento Nas próximas etapas precisamos calcular o hash para cada bloco criar uma cadeia de blocos e garantir a integridade do nosso blockchain Compreendendo Criptografia e Hashing Antes de nos aprofundarmos em blockchain é crucial entender a diferença entre criptografia e hashing pois esses conceitos são vitais para a segurança dos dados Criptografia é um algoritmo bidirecional projetado para compartilhar dados com segurança durante o transporte Se um hacker obtiver a chave ele poderá descriptografar os dados de volta à sua forma original Isso é ótimo para alguns casos de uso mas não para outros como senhas Já um Hashing é um algoritmo unidirecional Ele recebe dados de entrada e retorna uma sequência única de comprimento fixo conhecida como hash Um hash é projetado para ser irrever sível o que significa que não é possível retornar do hash aos dados originais1 É por isso que os hashes são cruciais para armazenar senhas com segurança e garantir a integridade dos dados em um blockchain Os programadores usam o hashing para transformar os dados de entrada em um valor de tamanho fixo Esse valor representa os dados de forma exclusiva e a técnica de hashing facilita a transmissão e o armazenamento seguro de várias formas de dados O hashing protege os dados contra acesso não autorizado e adulteração É um ingrediente essencial nos casos de uso de integridade e segurança de dados Nesta parte vamos explora tudo o que você precisa saber sobre hashing em Python O que é Hashing em Python Hashing converte dados de entrada como uma string arquivo ou objeto em uma string de bytes de tamanho fixo O hash ou digest representa a entrada de uma maneira única e reproduzível O hashing desempenha um papel significativo na detecção de manipulação de dados e no au mento da segurança Ele pode calcular um valor de hash para um arquivo mensagem ou outro tipo de dado Um aplicativo armazena o hash de forma segura para verificar posteriormente que os dados não foram adulterados Um dos usos mais comuns do hashing na segurança é o armazenamento de senhas O hashing é uma alternativa viável ao armazenamento de senhas de texto simples em um banco de dados Quando 1Bem pretendo assustar vocês em relação a essa informação Tabelas Rainbow são listas précomputadas de senhas comumente usadas e seus hashes correspondentes Essas tabelas podem facilitar a descoberta de senhas por invasores Acesse crackstationnet e insira um hash que você criou para verificar se ele realmente está seguro 85 um usuário digita sua senha o sistema faz o hash antes de armazenála no banco de dados Se um hacker acessar o banco de dados ele descobrirá que a senha é difícil de ser roubada As funções de hashing do Python tornam tudo isso possível Essas funções matemáticas per mitem que um aplicativo manipule dados em valores de hash Como criar uma função Hashing eficaz Uma função de hash deve atender aos seguintes critérios para ser eficaz e segura Determinística dada a mesma entrada a função deve sempre retornar a mesma saída Eficiente deve ser computacionalmente eficiente ao calcular o valor de hash de uma determi nada entrada Resistente a colisões a função deve minimizar a chance de duas entradas gerarem o mesmo valor de hash Uniforme as saídas da função devem ser distribuídas uniformemente no intervalo de valores de hash possíveis Não inversível deve ser improvável que um computador calcule o valor de entrada da função com base no valor de hash Não previsível prever os resultados da função deve ser um desafio dado um conjunto de entradas Sensível a alterações de entrada a função deve ser sensível a pequenas diferenças na entrada Pequenas alterações devem causar uma grande diferença no valor de hash resultante Casos de uso de Hashing Quando você tiver uma função de hashing adequada com todas essas características poderá aplicála a vários casos de uso As funções de hashing funcionam bem para Armazenamento de senhas o hashing é uma das melhores maneiras de armazenar senhas de usuários em sistemas modernos O Python combina vários módulos para fazer hash e proteger as senhas antes de armazenálas em um banco de dados Armazenamento em cache o hashing armazena a saída de uma função para economizar tempo ao chamála posteriormente Recuperação de dados o Python usa uma tabela de hash com uma estrutura de dados de dicionário integrada para recuperar rapidamente os valores por chave Assinaturas digitais o hashing pode verificar a autenticidade das mensagens que têm assi naturas digitais Verificações de integridade de arquivos o hashing pode verificar a integridade de um arquivo durante sua transferência e download 86 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos Função de Hashing integrada do Python A função de hashing integrada do Python hash retorna um valor inteiro que representa o objeto de entrada Em seguida o código usa o valor de hash resultante para determinar o local do objeto na tabela de hash Essa tabela de hash é uma estrutura de dados que implementa dicionários e conjuntos O código abaixo demonstra como a função hash funciona mystring Ola Mundo Calculate the hash value of the string hashvalue hashmystring Print the string and its hash value printString mystring printValor Hash hashvalue cuja saída é String Ola Mundo Valor Hash 2532871366394334124 Observe que ao rodar novamente o código iremos obter um novo valor para o hash da mensa gem String Ola Mundo Valor Hash 6006117206146337754 O valor do hash é diferente quando invocado uma segunda vez porque as versões recentes do Python versões 33 e posteriores por padrão aplicam uma semente de hash aleatória para essa função A semente muda em cada invocação do Python Em uma única instância os resultados serão idênticos Por exemplo vamos colocar esse código em nosso arquivo exemplo02py mystring Ola Mundo Calculando 2 valores hash da string hashvalue1 hashmystring hashvalue2 hashmystring Imprimindo a string e seus respectivos hashs printString mystring printValor Hash 1 hashvalue1 printValor Hash 2 hashvalue2 Obtemos a seguinte saída String Ola Mundo Valor Hash 1 5459162928360796302 Valor Hash 2 5459162928360796302 87 Limitações do Hashing Embora a função hash do Python seja promissora para vários casos de uso suas limitações a tornam inadequada para fins de segurança Veja como Ataques de colisão uma colisão ocorre quando duas entradas diferentes produzem o mesmo valor de hash Um invasor pode usar o mesmo método de criação de entrada para contornar medidas de segurança que dependem de valores de hash para autenticação ou verificações de integridade de dados Tamanho de entrada limitado como as funções de hash produzem uma saída de tamanho fixo independentemente do tamanho da entrada uma entrada de tamanho maior do que a saída da função de hash pode causar uma colisão Previsibilidade uma função de hash deve ser determinística fornecendo o mesmo resultado sempre que você fornecer a mesma entrada Os invasores podem tirar proveito desse ponto fraco précompilando valores de hash para muitas entradas e em seguida comparandoos com hashes de valoresalvo para encontrar uma correspondência Esse processo é chamado de ataque de tabela arcoíris Para evitar ataques e manter seus dados seguros use algoritmos de hash seguros projetados para resistir a essas vulnerabilidades Uso do hashlib para hashing seguro em Python Em vez de usar a função hash nativa do Python utilize hashlib para um hashing mais seguro Este módulo do Python oferece uma variedade de algoritmos de hash para criptografar dados de forma segura Estes algoritmos incluem MD5 SHA1 e a família SHA2 mais segura que engloba SHA256 SHA384 SHA512 entre outros MD5 O algoritmo criptográfico md5 amplamente utilizado revela um valor de hash de 128 bits Use o código abaixo para gerar um hash md5 usando o construtor da hashlib import hashlib text Ola Mundo hashobject hashlibmd5textencode printhashobjecthexdigest A saída do código acima será consistente em todas as execuções 973de02327c22cb78f9c1d525fdbd039 Observação o método hexdigest no código acima retorna o hash em um formato hexade cimal seguro para qualquer apresentação não binária como email 88 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos SHA1 A função de hash SHA1 protege os dados criando um valor de hash de 160 bits Use o código abaixo com o construtor sha1 para o hash SHA1 do módulo hashlib import hashlib text Ola Mundo hashobject hashlibsha1textencode printhashobjecthexdigest A saída do código acima cbdf6ff8ea93c229d8e019563c538a0838387f58 SHA256 Há várias opções de hash na família SHA2 O construtor hashlib SHA256 gera uma versão mais segura dessa família com um valor de hash de 256 bits Os programadores costumam usar o SHA256 para criptografia como assinaturas digitais ou códigos de autenticação de mensagens O código abaixo demonstra como gerar um hash SHA256 import hashlib text Ola Mundo hashobject hashlibsha256textencode printhashobjecthexdigest A saída do código acima af3724df163a19e336eb9f2c0b12fdcf9c206dd353077a7cc6c41755fae5d0b6 SHA384 SHA384 é um valor de hash de 384 bits Os programadores costumam usar a função SHA384 em aplicativos que precisam de mais segurança de dados Com base nos exemplos anteriores você provavelmente pode adivinhar que esta é uma instrução que gerará um hash SHA384 hashobject hashlibsha384textencode SHA512 O SHA512 é o membro mais seguro da família SHA2 Ele gera um valor de hash de 512 bits Os programadores o utilizam para aplicativos de alto rendimento como a verificação da integridade dos dados O código abaixo mostra como gerar um hash SHA512 com o módulo hashlib no Python hashobject hashlibsha512textencode 89 Como escolher um algoritmo de Hashing Como esses algoritmos são diferentes selecione o algoritmo de hashing com base no caso de uso e nos requisitos de segurança Aqui estão algumas etapas que você deve seguir Entenda o caso de uso o caso de uso determina o tipo de algoritmo a ser usado Por exemplo ao armazenar dados confidenciais como senhas o algoritmo de hash deve proteger contra ataques de força bruta Considere suas necessidades de segurança os requisitos de segurança do seu caso de uso dependem do tipo de dados que você pretende armazenar e eles determinam qual algoritmo escolher Por exemplo um algoritmo de hashing robusto é mais adequado para armazenar informações altamente sensíveis Pesquise os algoritmos de hashing disponíveis explore cada tipo de hashing para enten der seus pontos fortes e fracos Essas informações ajudam você a selecionar a melhor opção para o seu caso de uso Avalie o algoritmo de hashing selecionado depois que você escolher um algoritmo de hashing avalie se ele atende aos seus requisitos de segurança Esse processo pode envolver testes contra ataques ou vulnerabilidades conhecidas Implemente e teste o algoritmo de hashing por fim implemente e teste o algoritmo minuciosamente para garantir que ele funcione de forma correta e segura Como usar o Hashing para armazenamento de senhas O hashing tem excelente potencial para armazenar senhas um componente essencial da segurança cibernética O ideal é que o aplicativo faça hash e armazene as senhas em um banco de dados seguro para evitar acesso não autorizado e violações de dados No entanto o hash sozinho pode não ser suficiente para proteger as informações As senhas com hash ainda são suscetíveis a ataques de força bruta e de dicionário Os hackers geralmente usam essas práticas para adivinhar senhas e obter acesso não autorizado às contas Uma maneira mais segura de usar hashing para o armazenamento de senhas envolve a técnica de salting2 Salting adiciona strings ou caracteres únicos e aleatórios a cada senha antes de transformá la em hash O salt é único para cada senha e o aplicativo o armazena junto com a senha em hash no banco de dados Sempre que um usuário faz login o aplicativo recupera o salt do banco de dados adicionao à senha inserida e em seguida faz o hash do salt e da senha combinados Se um invasor ganhar acesso ao banco de dados ele terá que calcular o hash para cada senha e cada possível valor de salt Salting torna esses ataques mais complexos sendo uma técnica útil para desencorajar ataques de dicionário O módulo secrets do Python facilita o salting Esse módulo gera salts aleatórios armazenando senhas de forma segura e gerenciando tokens e chaves criptográficas O código abaixo usa a biblioteca hashlib e o módulo secrets para proteger ainda mais as senhas dos usuários 2Em traduções livres podemos considerar literalmente que estamos salgando falsificando ou imunizando a nossa codificação ou seja deixandoa mais aleatória 90 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos import hashlib import secrets Gera um salt aleatorio usando o modulo secrets salt secretstokenhex16 printsalt Define a senha do usuario password bolinhas Hash para a senha usando o salt e o algoritmo SHA256 hashobject hashlibsha256password saltencode Transforma a saida hash em uma representacao hexadecimal hashhex hashobjecthexdigest printhashhex As saídas são as seguintes 57bb950e7be8a47f02a0b1583ecc9afa e492515d1b26203aef1fcb579f9ef3d553df3577ceb0f18bfeaf3d791894669b Como usar o Hashing para verificação de integridade de dados O hashing também ajuda a verificar a integridade dos dados e a proteger os dados transmitidos contra modificações e adulterações Essa técnica de quatro etapas usa uma função de hash criptográfico para dar ao arquivo um valor de hash exclusivo Primeiro selecione a função de hash apropriada e use para gerar um valor de hash para os dados de entrada Armazene esse valor de hash e use para comparação quando necessário Sempre que você precisar verificar a integridade dos dados o aplicativo gerará o valor de hash dos dados atuais usando a mesma função de hash Em seguida o aplicativo compara o novo valor de hash com o valor armazenado para garantir que eles sejam idênticos Em caso afirmativo os dados não serão corrompidos O valor de hash é exclusivo e até mesmo uma pequena alteração nos dados de entrada aciona um valor de hash significativamente diferente Isso facilita a detecção de alterações ou modificações não autorizadas nos dados transmitidos As etapas abaixo demonstram o uso de uma função de hash para verificações de integridade de dados ETAPA 1 importe o módulo hashlib import hashlib ETAPA 2 use um algoritmo de hash hashlib def generatehashfilepath Abra o arquivo em modo binario with openfilepath rb as f Leia o conteudo do arquivo contents fread Gere o hash SHA256 do conteudo hashobject hashlibsha256contents Retorne a representacao hexadecimal do hash return hashobjecthexdigest ETAPA 3 chame a função e passe o caminho do arquivo filepath pathtomyfiletxt hashvalue generatehashfilepath printhashvalue ETAPA 4 gere hashes para o arquivo original e para o arquivo transmitido ou modificado Gernerando hash do arquivo original originalfilepath pathtomyfiletxt originalfilehash generatehashoriginalfilepath Transmitir ou modificar o arquivo por exemplo copiando para uma diferente localizao transmittedfilepath pathtotransmittedfiletxt Gerar o hash do arquivo transmitido transmittedfilehash generatehashtransmittedfilepath ETAPA 5 Compare os dos hashes if originalfilehash transmittedfilehash printO arquivo nao foi adulterado else printO arquivo foi adulterado O hashing é inestimável para a integridade de dados e a segurança de senhas Você aproveita ao máximo uma função de hashing quando implementa técnicas de hashing seguras como o uso do módulo hashlib e salting Essas técnicas ajudam a prevenir ataques do tipo rainbow colisões e outras vulnerabilidades de segurança que afetam o hashing Programadores frequentemente utilizam essas técnicas com funções de hashing em Python para garantir a integridade dos dados de arquivos e armazenar senhas de forma segura BLUF Bottom Line Up Front Resumo Inicial Uma blockchain é uma cadeia de blocos com dados em cada bloco onde 92 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos Os dados nos blocos são à prova de violação graças à criptografia Utilizam Tecnologia de Livrorazão Distribuído É pública completamente aberta a qualquer pessoa garantindo transparência Vamos começar analisando um único bloco em uma blockchain Cada bloco contém Dados Um identificador único gerado usando um algoritmo de hash Hash do Bloco Anterior o que corresponde a uma Liga os blocos Os dados armazenados em uma blockchain dependem do tipo de blockchain Por exemplo Bitcoin e Litecoin os dados podem incluir o remetente o destinatário e a quantidade de criptomoeda Blockchains de Geração 2 podem incluir dados personalizados além da criptomoeda Blockchains de Geração 3 podem armazenar quaisquer dados personalizados sem necessa riamente envolver criptomoeda Nas etapas a seguir desenvolveremos essa base calculando hashes ligando blocos e garantindo a integridade da nossa blockchain Criando nossa gênese do blockchain Já estudamos como criar hashs Também já definimos uma classe para a estrutura básica de um bloco em nossa blockchain Agora é hora de gerar um identificador único para cada bloco usando um algoritmo de hash Esse hash servirá como a impressão digital do bloco garantindo que qualquer al teração em seu conteúdo resulte em um hash completamente diferente mantendo assim a integridade da blockchain Como discutido um hash é uma função unidirecional que converte dados de entrada em uma sequência de caracteres de comprimento fixo geralmente uma combinação de números e letras No contexto da blockchain o hashing é usado para conectar blocos e proteger os dados dentro de cada bloco Se um único caractere no bloco mudar o hash mudará drasticamente alertando o sistema sobre uma possível violação Primeiro precisamos adicionar um método à nossa classe Block que irá gerar o hash com base nos dados do bloco import hashlib import json class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfhash selfcreatehash def createhashself blockstring fselfindexselfpriorhashselftimestampselfdata encode return hashlibsha256blockstringhexdigest class StudentBlockChain def initself selfchain selfcreategenesisblock def creategenesisblockself return Block0 29111979MyFirstBlockChainUVVbr0 if name main studentcoin StudentBlockChain firstblock studentcoincreategenesisblock printfirstblockdict printjsondumpsstudentcoinchain defaultlambda o odict indent 4 A saída do código acima é a seguinte index 0 timestamp 29111979 data MyFirstBlockChainUVVbr priorhash 0 hash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e Uma vez que a saída foi gerada vamos entender o que fizemos O primeiro bloco em qualquer blockchain é conhecido como bloco gênese Esse bloco é único porque não possui nenhum bloco anterior ao qual se referir portanto seu priorhash normalmente é definido como um valor padrão A classe de criação do bloco se chama StudentBlockChain mas podemos nomeála como quisermos O importante aqui é lembrar que ela é que gerenciará o blockchain O construtor desta classe inicializará o blockchain Inicialmente ele estará vazio Por fim compreenda que o primeiro bloco da blockchain conhecido como bloco genesis é especial porque não tem predecessor Sendo assim criamos um método chamado creategenesisblock que retornará este bloco Portanto o bloco gênese terá parâmetros fixos índice 0 carimbo de datahora uma data fixa por exemplo 29111979 dados MyFirstBlockChainUVVbr priorhash 0 Esses parâmetros garantem que o bloco gênese seja único e sirva como ponto de partida para a blockchain Com o bloco de gênese definido nossa blockchain está pronta para crescer Na próxima etapa exploraremos como adicionar novos blocos à blockchain e garantir que cada bloco esteja vinculado com segurança ao seu antecessor mantendo a integridade da cadeia Adicionando e gerenciando blocos Agora que nossa blockchain foi inicializada com o bloco genesis é hora de adicionar mais blocos à cadeia Nesta etapa criaremos métodos para recuperar o último bloco da cadeia e adicionar novos blocos garantindo que cada novo bloco faça referência ao anterior mantendo a integridade da blockchain ou seja continuaremos construindo a classe StudentBlockChain adicionando dois novos métodos getlastblock e addblock O objetivo do método getlastblock é retornar o último bloco no blockchain o que é essencial para vincular novos blocos à cadeia Quanto ao método addblock este cuidará da adição de novos blocos à blockchain Este método irá Definir o priorhash do novo bloco como o hash do último bloco na cadeia Calcular o hash do novo bloco Adicionar o novo bloco à blockchain Vamos à implementação import hashlib import json class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfhash selfcreatehash def createhashself blockstring fselfindexselfpriorhashselftimestampselfdata encode return hashlibsha256blockstringhexdigest class StudentBlockChain def initself selfchain selfcreategenesisblock def creategenesisblockself return Block0 29111979MyFirstBlockChainUVVbr0 def getlastblockself return selfchain1 def addblockself newblock newblockpriorhash selfgetlastblockhash newblockhash newblockcreatehash selfchainappendnewblock if name main studentcoin StudentBlockChain firstblock studentcoincreategenesisblock printfirstblockdict studentcoinaddblockBlock1 12121979 amount 27 studentcoinaddblockBlock2 01011980 amount 13 studentcoinaddblockBlock3 04021980 amount 75 printjsondumpsstudentcoinchain defaultlambda o odict indent4 O código apresenta então a seguinte saída index 0 timestamp 29111979 data MyFirstBlockChainUVVbr priorhash 0 hash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e index 1 timestamp 12121979 data amount 27 priorhash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e hash 8f3dbb1621eaa7c73f5ca4efd85706cd6d8d721e2c61701d32827ab7acefdefb index 2 timestamp 01011980 data amount 13 priorhash 8f3dbb1621eaa7c73f5ca4efd85706cd6d8d721e2c61701d32827ab7acefdefb hash e719478517ffc2e64881a9683c6b4acfc1f26c257f26181b01358aa4a209b35f index 3 timestamp 04021980 data amount 75 priorhash e719478517ffc2e64881a9683c6b4acfc1f26c257f26181b01358aa4a209b35f hash 72024f5e3b673f79b9363c3beeac24f1a259f985b036cbb7f5279ad08a3ba8e8 Com a capacidade de adicionar e gerenciar blocos nossa blockchain agora está funcional No entanto blockchains do mundo real envolvem mais complexidade como validação de transações garantia de consenso entre nós e muito mais Nas próximas etapas exploraremos recursos adicionais que podem ser adicionados para tornar nossa blockchain mais robusta e segura Validando o BlockChain Essa etapa é crítica para garantir que estamos lidando com um blockchain íntegro Para cumprila implementaremos um método para validar a blockchain garantindo que nenhum dado tenha sido adulterado Este é um aspecto crucial da tecnologia blockchain pois garante a confiabilidade de toda a cadeia Adicionaremos um método chamado validatingblockchain à classe StudentBlockChain O objetivo deste método será percorrer o blockchain a fim de verificar se o hash de cada bloco está correto e se cada bloco aponta para o bloco anterior correto Ou seja o método validatingblockchain fará um loop no blockchain começando pelo bloco após o bloco de gênese comparando o hash armazenado com o hash calculado e verificando se o priorhash do bloco atual corresponde ao hash do bloco anterior Vamos a implementação import hashlib import json class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfhash selfcreatehash def createhashself blockstring fselfindexselfpriorhashselftimestampselfdataencode return hashlibsha256blockstringhexdigest class StudentBlockChain def initself selfchain selfcreategenesisblock def creategenesisblockself return Block0 29111979MyFirstBlockChainUVVbr0 def getlastblockself return selfchain1 def addblockself newblock newblockpriorhash selfgetlastblockhash newblockhash newblockcreatehash selfchainappendnewblock def validatingblockchainself for i in range1 lenselfchain currentblock selfchaini previousblock selfchaini1 Check if has for the current block if currentblockhash currentblockcreatehash return False Check current block points to the previous blocks if currentblockpriorhash previousblockhash return False return True if name main studentcoin StudentBlockChain firstblock studentcoincreategenesisblock printfirstblockdict studentcoinaddblockBlock1 12121979 amount 27 studentcoinaddblockBlock2 01011980 amount 13 studentcoinaddblockBlock3 04021980 amount 75 printjsondumpsstudentcoinchain defaultlambda o odict indent4 Check if has for the current block printIs studentcoin Valid strstudentcoinvalidatingblockchain Changing the blockchain studentcoinchain1data amount 28 Tampering with the data printIs studentcoin Valid after change strstudentcoinvalidatingblockchain Update the hash to match the change data studentcoinchain1hash studentcoinchain1createhash printIs studentcoin Valid after updating the hash strstudentcoinvalidatingblockchain Temos a seguinte saída 98 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos index 0 timestamp 29111979 data MyFirstBlockChainUVVbr priorhash 0 hash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e index 1 timestamp 12121979 data amount 27 priorhash 6846f0bf7cf310fcb1e096cd24393c89b858d136279daeabba43ec9a90b25e4e hash 8f3dbb1621eaa7c73f5ca4efd85706cd6d8d721e2c61701d32827ab7acefdefb index 2 timestamp 01011980 data amount 13 priorhash 8f3dbb1621eaa7c73f5ca4efd85706cd6d8d721e2c61701d32827ab7acefdefb hash e719478517ffc2e64881a9683c6b4acfc1f26c257f26181b01358aa4a209b35f index 3 timestamp 04021980 data amount 75 priorhash e719478517ffc2e64881a9683c6b4acfc1f26c257f26181b01358aa4a209b35f hash 72024f5e3b673f79b9363c3beeac24f1a259f985b036cbb7f5279ad08a3ba8e8 Is studentcoin Valid True Is studentcoin Valid after change False Is studentcoin Valid after updating the hash False Esta etapa demonstrou a importância de validar uma blockchain para garantir sua integridade Embora nossa blockchain simples não inclua recursos avançados como prova de trabalho ou uma rede distribuída por enquanto ela nos serviu para ilustrar os princípios fundamentais da tecnologia blockchain Em cenários reais camadas adicionais de segurança e validação são necessárias para proteger contra adulterações e fraudes Note que a saída da função de hash pode ser usada como uma assinatura digital Imagine que você tem uma string com 10 milhões de caracteres digamos que você esteja escrevendo um romance e para garantir que esse romance não seja adulterado você diz a todos os seus leitores em potencial que eles precisam contar os 10 milhões de caracteres para garantir que o romance não seja corrompido Ninguém faria isso Mas com o hash você pode publicar a validação de saída com apenas 64 caracteres por email por exemplo e seus leitores em potencial podem fazer o hash do romance que comprambaixam e comparálos para garantir que o romance deles seja legítimo Então o que fizemos até aqui foi adicionar o hash do bloco pai na classe do bloco Dessa forma mantemos a assinatura digital do bloco pai em nosso bloco mais atual Isso significa que se nós por pura maldade alterarmos o conteúdo de qualquer bloco o hash do bloco pai em qualquer bloco filho será inválido e você será pego em flagrante Mas não poderíamos alterar o hash do bloco pai do bloco filho se quisermos alterar o conteúdo de qualquer bloco Sim podemos No entanto o processo 99 de alteração do conteúdo se torna mais difícil Nesse caso precisaríamos seguir dois passos Agora imagine que você tem 10 blocos e quer alterar o conteúdo do primeiro bloco Nesse caso você deve alterar o hash do bloco pai no bloco filho imediato Mas infelizmente existem ramificações invisíveis nisso Tecnicamente falando o hash do bloco pai em seu filho imediato é parte do conteúdo desse bloco Isso significaria que o hash do bloco pai em seu filho o neto do primeiro bloco seria inválido Portanto para seguirmos com essa alteração fraude precisaríamos alterar o hash do pai desse neto mas isso afeta o bloco subsequente E agora devemos alterar os hashes dos pais de todos os blocos Para isso são necessários 10 passos Ou seja usar o hash de um pai torna a adulteração muito mais difícil Entendendo as conexões que se realizam em cada novo bloco em função dos hashs é hora de aprendermos a inovação construída sobre ele Isso nos leva à prova de trabalho Na sequência iremos implementar uma prova de trabalho nessa arquitetura para demonstrar esse fundamento de segurança antes de avançarmos para a parte distribuída Prova de Trabalho Palavras são baratas ações são valiosas Portanto devemos ter em mente que embora todos possam fazer hashing nem todos estão dispostos a fazêlo muitas vezes Para garantir que alguém seja digno de proteger dados no blockchain ele deve estar disposto a realizar muitos processos de hash Esta é a base da tecnologia blockchain Na terminologia da blockchain aquele que escreve os hashs é chamado de minerador Para inserir a transação na blockchain o minerador precisa realizar algum trabalho primeiro Introduziremos uma variável nonce3 na classe Block para randomizar o conteúdo do bloco alterando assim o hash resultante Em seguida implementamos o método mineblock para garantir que o hash de cada bloco comece com um número específico de zeros representando o nível de dificuldade A variável nonce será inicializada como zero e incrementada sempre que tentarmos criar um hash válido O objetivo do nonce é proteger as transações em blockchain Veremos mais tarde que para criar uma transação deveremos obter o nonce de quantas transações foram criadas no blockchain Por exemplo se foram criadas três transações e desejamos criar uma nova transação deve usar 3 como valor para o nonce No Bitcoin por exemplo o algoritmo de prova de trabalho tem nome chamase Hashcash E não é muito diferente do exemplo básico que estamos tratando aqui É o algoritmo que os mineradores correm para resolver a fim de criar um novo bloco Em geral a dificuldade é determinada pelo número de zeros no início de uma sequência os mineradores são então recompensados por sua solução recebendo um número definido de Bitcoins No nosso código também vamos inserir essa variável difficulty para ajustar a dificuldade da mineração modificando assim o número de zeros à esquerda O ajustamos para quatro Perceba que a adição de um único zero à esquerda faz uma diferença exponencial no tempo necessário para encontrarmos uma solução No Bitcoin a dificuldade é ajustada a cada 2016 blocos por consenso na rede A dificuldade é aumentada para evitar a inevitável aceleração do hardware Isso porque a dificuldade da prova de trabalho é determinada por uma média móvel que visa um número médio de blocos por hora a serem processados Se eles forem gerados muito rápido a dificuldade deve aumentar a fim de se manter o controle do processamento Vamos à implementação 3Em tradução literal nenhum import hashlib import json class Block def initself index timestamp data priorhash selfindex index selftimestamp timestamp selfdata data selfpriorhash priorhash selfnonce 0 selfhash selfcreatehash def createhashself blockstring fselfindexselfpriorhashselftimestampselfdataselfnonceencode return hashlibsha256blockstringhexdigest def mineblockself difficulty while selfhashdifficulty 0 difficulty selfnonce 1 selfhash selfcreatehash printBlock Hash selfhash class StudentBlockChain def initself selfchain selfcreategenesisblock selfdifficulty 4 def creategenesisblockself return Block0 29111979MyFirstBlockChainUVWbr0 def getlastblockself return selfchain1 def addblockself newblock newblockpriorhash selfgetlastblockhash newblockhash newblockcreatehash newblockmineblockselfdifficulty selfchainappendnewblock def validatingblockchainself for i in range1 lenselfchain currentblock selfchaini previousblock selfchaini1 Check if has for the current block if currentblockhash currentblockcreatehash return False Check current block points to the previous blocks if currentblockpriorhash previousblockhash return False return True if name main studentcoin StudentBlockChain firstblock studentcoincreategenesisblock studentcoinaddblockBlock1 12121979 amount 27 studentcoinaddblockBlock2 01011980 amount 13 studentcoinaddblockBlock3 04021980 amount 75 printjsondumpsstudentcoinchain defaultlambda o odict indent4 102 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos index 1 timestamp 12121979 data amount 27 priorhash f10deec5d0033d56c0566a301fdfca632dc2f427442a87685ab5618156697817 nonce 67376 hash 0000a4266a75769d998f8a676657bfc8d684d623b8a2c533f28ea53ace44b2a6 index 2 timestamp 01011980 data amount 13 priorhash 0000a4266a75769d998f8a676657bfc8d684d623b8a2c533f28ea53ace44b2a6 nonce 99729 hash 0000aa4676e56b9c606ef178e5fbf50a5291789f766ad72923629b62eb914dea index 3 timestamp 04021980 data amount 75 priorhash 0000aa4676e56b9c606ef178e5fbf50a5291789f766ad72923629b62eb914dea nonce 36748 hash 00009e4c832667bffca97ef317693a88f3af4bf937fedf68cc67096f9b1736ec Para compreender melhor a nova saída do nosso código vamos destacar os pontos chaves Variável nonce é um número usado apenas uma vez no registro Ele é incrementado até que o hash atenda aos critérios de dificuldade Serve como um randomizador no sistema de Prova de Trabalho PoW do blockchain O nonce é incrementado continuamente até que o hash do bloco atenda a uma condição específica normalmente que comece com um certo número de zeros conforme determinado pelo nível de dificuldade Método mineblock este método garante que a criação de blocos seja computacionalmente intensiva exigindo que o hash comece com um determinado número de zeros A consequência desse métoda é a mineração processo de encontrar um hash de bloco válido O objetivo da mineração é encontrar um nonce que quando combinado com o conteúdo do bloco produza um hash que atenda ao nível de dificuldade exigido por exemplo comece com quatro zeros no nosso exemplo Variável difficulty o nível de dificuldade controla quantos zeros à esquerda são necessários no hash impactando diretamente a dificuldade de minerar um bloco O nível de dificuldade nesta blockchain é definido como 4 o que significa que um hash de bloco válido deve começar com pelo menos quatro zeros 0000 Isso garante que a criação de um bloco válido seja computacionalmente intensiva e não algo que possa ser feito instantaneamente aumentando a segurança quanto a modificações não autorizadas Sendo assim logo no início da saída do código podemos ver uma série de Hash de Bloco cada uma representando uma tentativa de encontrar um hash válido incrementando o nonce O nonce começa em 0 e é incrementado até que o hash atenda ao requisito de dificuldade 103 Assim que um hash válido é encontrado iniciando com quatro zeros ele é armazenado como o hash do bloco e o valor do nonce que atingiu esse hash é armazenado junto com ele Isso garante que cada bloco tenha um identificador único computacionalmente difícil de alterar Compreenda que o nonce e o nível de dificuldade tornam extremamente difícil alterar qualquer bloco na blockchain Se alguém tentar adulterar os dados de um bloco o hash será alterado e a cadeia deixará de ser válida a menos que o bloco seja reminerado encontrando um novo nonce para cada bloco subsequente uma tarefa computacionalmente proibitiva Ou seja Este processo garante que a criação de cada bloco exija esforço computacional o que protege o blockchain de adulterações Com a implementação da Prova de Trabalho nossa blockchain agora está mais segura e re sistente a adulterações Em uma blockchain real recursos de segurança adicionais como consenso distribuído e assinaturas criptográficas seriam necessários Nas próximas etapas revisitaremos o processo de validação e discutiremos como lidar com casos em que a blockchain for considerada inválida 104 Capítulo 4 Blockchain uma aplicação a sistemas distribuídos Atividade Análise e Extensão de um Código de Mineração Blockchain Uma vez que concluímos a parte introdutória sobre blockchain expomos códigos em Python que simulam e mineram os blocos com prova de trabalho simples usando hashlib e um nonce vamos agora analisar modificar e estender os código para criar uma aplicação distribuída e concorrente básica Os objetivos desta atividade são os seguintes Compreender os mecanismos de mineração por prova de trabalho PoW Explorar concorrência no processo de mineração uso de threads ou multiprocessing Simular uma rede blockchain distribuída com múltiplos nós minerando blocos Identificar desafios reais de consenso propagação e sincronização Vocês deverão cumprir as seguintes etapas 1 Análise e Explicação individual ou em dupla estudar o código fornecido no material introdutório de aula apresentar comentários explicativos nas seguintes partes Estrutura do bloco campos encadeamento via hash Algoritmo de prova de trabalho Verificação da validade do bloco Responda brevemente O que impede dois blocos de serem minerados simultaneamente Como o hash e o nonce garantem a dificuldade da mineração 2 Modificação e Concorrência Altere o código para executar mineração com múltiplas threads ou processos competindo para encontrar um nonce válido Quando uma thread encontrar o nonce as outras devem parar imediatamente Meça o tempo de mineração com 1 2 4 8 threads e registre os resultados em um gráfico Exemplo de Resultado Esperado Minerando com 1 thread Tempo 123s Minerando com 2 threads Tempo 68s Minerando com 4 threads Tempo 35s A partir do padrão de saída acima fornecer gráfico threads vs tempo de mineração Entrega Código com comentários Gráfico de desempenho Respostas conceituais Avaliação threadminerpy import hashlib import time import threading Classe que representa um bloco da blockchain class Block def initself index previoushash timestamp data nonce0 selfindex index selfprevioushash previoushash selftimestamp timestamp selfdata data selfnonce nonce selfhash selfcalculatehash Funcao que calcula o hash do bloco def calculatehashself value strselfindex selfprevioushash strselftimestamp selfdata strselfnonce return hashlibsha256valueencodehexdigest Funcao que executa a prova de trabalho mineracao def mineblockself difficulty stopevent prefix 0 difficulty while not selfhashstartswithprefix if stopeventisset return selfnonce 1 selfhash selfcalculatehash Avisar que um bloco foi minerado stopeventset printfBloco minerado com nonce selfnonce selfhash Blockchain basica com lista de blocos class Blockchain def initself difficulty4 selfchain selfcreategenesisblock selfdifficulty difficulty def creategenesisblockself return Block0 0 timetime Genesis Block def getlatestblockself return selfchain1 def addblockself newblock newblockprevioushash selfgetlatestblockhash newblockmineblockselfdifficulty threadingEvent selfchainappendnewblock Funcao para mineracao concorrente com multiplas threads def concurrentminingnumthreads difficulty latestblock Block1 0 timetime Bloco concorrente stopevent threadingEvent def mine blockcopy Blocklatestblockindex latestblockprevioushash latestblocktimestamp latestblockdata blockcopymineblockdifficulty stopevent threads starttime timetime for in rangenumthreads t threadingThreadtargetmine tstart threadsappendt for t in threads tjoin elapsedtime timetime starttime printf Mineracao com numthreads threads concluida em elapsedtime2f segundos Ponto de entrada principal if name main Altere os parametros para testar diferentes numeros de threads concurrentminingnumthreads4 difficulty4