Siguiendo con esta serie, en el presente artículo vamos a ser ambiciosos y analizaremos y clasificaremos los tipos de redes y de nodos (o clientes) existentes. Posteriormente nos adentraremos en los modelos y algoritmos de consenso más conocidos.
Después, tal como ya indiqué en la primera entrega, vamos a seguir estudiando nuestro cliente Geth; concretamente su avanzada CLI. En este punto aparecerá la JSON-RPC API (aunque ya la hemos vislumbrado anteriormente) y la presentaremos formalmente.
Para finalizar, haremos una introducción a las famosas DApps y cómo interactúan con el backend, es decir, con la red blockchain, esto es, mediante la Web3 API.
Empecemos
Las redes como Bitcoin, Ethereum, Litecoin o Monero se denominan Públicas, ya que cualquiera puede unirse y participar; están abiertas y contienen algún mecanismo de incentivo para animar a más participantes a unirse a la misma.
Por otro lado, redes como Hyperledger o R3 (Corda), son redes Privadas (financiadas muchas por consorcios de empresas) en las que tanto el proceso de participación y/o el proceso de consenso están limitados.
Existen dos conceptos que pueden combinarse con los dos tipos de redes anteriores y que en muchos foros los utilizan como sinónimos, pero que realmente no lo son. Me refiero a las Redes no permisionadas (permissionless networks), y las Redes permisionadas (permissioned networks), respectivamente.
En las redes no permisionadas, las transacciones pueden ser vistas por cualquier participante, y cualquiera puede lanzar también transacciones (y desplegar contratos). En las redes permisionadas, los participantes pueden ver solamente las transacciones relevantes para ellos o podrían ver todas las transacciones, así como lanzar a la red o no, nuevas transacciones (desplegar contratos).
Aunque podríamos pensar que una red pública es no permisionada, y una privada es permisionada, vemos que hay una diferencia semántica: en una red privada, necesito aprobación para participar en ella, y aun así solamente podría tener permisos para realizar ciertas operaciones/transacciones.
Otro asunto a destacar es que en las redes Blockchain, cada nodo u ordenador que se conecta juega un rol particular, y según la implementación de dicha red y según el algoritmo de consenso utilizado, tienen determinados nombres. Por ejemplo, nodo regular o de trabajo, nodo validador, nodo bootstrap (o de arranque), nodo minero, nodo sealer (o sellador) ...
En las redes Bitcoin principalmente existen los nodos completos (full nodes) que poseen toda la cadena de bloques (unos 160GB a día de hoy, según https://blockchain.info/charts/blocks-size?timespan=all), y verifican las transacciones que contienen para asegurarse de que estén completamente validados. Este tipo de nodos, con estas comprobaciones y validaciones, podrá confiar en la integridad de la red sin confiar expresamente en el minero.
También este tipo de nodos ofrecen otros tipos de servicios como transmitir y difundir bloques a otros nodos, presentar datos históricos, y ayudar a los nodos ligeros (lightweight nodes) que no tienen toda la cadena descargada (solamente las cabeceras de los bloques), a encontrar y procesar sus propias transacciones.
Consenso y Algoritmos
Llegado a este punto, vamos a profundizar en otra área importante que son los algoritmos de consenso. Con ello, tendremos un conocimiento bastante completo de toda esta tecnología.
Recordemos que el Consenso se refiere al proceso por el cual aseguramos la red Blockchain y por el cual validamos y ordenamos (evitando el doble gasto) las transacciones creadas por los participantes.
En las redes públicas cualquier nodo puede tener este rol de verificación, actualización e incorporación de bloques: basta con que reúna los requisitos y recursos necesarios. En cambio, en las redes privadas, este rol dependerá de los participantes, de la configuración propuesta y de los reglamentos definidos en dicha red: no se requiere un consenso mayoritario como en las redes públicas.
Hay múltiples modelos de consenso, pero hay dos típicos y archiconocidos que son Proof of Work y Proof of Stake.
Se basa en algoritmos Hash, siendo en Bitcoin calificado como Hashcash y en Ethereum apodado como Ethash (análogo al anterior, pero evita el uso de procesadores ASIC debido al uso intensivo de movimientos de datos en memoria). Coloquialmente, es conocido por el nombre de ‘minería’ y los nodos que se dedican a ello como ‘mineros’. Es un algoritmo costoso de resolver, pero muy fácil de verificar.
Funcionalmente es como sigue:
Como vemos, el uso de estas funciones matemáticas consume en el software de minado mucha energía y es razonable pensar que los ‘mineros’ se concentren en regiones geográficas con bajo coste eléctrico. Así mismo, podemos observar que el nodo con mayor capacidad de cómputo será el que acredite la transacción y cobre la recompensa (incentivo) asociada por este uso y gasto en sus recursos.
Este proceso no se basa en cómputo si no en el mayor porcentaje de posesión de tokens emitidos: digamos que depende de la riqueza (cantidad de monedas que tiene uno acumulado en un monedero conectado a la red) o de su participación (incluso buen comportamiento) en la red. Los nodos se conocen como ‘validadores’ y por ejemplo si el nodo X posee 2 monedas y el nodo Y tiene 1 moneda, entonces X tiene el doble de probabilidad de que sea llamado para validar un bloque de transacciones. Aquí se usan procesadores genéricos que consumen menor energía que en PoW.
Hay dos variantes notables:
Prueba de Importancia, Proof of Importance o PoI, que intenta evitar únicamente la acumulación de tokens y añade una ponderación a los participantes según su verdadera contribución en el sistema.
Prueba de Participación Delegada, Delegated Proof of Stake o DPoS, en la que ciertos ‘validadores’ pueden delegar sus derechos de participación en el consenso, votando a un determinado ‘validador’ o ‘representante’.
Esos dos algoritmos (PoW y PoS) se centran en redes públicas; en redes privadas y permisionadas se suele utilizar otros más eficientes: al fin y al cabo, aquí los participantes tienen la capacidad de restringir o asignar quién puede participar en el mecanismo de consenso, por lo que la potencia de cómputo y la acumulación de moneda no entran en juego o no son criterios para considerar. Revisemos los más llamativos.
Básicamente, mediante el envío de mensajes entre los nodos ‘validadores’ se intenta averiguar si existe alguno que falla o perturba a la red para aislarlo del sistema.
Existen versiones de implementación como son:
Practical Byzantine Fault Tolerance o PBFT
Federated Byzantine Agreement o FBA
Istambul Byzantine Fault Tolerance o IBFT
- Proof of Authority o PoA
Es un protocolo donde las transacciones son validadas por cuentas pre-aprobadas, algo así como los ‘administradores’ del sistema. Lógicamente, esta naturaleza centralizada hará que sea una implementación muy poco probable en una red pública.
Al nodo ‘validador’, en este contexto, se denomina ‘sealer’ o sellador.
Ethereum puede ser configurado para usar PoA, a través del protocolo Clique (ver más abajo, en TestNets).
Así que implementar una red privada o un consorcio basado en Ethereum y PoA es una solución muy válida: segura en cuanto a si confías en los nodos ‘sealers’, rápida en generación de bloques y computacionalmente poco intensiva.
Desarrollado por Intel, este consenso es un híbrido entre una lotería y un orden de llegada: cada nodo ‘validador’ recibe un tiempo de espera aleatorio y el ‘validador’ que tenga el menor tiempo de espera para un bloque de transacciones en particular, será elegido líder y creará el siguiente bloque.
Tras estos mecanismos y tipos de redes, vamos ahora a centrarnos en Ethereum que posee varias redes para pruebas. Son las llamadas redes de test o TestNets. En su red de producción o MainNet, actualmente se usa PoW (bajo el protocolo Ghost) y se desea migrar a PoS (nuevo protocolo Casper) con la ventaja principal de reducir el consumo energético.
Redes principales de Test:
Ropsten
-Basada en PoW
-La más análoga al actual entorno de producción
-Usada por los clientes Geth y Parity(*)
-Chain ID: 3
-Generación de bloques entre 20 a 30 sg.
Kovan
-Basada en PoA
-Nos es soportada por Geth, solamente por Parity(*)
-Chain ID: 42
-Generación de bloques alrededor de 5 a 10 sg.
-ETH no puede ser minado, debe ser solicitado desde un faucet(**)
Rinkeby
-Basada en PoA
-Solamente soportada por Geth
-Chain ID: 4
-Generación de bloques alrededor de 15 sg, pero es configurable
-ETH no puede ser minado, debe ser solicitado desde un faucet(**)
Morden
-Está descatalogada
-Chain ID: 2
La Ethereum pública MainNet tiene como identificador Chain ID el número 1.
(*) Parity es un cliente Ethereum implementado en Rust que posee además una Interfaz Gráfica. El objetivo es ser más ligero, rápido y seguro que Geth.
(**) Un faucet o grifo es generalmente una web que te proporciona criptomoneda a cambio de realizar algún comentario en una red social promocionando la misma.
La CLI de Geth
Geth es el cliente Ethereum escrito en Go o golang, necesario para participar o interactuar con la red.
Posee una Línea de Comandos con muchas posibilidades. Vamos a realizar un pequeño estudio de todo este potencial.
La sintaxis general es: > geth [options] command [command options] [arguments...]
Las [options]
se clasifican en varios grupos:
- Configuración.
- APIs.
- Consola.
- Cuentas.
- Gas.
- Minería.
- Logging.
- Depuración.
- Rendimiento y EMV.
- Networking.
- Opciones en desarrollo o experimentales.
- Miscelánea.
Los command
en:
- Gestión de ficheros y Base de Datos.
- Inicialización ('init').
- Cuentas
- Consola interactiva (*).
(*) La consola interactiva es otro interfaz que proporciona Geth, y que veremos en el siguiente apartado. Es una API Javascript para interactuar también con la red y en el fondo es un envoltorio de la API de comunicación JSON-RPC.
Antes de todo, un pequeño gráfico sobre cómo trabajamos con el cliente Geth a nivel de comunicaciones, redes y APIs.
Opciones de Configuración
--datadir "/chain-data"
El directorio para escribir los datos de la cadena.
--keystore "/key-data"
El directorio para el almacén de claves (por defecto dentro del directorio anterior datadir).
--networkid "value"
El ChainID que vimos anteriormente (por defecto, 1 MainNet).
--testnet
Se conecta al nodo de la TestNet Ropsnet.
--rinkeby
Se conecta al nodo de la TestNet Rinkeby.
--syncmode "fast"
Modo de sincronización(*)
--identity "MyNode"
Nombre del nodo personalizado
--dev
Modo desarrollador (red privada de un único nodo con una cuenta predefinida y saldo; con múltiples opciones de depuración y logging). En contraposición con --testnet.
(*) Modo de sincronización:
‘full’ tengo el estado completo de la cadena: cabeceras y cuerpos de los bloques y todo el historial de las transacciones.
‘fast’ tengo todos los bloques (cabeceras y cuerpos), pero solamente almacena los estados recientes de las transacciones (las últimas 1024).
‘light’ tiene solamente las cabeceras de los bloques. Parecido a un snapshot de la red. Es el más rápido en sincronizar, pero el menos seguro.
Opciones de APIs
JSON-RPC
--rpc
Habilita JSON-RPC.
--rpcaddr "host"
Por defecto localhost.
--rpcport "port"
Por defecto 8545.
--rpcapi "value, ..., "
APIs ofrecidas sobre el interfaz JSON-RPC (por defecto eth, web3 y net).
--rpccorsdomain "dominios, ..., " o "*"
Lista separada de dominios para evitar problemas con CORS (se añaden las cabeceras oportunas).
WS-RPC
--ws
Habilita WS-RPC.
--wsaddr "host"
Por defecto localhost.
--wsport "port"
Por defecto 8546.
--wsapi "value, ..., "
APIs ofrecidas sobre el interfaz WS-RPC.
--wsorigins "origen"
Origen para aceptar WS solicitudes.
IPC-RPC
--ipcdisable
Deshabilita IPC-RPC (por defecto está habilitado).
--ipcpath "geth.ipc"
Ruta y nombre del fichero para el socket/pipe IPC.
Opciones de Consola
--jspath "loadScript"
Path de los scripts JS, por defecto "."
--exec "JS statement"
Ejecuta una sentencia de la API Javascript (usado con los comandos 'console' y 'attach').
--preload "files, ..., "
Ficheros JS para precarga, usado con el comando 'console'
Ejemplo 1. > geth --verbosity "0" --datadir "./chain-data" --testnet --exec "personal.listAccounts" console["0x... "]
Ejemplo 2. Supongamos que utilidades.js contiene funciones JS, entonces dentro de la 'console' puedes llamarlas. > geth --verbosity "0" --datadir "./chain-data" --testnet --preload "./scripts/utilidades.js" console
hacerAlgo()
Opciones de Cuentas
--unlock "lista de cuentas separadas por coma o índices del array de cuentas"
Las cuentas están por defecto bloqueadas.
--password "password file"
Fichero de contraseñas (para una entrada no interactiva).
Opciones de Minería
--mine
Habilita el minado.
--minerthreads "hilos"
Número de hilos para minar.
--etherbase "cuenta"
Dirección pública para cargar las recompensas del minado (por defecto la primera de la lista o índice "0").
--targetgaslimit "value"
Máximo Gas gastado por bloque.
--gasprice "value"
Precio del gas mínimo para aceptar minar una transacción.
--extradata "datos"
Añade datos extra al bloque (no a la transacción). Hecho por el minero.
Opciones de Gas
--gpoblocks "value"
Número de bloques recientes para verificar el precio de Gas (por defecto 10).
--gpopercentile "value"
El precio del gas sugerido es el percentil dado de un conjunto de precios de gas de transacciones recientes (por defecto, 50).
Opciones de Logging
--verbosity "value"
0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (por defecto: 3).
--fakepow
Deshabilita la verificación de PoW.
--trace "filename"
Volcado de las trazas a un fichero.
Opciones de Redes
--bootnodes "value"
Los pares pueden ser añadidos manualmente o mediante bootnodes (nodos de arranque). Aquí se enumeran los enodes (separados por coma) que funcionan como nodos de arranque y realizan el descubrimiento de pares.
--port "value"
Puerto de comunicación entre nodos de la red (por defecto 30303).
--maxpeers "value"
Número máximo de pares (nodos) de la red (por defecto 25, si es 0 se deshabilita la red).
--nodiscover
Deshabilita el mecanismo de descubrimiento de pares para este nodo.
--netrestrict "value"
Restringe los intentos de conexión entrantes a las IPs o máscaras CIDR indicadas.
Comandos de Cuentas
account new
Crea una cuenta.
account list
Lista todas las cuentas.
account update
Actualiza la contraseña de la cuenta.
account import
Importa el fichero de claves.
Ejemplo 1. > geth --datadir "./chain-data" --testnet account list
Account #0: xxxxxxx
Account #1: yyyyyy
Ejemplo 2. > geth --datadir "./chain-data" --testnet account new
Your new account...
Passphrase:
...
Address: ...
Comandos de Consola interactiva
console
Inicia una sesión interactiva de consola.
attach "dirección del pipe" o "http://...:8545" o "ws://...:8546"
Se conecta a un cliente ya corriendo.
Consola JavaScript de Geth
Como acabamos de ver, Geth sirve APIs usando HTTP, WebSocket y otros protocolos para que las aplicaciones externas se comuniquen con él, y por extensión se comuniquen con la red Blockchain.
Geth implementa en su consola interactiva un entorno de ejecución (runtime) JavaScript, con el cual podemos ejecutar código JavaScript y por otro lado podemos invocar a la API de Gestión o Management API que proporciona diversos componentes para usarlos: admin, personal, miner, txpool, debug y web3.
Tres enlaces con información imprescindible son:
A continuación, revisaremos las opciones más llamativas de esta API JavaScript.
admin
Gestión de nodos y pares
admin.nodeInfo
admin.datadir
admin.peers
admin.peers.length
admin.peers[0]
admin.addPeers(url en formato enode://)
Añade el peer pero no persiste su información: mejor usar un fichero de nodos estáticos ‘static-nodes.json’.
Gestión de la cadena de bloques
admin.exportChain(filename)
admin.imporChain(filename)
Desde un nodo sincronizado podemos exportar la cadena de bloques para importarla en un nuevo nodo. Es un método mucho más rápido para preparar un nuevo nodo.
Gestión de interfaces RPC
admin.startRPC(...)
admin.startRPC("localhost", 8545, "web3")
admin.stopRPC()
admin.startWS(...)
admin.stopWS()
personal
Gestión de cuentas
personal.listAccounts
personal.newAccount("password")
personal.lockAccount(...)
personal.unlockAccount(...)
personal.sendTransaction(txn, "password")
txn = {from: "addressFrom", to: "addressTo", value: "valueInWei"}
miner
Gestión de minería
miner.start(numero_hilos)
miner.stop()
txpool
Contadores, resumen y detalle de las transacciones en el pool
txpool.status
txpool.inspect
txpool.content
Las transacciones pendientes son transacciones válidas y que están preparadas para ser procesadas e incluidas en el bloque (a la espera de ser minadas).
Las transacciones encoladas son transacciones cuyo ‘nonce’ no está en secuencia y pasarán a pendiente cuando su orden sea correcto. El ‘nonce’ es un contador incremental y único de transacción por cuenta o dirección origen.
debug
Información de depuración
debug.verbosity(nivel de log)
debug.dumpBlock(numero)
debug.traceBlockByNumber(numero)
debug.traceBlockByHash(hash)
debug.traceTransaction(txn)
Rendimiento
debug.gcStats()
debug.memStats()
debug.cpuProfile(fichero, segundos)
debug.startCPUProfile(fichero)
debug.stopCPUProfile()
Depuración a nivel de lenguaje Go
debug.goTrace(fichero, segundos)
debug.stacks()
debug.vmodule(identificador)
web3
Es la API Web3 que sustenta el desarrollo de DApps y que veremos en el siguiente módulo.
web3.eth.accounts
eth.accounts
web3.net.listening
net.listening
web3.fromWei(eth.getBalance(eth.coinbase), "ether")
Se puede abreviar si usamos los objetos que contiene eth, net, db y shh.
Introducción a las DApps
Una DApp o Aplicación descentralizada (Decentralized Application), es una aplicación web cuyo backend corre sobre una red P2P (peer-to-peer o red de pares) descentralizada y su código fuente es open source. En contraste a las aplicaciones web estándar, que tienen los recursos centralizados y son propiedad de organizaciones y empresas.
Cuando trabajamos con DApps, los programadores necesitarán acceder a la información de la cadena de bloques, así como a las transacciones. Para ello, está la API JSON-RPC que proporciona la red (cliente Geth), pero existe un wrapper o librería JavaScript que conecta tu navegador a la Blockchain y por tanto te ayuda a crear y trabajar con DApps. Se llama API Web3 y la librería web3.js.
Esta librería Web3 nos proporciona un subconjunto de funcionalidades respecto a las ofrecidas por la Consola interactiva JavaScript de Geth, vista en la sección anterior.
La API Web3 es como si fuera otro servidor corriendo en otro puerto y mientras que el navegador se conecta al servidor web por el puerto 80/443, lee todos los recursos (imágenes, CSS, ...) y parsea JavaScript, Web3 se conecta por el puerto 8545 (por defecto) a la JSON-RPC.
Web3 proporciona los siguientes objetos:
- eth Para interactuar con la Blockchain Ethereum.
- net Para interactuar con el estado de la red y el nodo.
- db Para interactuar con la base de datos local LevelDB.
- shh Para interactuar con la mensajería P2P usando el protocolo estándar Whisper.
Ejemplo. var Web3 = require('web3');
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545));
var balance = web3.eth.getBalance(web3.eth.coinbase);
Una prueba de DApp es el plugin de Chrome llamado MetaMask que enlaza la red Ethereum con el navegador sin necesidad de tener un nodo o cliente local corriendo.
Para finalizar, una reflexión: Geth (y la red Ethereum) proporciona una API JSON-RPC para interactuar. Pero, para no estar lidiando con los mensajes JSON, los verbos HTTP y las cabeceras, nos equipa con una API JavaScript y un modelo de objetos completo, que podemos usar desde la Consola interactiva de Geth o desde nuestras aplicaciones externas mediante la librería web3.js.
Muchas funcionalidades son comunes entre la CLI de Geth y la API JavaScript (tanto desde la Consola, como la API Web3).
Espero que os haya resultado interesante este post y os animo a profundizar con los conceptos y herramientas aquí mostradas.
Si te ha gustado te invito a que nos veamos en el próximo evento Blockchain Spain, donde habrá interesantes charlas sobre casos de uso y detalles técnicos. Y por supuesto, puedes estar al día de próximos posts siguiendo a nuestra cuenta de Twitter.
¡Hasta la próxima!