04/05/2018 | Blockchain,Tecnologías

Introducción a la programación de un Smart Contract

Smart Contract en Solidity

¡Hola a todos! En esta entrada vamos a mostrar cómo desplegar un smart contract escrito en Solidity, el lenguaje de programación de alto nivel de la Ethereum Virtual Machine (EVM), y crear una interfaz web para poder interactuar con el contrato desplegado. Para ello vamos a explorar la creación de un programa sencillo en Solidity, nuestro back-end, y los parámetros necesarios para interactuar con él desde una aplicación web, nuestro front-end.

Comenzaremos explicando el funcionamiento de Solidity.

Back-end

Cada nodo de una red blockchain contiene una copia idéntica de la cadena de bloques. Solidity es un lenguaje de programación influido por C++, Python y JavaScript que permite crear contratos compuestos por funciones que, una vez compilados para la EVM, se almacenan en la cadena de bloques, identificados por una dirección («hash»). Cada vez que queramos interactuar con nuestro contrato deberemos utilizar esta dirección para referirnos a él y poder acceder a las funciones que proporciona.

Para comprender mejor el funcionamiento de Solidity, comenzaremos creando un contrato muy sencillo. El ejemplo es un sistema de almacenamiento mínimo que permite guardar y obtener el valor que tiene almacenado.

Éste será nuestro contrato:

pragma solidity ^0.4.23;

contract Storage {
    uint storedData;

    function setData(uint x) public {
        storedData = x;
    }

    function getData() public constant returns (uint) {
        return storedData;
    }
}



Podemos ver su semejanza con otros lenguajes, aunque con unas particularidades concretas. En primer lugar es necesario indicar la versión del compilador de Solidity que vamos a utilizar, ya que al ser un lenguaje todavía en desarrollo y actualizado frecuentemente, se está modificando continuamente y añadiendo nuevas funciones. Esto puede hacer que aparezcan incompatibilidades entre las diferentes versiones. Por otro lado tenemos un elemento «contract» el cual está compuesto por funciones y variables, de manera similar a lo que sería una clase en otros lenguajes de programación como C++, con sus métodos y atributos.

Hemos creado una función “setData“ para guardar el valor en la blockchain y una función “getData“ para obtenerlo. Si quisiéramos que sólo nosotros fuésemos capaces de ver el valor guardado, Solidity pone a nuestra disposición el elemento “msg” y su propiedad “msg.sender”, que podemos usar para tal fin.

“msg.sender“ se refiere a el hash que identifica al usuario actual que está interactuando con nuestro smart contract. Si incluimos una llamada a “require“ en la función “getData”, podremos prohibir a cualquiera que no sea el creador del contrato el acceso a la función. Sin embargo, cualquiera podría modificar el valor.

pragma solidity ^0.4.23;

contract Storage {
	uint storedData;
	address owner;

 	constructor () public {
    		owner = msg.sender;
	}

	function setData(uint x) public {
    		storedData = x;
	}

	function getData() public constant returns (uint) {
    		require(owner == msg.sender);
    		return storedData;
	}
}

Obviamente podríamos poner el «require» en las dos funciones para que solo el «owner» pudiera modificar y leer el valor almacenado.

Con estos cambios, asignamos a la variable «owner» nuestra dirección o «hash» en el constructor, es decir, en el momento de creación del contrato para posteriormente realizar la comprobación en la función «getData» asegurándose de que el usuario que llama tenga la misma dirección que el creador para poder ver el contenido de dicha función.

Vamos a realizar este ejemplo en Remix, un IDE de Solidity que funciona en el navegador y que permite de forma lo más clara posible interactuar con el contrato.

Como podemos ver, al desplegar nuestro contrato le es asignado una dirección concreta.

Esta dirección o «hash» puede ser utilizado para enviar mensajes (llamadas), y conociendo el cuerpo del contrato o su interfaz (ABI), interaccionar con él en la cadena de bloques.

Front-end

Habiendo creado nuestro contrato, es posible almacenarlo en la cadena de bloques e interactuar con él desde un navegador. Esto compone el diseño de nuestro front-end y para su funcionamiento hemos de explicar la herramienta que nos permitirá conectar estos elementos entre sí: Web3.js.

  • Web3.js

Web3.js es una API diseñada para la interacción con la cadena de bloques de Ethereum. Creada para JavaScript (aunque existen APIs en otros lenguajes como Python), implementa los métodos necesarios para la comunicación de nuestro front con un nodo, normalmente basada en un protocolo RPC.

Como conocemos, la red Ethereum está compuesta por nodos y estos conservan una copia completa de la blockchain. Tan solo sería necesario hablar con uno de esos nodos para poder interaccionar con el smart contract, ya que lo contendrá dentro de la cadena de bloques al tener todos la idéntica. Cuando queremos llamar a una función de un smart contract, los elementos que debemos de dar a un nodo son:

  • La dirección del contrato al que invocamos.
  • La función que se desea llamar.
  • Las variables que se desea enviar a esa función.

Los nodos de la red se comunican mediante el puerto RPC como ya indicamos al crear un nodo de la red Alastria e interpretan los datos en formato JSON-RPC, y ahí es donde entra en juego Web3.

Web3 permite manejar los datos requeridos para la interacción del nodo de una forma más intuitiva. Los datos en formato JSON-RPC que requiere la llamada a una función del smart contract son de la forma:

{
   "jsonrpc":"2.0",
   "method":"eth_sendTransaction",
   "params":[
      {
         "from":"0xca35b7d915458ef540ade6068dfe2f44e8fa733c",
         "to":"0x14723a09acff6d2a60dcdf7aa4aff308fddc160c",
         "gas":"0x76c0",
         "gasPrice":"0x1184e74a000",
         "value":"0x9524e72a",
      }
   ],
   "id":1
}

Mientras que al llamarnos en nuestro front-end mediante Web3 sería:

Storage.methods.setData(value).send({ from: "account" })

Mucho más sencillo de entender ¿Verdad?. A la hora de diseñar una función en nuestro front-end para interaccionar con nuestro contrato, tendremos que llamar al objeto web3 de la librería Web3.js.

var web3 = require(‘web3’);

Las funciones más importantes en el desarrollo de smart contracts las explicaremos a continuación. Toda la documentación de esta API la podéis encontrar aquí.

web3.currentProvider: Es necesario para asignar un proveedor a nuestro objeto web3 para poder interaccionar. Lo veremos a continuación.

web3.eth.getAccounts: Define cual es la cuenta con la que estamos operando. Es conveniente llamarlo al iniciar nuestra aplicación.

Cabe destacar que Web3 devolverá siempre cualquier valor numérico como “BigNumber”, debido a las limitaciones de precisión numérica de los tipos nativos de JavaScript, lo que puede hacer laborioso el testeo de algunas funcionalidades diseñadas con números.

  • Metamask

Metamask es una extensión para Firefox y Chrome que permite la interacción segura y directa entre el navegador web del usuario y la cadena de bloques de Ethereum (o sus diferentes redes de prueba para el desarrollo). Para ello es capaz de manejar de forma segura las claves privadas de los diferentes usuarios junto a Web3.js implementado en la página web del smart contract.

Metamask inyecta su proveedor de web3 a nuestro navegador. Por ello es importante comprobar la función comentada anteriormente web3.currentProvider. La mayoría de los smart contracts que usan Metamask tienen unas líneas de código para comprobar si Metamask está funcionando correctamente e informar al usuario si existe algún fallo.

window.addEventListener('load', function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
    if (typeof web3 !== 'undefined') {
    // Use MetaMask's provider
    web3js = new Web3(web3.currentProvider);
 } else {
    // Handle the case where the user doesn't have web3. Probably
    // show them a message telling them to install Metamask in
    // order to use our app.
    // For example
    // web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
 }
 // Now you can start your app and access web3js freely:
 startApp()
})

Terminada ya la explicación de todos los elementos, disponemos a explicar cómo interactuamos con nuestro contrato en una aplicación web.
Primero hemos de importar las librerías necesarias y el ABI de nuestro contrato, en formato JSON y que Web3.js nos ayudará a manejar:

var Web3 = require(‘web3’);
var web3 = new Web3();
import Storage_artifacts from '../../build/contracts/Storage.json'
var Storage = contract(Storage_artifacts);

Posteriormente incluiríamos la pieza de código anterior para comprobar si Metamask está operativo. Ahora ya sería cuestión de diseñar las funciones que queramos para trabajar con nuestro contrato en Solidity. Un ejemplo para añadir un valor al smart contract desde nuestro navegador podría ser:

var value = document.getElementById(“myInputBox”).value;

Storage.deployed().then(function(instance) {
  return peerseed.setData(value);
});

Y si quisiéramos recuperar ese valor:


Storage.deployed().then(function(instance) {
  return peerseed.getData();
}).then(function(result) {
  console.log(“My stored value is” + result);
}

Con esto concluimos nuestra introducción al desarrollo y despliegue de smart contracts. Como podéis apreciar, hay muchos detalles a tener en cuenta, pero no dejéis que os intimide la tecnología. Blockchain ha abierto la puerta a un futuro de nuevas aplicaciones donde la seguridad y la confianza entre usuarios es la prioridad, y no podemos estar más emocionados. Continuaremos con futuras entradas referidas a blockchain. ¡No dejéis de seguirnos!


Compartir en:

Relacionados