Creando Smart Contracts en Ethereum

Ya hemos hablamos en el blog de cómo crear una red privada de Ethereum y que sirva de soporte para crear programas (Smart Contracts) y que estos respondan a ciertos eventos programados. Por poner un ejemplo, podríamos pensar en un contrato que se comporte de este modo:

Cuando el usuario A realice una transacción económica mayor a ‘n’ en esta dirección ‘M’ y antes del dia ‘Z’, marca como que el usuario ha pagado en los términos de forma correcta.

Esto supone una gran diferencia con los sistemas tradicionales ya que anteriormente al blockchain no era posible realizar transacciones condicionales. Ahora, podemos establecer un contrato inteligente entre partes, ya sea usuario/usuario, usuario/sistema o sistema/sistema.

Para poder entender bien la potencia de estos contratos, debemos conocer las características de la plataforma donde van a desplegarse. Y hoy vamos a centrarnos en las características de los Smart Contracts basados en Ethereum.

Ethereum como máquina de Touring

Como ya hemos comentado, un contrato inteligente es una forma de interactuar con la red, pero ¿cómo? Un contracto inteligente no deja se ser un programa escrito en un lenguaje de programación. En el caso de Ethereum, tiene su propio lenguaje llamado Solidity.

Solidity es un lenguaje muy parecido a Javascript, de alto nivel y que es capaz de interactuar con la Ethereum Virtual Machine (EVM).

Para entender qué es EVM, podemos pensar en la Java Virtual Machine que se ejecuta en cada máquina para ejecutar los programas, con la salvedad de que esta EVM se ejecuta en cada nodo de la red de Ethereum.

El programa escrito en solidity es compilado y desplegado a un nodo de la red, en la que los nodos implementan el protocolo Ghost (Greedy Heaviest Observed Subtree).

Este protocolo es el encargado de transmitir a los nodos la información de las transacciones lo más rápido posible sin tener pérdida de bloques o huérfanos (orphan or stale block).

Desplegar en la red en sí mismo es una transacción. Para entender más este protocolo podemos encontrar información en Internet, como por ejemplo aquí.

¿Solo Solidity?

En realidad hay implementaciones de Solidity en distintos lenguajes a modo de wrapper por ejemplo, Java o Python. En nuestro caso nos centraremos en el lenguaje nativo Solidity.

Para empezar a programar en Solidity tendremos a mano la documentación de referencia. Lo primero que necesitamos en un entorno de desarrollo. Nosotros utilizaremos Remix.

Remix es un entorno de desarrollo, compilación y despliegue de contratos inteligentes basado en explorador web. Funciona correctamente en Firefox o Chrome. Lo podemos descargar aquí siguiendo los paso de instalación que se detallan en la página.

Tras la instalación podemos acceder por url con nuestra IP, a modo de ejemplo:

http://192.168.32.78:8080 donde la ip es la adecuada a nuestro servidor de instalación de Remix-Ide.

En la parte central escribiremos nuestro código empezando por:

pragma solidity ^0.4.0;
contract Test {
    //    DECLARA VARIABLE ENTERO SIN SIGNO
    uint suma;
    //  DECLARA CONSTRUCTOR
    function Test(){}    
    // GET - SET
    function setSuma(uint valor) public {
    	assert(valor > 0);
    	suma = suma + valor;}
    function getSuma() public view returns(uint){ return suma; }
}

En la parte superior derecha pulsaremos la pestaña ‘Run’, dentro buscamos el combo box ‘Enviroment’ y seleccionamos ‘Web3 Provider’ e indicamos la URL de uno de nuestros nodos privados, por ejemplo http://192.168.41.88:8041.

En segundos queda configurado el entorno para la compilación y despliegue de nuestros contratos inteligentes.

El combo box ‘Environment’ contiene otros dos valores posibles:

  • ‘JavaScript VM’ nos permite compilar en memoria y desplegar sólo en memoria.
  • Por último ‘Injected Web3’ implica que usaremos para desplegar los contratos un cliente tipo MetaMask.

Para un contrato no muy extenso los valores que aparecen por defecto son suficientes.

Dentro de la pestaña ‘Run’ en el combo box aparece seleccionado nuestro ejemplo ‘Test’, pulsamos el botón ‘deploy’, esta acción compila y despliega al mismo tiempo en el nodo seleccionado.

Si todo es correcto nos devuelve:

creation of Test pending...
[block:327460 txIndex:0] from:0x60d...cd1e3, to:Test.(constructor), value:0 wei, 0 logs, data:0x608...70029, hash:0x624...55b5a

Con el número de contrato:

0xa5d93ab96478c6ee4c9a291cae12301f92ebe1f9

Aunque no estemos muy familiarizados con Smart Contracts y transacciones en Blockchain, podemos notar que el formato es muy similar a una dirección de un wallet.

Con esto habremos desplegado nuestro pequeño contrato dentro de nuestra red privada y podremos observar los cambios que se generan en el explorador de red Eth-Netstats, que si seguimos las instrucciones de este otro artículo, estará disponible en la URL http://localhost:8080.

Esta operación se puede repetir y nos devolverá un nuevo contrato cada vez. Llegado a este punto es importante señalar un concepto que puede resultar novedoso frente a cualquier programa que hayamos desarrollado hasta el momento sin usar blockchain: la migración de los datos.

Cada vez que creamos un contrato nuevo, los datos del anterior no se migran y continúan ligados al anterior contrato. Esto nos lleva a que si queremos mantener los datos de la versión anterior del contrato debemos realizar un proceso de migrado.

Este punto es importante mencionarlo, aunque no lo abordaremos en este momento para no perder el foco del objetivo de este artículo.

Una vez desplegado, podemos interactuar con él desde las casillas inferiores que tienen el nombrado de las variables y funciones declaradas.

La función setSuma establece el valor suma que se escribe en un bloque y, por lo tanto, “debe ser minado”.

Podemos verlo en el monitor de red (eth-netstats), pero hay que tener en cuenta que éste tardará unos segundos en refrescar, por lo que su valor no estará disponible en la función getSuma hasta que sea validado por un minero. En el monitor de red lo veremos en la columna de transacciones pendientes.

Una vez escrito en el bloque el valor pertenece a una transacción, la cual es inmutable y no puede modificarse ni eliminarse. Lo que sí se puede es actualizar el valor con una nueva transacción, que quedará reflejada en la cadena.

De esta forma tendremos todas las transacciones validadas que modifican el valor de la variable suma con un histórico de tiempo con los valores en cada transacción.

Esta situación nos posibilita un histórico inmutable, quedando para siempre la siguiente lectura (tantas como transacciones se generen el tiempo):

'en la Tx 0xa933b34ffc496c259f19981ee74b89d849240ef7da9187226a0ab1c154839289 se estableció el valor suma a 5'

TxHash: 0xa933b34ffc496c259f19981ee74b89d849240ef7da9187226a0ab1c154839289
TxReceipt Status: Success
Block Height: 5548781 (1 block confirmation)
TimeStamp: 29 secs ago (May-03-2018 11:05:37 AM +UTC)
From: 0xc5659ca749b89a62e3f5cff30598a7a396a5b2c9
To: 0xb5682549563959a2a0ab469ae93087525e84c654
Value: 0.050147 Ether ($36.27)
Gas Limit: 21000
Gas Used By Txn: 21000
Gas Price: 0.000000007 Ether (7 Gwei)
Actual Tx Cost/Fee: 0.000147 Ether ($0.11)
Nonce: 0

Vale, ¿y ahora qué hacemos?

Tenemos una red de mineros, un monitor de bloques, un contrato inteligente… Pero, ¿cómo interactuar con él fuera del editor de Solidity?

Para esto llegaron las DApps. Una DApp es una aplicación que accede a la red de Ethereum para realizar consultas o modificaciones en el contrato inteligente.

Una aplicación Dapp simple es una aplicación que utiliza un cliente html que es capaz de interactuar con la cadena de bloques, pública o privada. En una una red pública utilizaremos un cliente como proveedor de servicios como pueden ser MetaMask o Infura.io. En el caso para una red privada el proveedor de servicio será uno nuestros nodos.

En cualquiera de los casos, al diseñar la aplicación tendremos que tener en cuenta la seguridad de las cuentas de los usuarios y de los fondos (en nuestro caso usaremos Coinbase), ya que para poder acceder a ellos hay que utilizar la llave privada de las correspondientes carteras.

Por norma general tendremos un frontal para gestionar las solicitudes de los usuarios y un backend para gestionar un vault que contiene las claves privadas.

Por otra parte, las redes de blockchain tradicionales, como la red de Ethereum, no son especialmente rápidas, por lo que para realizar operaciones de lectura escritura hay que utilizar una base de datos intermedia que sirva de caché, mientras se confirman las transacciones de escritura o como apoyo para otras necesidades de la aplicación.

Por el momento, vamos a pensar en nuestra Dapp como la unión de las siguientes piezas:

  • Un smartcontract.
  • Un servidor Node.js para la parte de la aplicación web.
  • Una base de datos de aplicación. Nos valdrá una instancia de MongoDB.

Con esto y con los valores del ABI del contrato (Interfaz del contrato) generado al crear el smart contract, podremos crear una aplicación que gestione nuestro smart contract.

A modo de ejemplo, tendremos los siguientes datos:

  • url del nodo: http://192.168.30.78:8041
  • Contrato: 0xd50cb0274fc92933cf634d2e3b00e2e92c7c99f3
  • ABI:
[{"constant":true,"inputs":[],"name":"getSuma","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},
{"constant":false,"inputs":[{"name":"valor","type":"uint256"}],"name":"setSuma","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]
Coinbase:: 0x60dcea27b64a3fb1c020b7773e2da1f9670cd1e3

Estos datos se pueden obtener del editor de Solidity.

El contrato se encuentra en la pestaña ‘Run’. El ABI se encuentra en la pestaña Compile/ botón deploy/ sección webdeploy:

El siguiente código, es el código que utiliza Remix IDE para desplegar el ‘ByteCode’ del contrato programado y su test de ejecución para comprobar que este ha sido desplegado en la red. La comprobación la realiza dentro del call back con la sentencia if (typeof contract.address !== ‘undefined’’,

var testContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"getSuma","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"valor","type":"uint256"}],"name":"setSuma","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
var test = testContract.new(
   {
 	from: web3.eth.accounts[0],
 	data: '0x608060405234801561001057600080fd5b5060f18061001f6000396000f3006080604052
600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463fff
fffff16806374dcbe7014604e578063b34c1f9f146076575b600080fd5b348015605957600080fd5b50606060
a0565b6040518082815260200191505060405180910390f35b348015608157600080fd5b50609e60048036038
10190808035906020019092919050505060a9565b005b60008054905090565b60008111151560b757600080fd
5b8060005401600081905550505600a165627a7a7230582031fb6d7b5e7e239c93745476bc4a42a17eb9ba6d4
f4721b3758f3ce8aa954c210029',
 	gas: '4700000'
   }, function (e, contract){
	console.log(e, contract);
	if (typeof contract.address !== 'undefined') {
     	console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
	}
 })

La variable coinbase se encuentra en la pestaña “Run” dentro del combo box ‘Account’.

Con estos datos montamos un script .js para tener acceso a la cadena de bloques.  A modo de ejemplo tendríamos este script:

(() => {
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8042"));
const interface = JSON.parse('[{"constant":true,"inputs":[],"name":"getSuma",
"outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":
"view","type":"function"},{"constant":false,"inputs":[{"name":"valor","type":"uint256"}],
"name":"setSuma","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},
{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]');
const VotingContract = web3.eth.contract(interface);
const contractInstance = VotingContract.at('0xEcDd4fF0Cf2Ff3f6639d805837d806F4b0E36b1F');
web3.personal.unlockAccount(web3.eth.accounts[0], '1234', 0);
})();

El script anterior describe cómo accedemos al nodo de un minero y al contrato inteligente que desplegamos con Remix Ide en la cadena de bloques desde el servidor Node.js.

Y con esto hemos visto cómo se desarrolla un smart contract  en Ethereum y las distintas piezas que componen una Dapp. En un próximo post crearemos una Dapp desde cero y paso a paso.

Happy coding!

Referencias

Desde ya bien pequeñito empezó mi curiosidad de cómo Donkey Kong era capaz de arrojar barriles por las escaleras en aquella Nintendo Game & Watch de pantallas LCD. Hoy en Paradigma sigo manteniendo la misma curiosidad por la tecnología e ilusión que el día de Reyes del 82.

Ver toda la actividad de David Blanco

Escribe un comentario