Blockchain Python - Guia rápido

No tutorial sobre Blockchain, aprendemos em detalhes sobre a teoria por trás do blockchain. O blockchain é o bloco de construção fundamental por trás da moeda digital Bitcoin mais popular do mundo. O tutorial lidou profundamente com as complexidades do Bitcoin explicando totalmente a arquitetura do blockchain. A próxima etapa é construir nosso próprio blockchain.

Satoshi Nakamoto criou a primeira moeda virtual do mundo chamada Bitcoin. Olhando para o sucesso do Bitcoin, muitos outros criaram suas próprias moedas virtuais. Para citar alguns - Litecoin, Zcash e assim por diante.

Agora, você também pode querer lançar sua própria moeda. Vamos chamar isso de TPCoin (TutorialsPoint Coin). Você escreverá um blockchain para registrar todas as transações que lidam com TPCoin. O TPCoin pode ser usado para comprar Pizzas, Hambúrgueres, Saladas, etc. Pode haver outros provedores de serviço que ingressariam na sua rede e passariam a aceitar o TPCoin como moeda de prestação de seus serviços. As possibilidades são infinitas.

Neste tutorial, vamos entender como construir tal sistema e lançar sua própria moeda digital no mercado.

Componentes envolvidos no desenvolvimento do projeto Blockchain

Todo o desenvolvimento do projeto blockchain consiste em três componentes principais -

  • Client
  • Miners
  • Blockchain

Cliente

O cliente é quem vai comprar mercadorias de outros vendedores. O próprio cliente pode se tornar um vendedor e aceitar dinheiro de terceiros contra os bens que fornece. Presumimos aqui que o cliente pode ser um fornecedor e um destinatário de TPCoins. Assim, iremos criar uma classe de cliente em nosso código que tem a capacidade de enviar e receber dinheiro.

Mineiro

O Mineiro é aquele que pega as transações de um pool de transações e as monta em um bloco. O minerador deve fornecer uma prova de trabalho válida para receber a recompensa de mineração. Todo o dinheiro que o mineiro receber como taxa ficará para ele ficar com. Ele pode gastar esse dinheiro na compra de bens ou serviços de outros fornecedores registrados na rede, da mesma forma que um Cliente descrito acima faz.

Blockchain

Finalmente, um Blockchain é uma estrutura de dados que encadeia todos os blocos minerados em ordem cronológica. Essa corrente é imutável e, portanto, à prova de temperamento.

Você pode seguir este tutorial digitando o código apresentado em cada etapa em um novo bloco de notas Jupyter. Como alternativa, você pode baixar o bloco de notas Jupyter completo em www.anaconda.com .

No próximo capítulo, desenvolveremos um cliente que usa nosso sistema de blockchain.

Um cliente é alguém que possui TPCoins e os transaciona para bens / serviços de outros fornecedores na rede, incluindo o seu. Devemos definir umClientclasse para este fim. Para criar uma identificação globalmente única para o cliente, usamos PKI (Public Key Infrastructure). Neste capítulo, vamos falar sobre isso em detalhes.

O cliente deve poder enviar dinheiro de sua carteira para outra pessoa conhecida. Da mesma forma, o cliente deve ser capaz de aceitar dinheiro de terceiros. Para gastar dinheiro, o cliente criaria uma transação especificando o nome do remetente e o valor a ser pago. Para receber dinheiro, o cliente fornecerá sua identidade ao terceiro - essencialmente, um remetente do dinheiro. Não armazenamos o valor do saldo em dinheiro que o cliente mantém em sua carteira. Durante uma transação, calcularemos o saldo real para garantir que o cliente tenha saldo suficiente para fazer o pagamento.

Para desenvolver o Clientclasse e para o resto do código do projeto, vamos precisar importar muitas bibliotecas Python. Eles estão listados abaixo -

# import libraries
import hashlib
import random
import string
import json
import binascii
import numpy as np
import pandas as pd
import pylab as pl
import logging
import datetime
import collections

Além das bibliotecas padrão acima, vamos assinar nossas transações, criar hash dos objetos, etc. Para isso, você precisará importar as seguintes bibliotecas -

# following imports are required by PKI
import Crypto
import Crypto.Random
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

No próximo capítulo, vamos falar sobre a classe do cliente.

o Client classe gera o private e public chaves usando o Python integrado RSAalgoritmo. O leitor interessado pode se referir athis tutorialpara a implementação do RSA. Durante a inicialização do objeto, criamos chaves privadas e públicas e armazenamos seus valores na variável de instância.

self._private_key = RSA.generate(1024, random)
self._public_key = self._private_key.publickey()

Observe que você nunca deve perder sua chave privada. Para manutenção de registros, a chave privada gerada pode ser copiada em um armazenamento externo seguro ou você pode simplesmente escrever a representação ASCII dela em um pedaço de papel.

O gerado publica chave será usada como a identidade do cliente. Para isso, definimos uma propriedade chamadaidentity que retorna a representação HEX da chave pública.

@property
   def identity(self):
      return
binascii.hexlify(self._public_key.exportKey(format='DER'))
.decode('ascii')

o identityé exclusivo para cada cliente e pode ser disponibilizado ao público. Qualquer pessoa seria capaz de enviar moeda virtual para você usando esteidentity e ele será adicionado à sua carteira.

O código completo para o Client classe é mostrada aqui -

class Client:
   def __init__(self):
      random = Crypto.Random.new().read
      self._private_key = RSA.generate(1024, random)
      self._public_key = self._private_key.publickey()
      self._signer = PKCS1_v1_5.new(self._private_key)

   @property
   def identity(self):
      return
binascii.hexlify(self._public_key.exportKey(format='DER')).decode('ascii')

Cliente de Teste

Agora, vamos escrever um código que ilustrará como usar o Client classe -

Dinesh = Client()
print (Dinesh.identity)

O código acima cria uma instância de Client e o atribui à variável Dinesh. Imprimimos a chave pública deDinesh chamando seu identitymétodo. A saída é mostrada aqui -

30819f300d06092a864886f70d010101050003818d0030818902818100b547fafceeb131e07
0166a6b23fec473cce22c3f55c35ce535b31d4c74754fecd820aa94c1166643a49ea5f49f72
3181ff943eb3fdc5b2cb2db12d21c06c880ccf493e14dd3e93f3a9e175325790004954c34d3
c7bc2ccc9f0eb5332014937f9e49bca9b7856d351a553d9812367dc8f2ac734992a4e6a6ff6
6f347bd411d07f0203010001

Agora, vamos prosseguir para criar uma transação no próximo capítulo.

Neste capítulo, vamos criar um Transactionclasse para que um cliente possa enviar dinheiro para alguém. Observe que um cliente pode ser remetente ou destinatário do dinheiro. Quando você deseja receber dinheiro, algum outro remetente irá criar uma transação e especificar o seupublicendereço nele. Definimos a inicialização de uma classe de transação da seguinte forma -

def __init__(self, sender, recipient, value):
   self.sender = sender
   self.recipient = recipient
   self.value = value
   self.time = datetime.datetime.now()

o init método leva três parâmetros - o remetente public chave, do destinatário publicchave, e o valor a ser enviado. Eles são armazenados nas variáveis ​​de instância para uso por outros métodos. Além disso, criamos mais uma variável para armazenar o tempo da transação.

Em seguida, escrevemos um método utilitário chamado to_dictque combina todas as quatro variáveis ​​de instância mencionadas acima em um objeto de dicionário. Isso é apenas para colocar todas as informações da transação acessíveis por meio de uma única variável.

Como você sabe do tutorial anterior, que o primeiro bloco no blockchain é um Genesisquadra. O bloco Genesis contém a primeira transação iniciada pelo criador do blockchain. A identidade dessa pessoa pode ser mantida em segredo, como no caso dos Bitcoins. Então, quando esta primeira transação é criada, o criador pode apenas enviar sua identidade comoGenesis. Assim, ao criar o dicionário, verificamos se o remetente éGenesise se for assim, simplesmente atribuímos algum valor de string à variável de identidade; caso contrário, atribuímos a identidade do remetente aoidentity variável.

if self.sender == "Genesis":
   identity = "Genesis"
else:
   identity = self.sender.identity

Construímos o dicionário usando a seguinte linha de código

return collections.OrderedDict({
   'sender': identity,
   'recipient': self.recipient,
   'value': self.value,
   'time' : self.time})

Todo o código para o to_dict método é mostrado abaixo -

def to_dict(self):
   if self.sender == "Genesis":
      identity = "Genesis"
   else:
      identity = self.sender.identity

   return collections.OrderedDict({
      'sender': identity,
      'recipient': self.recipient,
      'value': self.value,
      'time' : self.time})

Finalmente, assinaremos este objeto de dicionário usando a chave privada do remetente. Como antes, usamos a PKI embutida com algoritmo SHA. A assinatura gerada é decodificada para obter a representação ASCII para impressão e armazenamento em nosso blockchain. osign_transaction o código do método é mostrado aqui -

def sign_transaction(self):
   private_key = self.sender._private_key
   signer = PKCS1_v1_5.new(private_key)
   h = SHA.new(str(self.to_dict()).encode('utf8'))
   return binascii.hexlify(signer.sign(h)).decode('ascii')

Agora vamos testar isso Transaction classe.

Teste de classe de transação

Para tanto, iremos criar dois usuários, chamados Dinesh e Ramesh. Dinesh enviará 5 TPCoins para Ramesh. Para isso, primeiro criamos os clientes chamados Dinesh e Ramesh.

Dinesh = Client()
Ramesh = Client()

Lembre-se de que quando você instancia um Client classe, o public andchaves privadas exclusivas para o cliente seriam criadas. Como Dinesh está enviando o pagamento para Ramesh, ele precisará da chave pública de Ramesh, que é obtida usando a identidade de propriedade do cliente.

Assim, vamos criar a instância da transação usando o seguinte código -

t = Transaction(
   Dinesh,
   Ramesh.identity,
   5.0
)

Observe que o primeiro parâmetro é o remetente, o segundo parâmetro é a chave pública do destinatário e o terceiro parâmetro é a quantidade a ser transferida. osign_transaction método recupera a chave privada do remetente do primeiro parâmetro para cantar a transação.

Depois que o objeto de transação for criado, você o assinará chamando seu sign_transactionmétodo. Este método retorna a assinatura gerada no formato para impressão. Geramos e imprimimos a assinatura usando as duas linhas de código a seguir -

signature = t.sign_transaction()
print (signature)

Ao executar o código acima, você verá uma saída semelhante a esta -

7c7e3c97629b218e9ec6e86b01f9abd8e361fd69e7d373c38420790b655b9abe3b575e343c7
13703ca1aee781acd7157a0624db3d57d7c2f1172730ee3f45af943338157f899965856f6b0
0e34db240b62673ad5a08c8e490f880b568efbc36035cae2e748f1d802d5e8e66298be826f5
c6363dc511222fb2416036ac04eb972

Agora que nossa infraestrutura básica de criação de um cliente e uma transação está pronta, agora teremos vários clientes fazendo várias transações como em uma situação da vida real.

As transações feitas por diversos clientes são enfileiradas no sistema; os mineiros pegam as transações dessa fila e as adicionam ao bloco. Eles então minerarão o bloco e o mineiro vencedor terá o privilégio de adicionar o bloco ao blockchain e, assim, ganhar algum dinheiro para si mesmo.

Descreveremos esse processo de mineração mais tarde, quando discutirmos a criação do blockchain. Antes de escrevermos o código para várias transações, vamos adicionar uma pequena função de utilidade para imprimir o conteúdo de uma determinada transação.

Exibindo transação

o display_transactionfunção aceita um único parâmetro do tipo de transação. O objeto de dicionário dentro da transação recebida é copiado para uma variável temporária chamadadict e usando as chaves de dicionário, os vários valores são impressos no console.

def display_transaction(transaction):
   #for transaction in transactions:
   dict = transaction.to_dict()
   print ("sender: " + dict['sender'])
   print ('-----')
   print ("recipient: " + dict['recipient'])
   print ('-----')
   print ("value: " + str(dict['value']))
   print ('-----')
   print ("time: " + str(dict['time']))
   print ('-----')

A seguir, definimos uma fila de transações para armazenar nossos objetos de transação.

Fila de transações

Para criar uma fila, declaramos um global list variável chamada transactions como segue -

transactions = []

Vamos simplesmente anexar cada transação recém-criada a esta fila. Observe que, por questões de brevidade, não implementaremos a lógica de gerenciamento de filas neste tutorial.

Criação de vários clientes

Agora, vamos começar a criar transações. Primeiro, criaremos quatro clientes que enviarão dinheiro uns aos outros para a obtenção de vários serviços ou mercadorias de terceiros.

Dinesh = Client()
Ramesh = Client()
Seema = Client()
Vijay = Client()

Neste ponto, temos quatro clientes chamados Dinesh, Ramesh, Seema e Vijay. Presumimos atualmente que cada um desses clientes possui algumas TPCoins em suas carteiras para transações. A identidade de cada um desses clientes seria especificada usando a propriedade de identidade desses objetos.

Criação da primeira transação

Agora, iniciamos nossa primeira transação da seguinte forma -

t1 = Transaction(
   Dinesh,
   Ramesh.identity,
   15.0
)

Nesta transação, Dinesh envia 5 TPCoins para Ramesh. Para que a transação seja bem-sucedida, teremos que garantir que Dinesh tenha dinheiro suficiente em sua carteira para este pagamento. Observe que precisaremos de uma transação de gênesis para iniciar a circulação de TPCoin no sistema. Você escreverá o código de transação para esta transação de gênesis muito em breve, enquanto lê.

Vamos assinar esta transação usando a chave privada de Dinesh e adicioná-la à fila de transações da seguinte forma -

t1.sign_transaction()
transactions.append(t1)

Após a primeira transação feita pela Dinesh, criaremos várias outras transações entre diferentes clientes que criamos acima.

Adicionar mais transações

Agora criaremos várias outras transações, cada transação distribuída algumas TPCoins para outra parte. Quando alguém gasta dinheiro, não é necessário que ele verifique se há saldos suficientes nesta carteira. O minerador, de qualquer maneira, estaria validando cada transação para o saldo que o remetente tem ao iniciar a transação.

Em caso de saldo insuficiente, o minerador marcará esta transação como inválida e não a adicionará a este bloco.

O código a seguir cria e adiciona mais nove transações à nossa fila.

t2 = Transaction(
   Dinesh,
   Seema.identity,
   6.0
)
t2.sign_transaction()
transactions.append(t2)
t3 = Transaction(
   Ramesh,
   Vijay.identity,
   2.0
)
t3.sign_transaction()
transactions.append(t3)
t4 = Transaction(
   Seema,
   Ramesh.identity,
   4.0
)
t4.sign_transaction()
transactions.append(t4)
t5 = Transaction(
   Vijay,
   Seema.identity,
   7.0
)
t5.sign_transaction()
transactions.append(t5)
t6 = Transaction(
   Ramesh,
   Seema.identity,
   3.0
)
t6.sign_transaction()
transactions.append(t6)
t7 = Transaction(
   Seema,
   Dinesh.identity,
   8.0
)
t7.sign_transaction()
transactions.append(t7)
t8 = Transaction(
   Seema,
   Ramesh.identity,
   1.0
)
t8.sign_transaction()
transactions.append(t8)
t9 = Transaction(
   Vijay,
   Dinesh.identity,
   5.0
)
t9.sign_transaction()
transactions.append(t9)
t10 = Transaction(
   Vijay,
   Ramesh.identity,
   3.0
)
t10.sign_transaction()
transactions.append(t10)

Ao executar o código acima, você terá dez transações na fila para os mineiros criarem seus blocos.

Operações de despejo

Como um gerenciador de blockchain, você pode querer revisar periodicamente o conteúdo da fila de transações. Para este propósito, você pode usar odisplay_transactionfunção que desenvolvemos anteriormente. Para despejar todas as transações na fila, apenas itere a lista de transações e para cada transação referenciada, chame odisplay_transaction função conforme mostrado aqui -

for transaction in transactions:
   display_transaction (transaction)
   print ('--------------')

As transações são separadas por uma linha tracejada para distinção. Se você executar o código acima, verá a lista de transações conforme mostrado abaixo -

sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c49214
4a9f463480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329e
c86794b04d773eb4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b
47e5157f8fe56c2ce3279c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311
c4d866c12d79d3fc3034563dfb0203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e
674abe7abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8ad
d126b6e1a1308fb98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa93977
04de625d1836d3f42c7ee5683f6703259592cc24b09699376807f28fe0e00ff882974484
d805f874260dfc2d1627473b910203010001
-----
value: 15.0
-----
time: 2019-01-14 16:18:01.859915
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c49214
4a9f463480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329e
c86794b04d773eb4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b
47e5157f8fe56c2ce3279c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311
c4d866c12d79d3fc3034563dfb0203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae14
3cbe59b3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fb
d9ee74b9e7ea12334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0
961b4f212d1fd5b5e49ae09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d0623
75799742a359b8f22c5362e5650203010001
-----
value: 6.0
-----
time: 2019-01-14 16:18:01.860966
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e
674abe7abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8ad
d126b6e1a1308fb98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa93977
04de625d1836d3f42c7ee5683f6703259592cc24b09699376807f28fe0e00ff882974484
d805f874260dfc2d1627473b910203010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876
f41338c62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cc
e25be99452a81df4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47
452590137869c25d9ff83d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f0
0e321b65e4c33acaf6469e18e30203010001
-----
value: 2.0
-----
time: 2019-01-14 16:18:01.861958
-----
--------------

Para resumir, imprimi apenas as primeiras transações da lista. No código acima, imprimimos todas as transações começando com a primeira transação, exceto para a transação do genesis que nunca foi adicionada a esta lista. Como as transações são adicionadas aos blocos periodicamente, geralmente você estará interessado em ver apenas a lista de transações que ainda não foram exploradas. Nesse caso, você precisará criar um apropriadofor loop para iterar pelas transações que ainda não foram extraídas.

Até agora, você aprendeu como criar clientes, permitir que eles se encontrem e manter uma fila de transações pendentes que serão mineradas. Agora, vem a parte mais importante deste tutorial, que é a criação de um blockchain. Você aprenderá isso na próxima lição.

Um bloco consiste em um número variável de transações. Para simplificar, em nosso caso, assumiremos que o bloco consiste em um número fixo de transações, que é três neste caso. Como o bloco precisa armazenar a lista dessas três transações, iremos declarar uma variável de instância chamadaverified_transactions como segue -

self.verified_transactions = []

Chamamos essa variável de verified_transactionspara indicar que apenas as transações válidas verificadas serão adicionadas ao bloco. Cada bloco também contém o valor hash do bloco anterior, de modo que a cadeia de blocos se torna imutável.

Para armazenar o hash anterior, declaramos uma variável de instância da seguinte maneira -

self.previous_block_hash = ""

Finalmente, declaramos mais uma variável chamada Nonce para armazenar o nonce criado pelo minerador durante o processo de mineração.

self.Nonce = ""

A definição completa do Block a aula é dada abaixo -

class Block:
   def __init__(self):
      self.verified_transactions = []
      self.previous_block_hash = ""
      self.Nonce = ""

Como cada bloco precisa do valor do hash do bloco anterior, declaramos uma variável global chamada last_block_hash como segue -

last_block_hash = ""

Agora vamos criar nosso primeiro bloco no blockchain.

Assumimos que o originador de TPCoins inicialmente distribui 500 TPCoins para um cliente conhecido Dinesh. Para isso, ele primeiro cria uma instância Dinesh -

Dinesh = Client()

Em seguida, criamos uma transação genesis e enviamos 500 TPCoins para o endereço público de Dinesh.

t0 = Transaction (
   "Genesis",
   Dinesh.identity,
   500.0
)

Agora, criamos uma instância de Block classe e chamá-lo block0.

block0 = Block()

Nós inicializamos o previous_block_hash e Nonce variáveis ​​de instância para None, já que esta é a primeira transação a ser armazenada em nosso blockchain.

block0.previous_block_hash = None
Nonce = None

A seguir, adicionaremos a transação t0 acima ao verified_transactions lista mantida dentro do bloco -

block0.verified_transactions.append (t0)

Neste ponto, o bloco está completamente inicializado e pronto para ser adicionado ao nosso blockchain. Estaremos criando o blockchain para este propósito. Antes de adicionar o bloco ao blockchain, faremos o hash do bloco e armazenaremos seu valor na variável global chamadalast_block_hashque declaramos anteriormente. Este valor será usado pelo próximo mineiro em seu bloco.

Usamos as duas linhas de codificação a seguir para fazer o hash do bloco e armazenar o valor do resumo.

digest = hash (block0)
last_block_hash = digest

Finalmente, criamos um blockchain como veremos no próximo capítulo.

Um blockchain contém uma lista de blocos encadeados entre si. Para armazenar a lista inteira, criaremos uma variável de lista chamada TPCoins -

TPCoins = []

Também escreveremos um método utilitário chamado dump_blockchainpara despejar o conteúdo de todo o blockchain. Primeiro imprimimos o comprimento do blockchain para saber quantos blocos estão presentes no blockchain.

def dump_blockchain (self):
   print ("Number of blocks in the chain: " + str(len (self)))

Observe que, com o passar do tempo, o número de blocos no blockchain seria extraordinariamente alto para impressão. Portanto, ao imprimir o conteúdo do blockchain, você pode ter que decidir sobre o intervalo que gostaria de examinar. No código abaixo, imprimimos todo o blockchain, pois não adicionaríamos muitos blocos na demonstração atual.

Para iterar pela cadeia, criamos um for loop da seguinte forma -

for x in range (len(TPCoins)):
   block_temp = TPCoins[x]

Cada bloco referenciado é copiado para uma variável temporária chamada block_temp.

Imprimimos o número do bloco como um título para cada bloco. Observe que os números começam com zero, o primeiro bloco é um bloco de gênese que é numerado com zero.

print ("block # " + str(x))

Dentro de cada bloco, armazenamos uma lista de três transações (exceto para o bloco genesis) em uma variável chamada verified_transactions. Nós iteramos esta lista em umfor loop e para cada item recuperado, chamamos display_transaction função para exibir os detalhes da transação.

for transaction in block_temp.verified_transactions:
   display_transaction (transaction)

Toda a definição da função é mostrada abaixo -

def dump_blockchain (self):
   print ("Number of blocks in the chain: " + str(len (self)))
   for x in range (len(TPCoins)):
      block_temp = TPCoins[x]
      print ("block # " + str(x))
      for transaction in block_temp.verified_transactions:
         display_transaction (transaction)
         print ('--------------')
      print ('=====================================')

Observe que aqui inserimos os separadores em pontos apropriados no código para demarcar os blocos e transações dentro dele.

Como agora criamos um blockchain para armazenar blocos, nossa próxima tarefa é criar blocos e começar a adicioná-lo ao blockchain. Para este propósito, iremos adicionar um bloco genesis que você já criou na etapa anterior.

Adicionar um bloco ao blockchain envolve anexar o bloco criado ao nosso TPCoins Lista.

TPCoins.append (block0)

Observe que, ao contrário do resto dos blocos no sistema, o bloco de origem contém apenas uma transação que é iniciada pelo originador do sistema TPCoins. Agora, você irá despejar o conteúdo do blockchain chamando nossa função globaldump_blockchain -

dump_blockchain(TPCoins)

Ao executar esta função, você verá a seguinte saída -

Number of blocks in the chain: 1
block # 0
sender: Genesis
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100ed272b52ccb539
e2cd779c6cc10ed1dfadf5d97c6ab6de90ed0372b2655626fb79f62d0e01081c163b0864
cc68d426bbe9438e8566303bb77414d4bfcaa3468ab7febac099294de10273a816f7047d
4087b4bafa11f141544d48e2f10b842cab91faf33153900c7bf6c08c9e47a7df8aa7e60d
c9e0798fb2ba3484bbdad2e4430203010001
-----
value: 500.0
-----
time: 2019-01-14 16:18:02.042739
-----
--------------
=====================================

Neste ponto, o sistema blockchain está pronto para uso. Agora permitiremos que os clientes interessados ​​se tornem mineradores, fornecendo-lhes uma funcionalidade de mineração.

Para habilitar a mineração, precisamos desenvolver uma função de mineração. A funcionalidade de mineração precisa gerar um resumo em uma determinada string de mensagem e fornecer uma prova de trabalho. Vamos discutir isso neste capítulo.

Função Message Digest

Vamos escrever uma função de utilidade chamada sha256 para criar um resumo de uma determinada mensagem -

def sha256(message):
return hashlib.sha256(message.encode('ascii')).hexdigest()

o sha256 função leva um message como um parâmetro, o codifica para ASCII, gera um resumo hexadecimal e retorna o valor ao chamador.

Função Mineira

Agora desenvolvemos o minefunção que implementa nossa própria estratégia de mineração. Nossa estratégia, nesse caso, seria gerar um hash na mensagem fornecida, prefixado com um determinado número de 1s. O número de 1 fornecido é especificado como um parâmetro paramine função especificada como o nível de dificuldade.

Por exemplo, se você especificar um nível de dificuldade de 2, o hash gerado em uma determinada mensagem deve começar com dois 1s - como 11xxxxxxxx. Se o nível de dificuldade for 3, o hash gerado deve começar com três 1's - como 111xxxxxxxx. Dados esses requisitos, desenvolveremos agora a função de mineração conforme mostrado nas etapas fornecidas a seguir.

Passo 1

A função de mineração leva dois parâmetros - a mensagem e o nível de dificuldade.

def mine(message, difficulty=1):

Passo 2

O nível de dificuldade precisa ser maior ou igual a 1, garantimos isso com a seguinte declaração de afirmação -

assert difficulty >= 1

etapa 3

Nós criamos um prefix variável usando o nível de dificuldade definido.

prefix = '1' * difficulty

Observe que se o nível de dificuldade for 2, o prefixo será “11” e se o nível de dificuldade for 3, o prefixo será “111” e assim por diante. Verificaremos se este prefixo existe no resumo gerado da mensagem. Para resumir a mensagem em si, usamos as duas linhas de código a seguir -

for i in range(1000):
   digest = sha256(str(hash(message)) + str(i))

Continuamos adicionando um novo número iao hash da mensagem em cada iteração e gere um novo resumo da mensagem combinada. Como entrada para osha256 a função muda em cada iteração, o digesto valor também mudaria. Nós verificamos se estedigest o valor está acima do definido prefix.

if digest.startswith(prefix):

Se a condição for satisfeita, encerraremos o for fazer um loop e retornar o digest valor para o chamador.

O todo mine o código é mostrado aqui -

def mine(message, difficulty=1):
   assert difficulty >= 1
   prefix = '1' * difficulty
   for i in range(1000):
      digest = sha256(str(hash(message)) + str(i))
      if digest.startswith(prefix):
         print ("after " + str(i) + " iterations found nonce: "+ digest)
      return digest

Para sua compreensão, adicionamos o print instrução que imprime o valor de resumo e o número de iterações necessárias para atender à condição antes de retornar da função.

Teste de função de mineração

Para testar nossa função de mineração, basta executar a seguinte instrução -

mine ("test message", 2)

Ao executar o código acima, você verá uma saída semelhante a esta abaixo -

after 138 iterations found nonce:
11008a740eb2fa6bf8d55baecda42a41993ca65ce66b2d3889477e6bfad1484c

Observe que o resumo gerado começa com “11”. Se você alterar o nível de dificuldade para 3, o resumo gerado começará com “111” e, claro, provavelmente exigirá mais número de iterações. Como você pode ver, um minerador com mais poder de processamento será capaz de minerar uma determinada mensagem antes. É assim que as mineradoras competem entre si para obter suas receitas.

Agora, estamos prontos para adicionar mais blocos ao nosso blockchain. Vamos aprender isso em nosso próximo capítulo.

Cada minerador pegará as transações de um pool de transações criado anteriormente. Para rastrear o número de mensagens já exploradas, temos que criar uma variável global -

last_transaction_index = 0

Agora teremos nosso primeiro minerador adicionando um bloco ao blockchain.

Adicionando o Primeiro Bloco

Para adicionar um novo bloco, primeiro criamos uma instância do Block classe.

block = Block()

Pegamos as 3 principais transações da fila -

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction

Antes de adicionar a transação ao bloco, o minerador verificará a validade da transação. A validade da transação é verificada por meio do teste de igualdade entre o hash fornecido pelo remetente e o hash gerado pelo minerador usando a chave pública do remetente. Além disso, o minerador verificará se o remetente tem saldo suficiente para pagar a transação atual.

Para resumir, não incluímos essa funcionalidade no tutorial. Depois que a transação é validada, nós a adicionamos aoverified_transactions lista no block instância.

block.verified_transactions.append (temp_transaction)

Incrementamos o índice da última transação para que o próximo minerador pegue as transações subsequentes na fila.

last_transaction_index += 1

Adicionamos exatamente três transações ao bloco. Uma vez feito isso, iremos inicializar o resto das variáveis ​​de instância doBlockclasse. Primeiro adicionamos o hash do último bloco.

block.previous_block_hash = last_block_hash

Em seguida, exploramos o bloco com um nível de dificuldade 2.

block.Nonce = mine (block, 2)

Observe que o primeiro parâmetro para o minefunção é um objeto binário. Agora, hash o bloco inteiro e criamos um resumo sobre ele.

digest = hash (block)

Finalmente, adicionamos o bloco criado ao blockchain e reinicializamos a variável global last_block_hash para o uso no próximo bloco.

Todo o código para adicionar o bloco é mostrado abaixo -

block = Block()
for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1

block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)
digest = hash (block)
TPCoins.append (block)
last_block_hash = digest

Adicionando mais blocos

Agora vamos adicionar mais dois blocos ao nosso blockchain. O código para adicionar os próximos dois blocos é fornecido abaixo -

# Miner 2 adds a block
block = Block()

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1
block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)digest = hash (block)
TPCoins.append (block)last_block_hash = digest
# Miner 3 adds a block
block = Block()

for i in range(3):
   temp_transaction = transactions[last_transaction_index]
   #display_transaction (temp_transaction)
   # validate transaction
   # if valid
   block.verified_transactions.append (temp_transaction)
   last_transaction_index += 1

block.previous_block_hash = last_block_hash
block.Nonce = mine (block, 2)
digest = hash (block)

TPCoins.append (block)
last_block_hash = digest

Ao adicionar esses dois blocos, você também verá o número de iterações necessárias para encontrar o Nonce. Neste ponto, nosso blockchain consiste em 4 blocos, incluindo o bloco de gênese.

Despejando Blockchain inteiro

Você pode verificar o conteúdo de todo o blockchain usando a seguinte declaração -

dump_blockchain(TPCoins)

Você veria uma saída semelhante à mostrada abaixo -

Number of blocks in the chain: 4
block # 0
sender: Genesis
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100ed272b52ccb539e2cd779
c6cc10ed1dfadf5d97c6ab6de90ed0372b2655626fb79f62d0e01081c163b0864cc68d426bbe943
8e8566303bb77414d4bfcaa3468ab7febac099294de10273a816f7047d4087b4bafa11f141544d4
8e2f10b842cab91faf33153900c7bf6c08c9e47a7df8aa7e60dc9e0798fb2ba3484bbdad2e44302
03010001
-----
value: 500.0
-----
time: 2019-01-14 16:18:02.042739
-----
--------------
=====================================
block # 1
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 15.0
-----
time: 2019-01-14 16:18:01.859915
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 6.0
-----
time: 2019-01-14 16:18:01.860966
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
value: 2.0
-----
time: 2019-01-14 16:18:01.861958
-----
--------------
=====================================
block # 2
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 4.0
-----
time: 2019-01-14 16:18:01.862946
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 7.0
-----
time: 2019-01-14 16:18:01.863932
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
value: 3.0
-----
time: 2019-01-14 16:18:01.865099
-----
--------------
=====================================
block # 3
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
value: 8.0
-----
time: 2019-01-14 16:18:01.866219
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100a070c82b34ae143cbe59b
3a2afde7186e9d5bc274955d8112d87a00256a35369acc4d0edfe65e8f9dc93fbd9ee74b9e7ea12
334da38c8c9900e6ced1c4ce93f86e06611e656521a1eab561892b7db0961b4f212d1fd5b5e49ae
09cf8c603a068f9b723aa8a651032ff6f24e5de00387e4d062375799742a359b8f22c5362e56502
03010001
-----
recipient:
30819f300d06092a864886f70d010101050003818d0030818902818100be93b516b28c6e674abe7
abdb11ce0fdf5bb728b75216b73f37a6432e4b402b3ad8139b8c0ba541a72c8add126b6e1a1308f
b98b727beb63c6060356bb177bb7d54b54dbe87aee7353d0a6baa9397704de625d1836d3f42c7ee
5683f6703259592cc24b09699376807f28fe0e00ff882974484d805f874260dfc2d1627473b9102
03010001
-----
value: 1.0
-----
time: 2019-01-14 16:18:01.867223
-----
--------------
sender:
30819f300d06092a864886f70d010101050003818d0030818902818100cba097c0854876f41338c
62598c658f545182cfa4acebce147aedf328181f9c4930f14498fd03c0af6b0cce25be99452a81d
f4fa30a53eddbb7bb7b203adf8764a0ccd9db6913a576d68d642d8fd47452590137869c25d9ff83
d68ebe6d616056a8425b85b52e69715b8b85ae807b84638d8f00e321b65e4c33acaf6469e18e302
03010001
-----
recipient: 
30819f300d06092a864886f70d010101050003818d0030818902818100bb064c99c492144a9f463
480273aba93ac1db1f0da3cb9f3c1f9d058cf499fd8e54d244da0a8dd6ddd329ec86794b04d773e
b4841c9f935ea4d9ccc2821c7a1082d23b6c928d59863407f52fa05d8b47e5157f8fe56c2ce3279
c657f9c6a80500073b0be8093f748aef667c03e64f04f84d311c4d866c12d79d3fc3034563dfb02
03010001
-----
value: 5.0
-----
time: 2019-01-14 16:18:01.868241
-----
--------------
=====================================

Neste tutorial, aprendemos como construir um projeto blockchain em Python. Existem muitas áreas em que você precisa adicionar mais funcionalidades a este projeto.

Por exemplo, você precisará escrever funções para gerenciar a fila de transações. Depois que as transações são mineradas e o bloco minerado é aceito pelo sistema, elas não precisam mais ser armazenadas.

Além disso, os mineiros certamente prefeririam pegar as transações com a taxa mais alta. Ao mesmo tempo, você terá que garantir que as transações com taxa baixa ou nenhuma taxa morram de fome para sempre.

Você precisará desenvolver algoritmos para gerenciar a fila. Além disso, o tutorial atual não inclui o código da interface do cliente. Você precisará desenvolver isso para clientes normais e para os mineiros. O projeto blockchain completo seria executado em várias outras linhas de código e está além do escopo deste tutorial. O leitor interessado pode baixar a fonte do bitcoin para um estudo mais aprofundado.

Conclusões

Este tutorial nítido deve ajudá-lo a começar a criar seu próprio projeto de blockchain.

Para o desenvolvimento de projeto de blockchain completo, você pode aprender mais com a fonte bitcoin .

Para projetos comerciais ou não comerciais maiores, você pode considerar o uso de Ethereum - uma plataforma de aplicativo blockchain pronta para usar.