La forma más rápida de aprender cómo funcionan las Blockchains es construir una Estás aquí porque, como yo, estás entusiasmado con el auge de las criptomonedas. Y quiere saber cómo funcionan las Blockchains, la tecnología fundamental detrás de ellas. Pero entender Blockchains no es fácil, o al menos no lo fue para mí. Caminé a través de videos densos, seguí tutoriales porosos y lidié con la frustración amplificada de muy pocos ejemplos. Me gusta aprender haciendo. Me obliga a tratar el tema a nivel de código, lo que hace que se pegue. Si hace lo mismo, al final de esta guía tendrá una cadena de bloques en funcionamiento con una sólida comprensión de cómo funcionan. Antes de empezar... Recuerde que una cadena de bloques es una cadena de registros llamada Bloques. Pueden contener transacciones, archivos o cualquier dato que desee, de verdad. Pero lo importante es que están mediante . secuencial e inmutable encadenados hashes Si no está seguro de qué es un hash, . aquí hay una explicación Debería sentirse cómodo leyendo y escribiendo algo básico de Python, así como también comprender cómo funcionan las solicitudes HTTP, ya que hablaremos con nuestra cadena de bloques a través de HTTP. ¿A quién va dirigida esta guía? Asegúrese de que + (junto con pip) esté instalado. También necesitarás instalar Flask y la maravillosa biblioteca de Solicitudes: ¿Qué necesito? Python 3.6 pip install Flask== requests== 0.12 .2 2.18 .4 Ah, también necesitará un cliente HTTP, como o cURL. Pero cualquier cosa servirá. Postman El código fuente está . ¿Dónde está el código final? disponible aquí Paso 1: construir una cadena de bloques Abre tu editor de texto favorito o IDE, personalmente yo ❤️ . Cree un nuevo archivo, llamado . Solo usaremos un único archivo, pero si se pierde, siempre puede consultar el . PyCharm blockchain.py código fuente Representando una cadena de bloques Crearemos un clase cuyo constructor crea una lista vacía inicial (para almacenar nuestra cadena de bloques) y otra para almacenar transacciones. Aquí está el plano para nuestra clase: Blockchain = [] self.current_transactions = [] def new_block(self): # Creates a Block and adds it to the chain pass def new_transaction(self): # Adds a transaction to the list transactions pass @staticmethod def hash(block): # Hashes a Block pass @property def last_block(self): # Returns the last Block the chain pass ( ): ( ): . class Blockchain object def __init__ self self chain new new of in (Modelo de nuestra Clase Blockchain) Nuestro La clase es responsable de administrar la cadena. Almacenará transacciones y tendrá algunos métodos auxiliares para agregar nuevos bloques a la cadena. Comencemos a desarrollar algunos métodos. Blockchain ¿Cómo se ve un bloque? Cada Bloque tiene un , una de tiempo (en tiempo de Unix), una , una (más sobre eso más adelante) y el . índice marca lista de transacciones prueba hash del Bloque anterior Aquí hay un ejemplo de cómo se ve un solo bloque: block = { : , : , : [ { : , : , : , } ], : , : } 'index' 1 'timestamp' 1506057125.900785 'transactions' 'sender' "8527147fe1f5426f9dd545de4b27ee00" 'recipient' "a77f5cdfa2934df3954a5c7c7da5df1f" 'amount' 5 'proof' 324984774000 'previous_hash' "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" ( Ejemplo de un Bloque en nuestra Blockchain) En este punto, la idea de una debería ser evidente: cada bloque nuevo contiene dentro de sí mismo, el hash del bloque anterior. si un atacante corrompió un bloque anterior en la cadena, los bloques posteriores contendrán hashes incorrectos. cadena Esto es crucial porque es lo que le da inmutabilidad a las cadenas de bloques: todos ¿Esto tiene sentido? Si no es así, tómese un tiempo para asimilarlo: es la idea central detrás de las cadenas de bloques. Adición de transacciones a un bloque Necesitaremos una forma de agregar transacciones a un Bloque. Nuestro El método es responsable de esto, y es bastante sencillo: new_transaction() { : sender, : recipient, : amount, }) self.last_block[ ] + ( ): ... ( , , , ): """ : : < > : : < > : : < > : : < > """ . . ( 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' 'recipient' 'amount' return 'index' 1 Después agrega una transacción a la lista, devuelve el del bloque al que se agregará la transacción, Esto será útil más adelante, para el usuario que envía la transacción. new_transaction() índice el siguiente que se extraerá. Crear nuevos bloques Cuando nuestro está instanciado, necesitaremos sembrarlo con un bloque de , un bloque sin predecesores. También necesitaremos agregar una a nuestro bloque de génesis que es el resultado de la minería (o prueba de trabajo). Hablaremos más sobre la minería más adelante. Blockchain génesis "prueba" Además de crear el bloque de en nuestro constructor, también desarrollaremos los métodos para , y : génesis new_block() new_transaction() hash() hashlib json time time = [] self.chain = [] # Create the genesis block self.new_block(previous_hash= , proof= ) def new_block(self, proof, previous_hash=None): block = { : len(self.chain) + , : time(), : self.current_transactions, : proof, : previous_hash or self.hash(self.chain[ ]), } # Reset the current list transactions self.current_transactions = [] self.chain.append(block) block def new_transaction(self, sender, recipient, amount): self.current_transactions.append({ : sender, : recipient, : amount, }) self.last_block[ ] + @property def last_block(self): self.chain[ ] @staticmethod def hash(block): # We must make sure that the Dictionary is Ordered, or we import import from import ( ): ( ): . class Blockchain object def __init__ self self current_transactions 1 100 "" " 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 " "" 'index' 1 'timestamp' 'transactions' 'proof' 'previous_hash' -1 of return "" " 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 " "" 'sender' 'recipient' 'amount' return 'index' 1 return -1 "" " Creates a SHA-256 hash of a Block :param block: <dict> Block :return: <str> " "" 'll have inconsistent hashes block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest() Lo anterior debería ser sencillo: he agregado algunos comentarios y de documentación para ayudar a mantenerlo claro. Casi hemos terminado de representar nuestra cadena de bloques. Pero en este punto, debe preguntarse cómo se crean, forjan o extraen nuevos bloques. cadenas Comprender la prueba de trabajo Un algoritmo de prueba de trabajo (PoW) es cómo se crean o nuevos bloques en la cadena de bloques El objetivo de PoW es descubrir un número que resuelva un problema. El número debe ser —computacionalmente hablando— por cualquiera en la red. Esta es la idea central detrás de la Prueba de trabajo. extraen . difícil de encontrar pero fácil de verificar Veremos un ejemplo muy simple para ayudar a que esto se asiente. Decidamos que el de algún entero x multiplicado por otro y debe terminar en 0. Entonces, . Y para este ejemplo simplificado, arreglemos . Implementando esto en Python: hash hash(x * y) = ac23dc...0 x = 5 hashlib sha256 x = y = # We don {x*y} The solution is y = {y} from import 5 0 't know what y should be yet... while sha256(f' '.encode()).hexdigest()[-1] != "0": y += 1 print(f' ') La solución aquí es . Dado que el hash producido termina en 0: y = 21 hash( * ) = e.. e860 5 21 1253e9373 .5e3600155 En Bitcoin, el algoritmo de Prueba de trabajo se llama . Y no es muy diferente de nuestro ejemplo básico anterior. Es el algoritmo que los mineros se apresuran a resolver para crear un nuevo bloque. En general, la dificultad está determinada por el número de caracteres buscados en una cadena. Luego, los mineros son recompensados por su solución al recibir una moneda, en una transacción. Hashcash La red puede verificar su solución. fácilmente Implementación de Prueba de trabajo básica Implementemos un algoritmo similar para nuestra cadena de bloques. Nuestra regla será similar al ejemplo anterior: p Encuentre un número que cuando se haga hash con la solución del bloque anterior sea un hash con 4 a la izquierda 0s es producido. hashlib json time time uuid uuid4 = self.valid_proof(last_proof, proof) is False: proof += proof @staticmethod def valid_proof(last_proof, proof): guess = f .encode() guess_hash = hashlib.sha256(guess).hexdigest() guess_hash[: ] == import import from import from import ( ): ... ( , ): """ : - ' ( ') 4 , ' - , ' : : < > : : < > """ 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 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 1 return "" " 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. " "" '{last_proof}{proof}' return 4 "0000" Para ajustar la dificultad del algoritmo, podríamos modificar el número de ceros iniciales. Pero 4 es suficiente. Descubrirá que la adición de un solo cero inicial hace una gran diferencia en el tiempo requerido para encontrar una solución. Nuestra clase está casi completa y estamos listos para comenzar a interactuar con ella mediante solicitudes HTTP. Paso 2: Nuestra Blockchain como API Vamos a utilizar Python Flask Framework. Es un micromarco y facilita la asignación de puntos finales a las funciones de Python. Esto nos permite hablar con nuestra cadena de bloques a través de la web mediante solicitudes HTTP. Crearemos tres métodos: para crear una nueva transacción a un bloque /transactions/new para decirle a nuestro servidor que extraiga un nuevo bloque. /mine para devolver la Blockchain completa /chain Configuración de matraz Nuestro "servidor" formará un solo nodo en nuestra red blockchain. Vamos a crear un código repetitivo: hashlib json textwrap dedent time time uuid uuid4 flask Flask = Flask(__name__) # Generate a globally unique address node node_identifier = str(uuid4()).replace( , ) # Instantiate the Blockchain blockchain = Blockchain() @app.route( , methods=[ ]) def mine(): @app.route( , methods=[ ]) def new_transaction(): @app.route( , methods=[ ]) def full_chain(): response = { : blockchain.chain, : len(blockchain.chain), } jsonify(response), __name__ == : app.run(host= , port= ) import import from import from import from import from import ( ): ... # class Blockchain object Instantiate our Node app for this '-' '' '/mine' 'GET' return "We'll mine a new Block" '/transactions/new' 'POST' return "We'll add a new transaction" '/chain' 'GET' 'chain' 'length' return 200 if '__main__' '0.0.0.0' 5000 Una breve explicación de lo que hemos añadido anteriormente: Instancia nuestro Node. Lea más sobre Flask . Línea 15: aquí Cree un nombre aleatorio para nuestro nodo. Línea 18: instanciar nuestro clase. Línea 21: Blockchain Cree el punto final, que es un solicitud. Línea 24–26: /mine GET Cree el punto final, que es un solicitud, ya que le enviaremos datos. Línea 28–30: /transactions/new POST Cree el punto final, que devuelve el Blockchain completo. Línea 32–38: /chain Ejecuta el servidor en el puerto 5000. Línea 40–41: El punto final de transacciones Así es como se verá la solicitud de una transacción. Es lo que el usuario envía al servidor: { : , : , : } "sender" "my address" "recipient" "someone else's address" "amount" 5 Como ya tenemos nuestro método de clase para agregar transacciones a un bloque, el resto es fácil. Escribamos la función para sumar transacciones: hashlib json textwrap dedent time time uuid uuid4 flask Flask, jsonify, request ... @app.route( , methods=[ ]) def new_transaction(): values = request.get_json() # Check that the required fields are the POST sender recipient amount Missing values sender recipient amount message Transaction will be added to Block {index} import import from import from import from import from import '/transactions/new' 'POST' in 'ed data required = [' ', ' ', ' '] if not all(k in values for k in required): return ' ', 400 # Create a new Transaction index = blockchain.new_transaction(values[' '], values[' '], values[' ']) response = {' ': f' '} return jsonify(response), 201 ( Un método para crear Transacciones) El punto final de la minería Nuestro punto final de minería es donde ocurre la magia, y es fácil. Tiene que hacer tres cosas: Calcular la prueba de trabajo Recompense al minero (nosotros) agregando una transacción que nos otorgue 1 moneda Forja el nuevo Bloque añadiéndolo a la cadena. hashlib json time time uuid uuid4 flask Flask, jsonify, request ... @app.route( , methods=[ ]) def mine(): # We run the proof work algorithm to get the next proof... last_block = blockchain.last_block last_proof = last_block[ ] proof = blockchain.proof_of_work(last_proof) # We must receive a reward finding the proof. # The sender is to signify that node has mined a coin. blockchain.new_transaction( sender= , recipient=node_identifier, amount= , ) # Forge the Block by adding it to the chain previous_hash = blockchain.hash(last_block) block = blockchain.new_block(proof, previous_hash) response = { : , : block[ ], : block[ ], : block[ ], : block[ ], } jsonify(response), import import from import from import from import '/mine' 'GET' of 'proof' for "0" this new "0" 1 new 'message' "New Block Forged" 'index' 'index' 'transactions' 'transactions' 'proof' 'proof' 'previous_hash' 'previous_hash' return 200 Tenga en cuenta que el destinatario del bloque extraído es la dirección de nuestro nodo. Y la mayor parte de lo que hemos hecho aquí es simplemente interactuar con los métodos de nuestra clase Blockchain. En este punto, hemos terminado y podemos comenzar a interactuar con nuestra cadena de bloques. Paso 3: Interactuando con nuestra Blockchain Puede usar cURL o Postman para interactuar con nuestra API a través de una red. Enciende el servidor: $ python blockchain.py * Running on http: //127.0.0.1:5000/ (Press CTRL+C to quit) Intentemos minar un bloque haciendo un solicitud de : GET http://localhost:5000/mine ( Usando Postman para hacer una solicitud GET) Vamos a crear una nueva transacción haciendo un solicitud de con un cuerpo que contiene nuestra estructura de transacción: POST http://localhost:5000/transactions/new ( Uso de Postman para hacer una solicitud POST) Si no está utilizando Postman, puede realizar la solicitud equivalente utilizando cURL: $ curl -X POST -H -d "Content-Type: application/json" '{ "sender": "d4ee26eee15148ee92c6cd394edd974e", "recipient": "someone-other-address", "amount": 5 }' "http://localhost:5000/transactions/new" Reinicié mi servidor y extraje dos bloques, para dar 3 en total. Inspeccionemos la cadena 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 Paso 4: Consenso Esto es muy genial. Tenemos una cadena de bloques básica que acepta transacciones y nos permite extraer nuevos bloques. Pero el objetivo de Blockchains es que deberían estar . Y si están descentralizados, ¿cómo diablos nos aseguramos de que todos reflejen la misma cadena? Esto se llama el problema del , y tendremos que implementar un Algoritmo de Consenso si queremos más de un nodo en nuestra red. descentralizados Consenso Registro de nuevos nodos Antes de que podamos implementar un algoritmo de consenso, necesitamos una forma de informar a un nodo sobre los nodos vecinos en la red. Cada nodo de nuestra red debe mantener un registro de otros nodos de la red. Por lo tanto, necesitaremos algunos puntos finales más: para aceptar una lista de nuevos nodos en forma de URL. /nodes/register para implementar nuestro algoritmo de consenso, que resuelve cualquier conflicto, para garantizar que un nodo tenga la cadena correcta. /nodes/resolve Tendremos que modificar el constructor de nuestra Blockchain y proporcionar un método para registrar nodos: ... from urllib.parse urlparse ... class Blockchain(object): def __init__(self): ... self.nodes = set() ... def register_node(self, address): parsed_url = urlparse(address) self.nodes.add(parsed_url.netloc) import "" " Add a new node to the list of nodes :param address: <str> Address of node. Eg. 'http://192.168.0.5:5000' :return: None " "" ( Un método para agregar nodos vecinos a nuestra Red) Tenga en cuenta que hemos utilizado un para contener la lista de nodos. Esta es una forma económica de garantizar que la adición de nuevos nodos sea idempotente, lo que significa que no importa cuántas veces agreguemos un nodo específico, aparecerá exactamente una vez. set() Implementando el Algoritmo de Consenso Como se mencionó, un conflicto es cuando un nodo tiene una cadena diferente a otro nodo. Para resolver esto, estableceremos la regla de que En otras palabras, la cadena más larga de la red es la . Usando este algoritmo, llegamos a un entre los nodos de nuestra red. la cadena válida más larga tiene autoridad. de facto consenso ... import requests = chain[ ] current_index = current_index < len(chain): block = chain[current_index] print(f ) print(f ) print( ) # Check that the hash the block is correct block[ ] != self.hash(last_block): False # Check that the Proof Work is correct not self.valid_proof(last_block[ ], block[ ]): False last_block = block current_index += True def resolve_conflicts(self): neighbours = self.nodes new_chain = None # We http: response.status_code == : length = response.json()[ ] chain = response.json()[ ] # Check the length is longer and the chain is valid length > max_length and self.valid_chain(chain): max_length = length new_chain = chain # Replace our chain we discovered a , valid chain longer than ours new_chain: self.chain = new_chain True False ( ) ... ( , ): """ : : < > : : < > , """ 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 0 1 while '{last_block}' '{block}' "\n-----------\n" of if 'previous_hash' return of if 'proof' 'proof' return 1 return "" " 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 " "" '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' //{node}/chain') if 200 'length' 'chain' if if if new if return return el primer metodo es responsable de verificar si una cadena es válida recorriendo cada bloque y verificando tanto el hash como la prueba. valid_chain() es un método que recorre todos nuestros nodos vecinos, sus cadenas y las verifica utilizando el método anterior. resolve_conflicts() descarga Si se encuentra una cadena válida, cuya longitud es mayor que la nuestra, reemplazamos la nuestra. Registremos los dos puntos finales en nuestra API, uno para agregar nodos vecinos y otro para resolver conflictos: @app.route( , methods=[ ]) def register_nodes(): values = request.get_json() nodes = values.get( ) nodes is None: , node nodes: blockchain.register_node(node) response = { : , : list(blockchain.nodes), } jsonify(response), @app.route( , methods=[ ]) def consensus(): replaced = blockchain.resolve_conflicts() replaced: response = { : , : blockchain.chain } : response = { : , : blockchain.chain } jsonify(response), '/nodes/register' 'POST' 'nodes' if return "Error: Please supply a valid list of nodes" 400 for in 'message' 'New nodes have been added' 'total_nodes' return 201 '/nodes/resolve' 'GET' if 'message' 'Our chain was replaced' 'new_chain' else 'message' 'Our chain is authoritative' 'chain' return 200 En este punto, puede tomar una máquina diferente si lo desea y activar diferentes nodos en su red. O active procesos usando diferentes puertos en la misma máquina. Hice girar otro nodo en mi máquina, en un puerto diferente, y lo registré con mi nodo actual. Por lo tanto, tengo dos nodos: y . http://localhost:5000 http://localhost:5001 ( Registro de un nuevo Nodo) Luego extraje algunos bloques nuevos en el nodo 2 para asegurarme de que la cadena fuera más larga. Después, llamé en el nodo 1, donde la cadena fue reemplazada por el algoritmo de consenso: GET /nodes/resolve ( Algoritmo de consenso en el trabajo) Y eso es un resumen... Reúna a algunos amigos para que lo ayuden a probar su Blockchain. Espero que esto te haya inspirado para crear algo nuevo. Estoy entusiasmado con las criptomonedas porque creo que Blockchains cambiará rápidamente la forma en que pensamos sobre las economías, los gobiernos y el mantenimiento de registros. planeo continuar con la Parte 2, en la que ampliaremos nuestra cadena de bloques para tener un mecanismo de validación de transacciones, así como también discutiremos algunas formas en las que puede producir su cadena de bloques. Actualización: Si disfrutó de esta guía, o tiene alguna sugerencia o pregunta, hágamelo saber en los comentarios. Y si ha detectado algún error, no dude en contribuir con el código aquí . ¿Está de acuerdo o en desacuerdo con algunas de las ideas presentadas en este artículo? Dejar nosotros sabemos sus pensamientos sobre Digg. ¿ artículos sobre Busca más Blockchain ? ¡Suscríbase a nuestro boletín en el pie de página a continuación! Echa un vistazo a nuestro podcast sobre blockchain.