Introducción a Ethereum

En esta breve introducción del curso de ethereum vamos a tratar los conceptos clave que son necesarios para aprender solidity.

Altcoin

Ethereum es un altcoin, que es una denominación inglesa de "alternative coin", que se refiere a las criptomonedas que se construyen a partir del código fuente de Bitcoin (forks) o cogiendo conceptos de bitcoin, aunque unas son más parecidas que otras.

La popularidad de Ethereum es que propone una plataforma para crear, compilar y desplegar smart contracts en una red de nodos blockchain.

Wallets y cuentas

Para almacenar estos tokens llamados Ether se usan Wallets (monederos digitales). Hay de varios tipos; físicos, en papel, como extensión del navegador, wallets online, o aplicaciones para el ordenador y el smartphone. Uno de los más conocidos programas para el ordenador es Exodus, y Coinbase (que te regala criptomonedas gratis) como wallet online.

Actualmente puedes usar estas invitaciones de Coinbase para obtener:
Stellar (Digital cash)
Compound (DeFi)
Band tokens (Oráculos)

Estas wallets dentro tienen varios pares de claves públicas/privadas donde se "almacena" el Ether. Esto se denomina una cuenta externa. Los smart contracts también tienen una cuenta que se crea en el momento en el que se despliega el smart contract.

Ethereum utiliza criptografía de curva elíptica (ECC) para generar estos pares. Usa encriptación de 256-bit.
Cada cuenta viene representada por una address.

Transacción y llamada

Una transacción es un mensaje que se envía desde una cuenta (dirección pública de ethereum) a otra cuenta (o a un contrato). Puede incluir datos y Ether. Una llamada es un mensaje que no contiene Ether. Por ejemplo llamar a un método de un smart contract.
Para dar por "buena" o "consolidada" una transacción, deberíamos ver unas 20 confirmaciones en la blockchain.
Estas transacciones requieren que el emisor firme la transacción antes de enviarla con su clave privada.

Algoritmo de consenso

Cada nodo que forma parte de una red Ethereum almacena su copia de la blockchain. Para asegurar que nadie corrompe esos datos en su propio nodo y lo envía a la red se utiliza un mecanismo que se llama algoritmo de consenso que en Ethereum es el PoW (Proof of Work), una prueba de trabajo, la famosa minería de ether, que involucra un cálculo muy intensivo para consolidar las transacciones en un bloque. Esta prueba de trabajo sirve para verificar que una transacción es legítima y para obtener ether (ETH) como recompensa al minar un bloque.

Revisa el tutorial de minería de ethereum antes de continuar.

En algún momento del 2019 este algoritmo de consenso se cambiará por el PoS (Proof of Stake), en este caso el minero que consolida un bloque de transacciones se determina por su cantidad de ether en su cartera. No suele haber una recompensa por bloque, más bien se obtienen las comisiones de las transacciones.

Uno de los problemas actuales con Ethereum es su escalabilidad, pues puede alcanzar unas pocas transacciones por segundo usando el PoW. La idea es cambiar este algoritmo de consenso por uno de tipo PoS, donde habrá un grupo de validadores que serán los futuros "mineros". Además de reducir el gasto de energía, se prevee alcanzar una tasa de transacciones por segundo varios órdenes de magnitud superior a la actual.

Unidades de Ether

Como hemos dicho el token de recompensa se llama Ether y se divide en varias unidades con nombres propios. Así pues la unidad más pequeña se denomina Wei y la más grande se denomina Tether.

Token ERC-20 y ERC-721

Los Tokens son un tipo concreto de smart contract que suele ser de varios tipos: ERC-20 que se crea por encima de la blockchain de Ethereum. Ese smart contract contiene una serie de métodos comunes que permiten transferir, y crear estos tokens. Es el token por excelencia que usa en las ICO (modelos de financiación de un proyecto en los que los usuarios compran tokens a cambio de Ether o Bitcoin).
Otro tipo de token es el ERC-721 que permite crear tokens que no sean fungibles, es decir que cada uno de ellos es singular y no se puede intercambiar por otro.

Muchos de los tokens populares han sido o son ERC-20 tales como EOS, TRON, VeChain,...

Máquina virtual de Ethereum (EVM)

Los smart contracts se ejecutan en una máquina virtual de Ethereum (EVM) que se comparte entre todos los nodos de la red. Cada nodo de la red ejecuta una máquina virtual de Ethereum.

Las transacciones tienen un coste para evitar que se inunde la red con spam y se almacene información irrelevante y extensa en la blockchain.
Se utiliza un token para medir el gasto computacional que se denomina Gas. Entonces cada transacción especifica su límite de gas y una comisión que pagará por el gas. Si el gas requerido es mayor que el límite de gas establecido, la transacción no se llevará a cabo y se abortará. Cada operación permitida en esa máquina virtual tiene un coste fijado de gas para poder calcular los costes.

Programas clientes

Los clientes de Ethereum más populares son Geth y Parity.

Geth (Go Ethereum) es un programa cliente desarollado en Go para arrancar un nodo de la red de Ethereum. Lo encontraremos para varios sistemas operativos.
El propio Geth proporciona una API JSON para que otras aplicaciones se pueden comunicar con él, usando HTTP o Websockets. También proporciona una consola de Javascript interactiva donde se puede hacer uso de ciertas APIs.
Geth tiene varios parámetros entre ellos los más utilizados son:

  • --datadir directorio
    Se usa para especificar la carpeta donde se almacena la blockchain. Si no se especifica por defecto lo guarda en el HOME del usuario dentro de la carpeta .ethereum
  • --networkid num
    Se usa para definir la red a la que nos vamos a conectar. La mainnet (la que se usa Ether real) tiene el valor 1, y la 2,3 etc son testnet (que no usan Ether real).

Redes privadas

Se puede usar Geth para crear una red privada con fines de desarrollo de aplicaciones. Para ello se usa el flag --dev

Smart contracts

El lenguaje más popular para desarrollar smart contracts es Solidity.

En este artículo se explica como crear un contrato de Ethereum usando Ethereum Wallet y Mist.

Ethereum es una plataforma descentralizada que ejecuta smart contracts: aplicaciones que se ejecutan exactamente como se programaron sin posibilidad de tiempo de inactividad, censura, fraude o interferencia de terceros.

Los smart contracts se programan habitualmente con Solidity que es una mezcla de varios lenguajes: C++, Javascript y Python, y es a mi modo de ver todavía un poco limitado.

Gracias a solidity y a varias tecnologías ya conocidas como html, css y javascript, se pueden crear Dapps (aplicaciones distribuidas). Cabe pensar que estas aplicaciones tienen parte de su backend en una blockchain (ya sea privada o pública). Es un cambio de paradigma muy interesante y es tendencia actualmente.

Como sugerencia, te recomiendo uno de los mejores centros educativos para aprender Ethereum y contratos inteligentes:

Actualmente Ethereum y solidity evolucionan rápidamente, y algunas sentencias y APIs van quedando obsoletas, por eso es probable que encuentres muchos ejemplos en Internet que ya no funcionen.

El curso de solidity está compuesto de una parte práctica interactiva y otra parte teórica (en la zona inferior).

Para realizar pruebas de compilación de smart contracts con solidity se puede usar el editor siguiente. Se pueden desplegar los smart contracts en la red de test - Ropsten.

Para poder compilar y desplegar tus smart contracts es necesario ser usuario registrado en un pack activo. Contacta con tu centro educativo para pedir una invitación del pack gratis durante 90 días (sólo para centros colaboradores con tutoriales.online)

Si necesitas ETH en tu cuenta de test para hacer las prácticas, puedes dejarnos un mensaje en el Chat.

Instrucciones para usar la parte práctica
  • Para poder editar y compilar smart contracts hay que estar registrado en un pack activo.
  • Para ver el último bloque minado o comprobar que el nodo de la red está activo apretar el botón "Ver info del nodo y confirmar que conectado=true".
  • Para compilar el smart contract apretar el botón "Compilar".
  • Para conocer el saldo de una cuenta (address) informar la address (clave pública) y apretar el botón "Saldo".
  • Para desplegar el smart contract se puede usar una cuenta en Metamask o informar la address (que tenga al menos 0.1 Ether) y la clave privada y apretar el botón "Desplegar smart contract usando clave...".
  • Para ver el detalle de la transacción informar el hash tx y apretar el botón "Ver transacción".
  • Para ver el recibo de la transacción (address del smart contract) informar el hash tx y apretar el botón "Ver recibo".
  • Para ver el detalle del bloque informar el bloque y apretar el botón "Ver bloque".

Editor de solidity


Detalles del smart contract

     

    Desplegar smart contract en la Red Ethereum de test - Ropsten

    Opciones de despliegue

    ... Ether

      Explorador de bloques y transacciones

       

      Detalle del bloque

      Detalle de la transacción

      Detalle del recibo

      Si este curso de solidity te parece útil, puedes realizar una colaboración en tokens ETH, USDT: 0x3699F28291A3aeAe75a9155239E095F42de910e4

      También nos gustaría recibir tu opinión, dudas y comentarios en el formulario de contacto.

      El lenguaje Solidity en detalle

      Un smart contract se ubica en una dirección concreta (address) dentro de la blockchain de Ethereum.

      Los smart contracts se ejecutan dentro de una máquina virtual de Ethereum (EVM). El código que ejecuta no tiene acceso a la red, al sistema de ficheros, o a otros procesos. Sólo tiene acceso limitado a otros smart contracts que se ubican en la blockchain.

      Existen dos tipos de cuentas: las cuentas externas que se controlan por pares de claves (pública/privada) y las cuentas de smart contract que se controlan por el código fuente del smart contract.
      La cuenta de un smart contract se crea en el momento que se desplega el smart contract en la blockchain.
      Cada cuenta tiene un saldo en unidades de wei (ether) que se puede modificar enviando transacciones que incluyan un valor.

      Una transacción es un mensaje que se envía de una cuenta a otra. Puede incluir datos binarios (payload) y ether.

      Una llamada es un mensaje que no involucra ether. Por ejemplo una llamada a un método de un smart contract.

      Cada transacción se carga con una cantidad de gas, que se consume durante su ejecución. El creador de la transacción fija el precio del gas, y debe pagar un total = precio del gas * gas utilizado de su cuenta de ether. Si sobra gas al realizar la transacción, después de la ejecución se le retorna.

      Cada cuenta de smart contract dispone de una región de memoria persistente que se llama "storage". También cuenta con otra región de memoria que se llama "memory" que se crea en cada llamada de mensaje.

      Las llamadas de mensajes se usan para invocar smart contract desde otros smart contract o para enviar ether a cuentas que no son de smart contract. Son similares a las transacciones.

      Los smart contracts pueden cargar código dinámico de otra dirección. El "storage", address y el saldo hacen referencia al smart contract que hace la llamada. Esto posibilita implementar librerías en solidity.

      Los smart contracts pueden guardar logs en una región especial de la blockchain, y se usa en el manejo de eventos. Los smart contracts no pueden acceder a los logs después de crearlos, pero éstos se pueden ver desde fuera de la blockchain, por ejemplo desde un "cliente ligero".

      Solidity, el lenguaje para programar smart contracts

      Podemos pensar que un smart contract es como en una clase en programación orientada a objetos.

      Los atributos serían las variables de estado y las propiedades serían las funciones que pueden modificar estas variables de estado.

      Tendría también su constructor (sólo se permite uno) aunque es opcional.

      Para definir la versión del compilador de un smart contract se usa la sentencia pragma con la versión de solidity y luego contract:

      pragma solidity ^0.4.18;
      
      contract GuardarDato {
        uint dato;
      
        function set(uint x) public {
          dato = x;
        }
      
        function get() public constant returns (uint) {
          return dato;
        }
      }

      Este smart contract de ejemplo no hace nada especial, sólo guarda un dato que es un número entero. Y cualquiera podría llamar al método set para cambiarlo.

      Dentro de un smart contract definimos habitualmente eventos, estructuras de datos y enums.

      Las funciones pueden tener modificadores que cambian el comportamiento. Por ejemplo la visibilidad: como privado o público.

      Las variables también pueden tener visibilidad, así una variable privada es visible por el resto de actores del blockchain, pero no es accesible desde el resto de smart contracts.

      Una variable pública se puede leer por el resto de smart contracts y actores.

      Las variables de estado se almacenan permanentemente en el smart contract.

      Los comentarios se escriben con // para una sola línea o con /* .... */ para varias líneas.

      Los ficheros de código fuente de solidity suelen tener la extensión .sol y usan la guía de estilos pep8 heredada de Python.

      Herencia

      Los smart contracts pueden heredar unos de otros con la sentencia is.

      La herencia múltiple está permitida.

      contract micontrato is contratopadre { 
        variables
        funciones 
      }

      Operador new

      Un smart contract puede crear otro smart contract con la sentencia new. Se puede enviar ether al crear el smart contract invocando al método value() después del constructor.

      Destrucción

      Un smart contract se puede destruir con la llamada selfdestruct(address), enviando el ether acumulado a esa address. Es la única manera de eliminar un smart contract de la blockchain. Aunque siempre es recomendable tener una variable de estado para desactivarlo que haga que se lancen throws al invocar sus funciones, así el ether será retornado.
      Es recomendable guardar el msg.sender en el constructor y luego pasarlo como argumento en selfdestruct.

      Contratos abstractos

      Son smart contracts que tienen algunas funciones sin implementación. Es decir Se define el nombre de la función, los parámetros de entrada y de salida, y se finaliza con un punto y coma. Estos smart contracts no se compilan, se usan como smart contracts base para otros smart contract que los heredan.

      Interfaces

      Los interfaces son similares a los smart contracts abstractos, pero no pueden tener ninguna función implementada. Tampoco pueden heredar de otros smart contracts o interfaces, no pueden definir un constructor, ni variables, ni structs, ni enums. Se usan en los smart contracts que los heredean.

      Existe un compilador muy completo online: Remix, un compilador de solidity en la nube o EthFiddle que es otro compilador en línea más limitado para Solidity.

      Solidity es un lenguaje tipado estáticamente, lo que significa que el tipo de cada variable(de estado y local) se tiene que especificar en tiempo de compilación. El lenguaje proporciona ciertos tipos de variables básicos, que se pueden combinar para formar tipos complejos.

      Tipos de variables

      bool: describe un valor que puede ser verdadero o falso. (true/false)

      uint: describen números enteros, con signo o sin signo de varios tamaños. El más pequeño es de 8 bits (uint8) y el más grande de 256 bits (uint256). Existen definiciones para todos los tamaños intermedios saltando de 8 en 8. Este tipo se usa también para cantidades y fechas (formato unix).

      Unidades de Ether: una cantidad puede tener un sufijo para determinar su medida. wei, finney, szabo, ether. Por defecto es wei.

      Unidades de tiempo: una cantidad puede tener un sufijo para determinar el tiempo. seconds, minutes, hours, days, weeks, years. Por defecto es seconds.

      address: es un tipo especial de 20 bytes que almacena una dirección de wallet de ethereum. Este tipo tiene 2 propiedades: balance (para conocer el saldo) y transfer (para enviar ether en unidades de Wei a un dirección.

      Nota: El Wei es la unidad más pequeña de Ether. 1 Ether equivale a 1018 Wei.

      msg: hace referencia al mensaje recibido por el smart contract. Sus propiedades son sender (la address del que envía), value (cantidad de ether en wei), data (los datos de la llamada), gas (el gas residual).

      Nota: Si el método es el constructor entonces msg.sender será el creador del smart contract.

      now: la fecha actual en formato unix (uint), es equivalente a block.timestamp.

      block: datos del bloque. Tiene las propiedades number (número de bloque actual), difficulty (dificultad actual del bloque), gasLimit (limite de gas del bloque).

      storage: almacenamiento persistente de hash.

      arrays fijos: son arrays con longitud fija como byte1, byte2, ...byte32 y tienen la propiedad length que muestra la longitud del array.

      arrays dinámicos: son arrays de longitud variable como bytes, string (codificado en UTF-8). Las cadenas de literales se encierran entre comillas dobles o simples. Soportan carácteres de escape especiales como \n y \uNNNN. También hay literales hexadecimales definidos con hex. Los arrays dinámicos se puede redimensionar si se encuentran en el storage (no en memoria) cambiando el valor de su propiedad length. Estos arays también tienen la función push que sirve para añadir un elemento al final del array. Esta función retorna la nueva longitud del array.

      enums: sirven para crear tipos específicos definidos por el usuario.

      struct: son tipos definidos por el desarrollador que agrupan varias variables.

      diccionarios (clave/valor): se crean con mapping(tipo de la clave => tipo del valor).

      this: hace referencia a la address del smart contract, así pues this.balance es el saldo acumulado en el smart contract.

      Las variables se pueden inicializar o limpiar con la sentencia delete. Pone a 0 los valores.

      Como el contenido de las variables se puede ver en la blockchain, desde fuera del smart contract, se puede ocultar el valor mediante hashing.

      Constantes

      Las variables de estado se pueden declarar como constantes. Por ello se tienen que asignar a un valor en tiempo de compilación. Sólo se permite valores numéricos o strings. Y también se permite asignarlas a funciones de hash (keccask256, sha256, etc).

      Persistencia

      Las variables complejas, como arrays y structs, tienen un modificador extra para indicar si queremos guardarla en memoria (memory) o en almacenamiento (storage). Por defecto es en memoria para los argumentos de las funciones y su retorno, en cambio para las variables de estado el valor por defecto es storage.

      Las funciones en solidity se especifican del siguiente modo:

      function () {internal|external} [pure|constant|view|payable] [returns ()]

      Pueden ser internas o externas. Los tipos de retorno no pueden estar vacíos. Sino retorna nada, no especificar la sentencia returns.

      Las funciones pueden retornar más de un parámetro, especificado cada uno dentro de la sentencia "returns".

      Por defecto las funciones son internas, con lo que habitualmente se omite este modificador. Las internas sólo son visibles en el smart contract actual y los que heredan de él.

      Visibilidad

      Las funciones pueden ser externas, internas, privadas o públicas. Por defecto son públicas.

      external:

      Las funciones externas forman parte del interfaz del smart contract y se pueden llamar desde otros smart contracts y vía transacciones. No se pueden llamar internamente.

      public:

      Las funciones públicas forman parte del interfaz del smart contract y se pueden llamar internamente o vía mensajes. Para las variables de estado públicas se genera automáticamente una función getter.

      internal:

      Las funciones y variables de estado internas sólo se pueden acceder internamente (desde el smart contract actual o smart contracts que derivan de este) sin usa this.

      private:

      Las funciones y variables de estado privadas son sólo visibles en el smart contract en el que están definidas, y no en smart contracts heredados.

      Sobrecarga

      Un smart contract puede tener varias funciones con el mismo nombre pero con argumentos diferentes. Esto también aplica a funciones heredadas de otro smart contract.

      Modificadores

      El modificador constant indica que la función no puede cambiar la variable. Se ejecuta localmente, no en el blockchain. Su uso está deprecado en favor de pure o view.

      El modificador view indica que la función no modifica el estado.

      Nota: se considera modificar el estado: escribir el valor de la variable, disparar un evento, crear otro smart contract, usar selfdestruct, enviar ether via llamadas, invocar a otra función que no sea pure o view, usar llamadas a bajo nivel, usar código ensamblador que contenga ciertos opcodes.

      El modificador pure indica que la función no cambia una variable ni la lee.

      Nota: se considera leer: leer el valor de una variable de estado, acceder a this.balance, o address.balance, acceder a métodos de block, tx, o msg, excepto msg.sig y msg.data, invocar a otra función que no sea pure, usar código ensamblador que contenga ciertos opcodes.

      El modificador payable indica que la función puede recibir ether.

      Se pueden crear modificadores personalizados para las funciones con la sentencia modifier. Por ejemplo esta es una que valida que sólo lo invoque el creador del smart contract:

      modifier soloCreador {
        if (msg.sender == owner)
        _
      } 

      Se puede llamar a funciones de otros smart contracts con llamadas externas, en ese caso se puede indicar el valor de wei y gas enviado con los métodos .value(v).gas(g)

      Estas llamadas pueden lanzar excepciones si no existe el smart contract, o el método externo lanza una excepción.

      Se suele definir una función de fallo, en caso de que se envien datos incorrectos, o se envía ether sin datos:

      function () {
        throw;
      }

      Para recibir ether, debe marcarse como payable, sino existe no se puede enviar ether al smart contract mediante transacciones. Excepto en el caso de ser el destino de una transacción coinbase (recompensa de un minero) o de un destino de selfdestruct.

      Estructuras de control de flujo

      Se permite la mayoría de sentencias de control que se usan en Javascript excepto switch y goto. Así pues podemos usar: if, else, while, do, for, break, continue, return, ?:

      Los paréntesis no se pueden omitir, y las llaves tampoco, a menos que sea una única asignación simple.

      Throw

      Se pueden lanzar excepciones, pero no se pueden capturar. La sentencia throw restablece el ether al sender y el estado.

      Tratamiento de errores

      El tratamiento de errores es muy básico en solidity. Existen varias sentencias para gestionar errores:
      assert (condicion) (hace un throws si la condición no se cumple, por ejemplo errores internos),
      require (condicion) (hace un throws si la condición no se cumple, por ejemplo para validar entradas),
      revert (aborta la ejecución y restablece los cambios de estado).

      Iteradores

      Las iteraciones estan limitadas en solidity, se puede recorrer un array, pero los mapas no se pueden recorrer sin conocer las "claves".

      Funciones matemáticas y criptográficas

      Tenemos disponibles varias funciones de hashing y de suma y multiplicación módulo.

      addmod(uint x, uint y, uint k) returns (uint):

      calcula (x + y) % k donde la suma se realiza con precisión arbitraria y no ajusta a 2**256. Assert que k != 0 desde la versión 0.5.0.

      mulmod(uint x, uint y, uint k) returns (uint):

      calcula (x * y) % k donde la suma se realiza con precisión arbitraria y no ajusta a 2**256. Assert que k != 0 desde la versión 0.5.0.

      keccak256(...) returns (bytes32):

      calcula el hash Ethereum-SHA-3 (Keccak-256) de los argumentos empaquetados

      sha256(...) returns (bytes32):

      calcula el hash SHA-256 de los argumentos empaquetados

      sha3(...) returns (bytes32):

      es un alias de keccak256

      ripemd160(...) returns (bytes20):

      calcula el hash RIPEMD-160 de los argumentos empaquetados

      ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):

      recupera la address asociada a la clave pública de la firma de la curva elíptica o retorna cero si hay error

      Nota: argumentos empaquetados significa que los argumentos se concatenan sin padding (relleno).

      En solidity se pueden definir eventos con la sentencia event MiEvento(a) y posteriormente se puede disparar ese evento llamando a MiEvento(a).

      Esto es útil para llamar a funciones Javascript de callback, en la parte de la vista de una Dapp (aplicación distribuida).

      Los argumentos se almacenan en el log de la transacción, una estructura de datos especial que hay en la blockchain.

      Los datos de los eventos no se pueden consultar desde dentro de un smart contract.

      Y en Javascript:

                var abi = /* abi generado por el compilador */;
                var ClientReceipt = web3.eth.contract(abi);
                var clientReceipt = ClientReceipt.at("0x1234...ab67" /* address */);
      
                var event = clientReceipt.Deposit();
      
                // esperar a que hayan cambios
                event.watch(function(error, result){
                    if (!error)
                    console.log(result);
                    });
      
                // O pasar una función de callback para ver cambios inmediatamente
                var event = clientReceipt.Deposit(function(error, result) {
                    if (!error)
                    console.log(result);
                    });