Skip to content

Latest commit

 

History

History
893 lines (503 loc) · 47.2 KB

La_Academia_de_Contratos_Inteligentes_de_Ethereum.md

File metadata and controls

893 lines (503 loc) · 47.2 KB

¡Bienvenid@ a la Academia de Smart Contracts de Ethereum en español! Este espacio tiene el propósito de presentar una guía introductoria, útil y sencilla, para todos aquellos que deseen empezar a comprender esta tecnología.

Índice de contenidos

¿Qué es Ethereum?

Podemos definir Ethereum de una forma aséptica y algo simplista como un software bajo licencia libre que permite una interconexión P2P distribuida de tal forma que una red de nodos que comparten una base de datos y un protocolo de estado, que pueden contener tanto objetos o variables como piezas enteras de código ("contratos") a modo de arquitectura de Von Neumann. [https://github.com/ethereum/go-ethereum/wiki/Ethereum-Specification Éstas] son sus especificaciones actualmente.

Ethereum fue ideadada por Vitalik Buterin a finales de 2013 en Ethereum: A Next-Generation Cryptocurrency and Decentralized Application Platform, formalmente descrita por Gavin Wood a principios de 2014 en su Yellow Paper, y finalmente lanzada el 30 de julio de 2015.

Según Taylor Gerring el propósito del Proyecto Ethereum es construir una mejor World Wide Web, entre otras características, más privada y descentralizada. Para ello, entre otras herramientas y aplicaciones, cuenta con un sistema de publicación de contenido estático (IPFS), un sistema de mensajería privada de bajo nivel (Whisper), un sistema de transacciones que no precisa necesariamente de terceros de confianza (Ethereum blockchain and EVM system) y una interfaz de usuario integrada (Mist).

Smart contracts

Smart contracts son piezas de código que se valen de un elemento de estado y de una red distribuida a fin de facilitar protocolos de estado y programas destinados a facilitar la ejecución automatizada de un contrato de forma distribuida. Su principal desarrollo conceptual fue llevado a cabo por Nick Szasbo durante la década de 1990. En 1996 escribe Smart Contracts: Building Blocks for Digital Markets y en 1997 Formalizing and Securing Relationships on Public Networks. En Ethereum estos contratos pueden ser implementados en varios lenguajes, y posteriormente son compilados en un set de instrucciones escritos en bytecode, que serán ejecutado en stack en la Máquina Virtual de Ethereum (EVM -no confundir con EMV-); y en el caso de dicho set sea válido, desplegará sus efectos en la cadena de bloques.

Cada smart contract (contract) consta de estructuras (struct), funciones (func) y estados (state), y es ejecutado en cada nodo de Ethereum simultáneamente, los cuales consensuan su resultado de forma distribuida. El Ethereum Development Tutorial hace notar que el coste de hacer uso de un poder de computación de una EVM tan confiable como ésta es tan alto, que, a efectos prácticos, en términos de carga y optimización se afirma que no debería pensar en programarse algo "que no pudieras ejecutar en un teléfono inteligente del año 1999".

Ether y gas

La unidad empleada en Ethereum es el éter (ether), una unidad de cuenta con 18 decimales, que es usada para pagar por la capacidad de procesamiento de la red. Para su desarrollo financiero inicial Ethereum realizó la venta de micromecenazgo (crowdfunding sale) de 60.102.216 éters que estuvo abierta del 7/23/2014 al 9/2/2014, llamada the Genesis Presale, obteniendo 31.591 BTC, que correspondieron a 18.439.086 dólares estadounidenses en aquel momento.

El precio del gas o gas price es un valor determinado por el creador de la transacción que es quien paga el mismo por ejecutar dicha llamada a un contrato o realizar una transacción. Si algo de gas sobra tras la ejecución, le es devuelto. Si el gas se termina antes de alcanzarse el requerido, se desencadena una excepción out of gas, la cual revierte todas las modificaciones hechas al estado por el actual llamada (call frame).

Cuentas

Hay dos tipos de cuentas en Ethereum que comparten el mismo espacio de direcciones: Cuentas de Usuario que son controladas por pares de llaves pública y privada custodiadas directa o indirectamente por uno o varios sujetos, y Cuentas de Contratos que son controladas por el propio código almacenado junto con la cuenta. A octubre de 2016 las cuentas de contratos representan un 10% de las cuentas totales.

Las Cuentas de Usuario están determinadas por las llaves públicas y éstas por las llaves privadas. Primero generas la Llave Privada de 64hex: Un número entero positivo aleatoriamente seleccionado (representado por una tabla de bytes con una longitud de 32 en formato big-endian) en el rango [1, secp256k1n − 1]. Entonces creas la Llave Pública de 64 bytes desde la llave privada usando el Algoritmo de Curva Elíptica de Firma Digital (ECDSA). La llave privada es creada aleatoriamente, pero la llave pública y el hash que se utiliza como dirección no es aleatorio. Las Direcciones de Ethereum son hashes de la llave pública. Para generar uno tienes que generar la llave privada primero. Entonces creas la dirección usando la llave pública de 64 bytes. Calcula el hash Keccak-256 de la llave pública. Deberías ahora tener una cadena de 32 bytes (nota: SHA3-256 se terminó convirtiendo en standard tras ser apoyada por el NIST pero Ethereum usa Keccack). Toma los últimos 20 bytes de esta llave pública (Keccak-256). O, en otras palabras, elimina los primeros 12 bytes. Estos 20 bytes son la dirección, 40 carácteres. Cuando se le añade el prefijo 0x pasa a tener los 42 carácteres de largo.

Convierte llaves públicas en direcciones modificar main.c y genKATShortMsg.cpp de KeccakTools con los cambios mencionados dará la posibilidad de calcular las direcciones ###Versiones Ethereum Launch Process Milestone:

  1. Frontier: La primera versión, el "salvaje Oeste".

  2. Homestead: Una red más estable.

  3. Metropolis: Un ecosistema diverso de software sencillo de usar, incluyendo Mist -el explorador de Dapps- y herramientas como el Mix -IDE-. A partir de este punto ya no se considera beta.

  4. Serenity: La transación al nuevo protocolo y actualizaciones de escalabilidad para ser implantable a nivel industrial.

Almacenamiento, memoria y pila

Cada cuenta tiene una primera área de memoria persistente la cual es llamada Almacenamiento (storage). El Almacenamiento es un valor-llave almacenado que mapea Palabras (words) de 256 bits/32bytes. No es posible enumerar el Almacenamiento desde dentro del contrato y es comparativamente costoso de leer y, incluso más, modificar lo almacenado. Un contrato no puede ni leer ni escribir ningún almacenamiento aparte del suyo.

La segunda área de memoria es llamada Memoria (memory), de la cual un contrato obtiene un ejemplo nuevo en blanco para cada llamada de mensaje (message call). La Memoria puede ser direccionada at byte level, pero ha de ser leída y escrita en Trozos (chunks) de 32 bytes (256 bits). La Memoria es más costosa cuanto más crece, escalando cuadráticamente.

La EVM no es una máquina de registro de entradas de bases de datos, sino una Pila de llamadas LIFO (stack o call stack), de tal forma que todas las operaciones computacionales se llevan a cabo en su área. Ésta tiene un tamaño máximo de 1024 elementos conteniendo Palabras de 256 bits. La estructura de la Pila, de arriba a abajo, es la siguiente: Es posible copiar uno de los primeros 16 elementos a la parte superior de la pila, es decir, intercambiar el elemento superior con cualquiera de los 16 elementos por debajo del mismo. Todas las operaciones toman dos elementos, uno a uno o los dos a la vez dependiendo de la operación, y ponen los resultados en la parte superior de la pila. Es posible mover elementos de la pila para almacenarnos en la memoria, pero no es posible acceder a elementos arbitrarios que se encuentren más abajo de la pila sin primero remover los que se encuentran primero.

Set de instrucciones

El Set de Instrucciones de la EVM es guardado en su mínima expresión para evitar incorrectas implementaciones las cuales podrían causar problemas. Todas las instrucciones operan en el más básico tipo de dato, Palabras de 256 bits. La aritmética habitual presenta operaciones compartivas con lógica binaria. Así, saltos condicionales o incondicionales son posibles. Además, los contratos pueden acceder propiedades relevates del bloque actual, como son su número y timestamp.

Llamadas

Llamadas de mensaje

Los contratos pueden llamar otras Cuentas de Contratos o enviar ethers a Cuentas de Usuario. Los Contratos pueden llamar otros contratos o enviar ethers a Cuentas de usuario mediante Llamadas de mensaje (mesaje calls). Las Llamadas de mensaje son similares a las transaciciones, en las que tienen una dirección fuente, una dirección objetivo, un payload de datos, ethers, gas y return data. De hecho, cada transacción consiste en una Llamada de mensaje de nivel superior la cual a su vez puede crear otras llamadas de mensaje.

Un contrato que realize una llamada (caller) puede decidir cuánto de su gas remanente debería ser enviado con la Llamada de mensaje interna y cuánto querría retener. Si una out-of-gas exception o cualquier otra excepción ocurre en la llamada interna, esto será señalado por un valor erróneo puesto en la pila. En Solidity, la llamada del contrato causa una excepción manual por defecto en tales situaciones, de tal forma que las excepciones son anuladas haciendo rebosar la Pila de llamadas. Es decir, en este caso, sólo el gas enviado junto con la llamada es utilizado y la operación realizada se considera nula revirtiéndose sus efectos.

Será de la Memoria de donde el contrato obtendrá un ejemplo nuevo en blanco para cada llamada de mensaje (message call) y obtiene acceso a la Llamada payload -la cual será provista en una área separada llamada calldata. Después de haber finalizado su ejecución, puede retornar datos lo cuales serán almacenados en la Memoria de llamada previamente determinada por el contrato que realiza la llamada. Las llamadas son limitadas a la profundidad de 1024, lo cual significa que durante operaciones más complejas, los loops deberían ser preferidos sobre las llamadas recursivas (recursive calls) -optimización-.

Llamadas delegadas / Librerías y código de llamadas

Ahí existe una especial variante de la llamada de mensaje, denominada Llamadas delegadas (delegatecall) que es idéntica a las llamadas de mensaje excepto por el hecho de que el código es ejecutado en el contexto del contrato llamador (su dirección objetivo siempre le pertenece) por lo que el msg.sender y el msg.value no cambian sus valores. Esto significa que el contrato puede dinamicamente cargar código de diferentes direcciones sobre la marcha. Almacenamiento, la dirección actual y el balance todavía se refieren al contrato que llama, sólo tienen payload de datos y el return data. Esto hace posible implementar la propiedad "librería" en Solidity: Librerías de código reutilizable que puede se raplicado a un contrato almacenado a fin de, por ejemplo, implementar estructuras más complejas.

Llamadas al registro de llamadas

Es posible almacenar datos en una estructura de datos especialmente indexada que mapee todos ellos hasta bien entrado el nivel del bloque. Esta propiedad de ir recopilando Registros de llamadas (called logs) es usada por Solidity a fin de implementar un histórico de eventos. Los contratos no pueden acceder llamadas a los logs después de que hayan sido creados, pero pueden eficientemente acceder desde fuera de la blockchain. Mientras que los datos de registro de llamadas esté almacenado en filtros bloom es posible buscar estos datos de una forma eficiente y criptográficamente segura, de tal forma que los pares de la red que no han descargado toda la blockchain (light clients) pueden aún así encontrar dichos registros. Los filtros de Bloom sirven para preguntar si un elemento está en un conjunto, y la respuesta puede ser "no, el elemento no está en el conjunto", de forma rotunda, siempre fiable, o bien "puede ser que el elemento esté en el conjunto", con un grado de certidumbre aproximado. De esta manera, Ethereum usa filtros bloom para verificar que se ha realizado una operación sin tener que consultar la información en toda la red.

Llamadas de creación de contratos

Los contratos pueden incluso crear otros contratos usando un operador (opcode) especial, y no únicamente llamar a direcciones. La única diferencia entre estas Llamadas de creación de contratos (create calls) y las Llamadas de mensaje (mensage call) corrientes es que el payload data es ejecutado y el resultado almacenado como código y el contrato que realiza la llamada de creación recibe la dirección del nuevo contrato en la Pila de llamadas. . ####Llamadas de autodestrucción La única posibilidad en la que un código puede ser retirado de la blockchain es cuando un contrato llama (selfdestruct call) a la dirección que realiza la operación de autodestrucción (SELFDESTRUCT). Los ethers sobrantes almacenados en la dirección son enviados a una dirección objetivo determinada y entonces el cógigo y el Almacenamiento son retirados. Fíjate que incluso si el código del contrato no contiene el operador SELFDESTRUCT, todavía puede llevar a cabo dicha operación usando delegatecall o callcode.

Comunicación entre nodos: Descubrimiento, Transporte Encriptado, Enmarcado y Control del Flujo

Las comunicaciones peer-to-peer entre los nodos usan el Protocolo Wire de Ethereum, un set de operadores y mensajes y su traducción correspondiente en hexadecimal, constituyendo 'per se' un sistema de mensajería privada de bajo nivel (Whisper). El Protocolo Wire fue basado en el [Protocolo RLP https://github.com/ethereum/wiki/wiki/RLP], siguiendo la [Especificación de Comunicación y Descubrimiento de Nodos RLPx https://github.com/ethereum/devp2p/blob/master/rlpx.md]. Otra opción de modelo para este propósito podría ser Swarm.

Árboles de Patricia Merkle y Clientes ligeros

En Ethereum los datos son almacenados en una estructura de datos denominado Árbol de Patricia Merkle, una estructura de árbol donde cada nodo en el árbol es el hash del siguiente. Cada set de parejas de llaves o valores mapea a un único hash raíz, y sólo un pequeño subset de nodos es necesitado para probar que una combinación de llaves/valores corresponde a un hash raíz particular del árbol. El tamaño de la complejidad de una prueba de Merkle escala linearmente con la altura del árbol; porque cada nodo del árbol tiene un número particular de nodos hijos (en nuestro caso, hasta 17), esto significa que el tamaño de la complejidad de una prueba Merkle es logarítima (es decir, O~log(n)) respecto a la cantidad de datos almacenados. Esto significa que, incluso si el árbol de estados completo tiene unos pocos gigabytes de tamaño, si un nodo recibe el estado raíz desde una fuente de confianza, dicho nodo tiene la habilidad de saber con total certeza la validez de cualquier información con árbol apenas descargando unos pocos kilobytes de datos como prueba (es decir, el hash es capaz de representar exactamente al bloque).

Una prueba SPV (Prueba Simple de Verificación) de un nodo en un árbol de Patricia consiste simplemente en un subset completo de tres nodos que fueron procesados a fin de acceder (o, más específicamente, los nodos árboles que necesitaban ser buscados en una base de datos que tuviera la capacidad de hacer búsquedas inversas de hashes). En una simple implementación de un ábol de Patricia, tomadno el valor asociado con una llave particular, se requiere desceder el ábrol hash, constamente buscando nodos en la base de datos por sus hashes, hasta que finalmente se alcanza el nodo final de la última rama; un simple algoritmo que produjera una prueba SPV lo que haría sería usar este sencillo algoritmo y grabar todas las búsquedas que fuesen hechas en la base de datos. La verificación SPV consistiría entonces en correr este sencillo algoritmo de búsqueda pero apuntando éste a la base de datos provista únicamente por los nodos en la prueba SPV; si hay un error y el nodo se encuentra, entonces la prueba es inválida.

El propósito de un cliente ligero es permitir a los usuarios en entornos de baja capacidad (sistemas embebidos de propiedad inteligente, teléfonos inteligentes y no tan inteligentes, extensiones de exploradores, equipos de escritorio ligeros, etcétera) a mantener una seguridad alta sobre el estado actual de una parte concreta del estado de Ethereum, o verificar la ejecución de una transacción. Aunque la seguridad ocmpleta es sólo posible para un 'full node', el protocolo de nodo ligero permite procesar alrededor de 1KB de datos por cada dos minutos, para recivir datos de la red sobre las partes del estado que les concierne, y estar seguros de que dichos datos son correctos, provistos de forma que la mayoría de los mineros estan siguiendo correctamente el protocolo, y quizás incluso aunque apenas mientras que un sólo 'full node' exista.

En Ethereum, un cliente ligero puede ser entendido como un cliente que descarga por defecto los 'headers' de los bloques, y verifica únicamente una pequeña porción de lo que precisa ser verificado, usando una tabla de hash distribuida en lugar de una base de datos con el árbol de nodos almacenado en su disco duro local. Para un "cliente parcialmente ligero", que procesa todo, pero se limita a la hora de utilizar el espacio de disco duro y almacena prácticamente nada, sustituyendo una lectura de la base de datos con un 'get request' de una tabla de hashes distribuida (DHT) que resulta suficiente para cumplir los requisitos. De hecho, de acuerdo con la técnica de Poda de Árboles de Estado todos los 'full clients', excepto los nodos de archivo (que serían mantenidos por propósitos relacionados con negocios, exploradores de bloques, etcétera) finalmente serían configurados como clientes parcialmente ligeros respecto al registro histórico más antiguo de unos cuantos miles de bloques. Formalmente, podemos decir que esto permitiría mantener la complejidad a razón de O(log(n)), aunque un mecanismo particular funcionaría con apenas O(sqrt(n)).

Otra opción para reducir el tiempo de descarga de la blockchain ha sido la implementación de los opcodes que permiten Descargas de Bloques en Paralelo.

Documentación