Construyendo el Blockchain mas pequeño con Python 3.6! PT.2

El laburo que hicimos con THC era relativamente fácil de hacer. Pero, con su simplicidad vinieron algunos defectos. En primer lugar, TanoChainCoin solo funcionaba en una sola máquina, por lo que estaba lejos de estar distribuido, y mucho menos descentralizado. En segundo lugar, los bloques se pueden agregar a la cadena tan rápido como la computadora host pueda crear un objeto de Python y agregarlo a una lista. En el caso de una cadena de bloques simple, eso no es un problema, pero ahora vamos a dejar que TanoChainCoin sea una criptomoneda real, por lo que necesitaremos controlar la cantidad de bloques (y monedas) que se pueden crear a la vez.

A partir de ahora, los datos de TanoChainCoin serán transacciones, por lo que el campo de datos de cada bloque será una lista de algunas transacciones. Definiremos una transacción de la siguiente manera:

Cada transacción será un objeto JSON que detalla el emisor de la moneda, el receptor de la moneda y la cantidad de TanoChainCoin que se está transfiriendo. Nota: Las transacciones están en formato JSON por una razón que detallaré en breve.

{
  "from": "71238uqirbfh894-random-public-key-a-alkjdflakjfewn204ij",
  "to": "93j4ivnqiopvh43-random-public-key-b-qjrgvnoeirbnferinfo",
  "amount": 3
}

Ahora que sabemos cómo serán nuestras transacciones, necesitamos una forma de agregarlas a una de las computadoras de nuestra red blockchain, llamada nodo. Para hacer eso, crearemos un servidor HTTP simple para que cualquier usuario pueda informar a nuestros nodos que se ha producido una nueva transacción. Un nodo podrá aceptar una solicitud POST con una transacción (como la anterior) como el cuerpo de la solicitud. Esta es la razón por la cual las transacciones tienen formato JSON; necesitamos que se transmitan a nuestro servidor en un cuerpo de solicitud (request).

pip install flask # Instalamos el framework del web server primero
from flask import Flask
from flask import request
node = Flask(__name__)

# Almacena las transacciones
# que este nodo tiene en la lista
this_nodes_transactions = []

@node.route('/txion', methods=['POST'])
def transaction():
  if request.method == 'POST':
    # En cada POST request,
    # entramos los datos de la transaccion
    new_txion = request.get_json()
    # Despues agregamos la transaccion a nuestra lista
    this_nodes_transactions.append(new_txion)
    # Como la transaccion fue exitosa
    # lo logueamos en la consola
    print "New transaction"
    print "FROM: {}".format(new_txion['from'])
    print "TO: {}".format(new_txion['to'])
    print "AMOUNT: {}\n".format(new_txion['amount'])
    # Le informamos al cliente q se realizo correctamente
    return "Solicitud de transaccion exitosa\n"

node.run()

Joya. Ya tenemos una forma de mantener un registro de los usuarios cuando envían TanoChainCoins entre ellos. Esta es la razón por la que las personas se refieren a blockchains como ledgers públicos distribuidos: todas las transacciones se almacenan para que todos las vean y se almacenan en cada nodo de la red.

Pero surge una pregunta: ¿de dónde obtienen las personas THC? En ninguna parte, todavía. Todavía no existe un TanoChainCoin, porque aún no se ha creado ni emitido ninguna moneda. Para crear monedas nuevas, la gente tiene que extraer nuevos bloques de TanoChainCoin. Cuando explotan con éxito nuevos bloques, se crea un nuevo THC y se lo recompensa a la persona que extrajo el bloque. La moneda luego se distribuye una vez que el minero envía el THC a otra persona.

No queremos que sea demasiado fácil extraer nuevos bloques de TanoChainCoin, porque eso creará demasiados THC y tendrán poco valor. Por el contrario, no queremos que sea demasiado difícil extraer nuevos bloques, porque no habría suficientes monedas para que todos las gasten, y serían demasiado caras para nuestro gusto. Para controlar la dificultad de extraer nuevos TanoChainCoin, implementaremos un algoritmo de prueba de trabajo (PoW). Un algoritmo de prueba de trabajo (Proof-Of-Work Algorithm) es esencialmente un algoritmo que genera un elemento que es difícil de crear pero fácil de verificar. El elemento se denomina prueba y, como suena, es una prueba de que una computadora realizó una cierta cantidad de trabajo.

En TanoChainCoin, crearemos un algoritmo de prueba de trabajo algo simple. Para crear un nuevo bloque, la computadora de un minero tendrá que incrementar un número. Cuando ese número sea divisible por 9 y el número de prueba del último bloque, se extraerá un nuevo bloque de TanoChainCoin y al minero se le entregará un THC.

 

miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi"

def proof_of_work(last_proof):
  # Crea la variable que vamos a usar para encontrar
  # la siguiente prueba de trabajo
  incrementor = last_proof + 1
  # Maniente incrementando el incrementor hasta que
  # sea igual a un numero divisible por 9
  # y a la prueba de trabajo del ultimo
  # bloque de la cadena
  while not (incrementor % 9 == 0 and incrementor % last_proof == 0):
    incrementor += 1
  # Una vez que se encuentra el numero,
  # podemos retornarlo como prueba de nuestro trabajo
  return incrementor

@node.route('/mine', methods = ['GET'])
def mine():
  # Toma la ultima prueba de trabajo
  last_block = blockchain[len(blockchain) - 1]
  last_proof = last_block.data['proof-of-work']
  # Encuentra la prueba de trabajo para el bloque actual siendo minado
  # Nota: El programa se mantendra en espera
  #       hasta que se resuelva la prueba de trabajo
  proof = proof_of_work(last_proof)
  # Una vez que se encuentra una prueba de trabajo valida,
  # sabemos que podemos minar un bloque asi que 
  # recompenzamos al minero agregando la transaccion.
  this_nodes_transactions.append(
    { "from": "network", "to": miner_address, "amount": 1 }
  )
  # Ahora podemos recuperar la data necesaria
  # para generar un nuevo bloque
  new_block_data = {
    "proof-of-work": proof,
    "transactions": list(this_nodes_transactions)
  }
  new_block_index = last_block.index + 1
  new_block_timestamp = this_timestamp = date.datetime.now()
  last_block_hash = last_block.hash
  # Lista de transacciones vacia
  this_nodes_transactions[:] = []
  # Ahora creamos el nuevo bloque
  mined_block = Block(
    new_block_index,
    new_block_timestamp,
    new_block_data,
    last_block_hash
  )
  blockchain.append(mined_block)
  # Le informamos al cliente que se mino un bloque.
  return json.dumps({
      "index": new_block_index,
      "timestamp": str(new_block_timestamp),
      "data": new_block_data,
      "hash": last_block_hash
  }) + "\n"

 

Ahora, podemos controlar la cantidad de bloques extraídos en un período de tiempo determinado, y podemos emitir monedas nuevas para que las personas de la red se envíen entre sí. Pero como dijimos, solo estamos haciendo esto en una computadora. Si las cadenas de bloques están descentralizadas, ¿cómo nos aseguramos de que la misma cadena esté en cada nodo?

Para hacer esto, hacemos que cada nodo difunda su versión de la cadena a los demás y les permite recibir las cadenas de otros nodos. Después de eso, cada nodo debe verificar las cadenas de los otros nodos para que cada nodo de la red pueda llegar a un consenso sobre cómo se verá la cadena de bloques resultante. Esto se llama algoritmo de consenso.

Nuestro algoritmo de consenso será bastante simple: si la cadena de un nodo es diferente de la de otro (es decir, si hay un conflicto), la cadena más larga de la red se mantendrá y todas las cadenas más cortas se eliminarán. Si no hay conflicto entre las cadenas en nuestra red, entonces continuamos.

@node.route('/blocks', methods=['GET'])
def get_blocks():
  chain_to_send = blockchain
  # convierte nuestros bloques en diccionarios
  # para que los podamos mandar como un objeto JSON mas tarde
  for block in chain_to_send:
    block_index = str(block.index)
    block_timestamp = str(block.timestamp)
    block_data = str(block.data)
    block_hash = block.hash
    block = {
      "index": block_index,
      "timestamp": block_timestamp,
      "data": block_data,
      "hash": block_hash
    }
  # Enviamos la cadena al que lo solicita
  chain_to_send = json.dumps(chain_to_send)
  return chain_to_send

def find_new_chains():
  # Toma los blockchains de todos los otros nodos
  other_chains = []
  for node_url in peer_nodes:
    # Solicita las cadenas usando un pedido GET (request)
    block = requests.get(node_url + "/blocks").content
    # Convierte el objeto JSON en un Diccionario Python.
    block = json.loads(block)
    # Lo suma a la lista
    other_chains.append(block)
  return other_chains

def consensus():
  # Toma los bloques de otros nodos
  other_chains = find_new_chains()
  # Si la cadena no es la mas larga,
  # almacenamos la cadena mas larga
  longest_chain = blockchain
  for chain in other_chains:
    if len(longest_chain) < len(chain):
      longest_chain = chain
  # Si la cadena mas larga no es nuestra,
  # definimos esta como nuestra nueva cadena
  blockchain = longest_chain

 

Ahi va el codigo completo:

miner_address = "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi"

def proof_of_work(last_proof):
  incrementor = last_proof + 1
  while not (incrementor % 9 == 0 and incrementor % last_proof == 0):
    incrementor += 1
  return incrementor

@node.route('/mine', methods = ['GET'])
def mine():
  last_block = blockchain[len(blockchain) - 1]
  last_proof = last_block.data['proof-of-work']
  proof = proof_of_work(last_proof)
  this_nodes_transactions.append(
    { "from": "network", "to": miner_address, "amount": 1 }
  )

  new_block_data = {
    "proof-of-work": proof,
    "transactions": list(this_nodes_transactions)
  }
  new_block_index = last_block.index + 1
  new_block_timestamp = this_timestamp = date.datetime.now()
  last_block_hash = last_block.hash
  this_nodes_transactions[:] = []
  mined_block = Block(
    new_block_index,
    new_block_timestamp,
    new_block_data,
    last_block_hash
  )
  blockchain.append(mined_block)
  return json.dumps({
      "index": new_block_index,
      "timestamp": str(new_block_timestamp),
      "data": new_block_data,
      "hash": last_block_hash
  }) + "\n"

 

Estamos a punto de terminar.

Después de ejecutar el código completo del servidor TanoChainCoin, ejecuta los siguientes comandos en tu terminal. (Suponiendo que tenes cURL instalado.)

Crea una transacción:

curl "localhost:5000/txion" \
     -H "Content-Type: application/json" \
     -d '{"from": "akjflw", "to":"fjlakdj", "amount": 3}'

 

Mina un bloque nuevo:

curl localhost:5000/mine

 

Vemos que después de la extracción recibimos información interesante sobre nuestro nuevo bloque.

{
  "index": 2,
  "data": {
    "transactions": [
      {
        "to": "fjlakdj",
        "amount": 3,
        "from": "akjflw"
      },
      {
        "to": "q3nf394hjg-random-miner-address-34nf3i4nflkn3oi",
        "amount": 1,
        "from": "network"
      }
    ],
    "proof-of-work": 36
  },
  "hash": "151edd3ef6af2e7eb8272245cb8ea91b4ecfc3e60af22d8518ef0bba8b4a6b18",
  "timestamp": "2017-07-23 11:23:10.140996"
}

 

Hemos creado una cadena de bloques bastante grande en este punto.

Ahora, THC puede lanzarse en varias máquinas para crear una red, y las THC reales pueden extraerse.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s