cliente-blockchain-bo v1.1.3
Biblioteca de interacción con cadena de bloques hyperledger fabric
Colección de funciones para interactuar con la cadena de bloques. Usa las librerías fabric-client o fabric-network (a elección).
Instalación
npm install --save cliente-blockchain-bo
Requerimientos
Se conecta a una cadena de bloques mediante un contrato inteligente instalado en la blockchain, el nombre del contrato inteligente (chaincode) y las credenciales para la interacción se encuentran en archivos de configuración y llaves.
- El chaincode se especifica en el archivo
config.yaml
. La ubicación de este archivo se especifica al inicializar esta biblioteca (ver ejemplos de inicialización). - Las credenciales de conexión se encuentran en los directorios
crypto-config
. La ubicación de estos directorios se especifica en el archivoconfig.yaml
. - Para conectarse se requieren credenciales de usuarios enrolados que se guardan en carpetas.
- En caso de usar fabric-network (recomendado) en una carpeta
wallet
- En caso de usar fabric-client (experimental) en una carpeta
hfc-key-store
- En caso de usar fabric-network (recomendado) en una carpeta
Inicialización
let blockchainClienteBo = require("cliente-blockchain-bo");
// Inicializar
const bcCliente = await blockchainClienteBo.iniciar("<ruta absoluta del archivo de configuracion config.yaml>");
if (bcCliente.iniciado === false) {
// caso erroneo
// console.log(boCliente.errors);
}
// ejemplo suponiendo que el archivo de configuracion config.yaml esta en el directorio actual
bcCliente = await blockchainClienteBo.iniciar(process.cwd() + '/config.yaml');
Uso
Fabric Network
Usando la biblioteca de inserción/consulta recomendada.
Introducir datos
try {
let respuesta = await boClient.escribirRegistroFabricNetwork({
fcn: 'funcion_fcn',
chaincode: 'nombre_del_chaincode'
},
// argumentos (todos deben ser string)
'argumento 1',
'argumento 2',
// ...
'argumento n'
);
console.log('Respuesta en bruto:', respuesta);
console.log('Respuesta toString:', respuesta.toString());
} catch (e) {
console.log(' --- error al escribir datos\n', e);
}
Consultar datos
try {
resp = await boClient.consultarRegistroFabricNetwork({
chaincode: 'nombre del chaincode', // opcional
fcn: 'funcion fcn', // opcional
timeout: 20000, // opcional
}, 'identificador es obligatorio');
console.log('respuesta en bruto::', resp);
console.log('respuesta toString()', resp.toString());
} catch (e) {
console.log('error al consultar:', e);
}
Fabric Client
Para escribir, la biblioteca selecciona al azar el peer al que envía la petición para escritura.
// escribir
let resultado;
try {
// se indican parametros
res = await boClient.escribirRegistro({
identificador: 'identificador unico',
datos: datos, // array de datos que se van a introducir (de acuerdo al chaincode)
timeout: 12000, // maximo tiempo de espera en este caso 12s. (opcional)
autogenerarIdentificador: false, // Para obviar el identificador y usar el transaction_id como identificador unico (opcional)
maximoIntentos: 3, // Indica maximo numero de intentos (opcional)
chaincode: 'nombre', // nombre de chaincode especifico (opcional)
fcn: 'funcionChaincode', // funcion del chaincode a usar (opcional)
nArgIdentificador: <number> // Para mandar el identificador como numero de argumento indicado (opcional)
});
if (resultado.finalizado !== true) {
// error
}
} catch(e) {
// Error general
}
// consultar
let consulta;
try {
consulta = await bcCliente.consultarRegistro('<identificador>');
// otras formas de consultar
// La siguiente especifica un timeout de 5000 ms, 5 intentos como maximo, usa el chaincode 'n1' y la funcion 'fcn1' de ese chaincode
// consulta = await bcCliente.consultarRegistro('<identificador>', 5000, 5, 'n1', 'fcn1');
if (consulta.finalizado !== true) {
// caso erroneo
}
// mostrar datos
console.log(consulta.resultado);
/* NOTA: consulta.resultado es la respuesta del chaincode y el tipo de respuesta esta definida por este. Si la respuesta fuese un buffer si es conveniente se puede convertir a cadena con:
consulta.resultado.toString(); */
} catch(e) {
// .. caso erroeno..
}
// consultar detalles de una transaccion
let detalles;
try {
detalles = await bcCliente.consultarDetallesTransaccion(<transaction_id>)
} catch (e) {
// caso erroneo
}
// Tambien existen los metodos que usan fabric-network: consultarRegistroFabricNetwork, escribirRegistroFabricNetwork, que hacen tareas similares *experimentales*.
Archivo de configuración
El <archivo de configuracion>
debe tener la estructura que se proporciona en el archivo config.sample.yaml
.
Estructura del archivo de configuración:
---
name: nombre-de-la-red
version: 1.0.0
user: Nombre de usuario enrolado
channel: Nombre del canal
# se recomiendan rutas absolutas
walletPath: "<ruta del directorio wallet>" # si se usa fabric-network
hfcKeyStorePath: "<ruta del directorio hfc-keysotre>" # si se usa fabric-client
chaincode:
name: Nombre del chaincode
fcns:
introducir: createDoc
consultar: queryDoc
fcn1: nombreOtraFuncion1
fcn2: nombreOtraFuncion2
client:
organization: Nombre de la Organizacion
connection:
timeout:
peer:
endorser: '300'
organizations:
Dominio:
mspid: OrganizacionMSP
peers:
- peer0.dominio.gob.bo
- peer1.dominio.gob.bo
- peer2.dominio.gob.bo
certificateAuthorities:
- ca.gob.bo
peers:
peer0.dominio.gob.bo:
url: grpcs://<ip-peer0>:<puerto>
tlsCACerts:
# recomendado usar rutas absolutas
path: "/home/alguien/crypto-config/peerOrganizations/dominio.gob.bo/tlsca/tlsca.dominio.gob.bo-cert.pem"
grpcOptions:
ssl-target-name-override: peer0.dominio.gob.bo
peer1.dominio.gob.bo:
url: grpcs://<ip-peer1>:<puerto>
tlsCACerts:
# recomendado usar rutas absolutas
path: "/home/alguien/crypto-config/peerOrganizations/dominio.gob.bo/tlsca/tlsca.dominio.gob.bo-cert.pem"
grpcOptions:
ssl-target-name-override: peer1.dominio.gob.bo
peer2.dominio.gob.bo:
url: grpcs://<ip-peer2>:<puerto>
tlsCACerts:
# recomendado usar rutas absolutas
path: "/home/alguien/crypto-config/peerOrganizations/dominio.gob.bo/tlsca/tlsca.dominio.gob.bo-cert.pem"
grpcOptions:
ssl-target-name-override: peer2.dominio.gob.bo
certificateAuthorities:
ca.gob.bo:
url: https://<ip-ca>:<puerto>
caName: ca.gob.bo
tlsCACerts:
# recomendado usar rutas absolutas
path: "/home/alguien/crypto-config/peerOrganizations/dominio.gob.bo/tlsca/tlsca.dominio.gob.bo-cert.pem"
httpOptions:
verify: false
Nota sobre resolución de dominios
En algunos casos es necesario hacer que el sistema resuelva la dirección IP de los peers por ejemplo si peer0.dominio.gob.bo
apunta a la ip: 192.168.1.100
, en Debian se puede asociar ips y dominios agregando en /etc/hosts
, por ejemplo:
192.168.1.100 peer0.dominio.gob.bo
192.168.1.102 peer1.dominio.gob.bo
192.168.1.104 peer2.dominio.gob.bo
192.168.1.103 ca.gob.bo
Ejemplos de uso básico
Primero se require instalar todas las dependencias y ajustar el archivo config.yaml
.
Fabric network (recomendado)
Script test-fabric-network.js
, se ejecuta con node test-fabric-network.js
.
const blockchainClienteBo = require('cliente-blockchain-bo');
async function main() {
const boClient = await blockchainClienteBo.iniciar(process.cwd() + '/config.yaml');
console.log('boCLiente Object Inicializacion:::', boClient);
let resp;
// // escribir enviando un identificador
const chaincodeName = 'chaincodeprueba';
let n = Math.random()*99999999999;
let hashDatos = '702996976c';
let ci = '900900900';
let nombres = 'Lucaz Monsar';
let primer_apellido = 'Tritón';
let segundo_apellido = 'Montes';
let descripcion = 'Registro-de-pruebas';
let extra = 'Algoritmo Hash Usado: sha256';
let tx_id;
// // escribir datos
console.log(`Escribir por chaincode ${chaincodeName}:`);
console.log('-----------------------------------------------------------------------------');
try {
resp = await boClient.escribirRegistroFabricNetwork({
autogenerarIdentificador: true,
timeout: 12000,
fcn: 'initDoc',
chaincode: chaincodeName
},
// argumentos
hashDatos,
ci,
nombres,
primer_apellido,
segundo_apellido,
descripcion,
extra
);
console.log('------------ detalles respuesta');
console.log(resp);
console.log('--- to String');
console.log(resp);
let respuesta = JSON.parse(resp.toString());
tx_id = respuesta.TxId; // transaction_id
console.log('--- JSON.parse .... to String');
console.log(JSON.parse(resp.toString()));
console.log(respuesta.Timestamp);
console.log(respuesta.TxID);
console.log('--- respuesta.Value');
console.log(respuesta.Value);
} catch (e) {
console.log(' --- error al escribir datos\n', e);
}
console.log('------ end escribir ---');
setTimeout(async () => {
// consultar datos
console.log(`\nConsultar por CI (historico) ${ci}`);
console.log('-----------------------------------------------------------------------------');
try {
resp = await boClient.consultarRegistroFabricNetwork(
{
chaincode: chaincodeName,
fcn: 'queryDocsByCI'
},
ci // identificador para la consulta
);
console.log('---- resultado (completo)');
console.log(resp);
console.log('---- res.resultado.toString()');
console.log(resp.toString(), '\n\n');
let parsed = JSON.parse(resp.toString());
console.log(parsed);
console.log('ci:', parsed[0].Record.ci);
console.log('hashdatos:', parsed[0].Record.hashdatos);
console.log('transactionid:', parsed[0].Record.transactionid);
console.log('nombres:', parsed[0].Record.nombres);
} catch (e) {
console.log('Error consulta:', e);
}
console.log(`\nConsultar por hash (historico) ${hashDatos}`);
console.log('-----------------------------------------------------------------------------');
try {
resp = await boClient.consultarRegistroFabricNetwork({
chaincode: chaincodeName,
fcn: 'getHistoryForDoc'
}, hashDatos);
console.log('---- resultado (completo)');
console.log(resp);
console.log('---- res.resultado.toString()');
console.log(resp.toString(), '\n\n');
console.log(JSON.parse(resp.toString()));
} catch (e) {
console.log('Error consulta:', e);
}
process.exit(0);
}, 3550);
}
main();
Fabric client
Script test-fabric-client.js
, se ejecuta con node test-fabric-client-js
.
const blockchainClienteBo = require('cliente-blockchain-bo');
async function main() {
const boClient = await blockchainClienteBo.iniciar(process.cwd() + '/config.yaml');
let res;
// escribir enviando un identificador
console.log('\n---------- escribir con identificador dado ------------------');
let identificador = Math.random()*1000000; // numero al azar
// siempre un array, en este caso un array de un elemento.
let datos = [JSON.stringify({
'argumento1', // arg1
45121.1141, // arg2
razon: `Pruebas con id ${identificador}` // arg3
})];
let tx_id;
try {
res = await boClient.escribirRegistro({
identificador: 'test' + identificador,
datos: datos,
timeout: 12000,
chaincode: 'c1',
fcn: 'createDoc'
});
console.log('Escribir resultado::::::::::', res);
tx_id = res.resultado.tx_id; // transaction_id
} catch (e) {
console.log('escribir error::::::', e);
}
console.log('consultar:::::::::::::');
try {
res = await boClient.consultarRegistro('test' + identificador);
console.log('resultado.toString():::::', res.resultado.toString());
} catch (e) {
console.log('consultar error xxxxx', e);
}
// escribir usando el transaction_id como identificador
try {
console.log('\n escribir usando transaction_id como identificador ------------------------');
datos = [JSON.stringify({
razon: `Introduciendo datos con transaction_id como identificador`,
adicional: `test ${Math.random()*1000000}`
})];
res = await boClient.escribirRegistro({
datos: datos,
timeout: 12000,
autogenerarIdentificador: true
});
console.log('Escribir resultado:::::::::::::::::', res);
tx_id = res.resultado.tx_id;
} catch (e) {
console.log('escribir error::::::', e);
}
console.log('consultar por transaction_id:::::::::::::');
try {
res = await boClient.consultarRegistro(tx_id);
console.log('resultado.toString() ::::::', res.resultado.toString());
} catch (e) {
console.log('consultar error xxxxx', e);
}
console.log('consultar detalles de una trasaccion::::::::::::::::::');
try {
const util = require('util');
res = await boClient.consultarDetallesTransaccion(tx_id);
console.log('res.resultado\n:', util.inspect(res.resultado, {showHidden: false, depth: null}));
} catch (e) {
console.log('Error:', e);
}
process.exit(0);
}
main();