Back-End

21 nov, 2017

Como o Bitcoin força o consenso entre os generais bizantinos?

100 visualizações
Publicidade

“É possível quebrar a blockchain?”

Essa é uma questão justa. Se você conhece alguma coisa sobre a arquitetura blockchain, instintivamente conclui que “não”, é bastante improvável que alguém a quebre. Na prática, é basicamente impossível.

É bastante surpreendente que a maioria dos meus programadores pares tenham muita dificuldade para superar o preconceito contra criptomoedas. Não tenho ideia de onde esse preconceito vem, mas conheço pessoas muito inteligentes que podem resolver os problemas mais difíceis de escalabilidade da web, mas que nunca antes olhou para o papel original e extremamente curto de Satoshi Nakamoto descrevendo o blockchain.

Sério, o paper Bitcoin: A Peer-to-Peer Electronic Cash System é tão ridiculamente pequeno e fácil de entender que a maioria dos estudantes de informática deve ser capaz de compreendê-lo. Então, todos os programadores inteligentes que eu conheço devem poder entendê-lo em uma pausa para o café. Qualquer programador médio deve poder ler e entender esse documento em mais ou menos 30 minutos.

Você pode simplificar um modelo mental como uma Linked List, e cada nó da lista é o que chamamos de bloco. Cada bloco é uma estrutura estúpida com os ponteiros anteriores/posteriores e um corpo composto por uma estrutura semelhante à árvore (uma árvore Merkle, para ser mais exato).

A pegadinha é que cada bloco possui a assinatura hash do bloco anterior, criando assim uma “cadeia” segura. Daí o “block-chain”.

Sim, em termos de Ciência da Computação, estamos lidando com níveis de graduação de estruturas de dados aqui. Se você entender uma Linked List e uma árvore binária estúpida, além da coisa de criptografia mais fácil de entender, um estúpido Digest Hash, como SHA256 e, boom, você entende o básico do banco de dados blockchain.

Sim, é apenas um banco de dados. Um banco de dados distribuído para ser mais exato. Ou um banco de dados distribuído muito grosso e simples para esse assunto. Não é muito eficiente, e isso contrasta com os bancos de dados distribuídos NoSQL mais sérios, como Redis ou Cassandra. Portanto, as habilidades de consulta são basicamente inexistentes, além de encontrar um bloco por sua identidade.

Claro, o código-fonte do Bitcoin é mais sofisticado do que isso, mas o básico é realmente tão ridículo que você não precisa de mais de 20 linhas de código Ruby para replicá-lo. Confira esta implementação de exemplo de Gerald Bauer.

require "digest"    # for hash checksum digest function SHA256

class Block

  attr_reader :index
  attr_reader :timestamp
  attr_reader :data
  attr_reader :previous_hash
  attr_reader :hash

  def initialize(index, data, previous_hash)
    @index         = index
    @timestamp     = Time.now
    @data          = data
    @previous_hash = previous_hash
    @hash          = calc_hash
  end

  def calc_hash
    sha = Digest::SHA256.new
    sha.update( @index.to_s + @timestamp.to_s + @data + @previous_hash )
    sha.hexdigest
  end


  def self.first( data="Genesis" )    # create genesis (big bang! first) block
    ## uses index zero (0) and arbitrary previous_hash ("0")
    Block.new( 0, data, "0" )
  end

  def self.next( previous, data="Transaction Data..." )
    Block.new( previous.index+1, data, previous.hash )
  end

end

Certo?

Uma pergunta permanece: como essa estrutura estúpida se tornou um banco de dados “distribuído”?

Agora, ou você precisa ter uma “cópia-mestra” centralizada, da qual todas as outras cópias são replicadas, ou você precisa de alguma forma de “consenso” entre as diferentes cópias.

Como chegar ao consenso entre o nó rogue e aleatório em todo o mundo? Esse é o problema que se chama “Byzantine Fault Tolerance” (“tolerância a falhas bizantinas”, em tradução livre), explicado e resolvido magistralmente por Barbara Liskov e Miguel Castro, do MIT, em 1999.

Em poucas palavras, imagine que você tem generais bizantinos, cada um com seus próprios exércitos, em torno de uma cidade hostil. Agora, você pode atacar ou recuar. Mas todos os generais devem fazer uma coisa ou outra, em consenso. Como chegar ao consenso quando não há comunicação direta com todos os generais e, pior ainda, quando alguns dos generais podem ser traidores ou agentes duplos?

Esse é o tipo de problema que enfrentamos aqui. Qualquer pessoa na Internet pode fazer o download de uma cópia do blockchain, e elas podem verificar se os blocos são válidos e não adulterados por recompor os hash digests para cada bloco.

Mas como você adiciona novos blocos e faz os outros nós aceitarem seu novo bloco?

Foi por isso que Satoshi adicionou a chamada “Proof of Work” (“Prova de Trabalho”) à equação. Lembra que eu disse que cada bloco é encadeado ao anterior, contendo o hash do bloco anterior? A computação de um hash digest é bastante trivial nos dias de hoje.

Em Ruby, se você fizer isto:

Digest::SHA256.hexdigest("abcd")
# => "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589"
Digest::SHA256.hexdigest("123")
# => "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"

Demora uma fração de milissegundo para rodar.

Agora, e se eu pedir que você encontre o hash que começa com uma certa quantidade de “zeros” no início do hash?

Por exemplo:

# I want to find 4 zeros ("0000") in the hash:
Digest::SHA256.hexdigest("79026" + "123")
# => "0000559fb4a55f135c7db3d83405b86b4b63cd035993873a5b676bae08b64334"

Como eu sei que tive que incluir “79026” no início? Eu não sei, eu tenho que começar de 0 e incrementar um por um até encontrar o hash com o formato que eu quero.

Se verificarmos o exemplo de Gerald, implementaríamos essa pesquisa desta forma:

def compute_hash_with_proof_of_work( difficulty="00" )
  nonce = 0
  loop do
    hash = calc_hash_with_nonce( nonce )
    if hash.start_with?( difficulty )
      return [nonce,hash]    ## bingo! proof of work if hash starts with leading zeros (00)
    else
      nonce += 1             ## keep trying (and trying and trying)
    end
  end
end

def calc_hash_with_nonce( nonce=0 )
  sha = Digest::SHA256.new
  sha.update( nonce.to_s + "123" )
  sha.hexdigest
end

Apenas um simples SHA256 leva a algo entre 0.000010 e 0.000020 segundos (lembre-se: frações de milissegundos). Agora, quanto tempo leva para descobrir “79026” (que chamamos de “nonce”)?

> puts Benchmark.measure { compute_hash_with_proof_of_work("0000") }
  0.190000   0.000000   0.190000 (  0.189615)

Sim, consideravelmente mais, agora leva 0,18 segundos em vez de 0,000020. Podemos aumentar a variável “difficult” para tornar ainda mais difícil de encontrar o nonce. E é exatamente assim que o Bitcoin é implementado: cada bloco ajusta o difficult, então o tempo mais rápido que se leva para encontrar o hash para o próximo bloco é de cerca de 10 minutos.

E isso, meus amigos, é o que chamamos de “MINERAÇÃO“. O que um mineiro faz é calcular um loop, incrementando o nonces, sobre o resumo do bloco para encontrar o nonce correto.

Quando um nonce é encontrado, o mineiro pode adicionar o bloco ao blockchain e transmiti-lo para outros nós. Os outros nós podem então verificar novamente (e agora é apenas o procedimento de 0.000020 segundos novamente, super-rápido).

Quando os nós controlam e confirmam o nonce, todos eles adicionam o bloco ao topo do blockchain. E, geralmente, quando os outros mineiros continuam adicionando outros blocos em cima disso, esse bloco se torna “solidificado”. O bloco mais recente no topo do blockchain geralmente é instável, mas uma vez que você tem mais blocos em cima dele, ele é considerado mais “garantido”. É por isso que a maioria das trocas e outros serviços que aceitam Bitcoin aguardam a chamada confirmação de “6 blocos”.

E pela dificuldade ser tal, que o nó mais rápido leva em torno de “10 minutos” para encontrar esse nonce, um bloco é dito ser “seguro” quando cerca de uma hora se passou e seis blocos foram adicionados após ele.

Podemos quebrar isso?

Agora você entenderá por que falamos de “poder de hash” quando falamos de mineração.

A mineração é o ato de assinar e confirmar blocos para o blockchain. É um serviço de manutenção, razão pela qual você recompensa os mineiros com “taxas de transação” e algumas “satoshis” (frações de 1 Bitcoin) pelo seu trabalho. E também por que você chama essa “Proof of Work”, porque quando alguém descobre um nonce, sabemos que foi necessária uma grande quantidade de computação hash.

E também por que falamos sobre CPUs ou GPUs que os mineiros usam para ter “taxas de hash”. Você precisa ter uma capacidade absurda para poder mineirar Bitcoins atualmente. Ninguém usará uma plataforma construída em casa para fazê-lo. É preciso construir hardware especial, como o famoso AntMiners. Um USD 1,500 AntMiner S9 é capaz de calcular aproximadamente 14 TH.

Cada criptomoeda diferente do Bitcoin calcula hashes de maneira diferente, de modo que a hashrate difere de moeda para moeda.

O atual Hash Power de toda a rede de consenso de Bitcoin atinge quase 14 EH (exa-hashes ou milhões de tera-hashes).

Então, digamos que eu sou um bilionário e quero trollar a comunidade de Bitcoin, acrescentando poder de hash suficiente para superar todo o poder de hash da rede. Eu teria que comprar 1 milhão de AntMiner s9, ou um investimento de aproximadamente US$ 1,5 bilhão! E isso sem adicionar a energia necessária para inicializar e executar essas máquinas, é claro.

Mas, mesmo assim, você sabe o que acontece? Lembra-se da variável difficult que mencionei acima? Ela irá se ajustar novamente, para garantir que o próximo bloco demore 10 minutos para calcular novamente!

Então, não, mesmo se você estiver disposto a desperdiçar US$ 1,5 bilhão, você não vai quebrá-lo. E é assim que o Bitcoin lida com os generais bizantinos nessa rede de consenso.

***

Artigo traduzido com autorização do autor. Publicado originalmente em: http://www.akitaonrails.com/2017/11/01/how-does-bitcoin-force-consensus-among-byzantine-generals