Desarrollo de ÐApps RSK+Python en un VPS con Ubuntu 16.04

La madureza a la que están llegando los proyectos basados en tecnología blockchain y el momentum que tienen están atrayendo la atención de muchos desarrolladores. Abruma la diáspora de plataformas y de herramientas de desarrollo de smart contracts y ÐApps. Más aún desde que RSK permite la ejecución de smart contracts con la EVM (Ethereum Virtual Machine) sobre la plataforma Bitcoin.

Siendo el primer artículo sobre esta temática, el objetivo de esta serie de artículos es facilitar el entorno necesario para el desarrollo de aplicaciones basadas en blockchain con smart contracts en Ethereum y Bitcoin sin entrar en detallar la filosofía o tecnología de las plataformas blockchain.

Como somero repaso, la tecnología blockchain se basa en un despliegue descentralizado, distribuído, inmutable de cuentas (wallets) y en algunas de ellas también de aplicaciones (smart contracts). Las redes blockchain se nutren de nodos que se convierten en valedores de la misma blockchain al replicarla y procesar (minar) las transacciones de los contratos y transferencias de fondos entre carteras (wallets). La magnífica charla realizada por Álex Casas en el encuentro organizado por DevAcademy y Blocknitive resume perfectamente el origen del dinero y su plasmación en las plataformas blockchain. En el mismo evento, Guillermo Lapuente expuso el caso de despliegue de un contrato sobre RSK mediante Truffle.

Sin disponer de la extensa experiencia y conocimientos en Blockchain de otros miembros de la comunidad, me he propuesto emular dicho ejemplo a través de Python. Al ser una prueba iniciática, es muy posible que tanto los conceptos como el código presentado pueda ser mejorado.

Para desarrollar aplicaciones con smart contracts por el lado de la infraestructura es necesario disponer de un nodo (se aconseja instanciar uno local) contra el que desplegar nuestros smarts contracts y para la ejecución de las funciones de los mismos.

Los smart contracts deben estar escritos en un lenguaje que la máquina virtual de Ethereum (EVM) pueda interpretar; mayoritáriamente usan el lenguage Solidity (con semejanza al Javascript).

Para compilar y desplegar esos contratos habitualmente se utiliza un framework en entornos JavaScript. El ejemplo que se ilustra se basará en el compilador solc y la librería web3.py.

La interacción con el contrato desplegado puede realizarse con el terminal del nodo desplegado mediante la consola o bien en formato web basado en Javascript, Angular, React, Python, etc. usando las librerías de conexión a los nodos de las plataformas de blockchain (tanto para Ethereum, Bitcoin u otras). Para la edición de los ficheros de la webapp existen varias alternativas en código abierto que permiten la edición contextualizada y hasta disponer de consola de ejecución integrada.

Finalmente, en el caso de ejecutar las ÐApps en un entorno web, se recomienda usar un gestor de cuentas integrado con el navegador para la realización y confirmación de pagos y transferencias que la aplicación requiera. De esta manera se reducen las posibilidades de errores al insertar manualmente las direcciones en el código.

Todas las herramientas y planteamientos comentados aquí disponen de multitud de alternativas con características muy similares y que deben ser analizadas para identificar la idoneïdad. La intención de los artículos es dotar una base y una guía por dónde empezar entre tanta diversidad.

Ethereum+Python

La alternativa desde Python para desplegar aplicaciones sobre Ethereum requiere el uso de Python3 (por la librería web3) junto con el compilador solc de Solidity y los Jupyter Notebooks para la ejecución de aplicaciones en un entorno virtual.

$ sudo apt-get update
$ sudo apt-get install python3-dev solc

Los entornos virtuales permiten la gestión de diferentes configuraciones (paquetes, dependencias, versiones, etc) de entornos Python en una misma máquina sin interfencias. Una vez disponibles, la creación e instancia del entorno virtual se asemeja a Python2.7..

$ mkdir .dapps_py3
$ virtualenv -p python3 ~/.dapps_py3
Running virtualenv with interpreter /usr/bin/python3
Using base prefix '/usr'
New python executable in /home/esteve/.dapps_py3/bin/python3
Also creating executable in /home/esteve/.dapps_py3/bin/python
Installing setuptools, pip, wheel...done.
$ source ~/.dapps_py3/bin/activate
(.dapps_py3)~$ cd .dapps_py3
(.dapps_py3)~/.dapps_py3$

Para eso entorno virtual se necesita instalar algunas librerías mediante pip (previa actualización del mismo) como es la de conexión a los nodos de ethereum (web3), el wrapper para el compilador de Solidity (py-solc), el gestor de notebook (Jupyter) y el gestor de JSON (jsonstream).

Finalmente, se instancia el Jupyter Notebook en el puerto (8899 en este caso) sin ejecutar el navegador al estar en un servidor entorno gráfico. Se ejecuta mediante setsid para que al cerrar el terminal no se mate el proceso lanzado. Dicho puerto será redireccionado en local para poder trabajar con las funcionalidades de Jupyter Notebook.

(.dapps_py3)~/.dapps_py3$ pip install --upgrade pip
Requirement already up-to-date: pip in ./lib/python3.5/site-packages (10.0.0)
(.dapps_py3)~/.dapps_py3$ pip install --pre web3
...
(.dapps_py3)~/.dapps_py3$ pip install py-solc jupyter jsonstream
...
(.dapps_py3)~/.dapps_py3$ setsid jupyter notebook --no-browser --port=8899
(.dapps_py3)~/.dapps_py3$ [I 15:46:33.808 NotebookApp] Serving notebooks from local directory: /home/esteve/.dapps_py3
[I 15:46:33.808 NotebookApp] 0 active kernels
[I 15:46:33.808 NotebookApp] The Jupyter Notebook is running at:
[I 15:46:33.808 NotebookApp] http://localhost:8899/?token=3e5ea62829f6538a99d629b2f69d01ffbdfa0cf203965010
[I 15:46:33.808 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 15:46:33.809 NotebookApp] 
 
 Copy/paste this URL into your browser when you connect for the first time,
 to login with a token:
 http://localhost:8899/?token=3e5ea62829f6538a99d629b2f69d01ffbdfa0cf203965010

A partir de este momento, en el puerto 8899 está un entorno Jupyter Notebook sobre Python3. Para poder trabajar visualmente con un navegador, se puede realizar un túnel para redirigir el puerto en cuestión y emularlo en local. Para asegurarnos de que no hay nada en el puerto local que se va a utilizar, liberamos con lsof cualquier servicio que corra en local:8899. Enlazamos en ese momento a través de un túnel SSH el puerto remoto del VPS con el local pasando como parámetros el usuario y dominio del VPS.

estevet$ lsof -ti:8899 | xargs kill -9
estevet$ ssh -N -f -L localhost:8899:localhost:8899 username@vpsdomain.com

En el puerto 8899 del ordenador local tendremos el puerto del servidor remoto con el Jupyter Notebook. Accediendo con un navegador a http://localhost:8899 nos enseña el servicio en activo y pide la confirmación del token para acceder al notebook tal y como muestra la última línea de la ejecución de jupyter notebook.

 

Jupyter Notebook Python3 - Token
Jupyter Notebook Python3 – Token

 

Jupyter Notebook Python3 - Listado
Jupyter Notebook Python3 – Listado

Comprobando que se disponen de los paquetes necesarios mediante pip, ya sólo queda enlazar a un nodo de Etehreum o RSK.

Para disponer de un nodo de RSK, tan sólo deben instalarse el paquete del servidor, que corre como una aplicación de Java con un uso muy intensivo de los procesadores.


(.dapps_py3) :~/.dapps_py3$ sudo add-apt-repository ppa:rsksmart/rskj
[sudo] password for esteve: 
 RSKj is a Java implementation of the RSK protocol.
 More info: https://launchpad.net/~rsksmart/+archive/ubuntu/rskj
Press [ENTER] to continue or ctrl-c to cancel adding it
gpg: keyring `/tmp/tmp8n81bhxo/secring.gpg' created
gpg: keyring `/tmp/tmp8n81bhxo/pubring.gpg' created
gpg: requesting key 518C7BEA from hkp server keyserver.ubuntu.com
gpg: /tmp/tmp8n81bhxo/trustdb.gpg: trustdb created
gpg: key 518C7BEA: public key "Launchpad PPA for RSK Labs" imported
gpg: Total number processed: 1
gpg:               imported: 1  (RSA: 1)
OK
(.dapps_py3) :~/.dapps_py3$ sudo apt-get update
(.dapps_py3) :~/.dapps_py3$ sudo apt-get install rskj

La instalación es muy sencilla y tan sólo deben aceptarse las licencias y seleccionar la red de conexión.

 

RSK Instalación

En el selector de la red podemos seleccionar Testnet aunque más adelante se cambiará a RegTest para mostrar el cambio de configuración de selección de red.

RSK Instalación – Selección de red

Para conocer los detalles de la instalación de RSK, se puede acceder al fichero /etc/rsk/node.conf en en que identifica el puerto 4444 para la conexión para interactuar y que al iniciarse no cuenta con ninguna cuenta. El compilador es solc anteriormente instalado. El estado del servicio se comprueba con status y para pararlo con stop


...

rpc {
enabled = true

# Interface to bind rpc
# Make sure you are using the correct bind. Default value: localhost
address = localhost

port = 4444
...
# account loaded when the node start.
wallet {
 accounts = []
}
...
# solc compiler path
solc.path = /usr/bin/solc
...

El archivo de configuración que viene por defecto se conecta a TestNet (selección hecha en la instalación) y se convierte en un nodo activo. Se puede modificar la configuración para que el nodo actúe en modo aislado. En el directorio /etc/rsk se encuentran algunos ejemplos de archivos de configuración que se pueden usar como base.

(.dapps_py3) :~/.dapps_py3$ sudo ln -sf /etc/rsk/regtest.conf /etc/rsk/node.conf 

El archivo de configuración de RegTest está casi completo y listo para su uso. Se puede realizar el cambio para que se permita la creación de cuentas; teniendo en cuenta que se crean automáticamente al iniciar un nodo en modo aislado. El mensaje de salutación se debe actualizar a RegTest para ser coherentes.

...
# account loaded when the node start.
wallet {
enabled = true,
accounts = []
}
... 
# hello phrase will be included in the hello message of the peer
hello.phrase = RegTest
...

El servicio puede iniciarse entonces.

(.dapps_py3) :~/.dapps_py3$ sudo service rsk start
(.dapps_py3) :~/.dapps_py3$ ps -edaf | grep rsk
rsk 15397 1 85 12:11 ? 00:00:09 /usr/bin/java -Dlogback.configurationFile=/etc/rsk/logback.xml -cp /usr/share/rsk/rsk.jar co.rsk.Start 2>&1 &
esteve 15442 14315 0 12:11 pts/7 00:00:00 grep --color=auto rsk
(.dapps_py3) :~/.dapps_py3$ sudo service rsk status
● rsk.service - RSK Node
   Loaded: loaded (/lib/systemd/system/rsk.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2018-04-19 11:26:26 CEST; 37s ago
 Main PID: 14545 (java)
    Tasks: 44
   Memory: 744.9M
      CPU: 28.311s
   CGroup: /system.slice/rsk.service
           └─14545 /usr/bin/java -Dlogback.configurationFile=/etc/rsk/logback.xml -cp /usr/share/rsk/rsk.jar co.rsk.Start 2>&1 &

El nodo local aislado está funcionando y puede comprobarse su actividad en /var/log/rsk/rsk.log.

tail -f /var/log/rsk/rsk.log -n 100

Como recordatorio, comentar que los nodos activos son altamente demandantes de recursos con lo que a veces es mejor instanciarlos sólo cuando sea necesario. Volviendo al entorno de Jupyter Notebook, se pueden consultar algunas de las carácterísticas del nodo. Primero se evaluarán las librerías instaladas así como sus versiones.

En este caso web3 tiene la versión 4.1.0 y py-sol en la 2.1.0. Podemos consultar qué hay en el nodo recientemente levantado.

Al ejecutar el código, el nodo no está sincronizado (dado que es un nodo aislado) y se está en el bloque 1. Se han creado 11 cuentas teniendo la primera fondos más que suficientes (mostrados en formato Wei). La cuenta creada

0x003C91b86BeF749fBd78e83d8573501271728836

aparece en el listado de cuentas aunque en cambio no dispone de saldo.

Teniendo el nodo funcionando correctamente, se puede ejecutar un contrato de prueba para evaluar su correcto funcionamiento.

Solidity permite ejecutar lógica en forma de aplicaciones en una blockchain. El contrato de este ejemplo es una función de salutación (saluda) que devuelve una cadena de texto predeterminado (‘¡Saludo inicial!’) en el constructor y otra función (cambiarSaludo) que permite actualizar el contenido del saludo. La primera será de consulta y la segunda de modificación que llevará asociada un coste para su ejecución. Se definen sendas variables global para el saludo (saludo) y otro como contador (num_saludos) que se pueden consultar en todo momento. La versión de pragma se define como 0.4.19 aunque remix permite compilar en 0.4.22, la versión de solc instalada no la soporta.

Dicho contrato se puede comprobar el contrato en sí en el entorno de remix de Ethereum con la dirección

0xaecf5ca446b1ab35c28d021594854a32e0a80524

en el entorno Ropsten. Para el uso de remix en modo de compilación o funciones de modificación se requiere de una dirección con saldo positivo en el entorno Ropsten y un gestor de direcciones como MetaMask. Los gestores de cuentas son altamente necesarios para interactuar con ÐApps en modo web en cuanto las transacciones requieren de escritura en la blockchain.

Remix-Prueba contrato desplegando

Una vez el contrato se ha desplegado, actualizamos el saludo y tanto el valor de la variable saludo se actualiza (¡Ea!) como el contador de número de saludos aumenta (1).

Remix-Prueba contrato actualizando contenido

Una vez se comprueba que el contrato funciona correctamente, éste se incluye en el código de Python en el notebook para dotar de claridad aunque podría referenciarse externamente. Es más que recomendable bucear en la documentación existente sobre las llamadas de web3.py.

En el primer módulo se define el contrato anteriormente comentado y se compila.

En el segundo módulo, el contrato compilado se despliega en la blockchain con el resultante hash de la transacción

0xb766eb8ade4353cbd9e097dd4aff4a47c753b736ed3242730087b46b2f065b52

previa conversión con la función toHex() para poder utilizar el objeto.

En el tercer módulo, de la transacción se obtiene la dirección de la cuenta en la que se aloja el contrato encontrándose en la clave ‘contractAddress’

0x77045E71a7A2c50903d88e564cD72fab11e82051

El cuarto módulo es muy similar al anterior, mostrando el contenido de la transaacción anterior.

El quinto módulo instancia un contrato basándose en una dirección, que es la obtenida en el módulo tercero. Se muestra la definición ABI y los valores inicial del saludo. Finaliza el módulo con la ejecución de la modificación del saludo a través de cambiarSaludo().

El sexto y último módulo muestra el nuevo valor que contiene la variable saludo en el contrato Saludos.

En este momento se dispone de un contrato Solidity desplegado en la blockchain RSK.