Despliegue de web ÐApp con Quorum+Angular+Python+Flask en un VPS con Ubuntu 16.04

En artículos anteriores se ha comprobado que Python cuenta con herramientas suficientemente versátiles como para poder desarrollar una ÐApp contra RSK. En el caso que nos ocupa se hace un planteamiento de concepción, desarrollo y despliegue de una web ÐApp basado en Quorum con RAFT sobre Angular y Python+Flask+CherryPy.

Siguiendo indicaciones de Alfonso de la Rocha en su reciente presentación de HarvardIT, lo primero sería identificar el problema. A muchos nos ha pasado que al prestar libros, vinilos o DVD a nuestros amigos o conocidos se les pierde la pista para siempre cayendo en el olvido. Como prueba de concepto, nos planteamos crear una web ÐApp que permita la gestión de dichos libros que se prestan entre amigos para que no nos digan ‘Ya te lo devolví’ o ‘Nunca me lo dejaste’. Con una blockchain que refleje todos los préstamos realizados el único problema que quedará será la voluntad o posibilidad de devolver el préstamo, que quedaría fuera del ámbito tecnológico.

En esta serie de artículos se describirá los pasos seguidos para el desarrollo de la ÐApp así como la configuración de los nodos de la red permisionada basada en Quorum.

Despliegue de web ÐApp con Quorum+Angular+Python+Flask en un VPS con Ubuntu 16.04
[1] [Quorum]
[2] [Solidity]
[3] [Python]
[4] [Angular]

La información descrita en este artículo diferirá de la mostrada en el repositorio GiveLibAck en tanto en cuanto aquí se muestran pasos intermedios que pueden saltarse bajándose el contenido del repositorio como por ejemplo los binarios de Ubuntu o la estructura de directorios de los nodos junto con los keystores de las cuentas.

Procesos de negocio

Para la prueba de concepto se definen una serie de acciones que los usuarios pueden realizar y que posteriormente se deben mapear en los contratos y el backend.

Básicamente los usuarios pueden

  • registrarse y registrar libros
  • actualizar algunos datos de los libros registrados
  • pedir el préstamos de libros o su retorno si están en préstamo
  • dejar libros en préstamo o retornar los libros prestados
  • identificar quién tiene un libro, qué libros ha creado en el sistema un usuario y qué libros tiene actualmente en posesión (de su creación o prestados) un usuario

Solución tecnológica

Como topología se escoge una red permisionada basada en Quorum, con metología de consenso RAFT y tokens ERC721 para la identificación única de cada uno de los activos (libros). Se desplegarán varios nodos siendo cada nodo representante de cada usuario que interactúa con la aplicación.

Como backend se usa Python 3.5 (necesario para poder interactuar con el módulo web3) que tendrá activos endpoints del servidor web (WSGI) de Flask y gestionado por la capa WSGI de CherryPy para dotar de envergadura al servidor HTTP.

Como frontend se usa Angular desplegando el framework directamente interactuando con Python a con CORS.

Instalación de la infraestructura y desarrollo

A partir de ese momento se puede empezar creando un esqueleto de lo que será el proyecto en un repositorio abierto en GitHub siguiendo la filosofía de transparencia y compartición de conocimiento.

El entorno de uso puede ser tanto un servidor dedicado con una versión de Ubuntu 16.04 como una imagen virtual de la misma versión para ordenadores de sobremesa como los disponibles en osboxes tanto para VirtualBox como para VMWare. En este caso se documenta la instalación y configuración en una imagen de osboxes como paso previo a la migración a producción en un servidor.

Backend

El backend estará basado en un entorno virtual con Python 3.5 (por las dependencias del módulo web3) y una serie de nodos instanciados en diferentes máquinas creando una constelación Quorum.

Backend – Quorum

El objetivo de la red de nodos es que sea permisionada y que cada usuario tenga un nodo. Como herramienta se decide por Quorum con dos nodos. Como metodología para el consenso, Quorum acepta tanto RAFT como Istanbul BFT. En este ejemplo se implementará con RAFT sin uso de comunicación privada.

Instalación

En los documentos informativos de Quorum, se recomienda usar unas imágenes con configuraciones hechas disponibles en Vagrant aunque en este caso se ha preferido la compilación en local para no añadir más capas de virtualización de entornos más allá del de Python. De la misma manera, para este ejemplo se ha decidido crear la configuración de la red desde cero sin basarse en el conocido y disponible ejemplo de 7 nodos con el fin de enseñar los pasos para la instalación y configuración de los nodos y una red.

Para ello se necesita descargar el repositorio de Quorum y el compilador de Go superior a la versión 1.6.3 (disponible en Ubuntu 16.04) y que requiere de un respositorio externo. Los binarios que se compilan se copian en un directorio que bien puede ser /usr/lib/bin o en caso de ser otro directorio que no esté en el $PATH deben añadirse. Se puede añadir el $PATH en .bashrc y cargarlo con source ~/.bashrc.

osboxes@osboxes:~$ mkdir Desktop/giveliback
osboxes@osboxes:~$ cd Desktop/giveliback
osboxes@osboxes:~/Desktop/giveliback$ git clone https://github.com/jpmorganchase/quorum.git 
osboxes@osboxes:~/Desktop/giveliback$ sudo add-apt-repository ppa:gophers/archive 
osboxes@osboxes:~/Desktop/giveliback$ sudo apt-get update 
osboxes@osboxes:~/Desktop/giveliback$ sudo apt-get install golang-1.10-go 
osboxes@osboxes:~/Desktop/giveliback$ PATH=$PATH:/usr/lib/go-1.10/bin
osboxes@osboxes:~/Desktop/giveliback$ go version 
go1.10 linux/amd64 

Disponiendo del repositorio de Quorum y del compilador Go, se procede a la compilación del repositorio (make all). Los binarios resultantes se localizan en quorum/build/bin. Se puede realizar la comprobación de la cimpilación con (make test). Se añaden los binarios compilados en el $PATH habiendo creado un enlace simbólico (quorum_bin) en el repositorio giveliback. Esos serán los binarios a utilizar para la configuración e inicialización de los nodos.

osboxes@osboxes:~/Desktop/giveliback$ $ cd quorum
osboxes@osboxes:~/Desktop/giveliback/quorum$ make all
...

Se puede optar también por usar la última distribución disponible, en la redacción de este artículo es la 2.0.2. y realizar el mismo proceso de compilación de los binarios.

wget https://github.com/jpmorganchase/quorum/archive/v2.0.2.tar.gz
tar -xzvf v2.0.2.tar.gz
cd quorum.2.0.2
make all

Una vez descargados los ficheros, se procede a la compilación de los binarios y añadirlos a través de un directorio simbólico al $PATH.

osboxes@osboxes:~/Desktop/giveliback/quorum$ cd .. 
osboxes@osboxes:~/Desktop/giveliback$ ln -s quorum/build/bin/ quorum_bin 
osboxes@osboxes:~/Desktop/giveliback$ PATH=$PATH:/home/osboxes/Desktop/giveliback/quorum_bin 
osboxes@osboxes:~/Desktop/giveliback$ which geth
/home/osboxes/Desktop/giveliback/quorum_bin/geth
Configuración del nodo

La configuración de un nodo se puede guardar en un archivo tipo TOML con los parámetros que sean necesarios para poder instanciar cómodamente las siguientes veces. Bien se puede guardar el contenido mostrado anteriormente en un archivo (config.toml) y modificar su contenido con un editor de texto o bien se puede generar dicho contenido con los parámetros de interés de origen.

En este caso se definen tanto los directorios de datos (datadir) como los módulos a cargar (entre ellos quorum) así como los puertos de conectividad en el fichero config.toml. La contraseña se referencia en un archivo (password.txt) para dotar de comodidad. Dicho archivo puede estar vacío pero debe tener el mismo contenido que la contraseña usada al crear las cuentas. En este caso la contraseña es ‘password’. Permissioned forzará el parámetro de aprobación de nodos previo a la inclusion del cluster.

Usando como parámetro dumpconfig, muestra en pantalla los valores de inicialización de la cadena de bloques. EnableNodePermission se tipifica como afirmativa.

osboxes@osboxes:~/Desktop/giveliback$ echo "password" > password.txt
osboxes@osboxes:~/Desktop/giveliback$ head password.txt 
password
osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node1 --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --permissioned --nodiscover --rpcport 22000 --port 21000 dumpconfig > config.toml
WARN [07-05|18:45:10] No etherbase set and no accounts found as default

El contenido del fichero de configuración contiene los parámetros asignados.

En la creación del fichero de configuración, también se crea el directorio que albergará el nodo (node1) con un subdirectorio para las cuentas (keystore) actualmente vacío. Muestra una alerta al no estar definidas las cuentas de inicio ni definirse el bloque génesis. Se subsanará dicha situación más adelante.

osboxes@osboxes:~/Desktop/giveliback$ ls qdata/node1 
keystore 
osboxes@osboxes:~/Desktop/giveliback$ ls qdata/node1/keystore/ 
osboxes@osboxes:~/Desktop/giveliback$ 

En caso de querer levantar un nodo con la configuración guardada, se referencia dicho fichero de configración al instanciar el nodo. En caso de levantar el nodo sin tener ningún fichero de permissioned-nodes.json, el nodo correrá sin conexión alguna a otros nodos. Una vez se configuren los nodos se puede modificar el archivo para añadir los nodos que a los que se permite conectar.

Creación de cuentas

Entre las dos formas más comunes de inicar un nodo con cuentas con saldo, una propone crear las cuentas sin tener el nodo iniciado para poder generar el bloque genesis y en la otra se levanta el nodo y se crean las cuentas interactivamente desde la consola. Aunque el resultado es el mismo, la segunda requiere del borrado de la cadena de bloques del nodo previa a la inicialización del bloque génesis debido a su inicialización previa.

En caso de querer configurar las cuentas antes de levantar por pimera vez el nodo, geth permite la interacción contra un nodo en modo offline para la creación de cuentas. En este caso se procede a la creación de cuatro cuentas.

osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node1 --password password.txt account new
newWARN [07-05|18:46:34] No etherbase set and no accounts found as default 
Address: {3b6927fe4a4a4d44c3445292d375542cf299661c}
osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node1 --password password.txt account new
Address: {513f15ec9fc190cbc2ac25c6d6acdb58253f80d7}
osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node1 --password password.txt account new
Address: {5030a651df562c629cfc92928210a51d2e688d6d}
osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node1 --password password.txt account new
Address: {00d631971be081c4de18fb6fca0c2723782299f7}

A partir de este momento, se han creado cuatro cuentas (wallets) en el directorio /node1/keystore que coinciden con los nombres de los archivos (uno por cuenta).

osboxes@osboxes:~/Desktop/giveliback$ls -la /home/osboxes/Desktop/giveliback/qdata/node1/keystore/ 
total 24 
drwx------ 2 osboxes osboxes 4096 jul 05 18:46 . 
drwx------ 4 osboxes osboxes 4096 jul 05 18:47 .. 
-rw------- 1 osboxes osboxes 491 jul 05 18:46 UTC--2018-07-05T18-46-07.617126785Z--3b6927fe4a4a4d44c3445292d375542cf299661c 
-rw------- 1 osboxes osboxes 491 jul 05 18:47 UTC--2018-07-05T18-47-06.750007367Z--513f15ec9fc190cbc2ac25c6d6acdb58253f80d7 
-rw------- 1 osboxes osboxes 491 jul 05 18:47 UTC--2018-07-05T18-47-20.325374470Z--5030a651df562c629cfc92928210a51d2e688d6d 
-rw------- 1 osboxes osboxes 491 jul 05 18:47 UTC--2018-07-05T18-47-35.049413162Z--00d631971be081c4de18fb6fca0c2723782299f7 

Una vez se han creado las cuentas necesarias, se define el bloque de génesis con algunas cuentas tomando como ejemplo el documento de Quorum.

A modo ilustrativo y divulgativo, se muestra la alternativa del segundo método con la creación de cuentas con un nodo activo con la interacción con la consola Javascript de geth y que requerirá el borrado de la cadena de bloques. Estos pasos son opcionales. Al iniciarse el nodo, muestra la conocida alerta de que no hay cuentas.

osboxes@osboxes:~/Desktop/giveliback$ geth --config config.toml
INFO [07-05|18:49:46] Starting peer-to-peer node instance=Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
INFO [07-05|18:49:46] Allocated cache and file handles database=/home/osboxes/Desktop/giveliback/qdata/node1/geth/chaindata cache=128 handles=1024
INFO [07-05|18:49:46] Writing default main-net genesis block 
INFO [07-05|18:49:47] Initialised chain configuration config="{ChainID: 1 Homestead: 1150000 DAO: 1920000 DAOSupport: true EIP150: 2463000 EIP155: 2675000 EIP158: 2675000 Byzantium: 4370000 IsQuorum: false Engine: ethash}"
INFO [07-05|18:49:47] Disk storage enabled for ethash caches dir=/home/osboxes/Desktop/giveliback/qdata/node1/geth/ethash count=3
INFO [07-05|18:49:47] Disk storage enabled for ethash DAGs dir=/home/osboxes/.ethash count=2
INFO [07-05|18:49:47] Initialising Ethereum protocol versions="[63 62]" network=1
INFO [07-05|18:49:47] Loaded most recent local header number=0 hash=d4e567…cb8fa3 td=17179869184
INFO [07-05|18:49:47] Loaded most recent local full block number=0 hash=d4e567…cb8fa3 td=17179869184
INFO [07-05|18:49:47] Loaded most recent local fast block number=0 hash=d4e567…cb8fa3 td=17179869184
INFO [07-05|18:49:47] Regenerated local transaction journal transactions=0 accounts=0
INFO [07-05|18:49:47] Starting P2P networking 
INFO [07-05|18:49:47] HTTP endpoint opened: http://0.0.0.0:22000 
INFO [07-05|18:49:47] RLPx listener up self="enode://7b09227fe9bd9596708e455355f6e8b183910b8275b18d56c3a105f7053688d1e9a4e39cad334f9041f0c26b16d3c5bac23c8740da55661bf15873d66ba223fe@[::]:21000?discport=0"
INFO [07-05|18:49:47] IPC endpoint opened: /home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc 

Con otro terminal se realiza una conexión al nodo activo y en marcha mediante IPC (Inter-process Communications). Dicha sesión requiere de acceso a los valores del $PATH definidos anteriormente para poder acceder a los binarios de go y geth. Se puede conectar al nodo local también por http://ip:puerto o ws://ip:puerto.

La creación de cuentas se realiza a través del módulo personal (personal.newAccount()) que recibe como dirección 0x3b6927fe4a4a4d44c3445292d375542cf299661c aunque sin ethers en dicha cuenta ni en las cuatro creadas anteriormente a la espera del bloque génesis. Para salir de la consola se usa el comando exit.

osboxes@osboxes:~$ geth attach ipc:/home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
coinbase: 0x3b6927fe4a4a4d44c3445292d375542cf299661c
at block: 0 (Thu, 01 Jan 1970 01:00:00 CET)
datadir: /home/osboxes/Desktop/giveliback/qdata/node1
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> eth.accounts
["0x3b6927fe4a4a4d44c3445292d375542cf299661c", "0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7", "0x5030a651df562c629cfc92928210a51d2e688d6d", "0x00d631971be081c4de18fb6fca0c2723782299f7"]
> personal.newAccount()
Passphrase:
Repeat passphrase:
"fafcf76cd2dfb68425475b6a42c23a7bb97ce8ba"
> eth.accounts
["0x3b6927fe4a4a4d44c3445292d375542cf299661c", "0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7", "0x5030a651df562c629cfc92928210a51d2e688d6d", "0x00d631971be081c4de18fb6fca0c2723782299f7", "fafcf76cd2dfb68425475b6a42c23a7bb97ce8ba"]
> eth.getBalance(eth.coinbase)
0
> exit
osboxes@osboxes:~$

En la versión 2 de Quorum la API para quorum ya no está disponibe y, por ende, la funcionalidad de privateFor para transacciones privadas. Es por eso que en el listado de módulos no aparece ‘quorum:1.0’ como debiera y genera un error al invocar ‘privateFor’ en las transacciones.

En este segundo método, para la generación del bloque génesis, se necesita borrar el contenido de los directorios creados de la cadena de bloque y de los nodos. Parando el servicio del nodo y borrando la base de datos, el nodo se inicializará de nuevo.

osboxes@osboxes:~/Desktop/giveliback$ ls -la qdata/node1
total 16
drwx------ 4 osboxes osboxes 4096 jul 5 18:50 .
drwx------ 12 osboxes osboxes 4096 jul 5 18:50 ..
drwx------ 3 osboxes osboxes 4096 jul 5 18:50 geth
drwx------ 2 osboxes osboxes 4096 jul 5 18:46 keystore
osboxes@osboxes:~/Desktop/giveliback$ ls -la qdata/node1/geth/
total 16
drwx------ 3 osboxes osboxes 4096 jul 5 18:46 .
drwx------ 4 osboxes osboxes 4096 jul 5 18:47 ..
drwxr-xr-x 2 osboxes osboxes 4096 jul 5 18:46 chaindata
-rw-r--r-- 1 osboxes osboxes 0 jul 3 18:46 LOCK
-rw------- 1 osboxes osboxes 64 jul 3 18:46 nodekey
-rwxr-xr-x 1 osboxes osboxes 0 jul 3 18:46 transactions.rlp
osboxes@osboxes:~/Desktop/giveliback$rm -rf qdata/node1/geth
osboxes@osboxes:~/Desktop/giveliback$

A partir de este momento, el nodo está en las mismas condiciones que si se hubiera usado sólo el primer método de creación de cuentas.

Se han creado cinco cuentas (wallets) en al directorio /node1/keystore que coinciden con los nombres de los archivos (uno por cuenta) y que se definirán en el archivo de génesis.

osboxes@osboxes:~$ ls -la /home/osboxes/Desktop/giveliback/qdata/node1/keystore/
total 28
drwx------ 2 osboxes osboxes 4096 jul 05 18:46 . 
drwx------ 4 osboxes osboxes 4096 jul 05 18:47 .. 
-rw------- 1 osboxes osboxes 491 jul 05 18:46 UTC--2018-07-05T18-46-07.617126785Z--3b6927fe4a4a4d44c3445292d375542cf299661c 
-rw------- 1 osboxes osboxes 491 jul 05 18:47 UTC--2018-07-05T18-47-06.750007367Z--513f15ec9fc190cbc2ac25c6d6acdb58253f80d7 
-rw------- 1 osboxes osboxes 491 jul 05 18:47 UTC--2018-07-05T18-47-20.325374470Z--5030a651df562c629cfc92928210a51d2e688d6d 
-rw------- 1 osboxes osboxes 491 jul 05 18:47 UTC--2018-07-05T18-47-35.049413162Z--00d631971be081c4de18fb6fca0c2723782299f7
-rw------- 1 osboxes osboxes 491 jul 05 12:42 UTC--2018-07-05T18-50-39.250076751Z--fafcf76cd2dfb68425475b6a42c23a7bb97ce8ba
Bloque génesis

Se procede a la definición del bloque de génesis con algunas cuentas tomando como ejemplo el documento de Quorum.

En el fichero genesis.json se definen las cuentas, sus balances así como la operativa de la cadena de bloques.

Al estar presentes los ficheros de las cuentas en el directorio keystore, se asignarán los fondos según el fichero de configuración. La salida se guarda en un nuevo archivo de log (node1.log).

osboxes@osboxes:~/Desktop/giveliback$ geth --datadir qdata/node1 init genesis.json 2>>qdata/logs/node1.log

El resultado de la ejecución anterior confirma la creación de bloque inicial.

INFO [07-05|18:54:18] Allocated cache and file handles database=/home/osboxes/Desktop/giveliback/qdata/node1/geth/chaindata cache=16 handles=16
INFO [07-05|18:54:18] Writing custom genesis block
INFO [07-05|18:54:18] Successfully wrote genesis state database=chaindata hash=64e3f2…8aeb0e
INFO [07-05|18:54:18] Allocated cache and file handles database=/home/osboxes/Desktop/giveliback/qdata/node1/geth/lightchaindata cache=16 handles=16
INFO [07-05|18:54:18] Writing custom genesis block
INFO [07-05|18:54:18] Successfully wrote genesis state database=lightchaindata 

Levantando el nodo de nuevo una vez se ha escrito el bloque génesis.

osboxes@osboxes:~/Desktop/giveliback$ geth --config config.toml 2>>qdata/logs/node1.log

Muestra en el log:

INFO [07-05|19:00:42] Starting peer-to-peer node instance=Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
INFO [07-05|19:00:42] Allocated cache and file handles database=/home/osboxes/Desktop/giveliback/qdata/node1/geth/chaindata cache=128 handles=1024
WARN [07-05|19:00:42] Upgrading database to use lookup entries 
INFO [07-05|19:00:42] Initialised chain configuration config="{ChainID: 10 Homestead: <nil> DAO: <nil> DAOSupport: false EIP150: 1 EIP155: 0 EIP158: 1 Byzantium: 1 IsQuorum: true Engine: unknown}"
INFO [07-05|19:00:42] Disk storage enabled for ethash caches dir=/home/osboxes/Desktop/giveliback/qdata/node1/geth/ethash count=3
INFO [07-05|19:00:42] Disk storage enabled for ethash DAGs dir=/home/osboxes/.ethash count=2
INFO [07-05|19:00:42] Initialising Ethereum protocol versions="[63 62]" network=1
INFO [07-05|19:00:42] Loaded most recent local header number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:00:42] Loaded most recent local full block number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:00:42] Loaded most recent local fast block number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:00:42] Regenerated local transaction journal transactions=0 accounts=0
INFO [07-05|19:00:42] Starting P2P networking 
INFO [07-05|19:00:42] HTTP endpoint opened: http://0.0.0.0:22000 
INFO [07-05|19:00:42] Database deduplication successful deduped=0
INFO [07-05|19:00:42] RLPx listener up self="enode://97df36859bcce4b644464f748b29dc433fa3bc142d2e00fe29834758d82576db6933ec1af1f92bbb5dcee5a5891a3124a7f32faf2657f62630ddf75bf9194ddf@[::]:21000?discport=0"
INFO [07-05|19:00:42] IPC endpoint opened: /home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc 

La cadena de bloques se ha identificado como 10 para diferenciarla del resto de redes existentes y, por lo tanto, por defecto convertirse en una red privada identificándose como Quorum y prescindiendo de muchas de las características de los nodos públicos.

Accediendo de nuevo a la consola de Javascript de geth en otro terminal, éstas no están a cero.

osboxes@osboxes:~$ geth attach ipc:/home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
coinbase: 0x3b6927fe4a4a4d44c3445292d375542cf299661c
at block: 0 (Thu, 01 Jan 1970 01:00:00 CET)
datadir: /home/osboxes/Desktop/giveliback/qdata/node1
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> eth.accounts
["0x3b6927fe4a4a4d44c3445292d375542cf299661c", "0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7", "0x5030a651df562c629cfc92928210a51d2e688d6d", "0x00d631971be081c4de18fb6fca0c2723782299f7", "0xfafcf76cd2dfb68425475b6a42c23a7bb97ce8ba"]
> eth.getBalance(eth.coinbase)
1e+27
> web3.fromWei(eth.getBalance(eth.coinbase),"ether")
1000000000

Se dispone de un nodo con cinco cuentas con suficientes fondos. Se para el nodo para realizar la configuración del consenso. En el terminal al parar el proceso con Ctrl+C emite las líneas de salida en el log.

INFO [07-05|19:01:56] Got interrupt, shutting down...
INFO [07-05|19:01:56] HTTP endpoint closed: http://0.0.0.0:22000
INFO [07-05|19:01:56] IPC endpoint closed: /home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc
INFO [07-05|19:01:56] Blockchain manager stopped
INFO [07-05|19:01:56] Stopping Ethereum protocol
INFO [07-05|19:01:56] Ethereum protocol stopped
INFO [07-05|19:01:56] Transaction pool stopped
INFO [07-05|19:01:56] Database closed database=/home/osboxes/Desktop/giveliback/qdata/node1/geth/chaindata
Consenso

Quorum permite la configuración del consenso basándose en RAFT como Istanbul BFT.

En los despliegues con RAFT uno de los nodos de la red se elige como nodo líder. El nodo líder es el que gestiona las peticiones y coordina al resto de nodos. En caso de que no esté disponible el nodo líder, se elige otro de entre los nodos disponibles. Como ventajas, RAFT permite la creación de bloques bajo demanda y aumenta la velocidad de generación de bloques al estar enfocado a trnsacciones.

Istanbul BFT (Istanbul byzantine fault tolerant consensus protocol),realizado por AMIS, está inspirado en PBFT (Practical byzantine fault tolerant consensus protocol) de Miguel Castro y Barbara Liskov y también está enfocado a transacciones.

Los nodos permisionados serán aquellos que tengan acceso a la red y se comuniquen con sus nodos. Se dispone del primer nodo, con lo que sería posible editar el archivo permissioned-nodes.json para asignarle los valores del nodo. Ese mismo archivo es el que se asignará al nodo (node1) como static-nodes.json para el consenso basado en RAFT.

La identificación del nodo es

enode://97df36859bcce4b644464f748b29dc433fa3bc142d2e00fe29834758d82576db6933ec1af1f92bbb5dcee5a5891a3124a7f32faf2657f62630ddf75bf9194ddf@[::]:21000&discport=0

Esa identificación del nodo será usada para la gestión de nodos conformantes de la red. El listado de nodos contempla actualmente sólo el que se ha configurado en el array añadiéndole el puerto dónde se comunicará por RAFT (&raftport=50401).

[
"enode://97df36859bcce4b644464f748b29dc433fa3bc142d2e00fe29834758d82576db6933ec1af1f92bbb5dcee5a5891a3124a7f32faf2657f62630ddf75bf9194ddf@0.0.0.0:21000?discport=0&raftport=50401"
]

Se incluye este array en el archivo permissioned-nodes.json. Se pone una copia de permissioned-nodes.json y de static-nodes.json antes de la ejecución del nodo con RAFT. Los parámetros que se le añaden a la ejecución del nodo son relativos a la configuración de RAFT.

osboxes@osboxes:~/Desktop/giveliback$ cp permissioned-nodes.json qdata/node1/static-nodes.json
osboxes@osboxes:~/Desktop/giveliback$ cp permissioned-nodes.json qdata/node1
osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node1 --raft --emitcheckpoints --raftport 50401 --unlock 0 --password password.txt --config config.toml 2>>qdata/logs/node1.log

Al iniciar de nuevo el nodo con la configuración RAFT, éste se convierte en MINTER al no haber más nodos y finalmente desbloquea la primera cuenta.

INFO [07-05|19:09:28] Starting peer-to-peer node               instance=Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
INFO [07-05|19:09:28] Allocated cache and file handles         database=/home/osboxes/Desktop/giveliback/qdata/node1/geth/chaindata cache=128 handles=1024
INFO [07-05|19:09:28] Initialised chain configuration          config="{ChainID: 10 Homestead:  DAO:  DAOSupport: false EIP150: 1 EIP155: 0 EIP158: 1 Byzantium: 1 IsQuorum: true Engine: unknown}"
INFO [07-05|19:09:28] Disk storage enabled for ethash caches   dir=/home/osboxes/Desktop/giveliback/qdata/node1/geth/ethash count=3
INFO [07-05|19:09:28] Disk storage enabled for ethash DAGs     dir=/home/osboxes/.ethash                                    count=2
INFO [07-05|19:09:28] Initialising Ethereum protocol           versions="[63 62]" network=1
INFO [07-05|19:09:28] Loaded most recent local header          number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:09:28] Loaded most recent local full block      number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:09:28] Loaded most recent local fast block      number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:09:28] Loaded local transaction journal         transactions=0 dropped=0
INFO [07-05|19:09:28] Regenerated local transaction journal    transactions=0 accounts=0
INFO [07-05|19:09:28] Starting P2P networking 
INFO [07-05|19:09:28] starting raft protocol handler 
INFO [07-05|19:09:28] loaded the latest applied index          lastAppliedIndex=0
INFO [07-05|19:09:28] replaying WAL 
INFO [07-05|19:09:28] RLPx listener up                         self="enode://97df36859bcce4b644464f748b29dc433fa3bc142d2e00fe29834758d82576db6933ec1af1f92bbb5dcee5a5891a3124a7f32faf2657f62630ddf75bf9194ddf@[::]:21000?discport=0"
INFO [07-05|19:09:28] loading WAL                              term=0 index=0
INFO [07-05|19:09:28] startRaft                                raft ID=1
INFO [07-05|19:09:28] starting a new raft log                  initial cluster size of=1
raft2018/07/05 19:09:28 INFO: 1 became follower at term 0
raft2018/07/05 19:09:28 INFO: newRaft 1 [peers: [], term: 0, commit: 0, applied: 0, lastindex: 0, lastterm: 0]
raft2018/07/05 19:09:28 INFO: 1 became follower at term 1
INFO [07-05|19:09:28] IPC endpoint opened: /home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc 
INFO [07-05|19:09:28] HTTP endpoint opened: http://0.0.0.0:22000 
INFO [07-05|19:09:28] ignoring expected ConfChangeAddNode for initial peer raft id=1
INFO [07-05|19:09:28] start snapshot                           applied index=0 last snapshot index=0
INFO [07-05|19:09:28] compacted log                            index=0
INFO [07-05|19:09:28] persisted the latest applied index       index=1
raft2018/07/05 19:09:29 INFO: 1 is starting a new election at term 1
raft2018/07/05 19:09:29 INFO: 1 became candidate at term 2
raft2018/07/05 19:09:29 INFO: 1 received MsgVoteResp from 1 at term 2
raft2018/07/05 19:09:29 INFO: 1 became leader at term 2
raft2018/07/05 19:09:29 INFO: raft.node: 1 elected leader 1 at term 2
INFO [07-05|19:09:29] persisted the latest applied index       index=2
INFO [07-05|19:09:29] QUORUM-CHECKPOINT                        name=BECAME-MINTER
INFO [07-05|19:09:29] Not minting a new block since there are no pending transactions 
INFO [07-05|19:09:37] Unlocked account                         address=0x3b6927fe4A4A4D44C3445292d375542CF299661C

En otro terminal se observa que no hay otros nodos conectados mediante admin.peers.

osboxes@osboxes:~$ geth attach ipc:/home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
coinbase: 0x3b6927fe4a4a4d44c3445292d375542cf299661c
at block: 0 (Thu, 01 Jan 1970 01:00:00 CET)
datadir: /home/osboxes/Desktop/giveliback/qdata/node1
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 raft:1.0 rpc:1.0 txpool:1.0 web3:1.0
> admin.peers
[]
Constelaciones

Las constelaciones son gestores de transacciones cuya función principal es implementar el «motor de privacidad» en Quorum. Está escrito en Haskell, lanza un daemon o servicio que implementa una red P2P. Descrubre automáticamente otros nodos en la red después de sincronizar con otro host. Sincroniza un directorio de claves públicas asignadas a los hosts receptores con otros nodos en la red para asegurar la privacidad. Expone una API pública que permite a otros nodos enviar cadenas de bytes encriptadas a su nodo y sincronizar, recuperando información sobre los nodos que su nodo conoce. También expone una API privada que permite el cifrado de la información a transmitir. Admite backends de almacenamiento como LevelDB, BerkeleyDB y SQLite.

Quorum Constellation Diagram
Quorum Constellation Diagram – Credit Quorum

Los pasos siguientes, pues, son los de instalación y configuración de la constelación. Para Ubuntu se necista la presencia de varias aplicaciones. Las constelaciones compiladas están publicadas en el repositorio, siendo la más reciente la 0.3.2.. Una vez descomprimida, los binarios se copian al directorio quorum_bin junto con geth, bootnode y demás. La generación de las claves (pública y privada) requiere de una contraseña que se puede dejar vacía. En este caso es ‘password’ igual que la usada en las cuentas y disponible en password.txt. En caso de haber usado una contraseña, es posible que la ejecución se demore.

Las claves se colocan en el directorio de ejecución de la constelación del nodo1 (qdata/node1) y deben protegerse contra accesos no deseados. Se hace lo mismo con la contraseña usada (password.txt) para su desbloqueo al instanciar la constelación.

Para levantar la constelación, se pueden pasar los parámetros en el mismo comando o bien usando un archivo de configuración con una estructura como la del ejemplo de Quorum.

osboxes@osboxes:~/Desktop/giveliback$ apt-get install libdb-dev libleveldb-dev libsodium-dev zlib1g-dev libtinfo-dev
...
osboxes@osboxes:~/Desktop/giveliback$ wget https://github.com/jpmorganchase/constellation/releases/download/v0.3.2/constellation-0.3.2-ubuntu1604.tar.xz ... (giveliback) osboxes@osboxes:~/Desktop/giveliback$ tar -xf constellation-0.3.2-ubuntu1604.tar.xz (giveliback) osboxes@osboxes:~/Desktop/giveliback$ cp constellation-0.3.2-ubuntu1604/constellation-node quorum_bin
osboxes@osboxes:~/Desktop/giveliback$ tar -xf constellation-0.3.2-ubuntu1604.tar.xz
osboxes@osboxes:~/Desktop/giveliback$ cp constellation-0.3.2-ubuntu1604/constellation-node quorum_bin
osboxes@osboxes:~/Desktop/giveliback$ cd qdata/node1 
osboxes@osboxes:~/Desktop/giveliback/qdata/node1$ constellation-node --generatekeys=node
Lock key pair node with password [none]: ********
osboxes@osboxes:~/Desktop/giveliback/qdata/node1$ ls -la node.*
-rw------- 1 osboxes osboxes 293 jul 05 19:11 node.key
-rw-rw-r-- 1 osboxes osboxes  44 jul 05 19:09 node.pub
osboxes@osboxes:~/Desktop/giveliback/qdata/node1$ head node.key 
{"data":{"aopts":{"variant":"id","memory":1048576,"iterations":10,"parallelism":4,"version":"1.3"},"snonce":"0RpcpqkhDQr1KB/x0TLSKdt+mQhZoONR","asalt":"y9zT+03Ho3T3QLm/oQpPHhwO9mCxTsLe2BLGjvanhas=","sbox":"U63457+8Xx8OenKl4w9pfU6Vopynhdy1NQFGo1ImZPb7DLRD+I9/WuFoXDz9O7w+"},"type":"argon2sbox"}
osboxes@osboxes:~/Desktop/giveliback/qdata/node1$ head node.pub 
soPCt8hAExYRgUouXLy59SiYGgxP3vj670PiAYx/wF4=
osboxes@osboxes:~/Desktop/giveliback/qdata/node1$ chmod 600 node.key
osboxes@osboxes:~/Desktop/giveliback/qdata/node1$ cd ../..
osboxes@osboxes:~/Desktop/giveliback$ cp password.txt qdata/node1
osboxes@osboxes:~/Desktop/giveliback$ constellation-node --url=https://127.0.0.1:9001/ --port=9001 --workdir=qdata/node1 --socket=constellation.ipc --publickeys=node.pub --privatekeys=node.key --othernodes=https://127.0.0.1:9001/ --password=password.txt 2>>qdata/logs/constellation1.log
Unlocking node.key
Unlocked node.key

Se debe instanciar la constelación en cada nodo y en este caso, el nodo 1 será el nodo de referencia. De igual manera que en la creación de las claves, la liberación de la clave puede tardar unos segundos.

En el archivo del log se aprecia que está en marcha y que ha generado certificados TLS.

19:10:40 [INFO] Log level is LevelWarn
19:11:53 [WARN] No TLS certificate or key found; generating tls-client-cert.pem/tls-client-key.pem
19:11:55 [WARN] No TLS certificate or key found; generating tls-server-cert.pem/tls-server-key.pem

Se deben proteger también estos archivos para otros usuarios así como se ha hecho con la llave del nodo.

osboxes@osboxes:~/Desktop/giveliback$ chmod 600 qdata/node1/*.pem

En el directorio de la constelación del nodo 1 se aprecian los archivos creados para los certificados TLS así como el ipc para la conexión del mismo.

osboxes@osboxes:~/Desktop/giveliback$ ls -la qdata/node1/
total 44
drwxrwxr-x 3 osboxes osboxes 4096 jul 05 19:10 .
drwx------ 6 osboxes osboxes 4096 jul 05 19:13 ..
...
srw------- 1 osboxes osboxes 0 jul 05 19:11 constellation.ipc
-rw------- 1 osboxes osboxes 293 jul 05 19:11 node.key
-rw-rw-r-- 1 osboxes osboxes 44 jul 05 19:11 node.pub
drwxrwxr-x 3 osboxes osboxes 4096 jul 05 19:11 storage
-rw-rw-r-- 1 osboxes osboxes 1887 jul 05 19:12 tls-client-cert.pem
-rw------- 1 osboxes osboxes 3243 jul 05 19:12 tls-client-key.pem
-rw-rw-r-- 1 osboxes osboxes 19 jul 05 19:11 tls-known-clients
-rw-rw-r-- 1 osboxes osboxes 19 jul 05 19:11 tls-known-servers
-rw-rw-r-- 1 osboxes osboxes 1887 jul 05 19:12 tls-server-cert.pem
-rw------- 1 osboxes osboxes 3243 jul 05 19:12 tls-server-key.pem

Levantando el nodo1 estaría la configuración completa del nodo. Para poder comprobar el correcto funcionamiento de la red Quorum, se crean un par de nodos más siguiendo las instrucciones anteriores. Todos los nodos compartirán cierta información como son las cuentas, los nodos permitidos y los nodos estáticos. Esos ficheros de configuración, pues, deberán estar presentes en todas las instancias. Las cuentas (wallets) y certificados, serán exclusivos de cada nodo y por lo tanto generados exprofeso para cada uno. Así pues, las cuentas existentes en el bloque genesis estarán físicamente en diferentes nodos.

Nodo 2

Para la configuración del nodo 2, casi se replican los mismos comandos anteriores.

Se crea el directorio del nodo 2 con la configuración del mismo. Se mueven los wallets que se quieren relacionar con el nodo 2 (0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7 en este caso).

Se genera la configuración con los puertos para el nodo 2 (RPC 22001 y http 21001) en el archivo config_2.toml.

osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node2 --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --permissioned --nodiscover --rpcport 22001 --port 21001 dumpconfig > config_2.toml
WARN [07-05|19:20:49] No etherbase set and no accounts found as default 
osboxes@osboxes:~/Desktop/giveliback$ 
osboxes@osboxes:~/Desktop/giveliback$ cp qdata/node1/keystore/UTC--2018-07-05T18-47-06.750007367Z--513f15ec9fc190cbc2ac25c6d6acdb58253f80d7 qdata/node2/keystore
osboxes@osboxes:~/Desktop/giveliback$ geth --datadir qdata/node2 init genesis.json 2>>qdata/logs/node2.log

Se genera el bloque génesis del nodo 2. Al generar el bloque de génesis, se asignará como etherbase la cuenta migrada.

Etherbase = "0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7"
DiscoveryV5Addr = ":21002"

Para instanciar la constelación del nodo 2, se deben generar las claves, proteger los permisos que los ficheros tienen, copiar el archivo de contraseña y finalmente inicializar el servicio.

osboxes@osboxes:~/Desktop/giveliback$ cd qdata/node2
osboxes@osboxes:~/Desktop/giveliback/qdata/node2$ constellation-node --generatekeys=node
Lock key pair node with password [none]: ********
osboxes@osboxes:~/Desktop/giveliback/qdata/node2$ chmod 600 node.key
osboxes@osboxes:~/Desktop/giveliback/qdata/node2$ cd ../..
osboxes@osboxes:~/Desktop/giveliback$ cp password.txt qdata/node2
osboxes@osboxes:~/Desktop/giveliback$ constellation-node --url=https://127.0.0.1:9002/ --port=9002 --workdir=qdata/node2 --socket=constellation.ipc --publickeys=node.pub --privatekeys=node.key --othernodes=https://127.0.0.1:9001/ --password=password.txt 2>>qdata/logs/constellation2.log

En el log de constellation1.log aparece el nuevo nodo y se registra.

19:21:45 [WARN] tls-known-clients (ca-or-tofu trust mode): Adding new fingerprint "00:08:88:6C:BE:43:20:34:0D:28:16:E8:4C:5E:20:E7:19:19:AE:E7:B9:AE:CE:94:C6:8E:2F:80:77:98:6E:36:00:1D:B1:54:29:FE:01:86:34:73:3B:49:80:C1:AB:28:FF:6F:DF:81:95:1B:3D:B9:87:7C:AE:F7:96:AE:13:33" for host 127.0.0.1
19:21:51 [WARN] tls-known-servers (ca-or-tofu trust mode): Adding new fingerprint "D0:2A:D9:74:2C:39:2A:52:16:D2:54:9E:96:6E:E5:31:B0:41:58:F8:91:68:01:85:50:42:33:BE:EE:2C:DC:15:C9:43:8D:B4:CD:5B:D9:32:41:CB:D6:41:E7:B7:40:56:27:DB:54:BD:36:88:83:C3:43:6D:CD:E2:1B:6F:5D:7E" for host 127.0.0.1

En el log de constellation2.log se detalla el mismo comportamiento.

19:21:21 [INFO] Log level is LevelWarn
19:21:41 [WARN] No TLS certificate or key found; generating tls-client-cert.pem/tls-client-key.pem
19:21:42 [WARN] No TLS certificate or key found; generating tls-server-cert.pem/tls-server-key.pem
19:21:45 [WARN] tls-known-servers (ca-or-tofu trust mode): Adding new fingerprint "25:73:EF:68:77:14:4E:75:E5:AC:1E:B9:BA:A5:0D:8D:17:9F:85:BC:24:E3:A3:9F:23:06:27:D0:AA:D8:45:83:32:2C:55:B5:78:AA:CA:FA:4F:BB:5C:46:03:7C:EE:1B:69:4B:7F:2C:27:EB:6C:71:37:DD:DC:2E:9C:2D:2A:89" for host 127.0.0.1
19:21:51 [WARN] tls-known-clients (ca-or-tofu trust mode): Adding new fingerprint "25:9C:FE:7F:FA:37:65:33:EA:F6:3D:4A:92:32:34:B6:A6:56:4B:66:F8:D8:78:D7:91:18:25:A8:12:E4:D2:D2:9B:67:09:D6:EB:23:38:A3:61:73:60:FD:59:FF:DA:15:F3:FF:60:8E:75:59:8A:F8:EF:9C:82:1F:ED:47:04:7F" for host 127.0.0.1

Los archivos creados deberán protegerse de la misma forma que en el nodo 1.

osboxes@osboxes:~/Desktop/giveliback$ chmod 600 qdata/node2/*.pem

Para conectar el segundo nodo al consenso RAFT se puede editar el archivo de los nodos permisionados y los estáticos y desplegar dichos en todos los directorios de los nodos para aceptar la inclusión del nodo 2.

De momento no hay forma trivial de identificar el hash del nodo mirando archivos de configuración o log o hasta el bloque génesis y sin instanciarlo. Se instancia el nodo sin archivos de static-nodes.json ni permissioned-nodes.json como circumloquio para obtener el hash del enode del segundo nodo que posteriormente se añadirá a sendos archivos.

osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node2 --raft --emitcheckpoints --raftport 50402 --unlock 0 --password password.txt --config config_2.toml 2>>qdata/logs/node2.log
Fatal: Raft-based consensus requires either (1) an initial peers list (in static-nodes.json) including this enode hash (99f8c274e8d7600d2fe687e6aeafff9ae96cea0c2b9d8b8c3f6a1b5356f4256c607ef3ee0a511b7fe6105b13461844dbc1f0519667a34b4d7249f65436fea767), or (2) the flag --raftjoinexisting RAFT_ID, where RAFT_ID has been issued by an existing cluster member calling `raft.addPeer(ENODE_ID)` with an enode ID containing this node's enode hash.

Los archivos permissioned-nodes.json y static-nodes.json tendrían un array con los dos nodos. Al instanciar los nodos debe añadirse el parámetro –permissioned para que se base en ese listado para la configuración de los nodos conectados.

[
"enode://97df36859bcce4b644464f748b29dc433fa3bc142d2e00fe29834758d82576db6933ec1af1f92bbb5dcee5a5891a3124a7f32faf2657f62630ddf75bf9194ddf@0.0.0.0:21000?discport=0&raftport=50401",
"enode://99f8c274e8d7600d2fe687e6aeafff9ae96cea0c2b9d8b8c3f6a1b5356f4256c607ef3ee0a511b7fe6105b13461844dbc1f0519667a34b4d7249f65436fea767@0.0.0.0:21001?discport=0&raftport=50402"
]

Existe otra opción que es más elegante y versátil. Desde uno de los nodos de la red (node1 en este caso) se añade el nodo 2 como peer desde la consola del nodo 1 y apuntar el nodo 2 con –raftjoinexisting 2 (que es el ID del nodo 2 en la red).

osboxes@osboxes:~/Desktop/giveliback$ geth attach ipc:/home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-f3d13152/linux-amd64/go1.10
coinbase: 0x3b6927fe4a4a4d44c3445292d375542cf299661c
at block: 0 (Thu, 01 Jan 1970 01:00:00 CET)
datadir: /home/osboxes/Desktop/giveliback/qdata/node1
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 raft:1.0 rpc:1.0 txpool:1.0 web3:1.0

> raft.addPeer("enode://0325ca376f24c195bf02cc5360124d887f5c0cf0372f4bccf53c824ff44c126bba1cc18ced369e7bf03c760d9ab66b28b7c27b159779e0cc06106e0a618045cf@0.0.0.0:21001?discport=0&raftport=50402")
2

Se levanta el nodo 2 pasando como parámetro el ID=2 recientemente asignado.

osboxes@osboxes:~/Desktop/giveliback$ geth --datadir=qdata/node2 --raft --raftjoinexisting 2 --emitcheckpoints --raftport 50402 --unlock 0 --password password.txt --config config_2.toml 2>>qdata/logs/node2.log

En el log del nodo 1 se aprecia la inclusión del nuevo peer y cuando éste ha sido levantado.

INFO [07-05|19:38:02] adding peer due to ConfChangeAddNode raft id=2
2018-07-05 19:38:02.099788 I | rafthttp: starting peer 2...
2018-07-05 19:38:02.100602 I | rafthttp: started HTTP pipelining with peer 2
2018-07-05 19:38:02.108672 I | rafthttp: started streaming with peer 2 (writer)
2018-07-05 19:38:02.119002 I | rafthttp: started peer 2
2018-07-05 19:38:02.119507 I | rafthttp: added peer 2
INFO [07-05|19:38:02] start snapshot applied index=2 last snapshot index=1
INFO [07-05|19:38:02] compacted log index=2
INFO [07-05|19:38:02] persisted the latest applied index index=3
2018-07-05 19:38:02.127878 I | rafthttp: started streaming with peer 2 (writer)
2018-07-05 19:38:02.128677 I | rafthttp: started streaming with peer 2 (stream MsgApp v2 reader)
2018-07-05 19:38:02.130745 I | rafthttp: started streaming with peer 2 (stream Message reader)
INFO [07-05|19:38:02] peer is currently unreachable peer id=2
... 
INFO [07-05|19:38:46] peer is currently unreachable peer id=2
2018-07-05 19:38:46.153732 I | rafthttp: peer 2 became active
2018-07-05 19:38:46.155193 E | rafthttp: failed to dial 2 on stream Message (peer 2 failed to find local node 1)
2018-07-05 19:38:46.155208 I | rafthttp: peer 2 became inactive
2018-07-05 19:38:46.218670 I | rafthttp: peer 2 became active
2018-07-05 19:38:46.280411 E | rafthttp: failed to dial 2 on stream Message (peer 2 failed to find local node 1)
2018-07-05 19:38:46.280443 I | rafthttp: peer 2 became inactive
2018-07-05 19:38:46.301582 I | rafthttp: peer 2 became active
INFO [07-05|19:38:46] finished sending snapshot raft peer=2
2018-07-05 19:38:46.411213 I | rafthttp: established a TCP streaming connection with peer 2 (stream Message writer)
2018-07-05 19:38:46.419697 I | rafthttp: established a TCP streaming connection with peer 2 (stream MsgApp v2 writer)
2018-07-05 19:38:46.419970 I | rafthttp: established a TCP streaming connection with peer 2 (stream MsgApp v2 reader)
2018-07-05 19:38:46.424318 I | rafthttp: established a TCP streaming connection with peer 2 (stream Message reader)
INFO [07-05|19:39:28] Regenerated local transaction journal transactions=0 accounts=0

En el log del nodo 2 se identifica al mismo como VERIFIER y conectado con otro nodo.

2018-07-05 19:38:45] Starting peer-to-peer node               instance=Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
INFO [07-05|19:38:45] Allocated cache and file handles         database=/home/osboxes/Desktop/giveliback/qdata/node2/geth/chaindata cache=128 handles=1024
WARN [07-05|19:38:46] Upgrading database to use lookup entries 
INFO [07-05|19:38:46] Initialised chain configuration          config="{ChainID: 10 Homestead:  DAO:  DAOSupport: false EIP150: 1 EIP155: 0 EIP158: 1 Byzantium: 1 IsQuorum: true Engine: unknown}"
INFO [07-05|19:38:46] Disk storage enabled for ethash caches   dir=/home/osboxes/Desktop/giveliback/qdata/node2/geth/ethash count=3
INFO [07-05|19:38:46] Disk storage enabled for ethash DAGs     dir=/home/osboxes/.ethash                                    count=2
INFO [07-05|19:38:46] Initialising Ethereum protocol           versions="[63 62]" network=1
INFO [07-05|19:38:46] Loaded most recent local header          number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:38:46] Loaded most recent local full block      number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:38:46] Loaded most recent local fast block      number=0 hash=64e3f2…8aeb0e td=0
INFO [07-05|19:38:46] Regenerated local transaction journal    transactions=0 accounts=0
INFO [07-05|19:38:46] Database deduplication successful        deduped=0
INFO [07-05|19:38:46] Starting P2P networking 
INFO [07-05|19:38:46] starting raft protocol handler 
INFO [07-05|19:38:46] RLPx listener up                         self="enode://99f8c274e8d7600d2fe687e6aeafff9ae96cea0c2b9d8b8c3f6a1b5356f4256c607ef3ee0a511b7fe6105b13461844dbc1f0519667a34b4d7249f65436fea767@[::]:21001?discport=0"
INFO [07-05|19:38:46] loaded the latest applied index          lastAppliedIndex=0
INFO [07-05|19:38:46] replaying WAL 
INFO [07-05|19:38:46] loading WAL                              term=0 index=0
INFO [07-05|19:38:46] startRaft                                raft ID=2
INFO [07-05|19:38:46] newly joining an existing cluster; waiting for connections. 
raft2018/07/05 19:38:46 INFO: 2 became follower at term 0
raft2018/07/05 19:38:46 INFO: newRaft 2 [peers: [], term: 0, commit: 0, applied: 0, lastindex: 0, lastterm: 0]
raft2018/07/05 19:38:46 INFO: 2 became follower at term 1
INFO [07-05|19:38:46] HTTP endpoint opened: http://0.0.0.0:22001 
INFO [07-05|19:38:46] IPC endpoint opened: /home/osboxes/Desktop/giveliback/qdata/node2/geth.ipc 
raft2018/07/05 19:38:46 INFO: 2 [term: 1] received a MsgHeartbeat message with higher term from 1 [term: 2]
raft2018/07/05 19:38:46 INFO: 2 became follower at term 2
raft2018/07/05 19:38:46 INFO: raft.node: 2 elected leader 1 at term 2
2018-07-05 19:38:46.154922 I | rafthttp: started HTTP pipelining with peer 1
2018-07-05 19:38:46.154972 E | rafthttp: failed to find member 1 in cluster 1000
2018-07-05 19:38:46.155306 E | rafthttp: failed to find member 1 in cluster 1000
INFO [07-05|19:38:46] QUORUM-CHECKPOINT                        name=BECAME-VERIFIER
2018-07-05 19:38:46.279462 E | rafthttp: failed to find member 1 in cluster 1000
2018-07-05 19:38:46.281517 E | rafthttp: failed to find member 1 in cluster 1000
2018-07-05 19:38:46.299226 I | rafthttp: peer 1 became active
raft2018/07/05 19:38:46 INFO: 2 [commit: 0, lastindex: 0, lastterm: 0] starts to restore snapshot [index: 3, term: 2]
raft2018/07/05 19:38:46 INFO: log [committed=0, applied=0, unstable.offset=1, len(unstable.Entries)=0] starts to restore snapshot [index: 3, term: 2]
raft2018/07/05 19:38:46 INFO: 2 restored progress of 1 [next = 4, match = 0, state = ProgressStateProbe, waiting = false, pendingSnapshot = 0]
raft2018/07/05 19:38:46 INFO: 2 restored progress of 2 [next = 4, match = 3, state = ProgressStateProbe, waiting = false, pendingSnapshot = 0]
raft2018/07/05 19:38:46 INFO: 2 [commit: 3] restored snapshot [index: 3, term: 2]
INFO [07-05|19:38:46] applying snapshot to raft storage 
INFO [07-05|19:38:46] updating cluster membership per raft snapshot 
INFO [07-05|19:38:46] adding new raft peer                     raft id=1
2018-07-05 19:38:46.343774 I | rafthttp: starting peer 1...
2018-07-05 19:38:46.343800 I | rafthttp: started HTTP pipelining with peer 1
2018-07-05 19:38:46.348731 I | rafthttp: started peer 1
2018-07-05 19:38:46.349384 I | rafthttp: added peer 1
INFO [07-05|19:38:46] updated cluster membership 
INFO [07-05|19:38:46] blockchain is caught up; no need to synchronize 
INFO [07-05|19:38:46] persisted the latest applied index       index=3
2018-07-05 19:38:46.370742 I | rafthttp: started streaming with peer 1 (writer)
2018-07-05 19:38:46.371952 I | rafthttp: started streaming with peer 1 (writer)
2018-07-05 19:38:46.372963 I | rafthttp: started streaming with peer 1 (stream MsgApp v2 reader)
2018-07-05 19:38:46.374011 I | rafthttp: started streaming with peer 1 (stream Message reader)
2018-07-05 19:38:46.391210 I | rafthttp: peer 1 became active
2018-07-05 19:38:46.414950 I | rafthttp: established a TCP streaming connection with peer 1 (stream Message writer)
2018-07-05 19:38:46.417773 I | rafthttp: established a TCP streaming connection with peer 1 (stream MsgApp v2 writer)
2018-07-05 19:38:46.443306 I | rafthttp: established a TCP streaming connection with peer 1 (stream MsgApp v2 reader)
2018-07-05 19:38:46.445711 I | rafthttp: established a TCP streaming connection with peer 1 (stream Message reader)
INFO [07-05|19:38:47] Unlocked account                         address=0x513F15Ec9fc190cBc2Ac25C6D6aCDB58253F80d7

Yendo al nodo 1, se observa que el nodo 2 se ha conectado

osboxes@osboxes:~/Desktop/giveliback$ geth attach ipc:/home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-f3d13152/linux-amd64/go1.10
coinbase: 0x15163b14667e705591f78b67a72cf3357b4b3d0d
at block: 0 (Thu, 01 Jan 1970 01:00:00 CET)
datadir: /home/osboxes/Desktop/giveliback/qdata/node1
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 raft:1.0 rpc:1.0 txpool:1.0 web3:1.0

> admin.peers
[{
    caps: ["eth/63"],
    id: "99f8c274e8d7600d2fe687e6aeafff9ae96cea0c2b9d8b8c3f6a1b5356f4256c607ef3ee0a511b7fe6105b13461844dbc1f0519667a34b4d7249f65436fea767",
    name: "Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10",
    network: {
      localAddress: "127.0.0.1:21000",
      remoteAddress: "127.0.0.1:57834"
    },
    protocols: {
      eth: {
        difficulty: 0,
        head: "0x64e3f2590db3fc0100e0b4841ccb97e3bc166d644e84245e613b7557448aeb0e",
        version: 63
      }
    }
}]

De la misma manera, si se conecta al nodo 2 se ve el nodo 1 conectado.

osboxes@osboxes:~/Desktop/giveliback$ geth attach ipc:/home/osboxes/Desktop/giveliback/qdata/node2/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
coinbase: 0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7
at block: 0 (Thu, 01 Jan 1970 01:00:00 CET)
datadir: /home/osboxes/Desktop/giveliback/qdata/node2
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 raft:1.0 rpc:1.0 txpool:1.0 web3:1.0

> admin.peers
[{
caps: ["eth/63"],
id: "97df36859bcce4b644464f748b29dc433fa3bc142d2e00fe29834758d82576db6933ec1af1f92bbb5dcee5a5891a3124a7f32faf2657f62630ddf75bf9194ddf",
name: "Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10",
network: {
localAddress: "127.0.0.1:57834",
remoteAddress: "127.0.0.1:21000"
},
protocols: {
eth: {
difficulty: 0,
head: "0x64e3f2590db3fc0100e0b4841ccb97e3bc166d644e84245e613b7557448aeb0e",
version: 63
}
}
}]
> 

A partir de éste momento, la información de los nodos participantes en el cluster está almacenado aunque en caso de que los nodos añadidos se conviertan en permanentes, se recomienda incluirlos en static-nodes.json y permissioned-nodes.json y cargar los nodos. Es también el método usado a la hora de compartir repositorios.

A modo de prueba desde el nodo 2 se ejecuta una transacción de prueba.

> eth.sendTransaction({from:eth.accounts[0]})
"0xc7a740952de31312abf06d217bf9da85019801c386524877d7cdbdb6e52f2af8"

En el log del nodo 2 se registra la petición de la transacción.

INFO [07-05|19:42:05] Submitted contract creation              fullhash=0xc7a740952de31312abf06d217bf9da85019801c386524877d7cdbdb6e52f2af8 to=0x584Df819e9C1C5ce6bD95Fffa67Ef0CEAD7E3d7c
INFO [07-05|19:42:05] QUORUM-CHECKPOINT                        name=TX-CREATED      tx=0xc7a740952de31312abf06d217bf9da85019801c386524877d7cdbdb6e52f2af8 to=0x584Df819e9C1C5ce6bD95Fffa67Ef0CEAD7E3d7c
INFO [07-05|19:42:05] QUORUM-CHECKPOINT                        name=TX-ACCEPTED     tx=0xc7a740952de31312abf06d217bf9da85019801c386524877d7cdbdb6e52f2af8
INFO [07-05|19:42:08] Imported new chain segment               blocks=1 txs=1 mgas=0.021 elapsed=2.985s mgasps=0.007 number=1 hash=201e8a…e85183
INFO [07-05|19:42:08] QUORUM-CHECKPOINT                        name=BLOCK-CREATED   block=201e8a4a2d311bf1a1524ce18b965b284a66b38cdf8c6783815733c3b5e85183
INFO [07-05|19:42:08] persisted the latest applied index       index=4
INFO [07-05|19:42:11] Generating ethash verification cache     epoch=1 percentage=83 elapsed=3.025s
INFO [07-05|19:42:12] Generated ethash verification cache      epoch=1 elapsed=3.531s

En el log del nodo 1 se observa que el nodo 1 es el que ha ejecutado la transacción minándola.

INFO [07-05|19:42:05] Generated next block block num=1 num txes=1
INFO [07-05|19:42:05] 🔨 Mined block number=1 hash=201e8a4a elapsed=2.818467ms
INFO [07-05|19:42:05] QUORUM-CHECKPOINT name=TX-ACCEPTED tx=0xc7a740952de31312abf06d217bf9da85019801c386524877d7cdbdb6e52f2af8
INFO [07-05|19:42:08] Imported new chain segment blocks=1 txs=1 mgas=0.021 elapsed=3.029s mgasps=0.007 number=1 hash=201e8a…e85183
INFO [07-05|19:42:08] QUORUM-CHECKPOINT name=BLOCK-CREATED block=201e8a4a2d311bf1a1524ce18b965b284a66b38cdf8c6783815733c3b5e85183
INFO [07-05|19:42:08] persisted the latest applied index index=4
INFO [07-05|19:42:08] Not minting a new block since there are no pending transactions 
INFO [07-05|19:42:11] Generating ethash verification cache epoch=1 percentage=84 elapsed=3.024s
INFO [07-05|19:42:12] Generated ethash verification cache epoch=1 elapsed=3.510s

En este momento se dispone de una red de dos nodos: nodo 1 (MINTER) y nodo 2 (VERIFIER).

Otra de las pruebas que se puede realizar es la de envío de fondos de una cuenta a otra estando las cuentas en nodos distintos.

osboxes@osboxes:~/Desktop/giveliback$ geth attach ipc:/home/osboxes/Desktop/giveliback/qdata/node1/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
coinbase: 0x3b6927fe4a4a4d44c3445292d375542cf299661c
at block: 5 (Sat, 29 Sep 48500222153 09:51:11 CET)
datadir: /home/osboxes/Desktop/giveliback/qdata/node1
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 raft:1.0 rpc:1.0 txpool:1.0 web3:1.0
> web3.fromWei(eth.getBalance(eth.accounts[0]),"ether")
1000000000
> eth.sendTransaction({from:eth.accounts[0], to: "0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7", value: web3.toWei(100000,"ether")})
"0x7e5b617a1628e2a9b0f5dcb19b8e828d6ce7f7e4ef31e507f7ef5a648bba1910"
> eth.getTransaction("0x7e5b617a1628e2a9b0f5dcb19b8e828d6ce7f7e4ef31e507f7ef5a648bba1910")
{
blockHash: "0x7fefc51f212466992159718149732a545cb7546b1b234737583842e6eb981789",
blockNumber: 2,
from: "0x3b6927fe4a4a4d44c3445292d375542cf299661c",
gas: 90000,
gasPrice: 0,
hash: "0x7e5b617a1628e2a9b0f5dcb19b8e828d6ce7f7e4ef31e507f7ef5a648bba1910",
input: "0x",
nonce: 0,
r: "0xde03cc9388eaa5121ff370bc79442ca932c64e4e24f355e47374b74a61e12ecc",
s: "0x3734266e807bc84e99ec89a8fe9338c5620b5c2940e2839a2021d5beca311ada",
to: "0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7",
transactionIndex: 0,
v: "0x1b",
value: 1e+23
}
> web3.fromWei(eth.getBalance(eth.accounts[0]),"ether")
999900000

Desde el nodo 2 se observa que ha aumentado el saldo de la cuenta.

osboxes@osboxes:~/Desktop/giveliback$ geth attach ipc:/home/osboxes/Desktop/giveliback/qdata/node2/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-df4267a2/linux-amd64/go1.10
coinbase: 0x513f15ec9fc190cbc2ac25c6d6acdb58253f80d7
at block: 7 (Sun, 02 Sep 48500395455 15:28:15 CET)
datadir: /home/osboxes/Desktop/giveliback/qdata/node2
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 raft:1.0 rpc:1.0 txpool:1.0 web3:1.0

> web3.fromWei(eth.getBalance(eth.accounts[0]),"ether")
1000100000
> 

Aunque en este caso no se precisa, al disponer de una constelación se pueden probar de enviar transacciones privadas y contratos con partes privadas usando la misma para su encriptación. Al no estar disponible el módulo de quorum en la versión 2, en caso de realizar una transacción en la versión 1 que sólo fuera visible por un nodo se incluye el parámetro privateFor con un array con los valores de la claves públicas del nodos a los que se les permite visibilidad. Las claves públicas se encuentran en node.pub.

osboxes@osboxes:~/Desktop/giveliback$ head qdata/node2/node.pub 
v2IxD7YLAaLw+RrA++G3JB82KQp5cQJVYEbyrJKpjQM=
osboxes@osboxes:~/Desktop/giveliback$ 

Ejecutando una prueba desde el nodo 1 y que fuera visible por el nodo 2.

eth.sendTransaction({from:eth.accounts[0], privateFor:["v2IxD7YLAaLw+RrA++G3JB82KQp5cQJVYEbyrJKpjQM="]})

La red de dos nodos, pues, está disponible para transacciones, despliegues de contratos e interacción. El siguiente paso es la definición de las funciones que la ÐApp tendrá por medio de smart contracts en Solidity.