Aprenda Blockchains contruindo o seu próprio

A maneira mais rápida de aprender como funciona o Blockchains é construir um.
Você está aqui porque, como eu está entusiasmado com a ascensão de Cryptocurrencies. E você quer saber como funciona o Blockchains – a tecnologia fundamental por trás deles.

Mas entender Blockchains não é fácil – ou pelo menos não era para mim. Atravessava vídeos densos, segui tutoriais porosos e lidei com a frustração ampliada de poucos exemplos.
Eu gosto de aprender fazendo. Isso me obriga a lidar com o assunto em um nível de código, o que o fará. Se você fizer o mesmo, no final deste guia você terá um Blockchain funcional com uma compreensão sólida de como eles funcionam.

Antes de começar …

Lembre-se de que uma cadeia de blocos é uma cadeia de registros seqüencial e imutável chamada Blocos. Eles podem conter, de verdade, transações, arquivos ou dados que você goste.
Mas o importante é que eles estão acorrentados usando hashes .

Se você não tem certeza do que é um hash, aqui está uma explicação .

Para quem é orientado este guia? Você deve ser confortável lendo e escrevendo alguns Python básicos, além de ter alguma compreensão de como os pedidos HTTP funcionam, pois estaremos conversando com o nosso Blockchain sobre HTTP.

O que eu preciso? Verifique se o Python 3.6 + (juntamente compip) está instalado. Você também precisará instalar o Flask e a maravilhosa biblioteca de Solicitações:

pip install Flask==0.12.2 requests==2.18.4

Ah, você também precisará de um Cliente HTTP, como o Postman ou o CURL. Mas qualquer coisa fará. Onde está o código final? O código-fonte está disponível aqui.

Passo 1: Construindo uma Cadeia de Bloqueio

Abra seu editor de texto favorito ou IDE, pessoalmente, eu prefiro o PyCharm ou Nano do Linux.
Crie um novo arquivo, chamado blockchain.py. Nós só usaremos um único arquivo, mas se você se perder, você sempre pode se referir ao código-fonte .

Representando um Blockchain
Criaremos um  Blockchain classe cujo construtor crie uma lista inicial vazia (para armazenar nossa cadeia de blocos) e outra para armazenar transações.
Aqui está o plano para a nossa classe:

lass Blockchain(object):
def __init__(self):
self.chain = []
self.current_transactions = []

def new_block(self):
# Creates a new Block and adds it to the chain
pass

def new_transaction(self):
# Adds a new transaction to the list of transactions
pass

@staticmethod
def hash(block):
# Hashes a Block
pass

@property
def last_block(self):
# Returns the last Block in the chain
pass

Nossa Blockchainclasse é responsável pelo gerenciamento da cadeia. Ele armazenará transações e terá alguns métodos auxiliares para adicionar novos blocos à cadeia. Vamos começar a elaborar alguns métodos.

Como é um bloco?
Cada bloco possui um índice , um carimbo de data / hora (no tempo Unix), uma lista de transações , uma prova (mais sobre isso mais tarde) e o hash do bloco anterior.
Aqui está um exemplo do que um único bloco parece:

block = {
‘index’: 1,
‘timestamp’: 1506057125.900785,
‘transactions’: [
{
‘sender’: “8527147fe1f5426f9dd545de4b27ee00”,
‘recipient’: “a77f5cdfa2934df3954a5c7c7da5df1f”,
‘amount’: 5,
}
],
‘proof’: 324984774000,
‘previous_hash’: “2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”
}

Neste ponto, a idéia de uma cadeia deve ser aparente – cada novo bloco contém dentro de si, o hash do bloco anterior. Isso é crucial porque é o que dá a imutabilidade de cadeia de bloqueios : se um atacante corrompesse um bloco anterior na cadeia, todos os blocos subseqüentes conterão hashes incorretos.
Isso faz sentido? Se isso não acontecer, leve algum tempo para deixá-lo afundar-é a idéia central por trás de blockchains.

Adicionando Transações a um Bloco
Precisamos de uma maneira de adicionar transações a um Bloco.
Nosso new_transaction()método é responsável por isso, e é bastante direto:

class Blockchain(object):

def new_transaction(self, sender, recipient, amount):
“””
Creates a new transaction to go into the next mined Block
:param sender: <str> Address of the Sender
:param recipient: <str> Address of the Recipient
:param amount: <int> Amount
:return: <int> The index of the Block that will hold this transaction
“””

self.current_transactions.append({
‘sender’: sender,
‘recipient’: recipient,
‘amount’: amount,
})

return self.last_block[‘index’] + 1

Depois de new_transaction()adicionar uma transação à lista, ela retorna o índice do bloco ao qual a transação será adicionada ao próximo a ser extraído. Isso será útil mais tarde, para o usuário que envia a transação.

Criando novos blocos
Quando Blockchainfor instanciado, precisamos gerá-lo com um bloqueio de gênese – um bloco sem predecessores. Também precisamos adicionar uma “prova” ao nosso bloco de gênese, que é o resultado da mineração (ou prova de trabalho). Vamos falar mais sobre mineração mais tarde.

Além de criar o bloqueio de gênese em nosso construtor, também buscaremos os métodos para new_block(), new_transaction()e hash():

import hashlib
import json
from time import time

class Blockchain(object):
def __init__(self):
self.current_transactions = []
self.chain = []

# Create the genesis block
self.new_block(previous_hash=1, proof=100)

def new_block(self, proof, previous_hash=None):
“””
Create a new Block in the Blockchain
:param proof: <int> The proof given by the Proof of Work algorithm
:param previous_hash: (Optional) <str> Hash of previous Block
:return: <dict> New Block
“””

block = {
‘index’: len(self.chain) + 1,
‘timestamp’: time(),
‘transactions’: self.current_transactions,
‘proof’: proof,
‘previous_hash’: previous_hash or self.hash(self.chain[-1]),
}

# Reset the current list of transactions
self.current_transactions = []

self.chain.append(block)
return block

def new_transaction(self, sender, recipient, amount):
“””
Creates a new transaction to go into the next mined Block
:param sender: <str> Address of the Sender
:param recipient: <str> Address of the Recipient
:param amount: <int> Amount
:return: <int> The index of the Block that will hold this transaction
“””
self.current_transactions.append({
‘sender’: sender,
‘recipient’: recipient,
‘amount’: amount,
})

return self.last_block[‘index’] + 1

@property
def last_block(self):
return self.chain[-1]

@staticmethod
def hash(block):
“””
Creates a SHA-256 hash of a Block
:param block: <dict> Block
:return: <str>
“””

# We must make sure that the Dictionary is Ordered, or we’ll have inconsistent hashes
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()

O acima deve ser direto – eu adicionei alguns comentários e docstrings para ajudar a mantê-lo claro. Já terminamos de representar a nossa cadeia de blocos. Mas neste momento, você deve estar se perguntando como novos blocos são criados, forjados ou minados.

Compreensão da Prova de Trabalho
Um algoritmo Prova de Trabalho (PoW) é como os novos Blocos são criados ou extraídos no bloco . O objetivo da PoW é descobrir um número que resolva um problema. O número deve ser difícil de encontrar, mas fácil de verificar -computação falando- por qualquer pessoa na rede. Esta é a ideia central da prova de trabalho.

Examinaremos um exemplo muito simples para ajudar a afundar.

Vamos decidir que o hash de algum inteiro xmultiplicado por outro ydeve terminar 0. Então hash(x * y) = ac23dc…0,. E para este exemplo simplificado, vamos consertar x = 5. Implementando isso no Python:

from hashlib import sha256
x = 5
y = 0 # We don’t know what y should be yet…
while sha256(f'{x*y}’.encode()).hexdigest()[-1] != “0”:
y += 1
print(f’The solution is y = {y}’)

A solução aqui é y = 21. Como o hash produzido termina em 0:

hash (5 * 21) = 1253e9373e … 5e3600155e860

Em Bitcoin, o algoritmo Prova de Trabalho é chamado Hashcash . E não é muito diferente do nosso exemplo básico acima. É o algoritmo que os mineiros correm para resolver para criar um novo bloco. Em geral, a dificuldade é determinada pelo número de caracteres procurados em uma string. Os mineiros são então recompensados ​​por sua solução ao receber uma moeda – em uma transação.

A rede pode verificar facilmente sua solução.

Implementando Prova de Trabalho básica
Vamos implementar um algoritmo similar para nossa cadeia de blocos. Nossa regra será semelhante ao exemplo acima:
Encontre um número p que, quando escondeu com a solução do bloco anterior, é produzido um hash com 4 s principais 0.

import hashlib
import json

from time import time
from uuid import uuid4

class Blockchain(object):

def proof_of_work(self, last_proof):
“””
Simple Proof of Work Algorithm:
– Find a number p’ such that hash(pp’) contains leading 4 zeroes, where p is the previous p’
– p is the previous proof, and p’ is the new proof
:param last_proof: <int>
:return: <int>
“””

proof = 0
while self.valid_proof(last_proof, proof) is False:
proof += 1

return proof

@staticmethod
def valid_proof(last_proof, proof):
“””
Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
:param last_proof: <int> Previous Proof
:param proof: <int> Current Proof
:return: <bool> True if correct, False if not.
“””

guess = f'{last_proof}{proof}’.encode()
guess_hash = hashlib.sha256(guess).hexdigest()
return guess_hash[:4] == “0000”

Para ajustar a dificuldade do algoritmo, poderíamos modificar o número de zeros de liderança. Mas 4 é suficiente. Você descobrirá que a adição de um único zero líder faz uma diferença gigantesca com o tempo necessário para encontrar uma solução.
Nossa classe está quase completa e estamos prontos para começar a interagir com ela usando solicitações HTTP.

Passo 2: Nosso bloqueio como API
Nós vamos usar o Python Flask Framework. É um micro-framework e facilita o mapeamento de pontos finais para as funções do Python. Isso nos permite conversar com nosso bloqueio na web usando solicitações HTTP.

Vamos criar três métodos:

/transactions/new  para criar uma nova transação para um bloco
/mine  para dizer ao nosso servidor para explorar um novo bloco.
/chain  para retornar o Blockchain completo.

Pote de instalação
Nosso “servidor” formará um único nó na nossa rede de cadeias de blocos. Vamos criar algum código de referência:

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4

from flask import Flask

class Blockchain(object):

# Instantiate our Node
app = Flask(__name__)

# Generate a globally unique address for this node
node_identifier = str(uuid4()).replace(‘-‘, ”)

# Instantiate the Blockchain
blockchain = Blockchain()

@app.route(‘/mine’, methods=[‘GET’])
def mine():
return “We’ll mine a new Block”

@app.route(‘/transactions/new’, methods=[‘POST’])
def new_transaction():
return “We’ll add a new transaction”

@app.route(‘/chain’, methods=[‘GET’])
def full_chain():
response = {
‘chain’: blockchain.chain,
‘length’: len(blockchain.chain),
}
return jsonify(response), 200

if __name__ == ‘__main__’:
app.run(host=’0.0.0.0′, port=5000)

Uma breve explicação sobre o que adicionamos acima:

Linha 15: Instancia nosso nó. Leia mais sobre Flask aqui .
Linha 18: Crie um nome aleatório para o nosso nó.
Linha 21: instanciar nossa Blockchainclasse.
Linha 24-26: Crie o /mineponto final, que é uma GETsolicitação.
Linha 28-30: Crie o /transactions/newnó de extremidade, que é um POSTpedido, já que enviaremos dados para ele.
Linha 32-38: Crie o /chainponto final, que retorna a cadeia Block completa.
Linha 40-41: Executa o servidor na porta 5000.

O ponto final das transações
Isto é o que o pedido de uma transação se parecerá. É o que o usuário envia para o servidor:

{
“remetente”: “meu endereço”,
“destinatário”: “endereço de outra pessoa”,
“montante”: 5
}

Como já temos nosso método de classe para adicionar transações para um bloco, o resto é fácil. Vamos escrever a função para adicionar transações:

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4
from flask import Flask, jsonify, request

@app.route(‘/transactions/new’, methods=[‘POST’])
def new_transaction():
values = request.get_json()

# Check that the required fields are in the POST’ed data
required = [‘sender’, ‘recipient’, ‘amount’]
if not all(k in values for k in required):
return ‘Missing values’, 400

# Create a new Transaction
index = blockchain.new_transaction(values[‘sender’], values[‘recipient’], values[‘amount’])

response = {‘message’: f’Transaction will be added to Block {index}’}
return jsonify(response), 201

O ponto final da mineração
Nosso ponto final de mineração é onde a magia acontece, e é fácil. Tem que fazer três coisas:

Calcule a Prova de Trabalho
Recompense o mineiro (nos) adicionando uma transação concedendo-nos 1 moeda
Forja o novo bloco adicionando-o à corrente:

import hashlib
import json

from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

@app.route(‘/mine’, methods=[‘GET’])
def mine():
# We run the proof of work algorithm to get the next proof…
last_block = blockchain.last_block
last_proof = last_block[‘proof’]
proof = blockchain.proof_of_work(last_proof)

# We must receive a reward for finding the proof.
# The sender is “0” to signify that this node has mined a new coin.
blockchain.new_transaction(
sender=”0″,
recipient=node_identifier,
amount=1,
)

# Forge the new Block by adding it to the chain
previous_hash = blockchain.hash(last_block)
block = blockchain.new_block(proof, previous_hash)

response = {
‘message’: “New Block Forged”,
‘index’: block[‘index’],
‘transactions’: block[‘transactions’],
‘proof’: block[‘proof’],
‘previous_hash’: block[‘previous_hash’],
}
return jsonify(response), 200

Observe que o destinatário do bloco minado é o endereço do nosso nó. E a maioria do que fizemos aqui é apenas interagir com os métodos em nossa classe Blockchain. Neste ponto, terminamos e podemos começar a interagir com nossa cadeia de blocos.

Passo 3: interagindo com o nosso Blockchain
Você pode usar o antigo CURL ou Postman antigo para interagir com a nossa API através de uma rede.

Arranque o servidor:

$ python blockchain.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Vamos tentar extrair um bloco fazendo um GET pedido para http://localhost:5000/mine:

get-localhost

Vamos criar uma nova transação fazendo um POST pedido http://localhost:5000/transactions/new um corpo contendo nossa estrutura de transações:

Se você não estiver usando o Postman, então você pode fazer o pedido equivalente usando cURL:

$ curl -X POST -H “Content-Type: application/json” -d ‘{
“sender”: “d4ee26eee15148ee92c6cd394edd974e”,
“recipient”: “someone-other-address”,
“amount”: 5
}’ “http://localhost:5000/transactions/new”

Reiniciei meu servidor e extrai dois blocos, para dar 3 no total. Vamos inspecionar a cadeia completa, solicitando http://localhost:5000/chain:

{
“chain”: [
{
“index”: 1,
“previous_hash”: 1,
“proof”: 100,
“timestamp”: 1506280650.770839,
“transactions”: []
},
{
“index”: 2,
“previous_hash”: “c099bc…bfb7”,
“proof”: 35293,
“timestamp”: 1506280664.717925,
“transactions”: [
{
“amount”: 1,
“recipient”: “8bbcb347e0634905b0cac7955bae152b”,
“sender”: “0”
}
]
},
{
“index”: 3,
“previous_hash”: “eff91a…10f2”,
“proof”: 35089,
“timestamp”: 1506280666.1086972,
“transactions”: [
{
“amount”: 1,
“recipient”: “8bbcb347e0634905b0cac7955bae152b”,
“sender”: “0”
}
]
}
],
“length”: 3
}

Passo 4: Consenso
Isso é muito legal. Temos um Blockchain básico que aceita transações e nos permite explorar novos blocos. Mas o ponto inteiro de Blockchains é que eles deveriam ser descentralizados . E se eles são descentralizados, como podemos assegurar que todos eles refletem a mesma cadeia? Isso é chamado de problema do Consenso , e teremos que implementar um Algoritmo de Consenso se quisermos mais de um nó em nossa rede.

Registrando novos nós
Antes de poder implementar um Algoritmo de Consenso, precisamos de uma maneira de informar um nó sobre nós vizinhos na rede. Cada nó em nossa rede deve manter um registro de outros nós na rede. Assim, precisaremos de mais pontos finais:

/nodes/register para aceitar uma lista de novos nós na forma de URLs.
/nodes/resolve para implementar nosso Algoritmo de Consenso, que resolve qualquer conflito – para garantir que um nó tenha a cadeia correta.
Precisamos modificar o construtor do nosso Blockchain e fornecer um método.


from urllib.parse import urlparse

class Blockchain(object):
def __init__(self):

self.nodes = set()

def register_node(self, address):
“””
Add a new node to the list of nodes
:param address: <str> Address of node. Eg. ‘http://192.168.0.5:5000’
:return: None
“””

parsed_url = urlparse(address)
self.nodes.add(parsed_url.netloc)

Note que usamos um set()para manter a lista de nós. Esta é uma maneira barata de garantir que a adição de novos nós seja idempotente – o que significa que, não importa quantas vezes adicionamos um nó específico, ele aparece exatamente uma vez.

Implementando o Algoritmo de Consenso
Como mencionado, um conflito é quando um nó possui uma cadeia diferente para outro nó. Para resolver isso, faremos a regra de que a cadeia mais longa válida é autorizada. Em outras palavras, a cadeia mais longa na rede é de facto . Usando esse algoritmo, chegamos ao Consenso entre os nós em nossa rede.


import requests

class Blockchain(object)

def valid_chain(self, chain):
“””
Determine if a given blockchain is valid
:param chain: <list> A blockchain
:return: <bool> True if valid, False if not
“””

last_block = chain[0]
current_index = 1

while current_index < len(chain):
block = chain[current_index]
print(f'{last_block}’)
print(f'{block}’)
print(“\n———–\n”)
# Check that the hash of the block is correct
if block[‘previous_hash’] != self.hash(last_block):
return False

# Check that the Proof of Work is correct
if not self.valid_proof(last_block[‘proof’], block[‘proof’]):
return False

last_block = block
current_index += 1

return True

def resolve_conflicts(self):
“””
This is our Consensus Algorithm, it resolves conflicts
by replacing our chain with the longest one in the network.
:return: <bool> True if our chain was replaced, False if not
“””

neighbours = self.nodes
new_chain = None

# We’re only looking for chains longer than ours
max_length = len(self.chain)

# Grab and verify the chains from all the nodes in our network
for node in neighbours:
response = requests.get(f’http://{node}/chain’)

if response.status_code == 200:
length = response.json()[‘length’]
chain = response.json()[‘chain’]

# Check if the length is longer and the chain is valid
if length > max_length and self.valid_chain(chain):
max_length = length
new_chain = chain

# Replace our chain if we discovered a new, valid chain longer than ours
if new_chain:
self.chain = new_chain
return True

return False

O primeiro método valid_chain() é responsável por verificar se uma cadeia é válida através de cada bloco e verificar o hash e a prova.

resolve_conflicts() é um método que acompanha todos os nossos nós vizinhos, baixa suas cadeias e verifica-as usando o método acima. Se for encontrada uma cadeia válida, cujo comprimento é maior do que o nosso, substituímos o nosso.

Vamos registrar os dois pontos finais na nossa API, um para adicionar nós vizinhos e outro para resolver conflitos:

@app.route(‘/nodes/register’, methods=[‘POST’])
def register_nodes():
values = request.get_json()

nodes = values.get(‘nodes’)
if nodes is None:
return “Error: Please supply a valid list of nodes”, 400

for node in nodes:
blockchain.register_node(node)

response = {
‘message’: ‘New nodes have been added’,
‘total_nodes’: list(blockchain.nodes),
}
return jsonify(response), 201

@app.route(‘/nodes/resolve’, methods=[‘GET’])
def consensus():
replaced = blockchain.resolve_conflicts()

if replaced:
response = {
‘message’: ‘Our chain was replaced’,
‘new_chain’: blockchain.chain
}
else:
response = {
‘message’: ‘Our chain is authoritative’,
‘chain’: blockchain.chain
}

return jsonify(response), 200

Neste ponto, você pode pegar uma máquina diferente, se quiser, e gire diferentes nós na sua rede. Ou gire processos usando diferentes portas na mesma máquina. Eu girei outro nó na minha máquina, em uma porta diferente e registrei com meu nó atual. Assim, eu tenho dois nós: http://localhost:5000e http://localhost:5001.

Então, extrai alguns novos blocos no nó 2, para garantir que a cadeia fosse mais longa. Depois, liguei GET /nodes/resolvepara o nó 1, onde a cadeia foi substituída pelo Algoritmo de Consenso:

E isso é um invólucro

Espero que isso tenha inspirado você a criar algo novo.
Estou entusiasmado com Cryptocurrencies porque acredito que o Blockchains mudará rapidamente a maneira como pensamos sobre economias, governos e manutenção de registros.
Atualização: estou planejando o acompanhamento de uma Parte 2, onde expandiremos o nosso Blockchain para ter um Mecanismo de Validação de Transações, além de discutir algumas maneiras pelas quais você pode produzir seu Blockchain.

 

 

 

 

 

HostFirewall