Contratos inteligentes en Ethereum

6
8643

Con este tutorial podrás dar tus primeros pasos en el desarrollo de contratos inteligentes en la plataforma blockchain de
Ethereum.

Con un sencillo ejemplo veremos cómo se escribe un contrato, cómo se despliega, cómo se instancia en tu propia red privada
de Ethereum y, lo más importante, cómo se ejecuta.

Índice de contenidos

1. Introducción

La tecnología de blockchain es aún un camino en el que hay mucho por explorar y descubrir, pero a día de hoy ya podemos hacer
con ella cosas interesantes aparte de especular con las criptomonedas.

Bitcoin abrió el camino permitiendo el intercambio de criptomoneda de manera segura y descentralizada.

Ethereum va más allá y se puede considerar como una implementación de blockchain de segunda generación en la que se generaliza
la idea de los intercambios entre partes.

1.1. Blockchain

En este tutorial no voy a entrar a explicar los detalles de qué es y cómo funciona un blockchain.

Sin embargo, siendo seguramente el concepto más importante de toda esta tecnología sí voy a comentar muy por encima de
qué va para aquellos que aún no lo tengan claro.

Me dejo fuera muchos detalles para no desviar el foco del objetivo de este tutorial.

Un blockchain es una red para gestionar un libro de cuentas descentralizado y replicado en cada uno de los
nodos que componen la red.

Este libro consiste en una cadena secuencial de bloques inmutables, que vendrían a ser las páginas del libro.

Los nuevos apuntes de transacciones se van registrando en un nuevo bloque que una vez lleno es «sellado» y añadido al final
de la cadena.

Además, en todo el proceso se usan funciones criptográficas para evitar manipulación de información y garantizar la consistencia
de los datos.

1.2. Ethereum

En términos generales, Ethereum se define como una plataforma blockchain de ejecución de contratos descentralizada y sin
autoridad de confianza (confianza cero).

Los contratos son scripts que definen procesos desde simples intercambios de criptomoneda como los que hace Bitcoin hasta
políticas complejas que se desencadenan como consecuencia de eventos.

Los contratos trabajan sobre tokens, que determinan quiénes participan en un contrato y con cuánto valor.

Tanto los participantes del contrato como el propio contrato están identificados mediante una cuenta con una dirección única.
Las cuentas de participantes se llaman Externally Owned Accounts (EOA).

Además de identificar a cada uno de los elementos de la red Ethereum, las cuentas sirven de almacenamiento persistente
de información.

1.3. Contratos inteligentes

Un contrato normal especifica un acuerdo entre partes con unas acciones y condiciones de cumplimiento.

Un contrato se hace «inteligente» cuando incluye código y lógica que ejecuta las acciones y fuerza la aplicación de las
condiciones, es decir, código que hace que se cumpla el contrato sin necesidad de que ninguna de las partes intervenga manualmente.

Lo más interesante más allá de esa supuesta «inteligencia» es que el contrato se ejecuta dentro de la red, en la Ethereum
Virtual Machine (EVM).

Esta ejecución no sale gratis sino que se paga en forma de «gas», una pequeña cantidad de moneda ether aportada por uno
o más de los participantes en el contrato.

Un detalle importante es que la ejecución es descentralizada pero no distribuida, lo cual significa que se produce en todos
los nodos de la red. Esta es una de las razones por las que la EVM es tan lenta y solamente puede procesar unas 15 transacciones
por segundo.

El contrato se desarrolla en un lenguaje concreto (Solidity, Serpent o Mutan), se compila a EVM y, salvo excepciones, cumple
una interfaz estándar llamada ERC20.

En este tutorial usaremos Solidity.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro Retina 15′ (2.5 Ghz Intel Core i7, 16GB DDR3).
  • Sistema Operativo: Mac OS High Sierra 10.13.2
  • Homebrew 1.4.3
  • Geth 1.7.3
  • Ethereum Wallet 0.9.3
  • Visual Studio Code 1.19.2 con Solidity 0.0.31


3. Instalación

Obviamente no vamos a ejecutar el contrato del tutorial en la red Ethereum real, no se trata de ir tirando el dinero…

Tampoco vamos a pedir ether en ninguna de las redes de pruebas disponibles como
Rinkeby o
Ropsten, aunque te aconsejo que eches un vistazo a su funcionamiento.

En este tutorial montaremos nuestra propia red Ethereum privada y local con Geth.


3.1. La cartera de Ethereum

  1. Instalar la cartera de Ethereum con este comando de
    Homebrew:

            $ brew cask install ethereum-wallet
          

    Si no tienes Homebrew deberás ir a
    https://www.ethereum.org/ y descargar e instalar el paquete para tu sistema operativo.

    Muy importante: Si ya tenías instalada la cartera de Ethereum, asegúrate de desconectarla de la red y cerrarla antes
    de continuar.


3.2. El nodo en la red privada

Desde una terminal:

  1. Instalar el software de línea de comando de Ethereum con
    Homebrew:

            $ brew tap ethereum/ethereum
            $ brew install ethereum
          

    Alternativamente, accede a la página
    https://www.ethereum.org/cli
    y sigue las instrucciones para tu sistema operativo.

  2. Crear el directorio donde mantendremos todos los ficheros de la red privada:

            $ mkdir ether_adictos
            $ cd ether_adictos
          
  3. Crear una cuenta de origen, introduciendo una contraseña y anotando la dirección de la cartera para añadirle después un saldo
    inicial:

            $ geth --datadir testnet_data account new
          
  4. Crear otra cuenta que usaremos como destino de la ejecución de nuestro contrato:

            $ geth --datadir testnet_data account new
            
  5. Crear el siguiente fichero de configuración
    genesis.json, sustituyendo
    <DIRECCIÓN CUENTA> por el valor obtenido en el paso 3:

            {
              "config": {
                "chainId": 42,
                "homesteadBlock": 0,
                "eip155Block": 0,
                "eip158Block": 0
              },
              "nonce": "0x4200420042004200",
              "gasLimit": "0x8000000",
              "difficulty": "0x400",
              "alloc": {
                "<DIRECCIÓN CUENTA>": { "balance": "0x100000000" }
              }
            }
          

    En este fichero se especifican algunos parámetros de la red privada y se añade un saldo inicial a la cuenta de origen.

  6. Inicializar la red:

            $ geth --datadir testnet_data init genesis.json
          
  7. Arrancar la red, sustituyendo
    <RUTA IPC> por la ruta donde la cartera Ethereum busca el nodo de la red:

    • ~/Library/Ethereum/geth.ipc en Mac.
    • ~/.ethereum/geth.ipc en Linux.
    • ~/AppData/Roaming/Ethereum en Windows.
            $ geth --datadir testnet_data --identity "adictos1" --maxpeers 0 --networkid 42 --ipcpath <RUTA IPC>
          

    La terminal quedará bloqueada y mostrando el log de lo que va sucediendo.


3.3. La consola JavaScript de Ethereum

  1. Desde otra terminal conectarse a la red con la consola JavaScript sustituyendo
    <RUTA IPC> como en el paso anterior:

            $ geth attach <RUTA IPC>
          
  2. Ahora podemos comprobar que efectivamente hemos creado dos cuentas, la primera de ellas con algo de Ether:

            > eth.accounts.forEach(function (a) { console.info(a + ' - Balance: ' + eth.getBalance(a)); })
          
  3. Dejar la terminal abierta para controlar el proceso de minado que veremos más adelante en este tutorial.

    Después de eso se podrá cerrar la consola con:

            > exit
          

4. Los contratos


4.1. La interfaz ERC20

Aunque no es obligatorio, es muy recomendable que los contratos cumplan la interfaz ERC20, que permite a un contrato interactuar
con la cartera oficial de Ethereum y cualquier otro cliente compatible.

Esta interfaz define los siguientes métodos y callbacks:

      contract ERC20 {
        function name() constant returns (string name)
        function symbol() constant returns (string symbol)
        function decimals() constant returns (uint8 decimals)
        function totalSupply() constant returns (uint totalSupply);
        function balanceOf(address _owner) constant returns (uint balance);
        function transfer(address _to, uint _value) returns (bool success);
        function transferFrom(address _from, address _to, uint _value) returns (bool success);
        function approve(address _spender, uint _value) returns (bool success);
        function allowance(address _owner, address _spender) constant returns (uint remaining);
        event Transfer(address indexed _from, address indexed _to, uint _value);
        event Approval(address indexed _owner, address indexed _spender, uint _value);
      }
    

Las tres primeras funciones son opcionales; el resto es obligatorio implementarlas.

Como se puede ver a simple vista, la interfaz posibilita el intercambio de tokens del contrato usando cualquier herramienta
compatible.

Obviamente un contrato tendrá otras funciones y eventos para ejecutar sus políticas y forzar sus condiciones.


4.2. Tu primer contrato

Puedes usar cualquier editor de texto para programar en Solidity.

Yo estoy usando Visual Studio Code con la extensión Solidity de @juanfranblanco.

Un ejemplo de contrato mínimo sería el siguiente fichero contrato-minimo.sol:

    pragma solidity ^0.4.18;

    contract MyToken {
      /* Define un array con todos los balances de cuentas */
      mapping (address => uint256) public balanceOf;

      /* Inicializa el contrato con una cantidad inicial de tokens en la cuenta del creador del contrato */
      function MyToken(uint256 initialSupply) public {
        balanceOf[msg.sender] = initialSupply;
      }

      /* Enviar tokens */
      function transfer(address _to, uint256 _value) public {
        if (balanceOf[msg.sender] < _value) {
          revert(); // No hay crédito suficiente.
        }
        if (balanceOf[_to] + _value < balanceOf[_t
        o]) {
          revert(); // Evita overflow.
        }
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
      }
    }
  

Este es el significado de cada parte:

  • pragma solidity ^0.4.18;

    Especifica la versión de Solidity.
  • contract MyToken { … }

    Declaración del contrato llamado MyToken.
  • mapping (address => uint256) public balanceOf;

    Los contratos son un tipo de cuentas en Ethereum y por tanto tienen su propio almacenamiento persistente en forma de
    mapa clave -> valor.

    Ambos elementos son arrays de 32 bytes, aunque Solidity nos permite trabajar con tipos de más alto nivel.

    En este ejemplo estamos definiendo un mapa para guardar el balance de tokens de cada cuenta que haya participado en el
    contrato.
  • function MyToken(uint256 initialSupply) public { … }

    Este es el constructor del contrato y aquella cuenta EOA que lo invoque se anotará el saldo inicial de tokens dado en
    el constructor.
  • balanceOf[msg.sender] = initialSupply;

    Esta es la lógica del constructor que guarda en el mapa de balances el saldo inicial de tokens del creador del contrato.
  • function transfer(address _to, uint256 _value) public { … }
    Esta función implementa la lógica de transferir tokens desde una cuenta (el que invoca la función) a otra dada.
  • if (balanceOf[msg.sender] < _value) { revert(); }

    En los contratos hay que verificarlo todo antes de llevar a cabo ninguna acción irreversible.

    Aquí estamos comprobando que el emisor de la transferencia tiene fondos suficientes.
  • if (balanceOf[_to] + _value < balanceOf[_to]) { revert(); }

    Esta segunda comprobación evita fallos de overflow.
  • balanceOf[msg.sender] -= _value;

    Ahora se restan los tokens del balance de la cuenta origen.
  • balanceOf[_to] += _value;

    Y se suman al balance de la cuenta destino.


4.3. Instalación del contrato

La instalación manual del contrato en el blockchain de Ethereum es tediosa e implica los siguientes pasos:

  1. Compilar el contrato a bytecode EVM con el compilador
    solc que aún no hemos instalado en este tutorial.

    Esto generaría los binarios con el código en sí y el ABI para crear el contrato.
  2. Estimar el coste de creación del contrato (startGas) y el precio del gas (gasPrice).

    Más sobre esto en el apartado 5.1.
  3. Desde la consola JavaScript de Ethereum, instanciar la factoría del contrato usando el contenido del ABI.
  4. En la misma consola, ejecutar la transacción que instancia el contrato usando la factoría del paso anterior y el código compilado
    del contrato.

    En este punto es donde se especifica la cuenta creadora del contrato (o sea, la que ejecuta la transacción) y el saldo
    inicial.

    Esta función es asíncrona porque las transacciones las ejecutan los mineros cuando aceptan el encargo.

    Cuando la transacción se completa se recupera la dirección de la cuenta de la instancia del contrato.
  5. Arrancar el proceso de minado en el nodo para que complete la transacción de instalación del contrato.

En vez de esto, usaremos la cartera de Ethereum:

  1. En primer lugar, asegúrate de que el nodo privado sigue ejecutándose en una terminal.

    Si no es así, vuelve al último punto del apartado 3.2.
  2. Abre la Ethereum Wallet y comprueba que se conecta al nodo de la red privada (
    PRIVATE-NET):



    También deberían aparecer las dos cuentas EOA, una con saldo y la otra vacía.
  3. Ve a la pestaña
    CONTRATOS y selecciona la opción
    DEPLOY NEW CONTRACT.

    La cuenta de origen ya debería estar preseleccionada y la cantidad de ether 0, puesto que no vamos a realizar ninguna
    transferencia.
  4. Copia y pega el código fuente del contrato en la caja de texto
    SOLIDITY CONTRACT SOURCE CODE
    y selecciona
    My Token en el desplegable
    SELECT CONTRACT TO DEPLOY.

    Introducir también la cantidad de tokens iniciales que vas a repartir.
  5. A continuación, elige una tarifa (gasPrice) según las prisas que tengas y pulsa el botón
    DESPLEGAR.
  6. Entonces se nos presentará una ventana para verificar que todo es correcto, incluyendo el coste estimado de la transacción
    (startGas).

    Debemos introducir el password de la cuenta de origen (elegido en el apartado 3.2) y aceptar con
    SEND TRANSACTION.
  7. Si volvemos a la pestaña
    BILLETERAS veremos en la parte inferior una transacción pendiente de ejecutar:

  8. Ahora es el momento de volver a la consola JavaScript de Ethereum para minar ether durante el tiempo suficiente como para
    que se instale el contrato:

            > miner.start()
    
            ...esperamos 5 o 10 segundos...
    
            > miner.stop()
          
  9. Y ya deberíamos tener nuestro contrato listo para ser ejecutado:


4.4. Ejecución del contrato

Nuestro contrato solamente sirve para transferir cantidades de
My Token entre cuentas.

Para poder realizar este tipo de transacción, primero hay que añadir
My Token a la cartera de Ethereum:

  1. Ir a la pestaña
    CONTRATOS, seleccionar el contrato
    MY TOKEN, pulsar
    Copy address
    y confirmar que sabemos que esta cuenta está en una red de pruebas.
  2. Volver a los
    CONTRATOS y en
    CUSTOM TOKENS pulsar
    OBSERVAR FICHA.

    Pegar la dirección del contrato, asignar el nombre
    MY TOKEN y el símbolo
    MTK.

    Pulsar
    ACEPTAR para añadir el token a la cartera.

Ahora ya estamos listos para ejecutar la transferencia desde la propia cartera de Ethereum:

  1. En la pestaña
    BILLETERAS seleccionar la cuenta de destino (con saldo 0), pulsar el botón
    Copy address
    y confirmar la copia.
  2. En la pestaña
    ENVIAR, asegurar que la cuenta origen es la
    MAIN ACCOUNT y pegar en el campo
    TO
    la dirección copiada en el paso anterior.
  3. Seleccionar
    MY TOKEN en la lista de monedas e introducir una cantidad.

    Elegir también el gasPrice según nos interese esperar más o menos y confirmar el envío con el botón
    ENVIAR:

  4. En la siguiente pantalla veremos los datos de la transacción incluyendo una estimación del gas.

    Introducir la contraseña de la cuenta origen y pulsar
    SEND TRANSACTION para confirmar la transferencia.
  5. Igual que pasaba al crear el contrato, al final de la pestaña
    BILLETERAS aparecerá una transacción de tipo
    Contract Execution que está pendiente de ejecutar, así que hay que volver a minar durante unos segundos en la consola:

            > miner.start()
    
            ...esperamos 5 o 10 segundos...
    
            > miner.stop()
          
  6. Si ahora volvemos a
    BILLETERAS y seleccionamos la segunda cuenta veremos que, aunque el balance de ether sigue siendo 0, ahora al menos
    tiene la cantidad transferida del token MTK.

    También puede que te hayas fijado en que la cuenta principal ha aumentado su saldo de ether.

    Esto se debe a que esta es la cuenta a la que van a parar los beneficios del minado que hemos estado realizando.
  7. Para terminar, podemos cerrar la cartera Ethereum, la consola donde hemos controlado el minado y la terminal donde estaba corriendo
    el nodo de la red privada. El orden es importante.


5. El más allá


5.1. Coste de las transacciones

Las transacciones en Ethereum se ejecutan en los nodos de los mineros.

Para compensar la cesión de recursos hardware, cada transacción tiene un coste en gas que el emisor debe fijar a través
de los parámetros startGas y gasPrice.

El primero se estima fácilmente simulando la ejecución de la transacción desde la consola JavaScript de Ethereum:

    > eth.estimateGas({data: code})
  

Aquí más que en ningún otro lugar es imprescindible optimizar al máximo la lógica del contrato para no desperdiciar gas.

Otro aspecto crítico es que las operaciones sobre datos tengan un tiempo de ejecución constante independientemente del tamaño
de los datos.

Por ejemplo, una operación de búsqueda en una lista tiene un coste de ejecución O(n).

Si la búsqueda se basa en una clave podemos usar un mapa, teniendo entonces un tiempo de ejecución O(1).

Por otro lado, el gasPrice debe ser lo más pequeño posible que nos garantice que algún minero va a procesar nuestra transacción.

Según la prisa que tengamos pagaremos más o menos, aunque la comunidad de desarrolladores siempre intenta tirar por lo
bajo para evitar que los precios se disparen.

La mejor referencia del estado actual del gas está en:
https://ethgasstation.info/


5.2. Contratos complejos

El desarrollo de contratos inteligentes no difiere mucho del desarrollo de cualquier otro tipo de software.

Como tal debemos enfrentarnos a él siguiendo los mismos principios generales de análisis y diseño de software, y en particular
los principios SOLID.

Para ayudarnos en esta tarea, nuestra mejor opción es el framework
truffle.

Definitivamente es la base para cualquier desarrollo serio.


5.3. Seguridad y robustez

La seguridad y la robustez son aspectos críticos cuando estamos hablando de software que maneja dinero.

De hecho, el mayor robo de ether se consiguió a través de un agujero de seguridad en un contrato DAO, hecho que dio lugar
al fork de Ethereum Classic.

Para reducir al mínimo la probabilidad de fallos y agujeros debemos hacer dos cosas: seguir las mejores prácticas de desarrollo
y testear la funcionalidad del contrato.

Las mejores prácticas de diseño y desarrollo están documentadas en la web
Ethereum Smart Contract Best Practices.

Síguelas al pie de la letra.

Por otro lado, el testing es uno de los aspectos a mejorar en la plataforma Ethereum.

Otros blockchain más modernos como Cardano han nacido incorporando de serie la idea de la verificación automática de contratos.

Sin entrar en detalles, el testing en Ethereum se hace a través de truffle, que por debajo utiliza Mocha.


5.4. Cómo usar el contrato

Cuando nuestro contrato ya está desplegado e instanciado en la red Ethereum ¿cómo se supone que lo utilizan los usuarios?

Principalmente hay dos caminos para que los usuarios ejecuten las operaciones del contrato:

  • A través de alguno de los hub de aplicaciones descentralizadas, en concreto:
    https://www.stateofthedapps.com/
  • Con una aplicación móvil o web que se conecte a un nodo de la red Ethereum con una librería como
    web3j o directamente con llamadas remotas JSON-RPC.


6. Conclusiones

La situación actual de especulación en el mercado de las criptomonedas ha provocado un boom de aplicaciones descentralizadas
basadas en contratos inteligentes, la mayoría de ellas humo con poca o ninguna aportación de valor real.

Esto no quita para que la tecnología de blockchain de contratos inteligentes tenga sus casos de uso prácticos y útiles.

Simplemente hay que tener en cuenta los beneficios que nos aporta el concepto de registro público inalterable y verificado
basado en el consenso y sin autoridad central.

Tampoco deben olvidarse los perjuicios del blockchain. El más importante en mi opinión es el enorme gasto energético necesario para
realizar cualquier transacción, lo que limita la complejidad de los procesos que estamos dispuestos a automatizar.

En definitiva, se trata de una tecnología bastante nueva, con mucho recorrido y con buenas ideas por descubrir. Quizás la próxima sea la tuya…


7. Referencias

6 COMENTARIOS

    • Hola,

      En el apartado 4.3 del tutorial se explica esto.
      Los contratos se pueden crear a mano desde línea de comandos, pero esto es tedioso y complejo.
      Así que en el tutorial he hecho la creación a través de la cartera de Ethereum, que es más fácil y visual.

  1. Hola

    No entiendo como funciona la parte del dinero. Supongamos que se crea un contrato donde una empresa vende sus productos a crédito y los clientes tienen que hacer pagos mensuales por los productos adquiridos de esta empresa

    1.-Los clientes tienen que comprar Ether para poder hacer sus pagos(hacer transferencias a la cuenta Ethereum de la empresa) establecidos en estos contratos?
    2.-El dinero es fácilmente transferible de una cuenta bancaria a una cuenta Ethereum y viceversa?
    3.-Como afecta a las partes implicadas en estos contratos el no cumplirlos?, es decir, que obliga a cada una de las partes a cumplir con lo establecido?

    Agradecería mucho que me pudieras aclarar estas dudas, Saludos

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad