Herramientas y principios técnicos básicos para el desarrollo de contratos inteligentes usando la Blockchain de Ethereum
El pasado 30 de abril de 2020, con el aval de la Fundación Blockchain Colombia, tuvimos la oportunidad de transmitir virtualmente un webinar al cual llamamos “HERRAMIENTAS Y PRINCIPIOS TÉCNICOS BÁSICOS PARA EL DESARROLLO DE CONTRATOS INTELIGENTES USANDO LA BLOCKCHAIN DE ETHEREUM”. Satisfactoriamente despertó el interés de un grupo importante que nos acompañó en esa fecha, y aún más estimulante fue que nos solicitaran documentación para que pudieran consultar posteriormente la información, practicar algo de código y porque no convertir esta introducción que quisimos transmitir como la piedra angular en el inicio del camino como programadores de este tipo de tecnología. Esto último fue lo que nos animó a escribir este artículo que resume un poco lo que expusimos en dicho webinar y deja escritos los conceptos vistos para que puedan ser consultados en cualquier momento por alguien que esté interesado en emprender este camino.
Cabe mencionar en este punto que lo que vamos a exponer en este artículo, al igual que lo que se explicó en el webinar, está dirigido a personas que tienen conocimiento teórico básico acerca de la tecnología blockchain y se da por entendido que no es necesario empezar exponiendo esos conceptos que igual los vamos a mencionar, pero no nos vamos a detener ampliamente en ellos. De igual manera, este artículo tiene un enfoque mucho más técnico que teórico, por o cual es necesario por lo menos haber tenido algún acercamiento pequeño con algún lenguaje de programación para llevar a la práctica el código fuente resultante. De todas maneras, si no eres programador, puedes continuar leyendo pues el conocimiento nunca es de sobra ;).
De seguro si eres programador o tienes algún acercamiento con este ecosistema, te has dado cuenta que cada uno de nosotros tiene sus propias herramientas para empezar a realizar su trabajo y que cuenta con ellas siempre en cada momento de su carrera, generalmente adopta algunas nuevas, pero la esencia siempre se conserva. Desque que empecé mi carrera como programador blockchain, la primera herramienta que usé para dar los primeros pasos en la programación de contratos inteligentes usando la red de Ethereum fue (y sigue siendo) REMIX.
Remix es una herramienta excelente para empezar a dar los primeros pasos con contratos inteligentes, que tiene todo un entorno de desarrollo completo, con todas las herramientas necesarias para poder escribir y debuggear nuestros contratos sin la necesidad de instalar ninguna dependencia en nuestro computador o algo parecido. Simplemente ingresando a la URL https://remix.ethereum.org/ en ya tienes acceso a todo lo necesario para empezar tu camino como desarrollador blockchain.
Estando en su interfaz inicial vamos a dar un vistazo a las principales funciones necesarias para realizar nuestro primer acercamiento al desarrollo de un contrato inteligente sencillo, que es el objetivo de este artículo. Cabe anotar y remarcar que las herramientas que vamos a usar son sólo un abrebocas de lo que nos ofrece REMIX, ya que como dijimos anteriormente, esta plataforma nos ofrece todo un entorno de desarrollo extremadamente completo y prácticamente aquí podríamos desarrollar todos nuestros contratos inteligentes sin necesidad de una terminal o de un editor de texto.
(Nota: Aunque esto sea cierto la gran limitante que tiene REMIX es que es basado en el navegador, entonces si queremos hacer que nuestros contratos entren en relación con los otros módulos de nuestra aplicación (Front-End, Back-End, Bases de datos), definitivamente REMIX nos funcionará sólo para dar los primeros pasos de desarrollo de dicha aplicación).
En resumen, la estructura del layout de REMIX se divide en los siguientes cuatro módulos:
1. Panel de iconos: Al hacer click en cualquiera de los iconos que aparecen en este panel, las opciones del panel deslizante cambiarán.
2. Panel deslizante: En este panel se despliegan todas las opciones principales con las cuales puede interactuar. Entre las opciones más importantes están los archivos de los contratos inteligentes que hayas escrito en tu navegador, REMIX tiene la opción de guardar estos datos en el caché de tu computador.
3. Panel principal: Aquí se despliega, como lo veremos más adelante, una especie de editor de texto que es donde escribiremos el código de cada uno de nuestros contratos inteligentes que deseamos compilar.
4. Terminal: En este módulo podrás ver los resultados de todas tus interacciones con la interfaz de REMIX, así como de la ejecución de los scripts de los contratos inteligentes que compilemos en esta plataforma.
Lo primero que debemos hacer es activar los plugins más importantes para que de esta manera nuestro entorno de desarrollo nos ayude a tener un resultado de escritura y compilación de código con altos estándares de calidad. Para esto vamos a nuestro PANEL DE ICONOS en donde encontramos las siguientes opciones:
Estando allí, haz click en la opción de GESTOR DE PLUGINS y activa los siguientes haciendo click en el botón “Activate” de color verde que encontrarás al lado derecho de cada uno de ellos:
· SOLIDITY COMPILER
· DEPLOY & RUN TRANSACTIONS
· DEBUGER
Activándolos nuestro layout del PANEL DE ICONOS debió haber cambiado y se debe ver algo parecido a lo siguiente:
Teniendo nuestro entorno de desarrollo listo y activado para empezar nuestro código, vamos a empezar a desarrollarlo. Para esto, regresamos a la opción de EXPLORADOR DE ARCHIVOS, la cual siempre será la primera de nuestro PANEL DE ICONOS. Estando allí creamos un nuevo archivo haciendo click en el icono que aparece en la parte superior del PANEL DESLIZANTE de esta opción, tal como se muestra a continuación:
El ejercicio que realizaremos en este ejemplo es desplegar un contrato que guarde un mensaje, y que sólo la persona que es dueña del contrato pueda leer o cambiar el mensaje que éste contiene, algo así como una caja fuerte del mensaje, con lo cual el mejor nombre que le podemos poner a nuestro contrato sería “SafeEnvelope.sol”. Como se puede inferir, la extensión “.sol” corresponde a archivos de código que usan Solidity como lenguaje de programación:
Haciendo este proceso ya se puede ver que en nuestro panel principal se despliega una pantalla en blanco que es nuestro “lienzo” para empezar a escribir nuestro contrato inteligente:
¡Ahora si llegó la hora!, la que a todos nos gusta, por fin empezaremos a “codear”. Para empezar a desarrollar nuestro contratos inteligentes lo primero que debemos indicarle a nuestro compilador es la versión de éste que vamos a usar para que “vigile” nuestro código y nos permita ver si lo estamos escribiendo de buena manera, pues como puede ser evidente para nosotros como desarrolladores, las versiones de los lenguajes de programación van cambiando a través del tiempo y las reglas que en un pasado se aplicaban puede que ya no sean vigentes para la época actual. Sabiendo que el ecosistema blockchain, y en específico Ethereum, son tecnologías que actualmente están en constante desarrollo, esta pauta se hace aún más relevante con lo que la versión de nuestro compilador llega a ser algo fundamental de determinar en nuestro código. Para realizar esto, usamos una palabra reservada clave que se llama “pragma”.
Pragma es la abreviatura de Pragmatic Information, aplicando inglés directamente sería la traducción de información pragmática. En programación, el concepto “pragma” se refiere a:
- El código que consiste en información útil sobre cómo un compilador, intérprete o ensamblador debe procesar el programa o código fuente que escribamos.
- “pragma” no aporta nada al lenguaje de programación en sí.
- “pragma” solo afecta el comportamiento del compilador.
- la sintaxis de “pragma” puede variar en diferentes lenguajes de programación.
Teniendo en cuenta la definición, empezamos a escribir nuestro código de la siguiente manera:
Esta primera línea de código lo que le está diciendo al compilador es que tome las reglas del lenguaje de programación Solidity que estén en una versión mayor o igual a la 0.5.10 y menores a la versión 0.7.0. De esta manera, las reglas que estén fuera de este rango serán señaladas como errores de código o advertencias del mismo.
Después de definir nuestro ‘pragma’ debemos definir el nombre de nuestro contrato, el cual, si estás familiarizado con un lenguaje de programación como Javascript, se define igual a como se haría la instanciación de una clase, de la siguiente manera:
contract SafeEnvelope {}
Como se puede observar, se usa la palabra reservada ‘contract’ y, seguido a ésta, el nombre del contrato que por lo general es el mismo nombre que se le dio al archivo de nuestro contrato, en este caso ‘SafeEnvelope’.
Ya con nuestro contrato inteligente instanciado, es necesario que empecemos a recordar cual es el objetivo principal de nuestro proyecto, el cual, si recordamos, es salvaguardar un mensaje secreto que sólo el dueño del contrato pueda leer o pueda cambiar. Entonces, si nos ponemos a analizar a fondo, definitivamente necesitaremos una variable en donde podamos guardar el estado de ese mensaje y cambiarlo en el momento que necesitemos. Para lograr esto entonces vamos a instanciar nuestra primera variable de nuestro contrato de la siguiente manera:
contract SafeEnvelope {
string private secretMessage;
}
Como te puedes dar cuenta, la manera de instanciar una variable en Solidity, guarda la siguiente sintaxis:
- Tipo de variable: Puedes ver todos los tipos de variable que maneja Solidity en su documentación: https://solidity.readthedocs.io/en/v0.6.8/types.html
- Visibilidad de variable: Puede ser privada (private), interna (internal), externa (external) o pública (public) de acuerdo con las necesidades y funciones que vaya a cumplir dentro del contrato. Puedes ver más información acerca de esto aquí: https://solidity.readthedocs.io/en/v0.6.8/contracts.html#visibility-and-getters
- Nombre de la variable: Este nombre es una cadena de texto que usaremos para hacer la llamada de esta variable dentro de nuestro código. Este nombre puede ser cualquiera que se nos ocurra.
De esta manera ya tenemos en nuestro contrato un lugar donde guardar nuestro mensaje y de esta manera lograr el objetivo del mismo. Ahora bien, existe una función muy importante dentro de nuestros contratos inteligentes que es la que se ejecuta tan pronto desplegamos nuestro contrato y es la encargada de instanciar el primer estado de nuestro contrato, así como de las variables internas del mismo. Esta función recibe el nombre de “constructor” y, usando esa misma palabra, ya podemos adicionar este importante método a nuestro código:
contract SafeEnvelope {
string private secretMessage; constructor (string _initialMessage) public {
secretMessage = _initialMessage;
}
}
Cómo podemos ver aquí, esta variable recibe un parámetro el cual supondría el estado inicial de nuestro mensaje secreto. De igual manera podemos ver que los métodos de nuestros contratos, en este caso nuestro constructor, tienen la característica de poseer una visibilidad determinada, tal como lo explicamos anteriormente con nuestras variables. Si escribes este código en el compilador de REMIX te puedes dar cuenta que nos está lanzando un error de compilación que es el siguiente:
Este mensaje lo que nos quiere decir es que es necesario que le digamos donde queremos guardar la información del parámetro “_initialMessage”, que es el que deseamos asignarle a nuestra variable de estado de nuestro contrato “secretMessage”. Y ese “dónde” se refiere al estado de memoria que deseamos usar para realizar esta operación, el cual tiene dos opciones: MEMORY o STORAGE.
Para definir estos dos conceptos usaremos la siguiente tabla comparativa:
Teniendo en cuenta la definición de estos conceptos, podemos inferir que MEMORY lo usaremos para información que deseamos persista SOLAMENTE durante el tiempo de ejecución de la transacción de nuestro contrato, mientras que STORAGE la emplearemos cuando queremos que la información se guarde de manera permanente en la memoria de nuestro contrato sin importar el momento de la historia cuando invoquemos cualquiera de sus métodos. Con lo cual para este caso, usaremos MEMORY, pues deseamos que nuestro parámetro simplemente se asigne a nuestra variable y no cumpla más funciones dentro de nuestra lógica, por lo que, haciendo caso a nuestro compilador, el código quedará sin errores de la siguiente manera:
contract SafeEnvelope {
string private secretMessage;constructor (string memory _initialMessage) public {
secretMessage = _initialMessage;
}
}
La otra variable importante para nuestro contrato es determinar quién es el dueño del contrato, quién es aquella única persona que tiene la potestad tanto de leer como de cambiar el estado de nuestro mensaje secreto. Generalmente esta característica se le da a la persona o ente que despliega el contrato, la cual está representada dentro de un atributo de una variable global del sistema llamada ‘msg’. Esta variable guarda información importante acerca de las características que posee la transacción que se está llevando a cabo al momento del despliegue del contrato. Estas características o atributos son los siguientes:
Siendo una variable global, y teniendo en cuenta que este tipo de variables están presentes en cualquier lenguaje de programación, está disponible en cualquier momento y no es necesario instanciarla o ponerle algún tipo de atributo. De igual manera, viendo los atributos que posee nuestra variable, para poder determinar quien fue la persona o ente responsable del despliegue de nuestro contrato, podemos concluir que debemos usar ‘msg.sender’. Con esto en mente, vamos a aplicarlo a nuestro código:
contract SafeEnvelope {
string private secretMessage;
address public owner;
constructor (string memory _initialMessage) public {
secretMessage = _initialMessage;
owner = msg.sender;
}
}
En este código podemos ver que instanciamos una variable ‘owner’ de visibilidad pública para que cualquier persona pueda ver quien es el dueño de este contrato, así como de tipo ‘address’ pues este es el modelo que se usa para determinar las diferentes entidades que se definen en la red (contratos inteligentes, personas, etc). Habiendo definido el tipo y la visibilidad de la variable, le asignamos el valor de la persona encargada de la transacción de despliegue del contrato con la sentencia ‘owner = msg.sender’.
De esta manera ya tenemos inicializado nuestro contrato y las variables necesarias para que éste cumpla el objetivo que nos trazamos al inicio de nuestro proyecto. Ahora bien, lo que nos falta por desarrollar es darle la posibilidad a el dueño del contrato para que pueda leer el mensaje que guardó allí y de igual manera pueda cambiar el estado del mismo si es lo que desea. Para esto vamos a implementar dos métodos que nos permitan realizar estas acciones de la siguiente manera:
Con la implementación de los métodos ‘setNewEnvelopeMessage’, el cual le permitirá al dueño del contrato establecer un nuevo mensaje para el buzón seguro, así como del método ‘getEnvelopeMessage, que le posibilitará ver el estado actual del mensaje que tiene asignado en el mismo, el contrato inteligente que nos propusimos desarrollar para este ejercicio básico quedará listo para su despliegue en la blockchain de Ethereum en un entorno de producción totalmente completo.
Si deseas ver más a fondo la explicación de este código desarrollado en nuestro webinar, te invitamos a que lo veas en nuestra transmisión que realizamos ese día:
Como siempre, te invitamos a que nos sigas y nos continúes acompañando en este caminar en donde la tecnología Blockchain se convierta en una herramienta de uso masivo y de protección para los derechos fundamentales de las personas.