¡Los ojos temen, pero las manos pican!
En artículos anteriores, analizamos las tecnologías sobre las que se construyen las cadenas de bloques () y los casos que se pueden implementar con su ayuda (). ¡Ha llegado el momento de trabajar con las manos! Para implementar pilotos y PoC (Prueba de Concepto), prefiero usar nubes, ya que se puede acceder a ellas desde cualquier parte del mundo y, a menudo, no es necesario perder tiempo en la tediosa instalación del entorno, ya que hay configuraciones preinstaladas. Así que hagamos algo simple, como una red para transferir monedas entre participantes y la llamaremos modestamente Citcoin. Para esto, utilizaremos la nube de IBM y la blockchain universal Hyperledger Fabric. Primero, entendamos por qué Hyperledger Fabric se llama blockchain universal.

Hyperledger Fabric — blockchain universal
En términos generales, un sistema de información universal es:
- Un conjunto de servidores y un núcleo de software que ejecuta la lógica empresarial;
- Interfaces para interactuar con el sistema;
- Herramientas para registrar, autenticar y autorizar dispositivos / personas;
- Una base de datos que almacena datos operativos y archivados:

Se puede leer la versión oficial de qué es Hyperledger Fabric en , y en resumen, Hyperledger Fabric es una plataforma de código abierto que permite construir blockchains privados y ejecutar contratos inteligentes arbitrarios escritos en los lenguajes de programación JS y Go. Examinemos detalladamente la arquitectura de Hyperledger Fabric y asegurémonos de que es un sistema universal, en el que solo hay una especificidad en el almacenamiento y registro de datos. La especificidad radica en que los datos, como en todas las blockchains, se almacenan en bloques que se añaden a la blockchain solo si los participantes han llegado a un consenso y, después de la grabación, los datos no se pueden modificar o eliminar sin ser detectados.
Arquitectura de Hyperledger Fabric
En el esquema se presenta la arquitectura de Hyperledger Fabric:

Organizaciones — las organizaciones contienen nodos peer, es decir, la blockchain existe gracias al apoyo de las organizaciones. Diferentes organizaciones pueden unirse a un mismo canal.
Channel — estructura lógica que agrupa los nodos peer, así se define la blockchain. Hyperledger Fabric puede manejar simultáneamente varias blockchains con diferentes lógicas empresariales.
Proveedor de Servicios de Membresía (MSP) — es la CA (Autoridad de Certificación) para emitir identidades y asignar roles. Para crear un nodo es necesario interactuar con el MSP.
Nodos peer — verifican las transacciones, almacenan la cadena de bloques, ejecutan contratos inteligentes e interactúan con aplicaciones. Los pares tienen una identidad (certificado digital), que es emitido por una MSP. A diferencia de la red Bitcoin o Ethereum, donde todos los nodos son iguales, en Hyperledger Fabric los nodos desempeñan diferentes roles:
- El par puede ser par endosante (EP) y ejecutar contratos inteligentes.
- Par de compromiso (CP) — solo almacenan datos en la cadena de bloques y actualizan el 'Estado Mundial'.
- Par ancla (AP) — si varias organizaciones participan en la cadena de bloques, se utilizan pares ancla para la comunicación entre ellas. Cada organización debe tener uno o varios pares ancla. Con la ayuda de AP, cualquier par en la organización puede obtener información sobre todos los pares en otras organizaciones. Para la sincronización de información entre AP se utiliza .
- Par líder — si una organización tiene varios pares, solo el par líder recibirá bloques del servicio de ordenación y los entregará a los demás pares. El líder puede ser asignado estáticamente o ser seleccionado dinámicamente por los pares de la organización. Para la sincronización de información sobre los líderes también se utiliza el protocolo gossip.
Activos — entidades de valor que se almacenan en la cadena de bloques. Más concretamente, son datos clave-valor en formato JSON. Estos son los datos que se registran en la cadena de bloques 'Blockchain'. Tienen un historial almacenado en la cadena de bloques y un estado actual que se guarda en la base de datos 'Estado Mundial'. Las estructuras de datos se llenan de manera arbitraria dependiendo de las necesidades comerciales. No hay campos obligatorios, la única recomendación es que los activos deben tener un propietario y representar valor.
Libro mayor — se compone de la cadena de bloques 'Blockchain' y de la base de datos 'Estado Mundial', que contiene el estado actual de los activos. El Estado Mundial utiliza LevelDB o CouchDB.
Contrato inteligente — mediante contratos inteligentes se implementa la lógica empresarial del sistema. En Hyperledger Fabric, los contratos inteligentes se denominan chaincode. Con chaincode se definen los activos y las transacciones sobre ellos. Hablando en términos técnicos, los contratos inteligentes son módulos de programación escritos en lenguajes de programación JS o Go.
Política de endoso — para cada chaincode se pueden establecer políticas sobre cuántas y de quién se deben esperar confirmaciones para una transacción. Si no se establece una política, por defecto se utiliza: “la transacción debe ser confirmada por cualquier miembro (member) de cualquier organización en el canal”. Ejemplos de políticas:
- La transacción debe ser confirmada por cualquier administrador de la organización;
- Cualquier miembro (member) o cliente de la organización debe confirmarla;
- Cualquier peer de la organización debe confirmarla.
Servicio de ordenamiento — encapsula transacciones en bloques y las envía a los peers en el canal. Garantiza la entrega de mensajes a todos los peers en la red. Se utiliza un , para desarrollo y pruebas .
CallFlow

- La aplicación interactúa con Hyperledger Fabric, utilizando Go, Node.js o el SDK de Java;
- El cliente crea la transacción tx y la envía a los peers endorsing;
- El peer verifica la firma del cliente, ejecuta la transacción y envía la firma de respaldo de vuelta al cliente. El chaincode se ejecuta solo en los peers endorsing, y el resultado de su ejecución se envía a todos los peers. Este algoritmo de trabajo se llama consenso PBFT (Practical Byzantine Fault Tolerant). Se diferencia de en que el mensaje se envía y se espera la confirmación no de todos los participantes, sino solo de un conjunto específico;
- Después de que el cliente recibe el número de respuestas correspondiente a la política de respaldo, envía la transacción al servicio de ordenamiento;
- El servicio de ordenamiento forma un bloque y lo envía a todos los peers comprometedores. El servicio de ordenamiento garantiza la escritura secuencial de bloques, lo que excluye el llamado fork del libro mayor ();
- Los peers reciben el bloque, verifican nuevamente la política de respaldo, registran el bloque en la cadena de bloques y cambian el estado en la base de datos de "World state".
Es decir, se produce una separación de roles entre los nodos. Esto garantiza la escalabilidad y seguridad de la cadena de bloques:
- Los smart contracts (chaincode) son ejecutados por los peers endorsing. Esto garantiza la confidencialidad de los smart contracts, ya que no se almacenan en todos los participantes, sino solo en los peers endorsing.
- El servicio de ordenamiento debe funcionar rápidamente. Esto se asegura mediante que el servicio de ordenamiento solo forma bloques y los envía a un conjunto fijo de peers líderes.
- Los committing peers solo almacenan la cadena de bloques — puede haber muchos y no requieren de gran potencia ni de un funcionamiento instantáneo.
Para más detalles sobre las soluciones arquitectónicas de Hyperledger Fabric y por qué funciona de esta manera, se puede ver aquí: o aquí: .
Así que, Hyperledger Fabric es realmente un sistema versátil, con el que se puede:
- Implementar lógica de negocios arbitraria, utilizando el mecanismo de smart contracts;
- Registrar y obtener datos de una base de datos blockchain en formato JSON;
- Proporcionar y verificar acceso a la API, utilizando una Autoridad de Certificación.
Ahora que tenemos una mejor comprensión de las especificaciones de Hyperledger Fabric, ¡finalmente hagamos algo útil!
Desplegando blockchain
Planteamiento del problema
La tarea es implementar una red Citcoin con las siguientes funciones: crear una cuenta, obtener balance, recargar el saldo, transferir monedas de una cuenta a otra. Dibujemos el modelo de objeto, que luego se implementará en el contrato inteligente. Así que tendremos cuentas que se identifican por nombres (name) y contienen saldo (balance), y una lista de cuentas. Las cuentas y la lista de cuentas son, en términos de Hyperledger Fabric, activos. Por lo tanto, tienen un historial y un estado actual. Intentaré ilustrarlo claramente:

Las figuras superiores son el estado actual, que se almacena en la base 'World state'. Debajo de ellas, las figuras que muestran la historia, que se almacena en la blockchain. El estado actual de los activos cambia con transacciones. Un activo solo se puede cambiar en su totalidad, por lo que como resultado de ejecutar una transacción se crea un nuevo objeto y el valor actual del activo pasa a la historia.
IBM Cloud
Creamos una cuenta en . Para utilizar la plataforma blockchain, debe actualizarse a Pay-As-You-Go. Este proceso puede no ser rápido, ya que IBM solicita información adicional y la verifica manualmente. De lo positivo, puedo decir que IBM tiene buenos materiales de aprendizaje que permiten desplegar Hyperledger Fabric en su nube. Me gustó el siguiente ciclo de artículos y ejemplos:
A continuación se muestran capturas de pantalla de la plataforma Blockchain de IBM. Esto no es una guía para crear un blockchain, sino simplemente una demostración del alcance de la tarea. Así que, para nuestros propósitos, hacemos una Organización:

Creamos nodos: Orderer CA, Org1 CA, Orderer Peer:

Creamos usuarios:

Creamos un Canal y lo llamamos citcoin:

En esencia, un Canal es un blockchain, por lo que comienza con el bloque cero (Genesis block):

Escribimos un Smart Contract
/*
* Citcoin smart-contract v1.5 for Hyperledger Fabric
* (c) Alexey Sushkov, 2019
*/
'use strict';
const { Contract } = require('fabric-contract-api');
const maxAccounts = 5;
class CitcoinEvents extends Contract {
async instantiate(ctx) {
console.info('instantiate');
let emptyList = [];
await ctx.stub.putState('accounts', Buffer.from(JSON.stringify(emptyList)));
}
// Get all accounts
async GetAccounts(ctx) {
// Get account list:
let accounts = '{}'
let accountsData = await ctx.stub.getState('accounts');
if (accountsData) {
accounts = JSON.parse(accountsData.toString());
} else {
throw new Error('accounts not found');
}
return accountsData.toString()
}
// add a account object to the blockchain state identifited by their name
async AddAccount(ctx, name, balance) {
// this is account data:
let account = {
name: name,
balance: Number(balance),
type: 'account',
};
// create account:
await ctx.stub.putState(name, Buffer.from(JSON.stringify(account)));
// Add account to list:
let accountsData = await ctx.stub.getState('accounts');
if (accountsData) {
let accounts = JSON.parse(accountsData.toString());
if (accounts.length < maxAccounts)
{
accounts.push(name);
await ctx.stub.putState('accounts', Buffer.from(JSON.stringify(accounts)));
} else {
throw new Error('Max accounts number reached');
}
} else {
throw new Error('accounts not found');
}
// return object
return JSON.stringify(account);
}
// Sends money from Account to Account
async SendFrom(ctx, fromAccount, toAccount, value) {
// get Account from
let fromData = await ctx.stub.getState(fromAccount);
let from;
if (fromData) {
from = JSON.parse(fromData.toString());
if (from.type !== 'account') {
throw new Error('wrong from type');
}
} else {
throw new Error('Accout from not found');
}
// get Account to
let toData = await ctx.stub.getState(toAccount);
let to;
if (toData) {
to = JSON.parse(toData.toString());
if (to.type !== 'account') {
throw new Error('wrong to type');
}
} else {
throw new Error('Accout to not found');
}
// update the balances
if ((from.balance - Number(value)) >= 0 ) {
from.balance -= Number(value);
to.balance += Number(value);
} else {
throw new Error('From Account: not enought balance');
}
await ctx.stub.putState(from.name, Buffer.from(JSON.stringify(from)));
await ctx.stub.putState(to.name, Buffer.from(JSON.stringify(to)));
// define and set Event
let Event = {
type: "SendFrom",
from: from.name,
to: to.name,
balanceFrom: from.balance,
balanceTo: to.balance,
value: value
};
await ctx.stub.setEvent('SendFrom', Buffer.from(JSON.stringify(Event)));
// return to object
return JSON.stringify(from);
}
// get the state from key
async GetState(ctx, key) {
let data = await ctx.stub.getState(key);
let jsonData = JSON.parse(data.toString());
return JSON.stringify(jsonData);
}
// GetBalance
async GetBalance(ctx, accountName) {
let data = await ctx.stub.getState(accountName);
let jsonData = JSON.parse(data.toString());
return JSON.stringify(jsonData);
}
// Refill own balance
async RefillBalance(ctx, toAccount, value) {
// get Account to
let toData = await ctx.stub.getState(toAccount);
let to;
if (toData) {
to = JSON.parse(toData.toString());
if (to.type !== 'account') {
throw new Error('wrong to type');
}
} else {
throw new Error('Accout to not found');
}
// update the balance
to.balance += Number(value);
await ctx.stub.putState(to.name, Buffer.from(JSON.stringify(to)));
// define and set Event
let Event = {
type: "RefillBalance",
to: to.name,
balanceTo: to.balance,
value: value
};
await ctx.stub.setEvent('RefillBalance', Buffer.from(JSON.stringify(Event)));
// return to object
return JSON.stringify(from);
}
}
module.exports = CitcoinEvents;
Intuitivamente, aquí debería estar todo claro:
- Hay varias funciones (AddAccount, GetAccounts, SendFrom, GetBalance, RefillBalance) que será invocada por el programa demo utilizando la API de Hyperledger Fabric.
- Las funciones SendFrom y RefillBalance generan eventos (Event) que recibirá el programa demo.
- La función instantiate se llama una vez al instanciar un contrato inteligente. En realidad, se llama no una vez, sino cada vez que se cambia la versión del contrato inteligente. Por lo tanto, inicializar la lista con un array vacío es una mala idea, ya que al cambiar la versión del contrato inteligente perderemos la lista actual. Pero no hay problema, solo estoy aprendiendo).
- Las cuentas y la lista de cuentas (accounts) son estructuras de datos JSON. Se utiliza JS para manipular los datos.
- El valor actual del activo se puede obtener mediante la función getState, y actualizarse mediante putState.
- Al crear una cuenta se llama a la función AddAccount, en la que se compara el número máximo de cuentas en la blockchain (maxAccounts = 5). Y aquí hay un error (¿lo notaron?), que causa un crecimiento infinito del número de cuentas. Hay que evitar este tipo de errores).
Luego, cargamos el contrato inteligente en el canal e instanciamos:

Vemos la transacción para establecer el contrato inteligente:

Vemos los detalles de nuestro canal:

Como resultado, obtenemos el siguiente esquema de la red blockchain en la nube de IBM. También en el esquema hay un programa de demostración que se ejecuta en la nube de Amazon en un servidor virtual (hablaremos de él en la siguiente sección):

Creación de GUI para las llamadas a la API de Hyperledger Fabric
Hyperledger Fabric tiene una API que se puede utilizar para:
- Crear un canal;
- Conectar un peer al canal;
- Instalar e instanciar contratos inteligentes en el canal;
- Llamar a transacciones;
- Solicitar información en la blockchain.
Desarrollo de la aplicación
En nuestro programa de demostración, utilizaremos la API solo para llamar a transacciones y solicitar información, ya que ya hemos realizado los otros pasos utilizando la plataforma de blockchain de IBM. Vamos a escribir la GUI utilizando el stack tecnológico estándar: Express.js + Vue.js + Node.js. Se podría escribir un artículo separado sobre cómo comenzar a crear aplicaciones web modernas. Aquí dejo un enlace a la serie de conferencias que más me gustó: . Como resultado, se ha creado una aplicación cliente-servidor con una interfaz gráfica familiar en el estilo Material Design de Google. La API REST entre el cliente y el servidor consta de varias llamadas:
- HyperledgerDemo/v1/init — inicializar la blockchain;
- HyperledgerDemo/v1/accounts/list — obtener la lista de todas las cuentas;
- HyperledgerDemo/v1/account?name=Bob&balance=100 — crear la cuenta de Bob;
- HyperledgerDemo/v1/info?account=Bob — obtener información sobre la cuenta de Bob;
- HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — transferir dos monedas de Bob a Alice;
- HyperledgerDemo/v1/disconnect — cerrar la conexión con la blockchain.
La descripción de la API con ejemplos se encuentra en — un programa ampliamente conocido para probar APIs HTTP.
Aplicación demo en la nube de Amazon
La aplicación la subí a Amazon, ya que IBM aún no ha podido actualizar mi cuenta y permitir la creación de servidores virtuales. Como un toque final, añadí el dominio: . Mantendré el servidor encendido un poco, luego lo apagaré, ya que se acumulan costos de alquiler, y las monedas citcoin aún no están listadas en el intercambio) En el artículo incluiré capturas de pantalla de la demo para que quede claro cómo funciona. La aplicación demo puede:
- Inicializar la blockchain;
- Crear una cuenta (pero actualmente no se puede crear una nueva, ya que se ha alcanzado el número máximo de cuentas establecido en el contrato inteligente);
- Obtener la lista de cuentas;
- Transferir monedas citcoin entre Alice, Bob y Alex;
- Obtener eventos (pero en este momento no se pueden mostrar eventos, por lo que en la interfaz se indica que no se admiten eventos por simplicidad);
- Registrar acciones.
Primero inicializamos la blockchain:

Luego creamos nuestra cuenta, sin escatimar en el saldo:

Obtenemos la lista de todas las cuentas disponibles:

Elegimos el remitente y el destinatario, obteniendo sus saldos. Si el remitente y el destinatario son la misma persona, se recargará su saldo:

En el registro, seguimos la ejecución de las transacciones:

Eso es todo con el programa demo. A continuación, podemos ver nuestra transacción en la blockchain:

Y la lista general de transacciones:

Con esto hemos completado con éxito la implementación del PoC para crear la red Citcoin. ¿Qué más se necesita para que Citcoin se convierta en una red completa para transferir monedas? Muy poco:
- En la etapa de creación de la cuenta, implementar la generación de clave privada / pública. La clave privada debe almacenarse en el usuario de la cuenta, la pública en la blockchain.
- Realizar la transferencia de monedas, en la que se use la clave pública en lugar del nombre para identificar al usuario.
- Cifrar las transacciones que van del usuario al servidor con su clave privada.
Conclusión
Hemos implementado la red Citcoin con funciones: añadir cuenta, obtener saldo, recargar su cuenta, transferir monedas de una cuenta a otra. Entonces, ¿qué nos costó construir el PoC?
- Es necesario estudiar la blockchain en general y Hyperledger Fabric en particular;
- Aprender a utilizar las nubes de IBM o Amazon;
- Aprender el lenguaje de programación JS y algún framework web;
- Si necesitamos almacenar algunos datos fuera de la cadena de bloques, en una base de datos separada, deberíamos aprender a integrarnos, por ejemplo, con PostgreSQL;
- Y, por último, pero no menos importante, ¡sin conocimientos de Linux no se va a ninguna parte en el mundo moderno!)
Claro, no es ciencia espacial, ¡pero tendrás que esforzarte!
Códigos fuente en GitHub
He dejado los archivos fuente en . Descripción breve del repositorio:
El catálogo "servidor" — servidor de Node.js
El catálogo "client" — cliente de Node.js
El catálogo "blockchain" (los valores de los parámetros y las claves, por supuesto, no funcionan y se proporcionan solo como ejemplo):
- contract — código fuente del contrato inteligente
- wallet — claves del usuario para utilizar el API de Hyperledger Fabric.
- *.cds — versiones compiladas de contratos inteligentes
- *.json archivos — ejemplos de archivos de configuración para utilizar el API de Hyperledger Fabric
¡Es solo el principio!
Fuente: habr.com
